about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-03-16 20:48:40 +0000
committerbors <bors@rust-lang.org>2019-03-16 20:48:40 +0000
commit7cf074a1e655ac07d04d045667278fa1a9970b93 (patch)
treed2509e198c34af3e41cce2dbde186635384a418a
parent2c8bbf50db0ef90a33f986ba8fc2e1fe129197ff (diff)
parent2fd4cbb3f283b903f55444bb585d38a2539f4e8d (diff)
downloadrust-7cf074a1e655ac07d04d045667278fa1a9970b93.tar.gz
rust-7cf074a1e655ac07d04d045667278fa1a9970b93.zip
Auto merge of #58899 - petrochenkov:derval2, r=estebank
Do not accidentally treat multi-segment meta-items as single-segment

Fixes https://github.com/rust-lang/rust/issues/55168 and many other regressions from https://github.com/rust-lang/rust/pull/50030

Basically, attributes like `#[any::prefix::foo]` were commonly interpreted as `#[foo]` due to `name()` successfully returning the last segment (this applies to nested things as well `#[attr(any::prefix::foo)]`).
-rw-r--r--src/librustc/hir/check_attr.rs12
-rw-r--r--src/librustc/ich/impls_syntax.rs11
-rw-r--r--src/librustc/lint/levels.rs45
-rw-r--r--src/librustc/lint/mod.rs2
-rw-r--r--src/librustc/middle/lib_features.rs6
-rw-r--r--src/librustc/middle/stability.rs11
-rw-r--r--src/librustc/session/config.rs5
-rw-r--r--src/librustc/traits/on_unimplemented.rs14
-rw-r--r--src/librustc_driver/lib.rs2
-rw-r--r--src/librustc_incremental/assert_dep_graph.rs8
-rw-r--r--src/librustc_incremental/assert_module_sources.rs2
-rw-r--r--src/librustc_incremental/persist/dirty_clean.rs8
-rw-r--r--src/librustc_lint/builtin.rs4
-rw-r--r--src/librustc_lint/unused.rs8
-rw-r--r--src/librustc_metadata/native_libs.rs2
-rw-r--r--src/librustc_mir/dataflow/mod.rs2
-rw-r--r--src/librustc_passes/layout_test.rs6
-rw-r--r--src/librustc_plugin/load.rs4
-rw-r--r--src/librustc_resolve/build_reduced_graph.rs25
-rw-r--r--src/librustc_typeck/collect.rs12
-rw-r--r--src/librustdoc/clean/cfg.rs32
-rw-r--r--src/librustdoc/clean/mod.rs12
-rw-r--r--src/librustdoc/core.rs3
-rw-r--r--src/librustdoc/html/render.rs14
-rw-r--r--src/librustdoc/visit_ast.rs7
-rw-r--r--src/libsyntax/ast.rs9
-rw-r--r--src/libsyntax/attr/builtin.rs152
-rw-r--r--src/libsyntax/attr/mod.rs154
-rw-r--r--src/libsyntax/config.rs4
-rw-r--r--src/libsyntax/ext/expand.rs20
-rw-r--r--src/libsyntax/ext/tt/macro_rules.rs11
-rw-r--r--src/libsyntax/feature_gate.rs49
-rw-r--r--src/libsyntax/mut_visit.rs11
-rw-r--r--src/libsyntax/parse/attr.rs13
-rw-r--r--src/libsyntax/parse/lexer/mod.rs12
-rw-r--r--src/libsyntax/parse/parser.rs6
-rw-r--r--src/libsyntax/print/pprust.rs12
-rw-r--r--src/libsyntax/test.rs7
-rw-r--r--src/libsyntax_ext/deriving/custom.rs8
-rw-r--r--src/libsyntax_ext/deriving/generic/mod.rs9
-rw-r--r--src/libsyntax_ext/proc_macro_decls.rs67
-rw-r--r--src/libsyntax_ext/proc_macro_server.rs8
-rw-r--r--src/libsyntax_ext/test.rs2
-rw-r--r--src/libsyntax_pos/symbol.rs13
-rw-r--r--src/test/ui/parser/raw/raw-literal-self.rs5
-rw-r--r--src/test/ui/parser/raw/raw-literal-self.stderr8
-rw-r--r--src/test/ui/parser/raw/raw-literal-underscore.rs5
-rw-r--r--src/test/ui/parser/raw/raw-literal-underscore.stderr8
-rw-r--r--src/test/ui/proc-macro/attribute.rs94
-rw-r--r--src/test/ui/proc-macro/attribute.stderr108
-rw-r--r--src/test/ui/proc-macro/invalid-punct-ident-3.stderr2
-rw-r--r--src/test/ui/proc-macro/shadow-builtin.rs14
-rw-r--r--src/test/ui/proc-macro/shadow-builtin.stderr8
53 files changed, 565 insertions, 511 deletions
diff --git a/src/librustc/hir/check_attr.rs b/src/librustc/hir/check_attr.rs
index 86f7e149964..8602d159ba9 100644
--- a/src/librustc/hir/check_attr.rs
+++ b/src/librustc/hir/check_attr.rs
@@ -166,7 +166,7 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
         // ```
         let hints: Vec<_> = item.attrs
             .iter()
-            .filter(|attr| attr.name() == "repr")
+            .filter(|attr| attr.check_name("repr"))
             .filter_map(|attr| attr.meta_item_list())
             .flatten()
             .collect();
@@ -177,7 +177,7 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
         let mut is_transparent = false;
 
         for hint in &hints {
-            let name = if let Some(name) = hint.name() {
+            let name = if let Some(name) = hint.ident_str() {
                 name
             } else {
                 // Invalid repr hint like repr(42). We don't check for unrecognized hints here
@@ -185,7 +185,7 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
                 continue;
             };
 
-            let (article, allowed_targets) = match &*name.as_str() {
+            let (article, allowed_targets) = match name {
                 "C" | "align" => {
                     is_c |= name == "C";
                     if target != Target::Struct &&
@@ -233,7 +233,7 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
                 _ => continue,
             };
             self.emit_repr_error(
-                hint.span,
+                hint.span(),
                 item.span,
                 &format!("attribute should be applied to {}", allowed_targets),
                 &format!("not {} {}", article, allowed_targets),
@@ -242,7 +242,7 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
 
         // Just point at all repr hints if there are any incompatibilities.
         // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
-        let hint_spans = hints.iter().map(|hint| hint.span);
+        let hint_spans = hints.iter().map(|hint| hint.span());
 
         // Error on repr(transparent, <anything else>).
         if is_transparent && hints.len() > 1 {
@@ -313,7 +313,7 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
 
     fn check_used(&self, item: &hir::Item, target: Target) {
         for attr in &item.attrs {
-            if attr.name() == "used" && target != Target::Static {
+            if attr.check_name("used") && target != Target::Static {
                 self.tcx.sess
                     .span_err(attr.span, "attribute must be applied to a `static` variable");
             }
diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs
index f34423ccca6..496ccc888b6 100644
--- a/src/librustc/ich/impls_syntax.rs
+++ b/src/librustc/ich/impls_syntax.rs
@@ -197,7 +197,8 @@ impl<'a> HashStable<StableHashingContext<'a>> for [ast::Attribute] {
         let filtered: SmallVec<[&ast::Attribute; 8]> = self
             .iter()
             .filter(|attr| {
-                !attr.is_sugared_doc && !hcx.is_ignored_attr(attr.name())
+                !attr.is_sugared_doc &&
+                !attr.ident().map_or(false, |ident| hcx.is_ignored_attr(ident.name))
             })
             .collect();
 
@@ -224,7 +225,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for ast::Attribute {
                                           hcx: &mut StableHashingContext<'a>,
                                           hasher: &mut StableHasher<W>) {
         // Make sure that these have been filtered out.
-        debug_assert!(!hcx.is_ignored_attr(self.name()));
+        debug_assert!(!self.ident().map_or(false, |ident| hcx.is_ignored_attr(ident.name)));
         debug_assert!(!self.is_sugared_doc);
 
         let ast::Attribute {
@@ -359,15 +360,13 @@ fn hash_token<'a, 'gcx, W: StableHasherResult>(
     }
 }
 
-impl_stable_hash_for_spanned!(::syntax::ast::NestedMetaItemKind);
-
-impl_stable_hash_for!(enum ::syntax::ast::NestedMetaItemKind {
+impl_stable_hash_for!(enum ::syntax::ast::NestedMetaItem {
     MetaItem(meta_item),
     Literal(lit)
 });
 
 impl_stable_hash_for!(struct ::syntax::ast::MetaItem {
-    ident,
+    path,
     node,
     span
 });
diff --git a/src/librustc/lint/levels.rs b/src/librustc/lint/levels.rs
index 3c6635c0341..19e899ceb42 100644
--- a/src/librustc/lint/levels.rs
+++ b/src/librustc/lint/levels.rs
@@ -194,7 +194,7 @@ impl<'a> LintLevelsBuilder<'a> {
             struct_span_err!(sess, span, E0452, "malformed lint attribute")
         };
         for attr in attrs {
-            let level = match Level::from_str(&attr.name().as_str()) {
+            let level = match attr.ident_str().and_then(|name| Level::from_str(name)) {
                 None => continue,
                 Some(lvl) => lvl,
             };
@@ -221,7 +221,7 @@ impl<'a> LintLevelsBuilder<'a> {
                 match item.node {
                     ast::MetaItemKind::Word => {}  // actual lint names handled later
                     ast::MetaItemKind::NameValue(ref name_value) => {
-                        if item.ident == "reason" {
+                        if item.path == "reason" {
                             // found reason, reslice meta list to exclude it
                             metas = &metas[0..metas.len()-1];
                             // FIXME (#55112): issue unused-attributes lint if we thereby
@@ -255,13 +255,13 @@ impl<'a> LintLevelsBuilder<'a> {
             }
 
             for li in metas {
-                let word = match li.word() {
-                    Some(word) => word,
-                    None => {
-                        let mut err = bad_attr(li.span);
+                let meta_item = match li.meta_item() {
+                    Some(meta_item) if meta_item.is_word() => meta_item,
+                    _ => {
+                        let mut err = bad_attr(li.span());
                         if let Some(item) = li.meta_item() {
                             if let ast::MetaItemKind::NameValue(_) = item.node {
-                                if item.ident == "reason" {
+                                if item.path == "reason" {
                                     err.help("reason in lint attribute must come last");
                                 }
                             }
@@ -270,26 +270,27 @@ impl<'a> LintLevelsBuilder<'a> {
                         continue;
                     }
                 };
-                let tool_name = if let Some(lint_tool) = word.is_scoped() {
-                    if !attr::is_known_lint_tool(lint_tool) {
+                let tool_name = if meta_item.path.segments.len() > 1 {
+                    let tool_ident = meta_item.path.segments[0].ident;
+                    if !attr::is_known_lint_tool(tool_ident) {
                         span_err!(
                             sess,
-                            lint_tool.span,
+                            tool_ident.span,
                             E0710,
                             "an unknown tool name found in scoped lint: `{}`",
-                            word.ident
+                            meta_item.path
                         );
                         continue;
                     }
 
-                    Some(lint_tool.as_str())
+                    Some(tool_ident.as_str())
                 } else {
                     None
                 };
-                let name = word.name();
+                let name = meta_item.path.segments.last().expect("empty lint name").ident.name;
                 match store.check_lint_name(&name.as_str(), tool_name) {
                     CheckLintNameResult::Ok(ids) => {
-                        let src = LintSource::Node(name, li.span, reason);
+                        let src = LintSource::Node(name, li.span(), reason);
                         for id in ids {
                             specs.insert(*id, (level, src));
                         }
@@ -300,7 +301,7 @@ impl<'a> LintLevelsBuilder<'a> {
                             Ok(ids) => {
                                 let complete_name = &format!("{}::{}", tool_name.unwrap(), name);
                                 let src = LintSource::Node(
-                                    Symbol::intern(complete_name), li.span, reason
+                                    Symbol::intern(complete_name), li.span(), reason
                                 );
                                 for id in ids {
                                     specs.insert(*id, (level, src));
@@ -322,18 +323,18 @@ impl<'a> LintLevelsBuilder<'a> {
                                     lint,
                                     lvl,
                                     src,
-                                    Some(li.span.into()),
+                                    Some(li.span().into()),
                                     &msg,
                                 );
                                 err.span_suggestion(
-                                    li.span,
+                                    li.span(),
                                     "change it to",
                                     new_lint_name.to_string(),
                                     Applicability::MachineApplicable,
                                 ).emit();
 
                                 let src = LintSource::Node(
-                                    Symbol::intern(&new_lint_name), li.span, reason
+                                    Symbol::intern(&new_lint_name), li.span(), reason
                                 );
                                 for id in ids {
                                     specs.insert(*id, (level, src));
@@ -360,11 +361,11 @@ impl<'a> LintLevelsBuilder<'a> {
                                                               lint,
                                                               level,
                                                               src,
-                                                              Some(li.span.into()),
+                                                              Some(li.span().into()),
                                                               &msg);
                         if let Some(new_name) = renamed {
                             err.span_suggestion(
-                                li.span,
+                                li.span(),
                                 "use the new name",
                                 new_name,
                                 Applicability::MachineApplicable
@@ -383,12 +384,12 @@ impl<'a> LintLevelsBuilder<'a> {
                                                 lint,
                                                 level,
                                                 src,
-                                                Some(li.span.into()),
+                                                Some(li.span().into()),
                                                 &msg);
 
                         if let Some(suggestion) = suggestion {
                             db.span_suggestion(
-                                li.span,
+                                li.span(),
                                 "did you mean",
                                 suggestion.to_string(),
                                 Applicability::MachineApplicable,
diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs
index c01b0ae2ccc..b1ff66eb64f 100644
--- a/src/librustc/lint/mod.rs
+++ b/src/librustc/lint/mod.rs
@@ -724,7 +724,7 @@ pub fn struct_lint_level<'a>(sess: &'a Session,
 pub fn maybe_lint_level_root(tcx: TyCtxt<'_, '_, '_>, id: hir::HirId) -> bool {
     let attrs = tcx.hir().attrs_by_hir_id(id);
     for attr in attrs {
-        if Level::from_str(&attr.name().as_str()).is_some() {
+        if attr.ident_str().and_then(Level::from_str).is_some() {
             return true;
         }
     }
diff --git a/src/librustc/middle/lib_features.rs b/src/librustc/middle/lib_features.rs
index 9d15b0543cb..237b00db575 100644
--- a/src/librustc/middle/lib_features.rs
+++ b/src/librustc/middle/lib_features.rs
@@ -65,9 +65,9 @@ impl<'a, 'tcx> LibFeatureCollector<'a, 'tcx> {
                 for meta in metas {
                     if let Some(mi) = meta.meta_item() {
                         // Find the `feature = ".."` meta-item.
-                        match (&*mi.name().as_str(), mi.value_str()) {
-                            ("feature", val) => feature = val,
-                            ("since", val) => since = val,
+                        match (mi.ident_str(), mi.value_str()) {
+                            (Some("feature"), val) => feature = val,
+                            (Some("since"), val) => since = val,
                             _ => {}
                         }
                     }
diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs
index 0a2a375e1b2..2664d6eaa28 100644
--- a/src/librustc/middle/stability.rs
+++ b/src/librustc/middle/stability.rs
@@ -194,11 +194,12 @@ impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> {
         } else {
             // Emit errors for non-staged-api crates.
             for attr in attrs {
-                let tag = attr.name();
-                if tag == "unstable" || tag == "stable" || tag == "rustc_deprecated" {
-                    attr::mark_used(attr);
-                    self.tcx.sess.span_err(attr.span(), "stability attributes may not be used \
-                                                         outside of the standard library");
+                if let Some(tag) = attr.ident_str() {
+                    if tag == "unstable" || tag == "stable" || tag == "rustc_deprecated" {
+                        attr::mark_used(attr);
+                        self.tcx.sess.span_err(attr.span, "stability attributes may not be used \
+                                                           outside of the standard library");
+                    }
                 }
             }
 
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index ff18678f091..0ae7c10cf56 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -1839,7 +1839,7 @@ pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String
 
             match &mut parser.parse_meta_item() {
                 Ok(meta_item) if parser.token == token::Eof => {
-                    if meta_item.ident.segments.len() != 1 {
+                    if meta_item.path.segments.len() != 1 {
                         error!("argument key must be an identifier");
                     }
                     match &meta_item.node {
@@ -1850,7 +1850,8 @@ pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String
                             error!("argument value must be a string");
                         }
                         MetaItemKind::NameValue(..) | MetaItemKind::Word => {
-                            return (meta_item.name(), meta_item.value_str());
+                            let ident = meta_item.ident().expect("multi-segment cfg key");
+                            return (ident.name, meta_item.value_str());
                         }
                     }
                 }
diff --git a/src/librustc/traits/on_unimplemented.rs b/src/librustc/traits/on_unimplemented.rs
index fc0058a1df5..d0acaf674ae 100644
--- a/src/librustc/traits/on_unimplemented.rs
+++ b/src/librustc/traits/on_unimplemented.rs
@@ -107,7 +107,7 @@ impl<'a, 'gcx, 'tcx> OnUnimplementedDirective {
             {
                 if let Some(items) = item.meta_item_list() {
                     if let Ok(subcommand) =
-                        Self::parse(tcx, trait_def_id, &items, item.span, false)
+                        Self::parse(tcx, trait_def_id, &items, item.span(), false)
                     {
                         subcommands.push(subcommand);
                     } else {
@@ -118,7 +118,7 @@ impl<'a, 'gcx, 'tcx> OnUnimplementedDirective {
             }
 
             // nothing found
-            parse_error(tcx, item.span,
+            parse_error(tcx, item.span(),
                         "this attribute must have a valid value",
                         "expected value here",
                         Some(r#"eg `#[rustc_on_unimplemented(message="foo")]`"#));
@@ -177,10 +177,12 @@ 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(),
-                        c.value_str().map(|s| s.as_str().to_string())
-                    ))
+                    c.ident_str().map_or(false, |name| {
+                        options.contains(&(
+                            name.to_string(),
+                            c.value_str().map(|s| s.as_str().to_string())
+                        ))
+                    })
                 }) {
                     debug!("evaluate: skipping {:?} due to condition", command);
                     continue
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index c7b6f37fe29..6d708b8d458 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -683,7 +683,7 @@ impl RustcDefaultCalls {
 
                     let mut cfgs = sess.parse_sess.config.iter().filter_map(|&(name, ref value)| {
                         let gated_cfg = GatedCfg::gate(&ast::MetaItem {
-                            ident: ast::Path::from_ident(ast::Ident::with_empty_ctxt(name)),
+                            path: ast::Path::from_ident(ast::Ident::with_empty_ctxt(name)),
                             node: ast::MetaItemKind::Word,
                             span: DUMMY_SP,
                         });
diff --git a/src/librustc_incremental/assert_dep_graph.rs b/src/librustc_incremental/assert_dep_graph.rs
index 9fe9a60b9aa..c964f4cb19b 100644
--- a/src/librustc_incremental/assert_dep_graph.rs
+++ b/src/librustc_incremental/assert_dep_graph.rs
@@ -99,12 +99,12 @@ impl<'a, 'tcx> IfThisChanged<'a, 'tcx> {
     fn argument(&self, attr: &ast::Attribute) -> Option<ast::Name> {
         let mut value = None;
         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()),
+            match list_item.ident() {
+                Some(ident) if list_item.is_word() && value.is_none() =>
+                    value = Some(ident.name),
                 _ =>
                     // FIXME better-encapsulate meta_item (don't directly access `node`)
-                    span_bug!(list_item.span(), "unexpected meta-item {:?}", list_item.node),
+                    span_bug!(list_item.span(), "unexpected meta-item {:?}", list_item),
             }
         }
         value
diff --git a/src/librustc_incremental/assert_module_sources.rs b/src/librustc_incremental/assert_module_sources.rs
index 5cbb412052d..2c83501c86f 100644
--- a/src/librustc_incremental/assert_module_sources.rs
+++ b/src/librustc_incremental/assert_module_sources.rs
@@ -153,7 +153,7 @@ impl<'a, 'tcx> AssertModuleSource<'a, 'tcx> {
                     return value;
                 } else {
                     self.tcx.sess.span_fatal(
-                        item.span,
+                        item.span(),
                         &format!("associated value expected for `{}`", name));
                 }
             }
diff --git a/src/librustc_incremental/persist/dirty_clean.rs b/src/librustc_incremental/persist/dirty_clean.rs
index 633e61a0034..5551cf6b3b6 100644
--- a/src/librustc_incremental/persist/dirty_clean.rs
+++ b/src/librustc_incremental/persist/dirty_clean.rs
@@ -430,13 +430,13 @@ impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> {
             if DepNode::has_label_string(label) {
                 if out.contains(label) {
                     self.tcx.sess.span_fatal(
-                        item.span,
+                        item.span(),
                         &format!("dep-node label `{}` is repeated", label));
                 }
                 out.insert(label.to_string());
             } else {
                 self.tcx.sess.span_fatal(
-                    item.span,
+                    item.span(),
                     &format!("dep-node label `{}` not recognized", label));
             }
         }
@@ -576,13 +576,13 @@ fn expect_associated_value(tcx: TyCtxt<'_, '_, '_>, item: &NestedMetaItem) -> as
     if let Some(value) = item.value_str() {
         value
     } else {
-        let msg = if let Some(name) = item.name() {
+        let msg = if let Some(name) = item.ident_str() {
             format!("associated value expected for `{}`", name)
         } else {
             "expected an associated value".to_string()
         };
 
-        tcx.sess.span_fatal(item.span, &msg);
+        tcx.sess.span_fatal(item.span(), &msg);
     }
 }
 
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 7a7c49e4604..80c5eeeeac3 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -760,7 +760,7 @@ impl LintPass for DeprecatedAttr {
 impl EarlyLintPass for DeprecatedAttr {
     fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) {
         for &&(n, _, _, ref g) in &self.depr_attrs {
-            if attr.name() == n {
+            if attr.ident_str() == Some(n) {
                 if let &AttributeGate::Gated(Stability::Deprecated(link, suggestion),
                                              ref name,
                                              ref reason,
@@ -831,7 +831,7 @@ impl UnusedDocComment {
 
             let span = sugared_span.take().unwrap_or_else(|| attr.span);
 
-            if attr.name() == "doc" {
+            if attr.check_name("doc") {
                 let mut err = cx.struct_span_lint(UNUSED_DOC_COMMENTS, span, "unused doc comment");
 
                 err.span_label(
diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs
index 7d61547527f..648cae30da6 100644
--- a/src/librustc_lint/unused.rs
+++ b/src/librustc_lint/unused.rs
@@ -267,19 +267,21 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes {
             }
         }
 
-        let name = attr.name();
+        let name = attr.ident_str();
         if !attr::is_used(attr) {
             debug!("Emitting warning for: {:?}", attr);
             cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute");
             // Is it a builtin attribute that must be used at the crate level?
             let known_crate = BUILTIN_ATTRIBUTES.iter()
-                .find(|&&(builtin, ty, ..)| name == builtin && ty == AttributeType::CrateLevel)
+                .find(|&&(builtin, ty, ..)| {
+                    name == Some(builtin) && ty == AttributeType::CrateLevel
+                })
                 .is_some();
 
             // Has a plugin registered this attribute as one that must be used at
             // the crate level?
             let plugin_crate = plugin_attributes.iter()
-                .find(|&&(ref x, t)| name == &**x && AttributeType::CrateLevel == t)
+                .find(|&&(ref x, t)| name == Some(x) && AttributeType::CrateLevel == t)
                 .is_some();
             if known_crate || plugin_crate {
                 let msg = match attr.style {
diff --git a/src/librustc_metadata/native_libs.rs b/src/librustc_metadata/native_libs.rs
index 23898387cba..80786992cd9 100644
--- a/src/librustc_metadata/native_libs.rs
+++ b/src/librustc_metadata/native_libs.rs
@@ -76,7 +76,7 @@ impl<'a, 'tcx> ItemLikeVisitor<'tcx> for Collector<'a, 'tcx> {
                         k => {
                             struct_span_err!(self.tcx.sess, m.span, E0458,
                                       "unknown kind: `{}`", k)
-                                .span_label(item.span, "unknown kind").emit();
+                                .span_label(item.span(), "unknown kind").emit();
                             cstore::NativeUnknown
                         }
                     };
diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs
index 03f8ac67436..af2cd8f979a 100644
--- a/src/librustc_mir/dataflow/mod.rs
+++ b/src/librustc_mir/dataflow/mod.rs
@@ -146,7 +146,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.ident));
+                        &format!("{} attribute requires a path", item.path));
                     return None;
                 }
             }
diff --git a/src/librustc_passes/layout_test.rs b/src/librustc_passes/layout_test.rs
index 7fe3f5a36d8..373bcf7f0e2 100644
--- a/src/librustc_passes/layout_test.rs
+++ b/src/librustc_passes/layout_test.rs
@@ -53,9 +53,7 @@ impl<'a, 'tcx> VarianceTest<'a, 'tcx> {
                 // The `..` are the names of fields to dump.
                 let meta_items = attr.meta_item_list().unwrap_or_default();
                 for meta_item in meta_items {
-                    let name = meta_item.word().map(|mi| mi.name().as_str());
-                    let name = name.as_ref().map(|s| &s[..]).unwrap_or("");
-
+                    let name = meta_item.ident_str().unwrap_or("");
                     match name {
                         "abi" => {
                             self.tcx
@@ -88,7 +86,7 @@ impl<'a, 'tcx> VarianceTest<'a, 'tcx> {
 
                         _ => {
                             self.tcx.sess.span_err(
-                                meta_item.span,
+                                meta_item.span(),
                                 &format!("unrecognized field name `{}`", name),
                             );
                         }
diff --git a/src/librustc_plugin/load.rs b/src/librustc_plugin/load.rs
index 1b88cf05f40..bd11e0ce802 100644
--- a/src/librustc_plugin/load.rs
+++ b/src/librustc_plugin/load.rs
@@ -56,10 +56,10 @@ pub fn load_plugins(sess: &Session,
 
             for plugin in plugins {
                 // plugins must have a name and can't be key = value
-                match plugin.name() {
+                match plugin.ident_str() {
                     Some(name) if !plugin.is_value_str() => {
                         let args = plugin.meta_item_list().map(ToOwned::to_owned);
-                        loader.load_plugin(plugin.span, &name.as_str(), args.unwrap_or_default());
+                        loader.load_plugin(plugin.span(), name, args.unwrap_or_default());
                     },
                     _ => call_malformed_plugin_attribute(sess, attr.span),
                 }
diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs
index 5c9927011a7..12b87b9c9aa 100644
--- a/src/librustc_resolve/build_reduced_graph.rs
+++ b/src/librustc_resolve/build_reduced_graph.rs
@@ -463,10 +463,9 @@ impl<'a> Resolver<'a> {
                 if let Some(attr) = attr::find_by_name(&item.attrs, "proc_macro_derive") {
                     if let Some(trait_attr) =
                             attr.meta_item_list().and_then(|list| list.get(0).cloned()) {
-                        if let Some(ident) = trait_attr.name().map(Ident::with_empty_ctxt) {
-                            let sp = trait_attr.span;
+                        if let Some(ident) = trait_attr.ident() {
                             let def = Def::Macro(def.def_id(), MacroKind::ProcMacroStub);
-                            self.define(parent, ident, MacroNS, (def, vis, sp, expansion));
+                            self.define(parent, ident, MacroNS, (def, vis, ident.span, expansion));
                         }
                     }
                 }
@@ -812,14 +811,14 @@ impl<'a> Resolver<'a> {
                             break;
                         }
                         MetaItemKind::List(nested_metas) => for nested_meta in nested_metas {
-                            match nested_meta.word() {
-                                Some(word) => single_imports.push((word.name(), word.span)),
-                                None => ill_formed(nested_meta.span),
+                            match nested_meta.ident() {
+                                Some(ident) if nested_meta.is_word() => single_imports.push(ident),
+                                _ => ill_formed(nested_meta.span()),
                             }
                         }
                         MetaItemKind::NameValue(..) => ill_formed(meta.span),
                     }
-                    None => ill_formed(attr.span()),
+                    None => ill_formed(attr.span),
                 }
             }
         }
@@ -850,23 +849,23 @@ impl<'a> Resolver<'a> {
                 self.legacy_import_macro(ident.name, imported_binding, span, allow_shadowing);
             });
         } else {
-            for (name, span) in single_imports.iter().cloned() {
-                let ident = Ident::with_empty_ctxt(name);
+            for ident in single_imports.iter().cloned() {
                 let result = self.resolve_ident_in_module(
                     ModuleOrUniformRoot::Module(module),
                     ident,
                     MacroNS,
                     None,
                     false,
-                    span,
+                    ident.span,
                 );
                 if let Ok(binding) = result {
-                    let directive = macro_use_directive(span);
+                    let directive = macro_use_directive(ident.span);
                     self.potentially_unused_imports.push(directive);
                     let imported_binding = self.import(binding, directive);
-                    self.legacy_import_macro(name, imported_binding, span, allow_shadowing);
+                    self.legacy_import_macro(ident.name, imported_binding,
+                                             ident.span, allow_shadowing);
                 } else {
-                    span_err!(self.session, span, E0469, "imported macro not found");
+                    span_err!(self.session, ident.span, E0469, "imported macro not found");
                 }
             }
         }
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index afe658767f3..10e9613bf21 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -2326,7 +2326,7 @@ fn from_target_feature(
         if !item.check_name("enable") {
             let msg = "#[target_feature(..)] only accepts sub-keys of `enable` \
                        currently";
-            tcx.sess.span_err(item.span, &msg);
+            tcx.sess.span_err(item.span(), &msg);
             continue;
         }
 
@@ -2336,7 +2336,7 @@ fn from_target_feature(
             None => {
                 let msg = "#[target_feature] attribute must be of the form \
                            #[target_feature(enable = \"..\")]";
-                tcx.sess.span_err(item.span, &msg);
+                tcx.sess.span_err(item.span(), &msg);
                 continue;
             }
         };
@@ -2352,7 +2352,7 @@ fn from_target_feature(
                          this target",
                         feature
                     );
-                    let mut err = tcx.sess.struct_span_err(item.span, &msg);
+                    let mut err = tcx.sess.struct_span_err(item.span(), &msg);
 
                     if feature.starts_with("+") {
                         let valid = whitelist.contains_key(&feature[1..]);
@@ -2387,7 +2387,7 @@ fn from_target_feature(
                 feature_gate::emit_feature_err(
                     &tcx.sess.parse_sess,
                     feature_gate.as_ref().unwrap(),
-                    item.span,
+                    item.span(),
                     feature_gate::GateIssue::Language,
                     &format!("the target feature `{}` is currently unstable", feature),
                 );
@@ -2549,7 +2549,7 @@ fn codegen_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> Codegen
                 } else {
                     span_err!(
                         tcx.sess.diagnostic(),
-                        items[0].span,
+                        items[0].span(),
                         E0535,
                         "invalid argument"
                     );
@@ -2583,7 +2583,7 @@ fn codegen_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> Codegen
                 } else if list_contains_name(&items[..], "speed") {
                     OptimizeAttr::Speed
                 } else {
-                    err(items[0].span, "invalid argument");
+                    err(items[0].span(), "invalid argument");
                     OptimizeAttr::None
                 }
             }
diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs
index 45e1ea2d3a3..69445451503 100644
--- a/src/librustdoc/clean/cfg.rs
+++ b/src/librustdoc/clean/cfg.rs
@@ -8,7 +8,7 @@ use std::fmt::{self, Write};
 use std::ops;
 
 use syntax::symbol::Symbol;
-use syntax::ast::{MetaItem, MetaItemKind, NestedMetaItem, NestedMetaItemKind, LitKind};
+use syntax::ast::{MetaItem, MetaItemKind, NestedMetaItem, LitKind};
 use syntax::parse::ParseSess;
 use syntax::feature_gate::Features;
 
@@ -41,9 +41,9 @@ pub struct InvalidCfgError {
 impl Cfg {
     /// Parses a `NestedMetaItem` into a `Cfg`.
     fn parse_nested(nested_cfg: &NestedMetaItem) -> Result<Cfg, InvalidCfgError> {
-        match nested_cfg.node {
-            NestedMetaItemKind::MetaItem(ref cfg) => Cfg::parse(cfg),
-            NestedMetaItemKind::Literal(ref lit) => Err(InvalidCfgError {
+        match nested_cfg {
+            NestedMetaItem::MetaItem(ref cfg) => Cfg::parse(cfg),
+            NestedMetaItem::Literal(ref lit) => Err(InvalidCfgError {
                 msg: "unexpected literal",
                 span: lit.span,
             }),
@@ -58,7 +58,13 @@ 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 = match cfg.ident() {
+            Some(ident) => ident.name,
+            None => return Err(InvalidCfgError {
+                msg: "expected a single identifier",
+                span: cfg.span
+            }),
+        };
         match cfg.node {
             MetaItemKind::Word => Ok(Cfg::Cfg(name, None)),
             MetaItemKind::NameValue(ref lit) => match lit.node {
@@ -424,7 +430,7 @@ mod test {
 
     fn dummy_meta_item_word(name: &str) -> MetaItem {
         MetaItem {
-            ident: Path::from_ident(Ident::from_str(name)),
+            path: Path::from_ident(Ident::from_str(name)),
             node: MetaItemKind::Word,
             span: DUMMY_SP,
         }
@@ -433,12 +439,12 @@ mod test {
     macro_rules! dummy_meta_item_list {
         ($name:ident, [$($list:ident),* $(,)?]) => {
             MetaItem {
-                ident: Path::from_ident(Ident::from_str(stringify!($name))),
+                path: Path::from_ident(Ident::from_str(stringify!($name))),
                 node: MetaItemKind::List(vec![
                     $(
-                        dummy_spanned(NestedMetaItemKind::MetaItem(
+                        NestedMetaItem::MetaItem(
                             dummy_meta_item_word(stringify!($list)),
-                        )),
+                        ),
                     )*
                 ]),
                 span: DUMMY_SP,
@@ -447,10 +453,10 @@ mod test {
 
         ($name:ident, [$($list:expr),* $(,)?]) => {
             MetaItem {
-                ident: Path::from_ident(Ident::from_str(stringify!($name))),
+                path: Path::from_ident(Ident::from_str(stringify!($name))),
                 node: MetaItemKind::List(vec![
                     $(
-                        dummy_spanned(NestedMetaItemKind::MetaItem($list)),
+                        NestedMetaItem::MetaItem($list),
                     )*
                 ]),
                 span: DUMMY_SP,
@@ -587,7 +593,7 @@ mod test {
             assert_eq!(Cfg::parse(&mi), Ok(word_cfg("all")));
 
             let mi = MetaItem {
-                ident: Path::from_ident(Ident::from_str("all")),
+                path: Path::from_ident(Ident::from_str("all")),
                 node: MetaItemKind::NameValue(dummy_spanned(LitKind::Str(
                     Symbol::intern("done"),
                     StrStyle::Cooked,
@@ -622,7 +628,7 @@ mod test {
     fn test_parse_err() {
         with_globals(|| {
             let mi = MetaItem {
-                ident: Path::from_ident(Ident::from_str("foo")),
+                path: Path::from_ident(Ident::from_str("foo")),
                 node: MetaItemKind::NameValue(dummy_spanned(LitKind::Bool(false))),
                 span: DUMMY_SP,
             };
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index c80fd8fcd81..3aea661f6be 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -492,7 +492,7 @@ impl Item {
 
     pub fn is_non_exhaustive(&self) -> bool {
         self.attrs.other_attrs.iter()
-            .any(|a| a.name().as_str() == "non_exhaustive")
+            .any(|a| a.check_name("non_exhaustive"))
     }
 
     /// Returns a documentation-level item type from the item.
@@ -777,15 +777,15 @@ pub struct Attributes {
 impl Attributes {
     /// Extracts the content from an attribute `#[doc(cfg(content))]`.
     fn extract_cfg(mi: &ast::MetaItem) -> Option<&ast::MetaItem> {
-        use syntax::ast::NestedMetaItemKind::MetaItem;
+        use syntax::ast::NestedMetaItem::MetaItem;
 
         if let ast::MetaItemKind::List(ref nmis) = mi.node {
             if nmis.len() == 1 {
-                if let MetaItem(ref cfg_mi) = nmis[0].node {
+                if let MetaItem(ref cfg_mi) = nmis[0] {
                     if cfg_mi.check_name("cfg") {
                         if let ast::MetaItemKind::List(ref cfg_nmis) = cfg_mi.node {
                             if cfg_nmis.len() == 1 {
-                                if let MetaItem(ref content_mi) = cfg_nmis[0].node {
+                                if let MetaItem(ref content_mi) = cfg_nmis[0] {
                                     return Some(content_mi);
                                 }
                             }
@@ -3683,7 +3683,7 @@ impl Clean<Vec<Item>> for doctree::ExternCrate {
     fn clean(&self, cx: &DocContext<'_>) -> Vec<Item> {
 
         let please_inline = self.vis.node.is_pub() && self.attrs.iter().any(|a| {
-            a.name() == "doc" && match a.meta_item_list() {
+            a.check_name("doc") && match a.meta_item_list() {
                 Some(l) => attr::list_contains_name(&l, "inline"),
                 None => false,
             }
@@ -3722,7 +3722,7 @@ impl Clean<Vec<Item>> for doctree::Import {
         // #[doc(no_inline)] attribute is present.
         // Don't inline doc(hidden) imports so they can be stripped at a later stage.
         let mut denied = !self.vis.node.is_pub() || self.attrs.iter().any(|a| {
-            a.name() == "doc" && match a.meta_item_list() {
+            a.check_name("doc") && match a.meta_item_list() {
                 Some(l) => attr::list_contains_name(&l, "no_inline") ||
                            attr::list_contains_name(&l, "hidden"),
                 None => false,
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 47dbbc20980..dca6458c701 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -521,8 +521,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
             for attr in krate.module.as_ref().unwrap().attrs.lists("doc") {
                 let diag = ctxt.sess().diagnostic();
 
-                let name = attr.name().map(|s| s.as_str());
-                let name = name.as_ref().map(|s| &s[..]);
+                let name = attr.ident_str();
                 if attr.is_word() {
                     if name == Some("no_default_passes") {
                         report_deprecated_attr("no_default_passes", diag);
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 5109e775ebf..b3a816d17f5 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -562,8 +562,7 @@ pub fn run(mut krate: clean::Crate,
     // going to emit HTML
     if let Some(attrs) = krate.module.as_ref().map(|m| &m.attrs) {
         for attr in attrs.lists("doc") {
-            let name = attr.name().map(|s| s.as_str());
-            match (name.as_ref().map(|s| &s[..]), attr.value_str()) {
+            match (attr.ident_str(), attr.value_str()) {
                 (Some("html_favicon_url"), Some(s)) => {
                     scx.layout.favicon = s.to_string();
                 }
@@ -3714,19 +3713,19 @@ 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 path = attr.path.to_string();
 
     if attr.is_word() {
-        Some(name.to_string())
+        Some(path)
     } else if let Some(v) = attr.value_str() {
-        Some(format!("{} = {:?}", name, v.as_str()))
+        Some(format!("{} = {:?}", path, v.as_str()))
     } else if let Some(values) = attr.meta_item_list() {
         let display: Vec<_> = values.iter().filter_map(|attr| {
             attr.meta_item().and_then(|mi| render_attribute(mi))
         }).collect();
 
         if display.len() > 0 {
-            Some(format!("{}({})", name, display.join(", ")))
+            Some(format!("{}({})", path, display.join(", ")))
         } else {
             None
         }
@@ -3750,8 +3749,7 @@ fn render_attributes(w: &mut fmt::Formatter<'_>, it: &clean::Item) -> fmt::Resul
     let mut attrs = String::new();
 
     for attr in &it.attrs.other_attrs {
-        let name = attr.name();
-        if !ATTRIBUTE_WHITELIST.contains(&&*name.as_str()) {
+        if !attr.ident_str().map_or(false, |name| ATTRIBUTE_WHITELIST.contains(&name)) {
             continue;
         }
         if let Some(s) = render_attribute(&attr.meta().unwrap()) {
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index 0c99f6ddedd..b4db121fb53 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -178,9 +178,10 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             Some(kind) => {
                 let name = if kind == MacroKind::Derive {
                     item.attrs.lists("proc_macro_derive")
-                              .filter_map(|mi| mi.name())
+                              .filter_map(|mi| mi.ident())
                               .next()
                               .expect("proc-macro derives require a name")
+                              .name
                 } else {
                     name
                 };
@@ -193,8 +194,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
 
                     if let Some(list) = mi.meta_item_list() {
                         for inner_mi in list {
-                            if let Some(name) = inner_mi.name() {
-                                helpers.push(name);
+                            if let Some(ident) = inner_mi.ident() {
+                                helpers.push(ident.name);
                             }
                         }
                     }
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index a01a5bb0a36..1a0da73880c 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -443,14 +443,11 @@ pub struct Crate {
     pub span: Span,
 }
 
-/// A spanned compile-time attribute list item.
-pub type NestedMetaItem = Spanned<NestedMetaItemKind>;
-
 /// Possible values inside of compile-time attribute lists.
 ///
 /// E.g., the '..' in `#[name(..)]`.
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
-pub enum NestedMetaItemKind {
+pub enum NestedMetaItem {
     /// A full MetaItem, for recursive meta items.
     MetaItem(MetaItem),
     /// A literal.
@@ -464,7 +461,7 @@ pub enum NestedMetaItemKind {
 /// E.g., `#[test]`, `#[derive(..)]`, `#[rustfmt::skip]` or `#[feature = "foo"]`.
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub struct MetaItem {
-    pub ident: Path,
+    pub path: Path,
     pub node: MetaItemKind,
     pub span: Span,
 }
@@ -2207,7 +2204,7 @@ pub struct Item {
 impl Item {
     /// Return the span that encompasses the attributes.
     pub fn span_with_attributes(&self) -> Span {
-        self.attrs.iter().fold(self.span, |acc, attr| acc.to(attr.span()))
+        self.attrs.iter().fold(self.span, |acc, attr| acc.to(attr.span))
     }
 }
 
diff --git a/src/libsyntax/attr/builtin.rs b/src/libsyntax/attr/builtin.rs
index f7a000935ca..cdfb83c6e56 100644
--- a/src/libsyntax/attr/builtin.rs
+++ b/src/libsyntax/attr/builtin.rs
@@ -1,6 +1,6 @@
 //! Parsing and validation of builtin attributes
 
-use crate::ast::{self, Attribute, MetaItem, Name, NestedMetaItemKind};
+use crate::ast::{self, Attribute, MetaItem, NestedMetaItem};
 use crate::feature_gate::{Features, GatedCfg};
 use crate::parse::ParseSess;
 
@@ -10,8 +10,8 @@ use syntax_pos::{symbol::Symbol, Span};
 use super::{mark_used, MetaItemKind};
 
 enum AttrError {
-    MultipleItem(Name),
-    UnknownMetaItem(Name, &'static [&'static str]),
+    MultipleItem(String),
+    UnknownMetaItem(String, &'static [&'static str]),
     MissingSince,
     MissingFeature,
     MultipleStabilityLevels,
@@ -155,10 +155,7 @@ pub fn contains_feature_attr(attrs: &[Attribute], feature_name: &str) -> bool {
     attrs.iter().any(|item| {
         item.check_name("feature") &&
         item.meta_item_list().map(|list| {
-            list.iter().any(|mi| {
-                mi.word().map(|w| w.name() == feature_name)
-                         .unwrap_or(false)
-            })
+            list.iter().any(|mi| mi.is_word() && mi.check_name(feature_name))
         }).unwrap_or(false)
     })
 }
@@ -206,7 +203,7 @@ fn find_stability_generic<'a, I>(sess: &ParseSess,
             let meta = meta.as_ref().unwrap();
             let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
                 if item.is_some() {
-                    handle_errors(sess, meta.span, AttrError::MultipleItem(meta.name()));
+                    handle_errors(sess, meta.span, AttrError::MultipleItem(meta.path.to_string()));
                     return false
                 }
                 if let Some(v) = meta.value_str() {
@@ -225,9 +222,9 @@ fn find_stability_generic<'a, I>(sess: &ParseSess,
                     )+
                     for meta in metas {
                         if let Some(mi) = meta.meta_item() {
-                            match &*mi.name().as_str() {
+                            match mi.ident_str() {
                                 $(
-                                    stringify!($name)
+                                    Some(stringify!($name))
                                         => if !get(mi, &mut $name) { continue 'outer },
                                 )+
                                 _ => {
@@ -235,7 +232,7 @@ fn find_stability_generic<'a, I>(sess: &ParseSess,
                                     handle_errors(
                                         sess,
                                         mi.span,
-                                        AttrError::UnknownMetaItem(mi.name(), expected),
+                                        AttrError::UnknownMetaItem(mi.path.to_string(), expected),
                                     );
                                     continue 'outer
                                 }
@@ -243,7 +240,7 @@ fn find_stability_generic<'a, I>(sess: &ParseSess,
                         } else {
                             handle_errors(
                                 sess,
-                                meta.span,
+                                meta.span(),
                                 AttrError::UnsupportedLiteral(
                                     "unsupported literal",
                                     false,
@@ -255,7 +252,7 @@ fn find_stability_generic<'a, I>(sess: &ParseSess,
                 }
             }
 
-            match &*meta.name().as_str() {
+            match meta.ident_str().expect("not a stability level") {
                 "rustc_deprecated" => {
                     if rustc_depr.is_some() {
                         span_err!(diagnostic, item_sp, E0540,
@@ -274,11 +271,11 @@ fn find_stability_generic<'a, I>(sess: &ParseSess,
                             })
                         }
                         (None, _) => {
-                            handle_errors(sess, attr.span(), AttrError::MissingSince);
+                            handle_errors(sess, attr.span, AttrError::MissingSince);
                             continue
                         }
                         _ => {
-                            span_err!(diagnostic, attr.span(), E0543, "missing 'reason'");
+                            span_err!(diagnostic, attr.span, E0543, "missing 'reason'");
                             continue
                         }
                     }
@@ -294,13 +291,13 @@ fn find_stability_generic<'a, I>(sess: &ParseSess,
                     if let Some(feature) = feature {
                         rustc_const_unstable = Some(feature);
                     } else {
-                        span_err!(diagnostic, attr.span(), E0629, "missing 'feature'");
+                        span_err!(diagnostic, attr.span, E0629, "missing 'feature'");
                         continue
                     }
                 }
                 "unstable" => {
                     if stab.is_some() {
-                        handle_errors(sess, attr.span(), AttrError::MultipleStabilityLevels);
+                        handle_errors(sess, attr.span, AttrError::MultipleStabilityLevels);
                         break
                     }
 
@@ -309,16 +306,16 @@ fn find_stability_generic<'a, I>(sess: &ParseSess,
                     let mut issue = None;
                     for meta in metas {
                         if let Some(mi) = meta.meta_item() {
-                            match &*mi.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 },
+                            match mi.ident_str() {
+                                Some("feature") => if !get(mi, &mut feature) { continue 'outer },
+                                Some("reason") => if !get(mi, &mut reason) { continue 'outer },
+                                Some("issue") => if !get(mi, &mut issue) { continue 'outer },
                                 _ => {
                                     handle_errors(
                                         sess,
-                                        meta.span,
+                                        meta.span(),
                                         AttrError::UnknownMetaItem(
-                                            mi.name(),
+                                            mi.path.to_string(),
                                             &["feature", "reason", "issue"]
                                         ),
                                     );
@@ -328,7 +325,7 @@ fn find_stability_generic<'a, I>(sess: &ParseSess,
                         } else {
                             handle_errors(
                                 sess,
-                                meta.span,
+                                meta.span(),
                                 AttrError::UnsupportedLiteral(
                                     "unsupported literal",
                                     false,
@@ -347,7 +344,7 @@ fn find_stability_generic<'a, I>(sess: &ParseSess,
                                         if let Ok(issue) = issue.as_str().parse() {
                                             issue
                                         } else {
-                                            span_err!(diagnostic, attr.span(), E0545,
+                                            span_err!(diagnostic, attr.span, E0545,
                                                       "incorrect 'issue'");
                                             continue
                                         }
@@ -360,42 +357,44 @@ fn find_stability_generic<'a, I>(sess: &ParseSess,
                             })
                         }
                         (None, _, _) => {
-                            handle_errors(sess, attr.span(), AttrError::MissingFeature);
+                            handle_errors(sess, attr.span, AttrError::MissingFeature);
                             continue
                         }
                         _ => {
-                            span_err!(diagnostic, attr.span(), E0547, "missing 'issue'");
+                            span_err!(diagnostic, attr.span, E0547, "missing 'issue'");
                             continue
                         }
                     }
                 }
                 "stable" => {
                     if stab.is_some() {
-                        handle_errors(sess, attr.span(), AttrError::MultipleStabilityLevels);
+                        handle_errors(sess, attr.span, AttrError::MultipleStabilityLevels);
                         break
                     }
 
                     let mut feature = None;
                     let mut since = None;
                     for meta in metas {
-                        match &meta.node {
-                            NestedMetaItemKind::MetaItem(mi) => {
-                                match &*mi.name().as_str() {
-                                    "feature" => if !get(mi, &mut feature) { continue 'outer },
-                                    "since" => if !get(mi, &mut since) { continue 'outer },
+                        match meta {
+                            NestedMetaItem::MetaItem(mi) => {
+                                match mi.ident_str() {
+                                    Some("feature") =>
+                                        if !get(mi, &mut feature) { continue 'outer },
+                                    Some("since") =>
+                                        if !get(mi, &mut since) { continue 'outer },
                                     _ => {
                                         handle_errors(
                                             sess,
-                                            meta.span,
+                                            meta.span(),
                                             AttrError::UnknownMetaItem(
-                                                mi.name(), &["since", "note"],
+                                                mi.path.to_string(), &["since", "note"],
                                             ),
                                         );
                                         continue 'outer
                                     }
                                 }
                             },
-                            NestedMetaItemKind::Literal(lit) => {
+                            NestedMetaItem::Literal(lit) => {
                                 handle_errors(
                                     sess,
                                     lit.span,
@@ -422,11 +421,11 @@ fn find_stability_generic<'a, I>(sess: &ParseSess,
                             })
                         }
                         (None, _) => {
-                            handle_errors(sess, attr.span(), AttrError::MissingFeature);
+                            handle_errors(sess, attr.span, AttrError::MissingFeature);
                             continue
                         }
                         _ => {
-                            handle_errors(sess, attr.span(), AttrError::MissingSince);
+                            handle_errors(sess, attr.span, AttrError::MissingSince);
                             continue
                         }
                     }
@@ -483,8 +482,8 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat
             gated_cfg.check_and_emit(sess, feats);
         }
         let error = |span, msg| { sess.span_diagnostic.span_err(span, msg); true };
-        if cfg.ident.segments.len() != 1 {
-            return error(cfg.ident.span, "`cfg` predicate key must be an identifier");
+        if cfg.path.segments.len() != 1 {
+            return error(cfg.path.span, "`cfg` predicate key must be an identifier");
         }
         match &cfg.node {
             MetaItemKind::List(..) => {
@@ -502,7 +501,8 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat
                 true
             }
             MetaItemKind::NameValue(..) | MetaItemKind::Word => {
-                sess.config.contains(&(cfg.name(), cfg.value_str()))
+                let ident = cfg.ident().expect("multi-segment cfg predicate");
+                sess.config.contains(&(ident.name, cfg.value_str()))
             }
         }
     })
@@ -520,7 +520,7 @@ pub fn eval_condition<F>(cfg: &ast::MetaItem, sess: &ParseSess, eval: &mut F)
                 if !mi.is_meta_item() {
                     handle_errors(
                         sess,
-                        mi.span,
+                        mi.span(),
                         AttrError::UnsupportedLiteral(
                             "unsupported literal",
                             false
@@ -532,14 +532,14 @@ 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() {
-                "any" => mis.iter().any(|mi| {
+            match cfg.ident_str() {
+                Some("any") => mis.iter().any(|mi| {
                     eval_condition(mi.meta_item().unwrap(), sess, eval)
                 }),
-                "all" => mis.iter().all(|mi| {
+                Some("all") => mis.iter().all(|mi| {
                     eval_condition(mi.meta_item().unwrap(), sess, eval)
                 }),
-                "not" => {
+                Some("not") => {
                     if mis.len() != 1 {
                         span_err!(sess.span_diagnostic, cfg.span, E0536, "expected 1 cfg-pattern");
                         return false;
@@ -547,8 +547,9 @@ pub fn eval_condition<F>(cfg: &ast::MetaItem, sess: &ParseSess, eval: &mut F)
 
                     !eval_condition(mis[0].meta_item().unwrap(), sess, eval)
                 },
-                p => {
-                    span_err!(sess.span_diagnostic, cfg.span, E0537, "invalid predicate `{}`", p);
+                _ => {
+                    span_err!(sess.span_diagnostic, cfg.span, E0537,
+                              "invalid predicate `{}`", cfg.path);
                     false
                 }
             }
@@ -602,7 +603,9 @@ fn find_deprecation_generic<'a, I>(sess: &ParseSess,
             MetaItemKind::List(list) => {
                 let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
                     if item.is_some() {
-                        handle_errors(sess, meta.span, AttrError::MultipleItem(meta.name()));
+                        handle_errors(
+                            sess, meta.span, AttrError::MultipleItem(meta.path.to_string())
+                        );
                         return false
                     }
                     if let Some(v) = meta.value_str() {
@@ -630,22 +633,23 @@ fn find_deprecation_generic<'a, I>(sess: &ParseSess,
                 let mut since = None;
                 let mut note = None;
                 for meta in list {
-                    match &meta.node {
-                        NestedMetaItemKind::MetaItem(mi) => {
-                            match &*mi.name().as_str() {
-                                "since" => if !get(mi, &mut since) { continue 'outer },
-                                "note" => if !get(mi, &mut note) { continue 'outer },
+                    match meta {
+                        NestedMetaItem::MetaItem(mi) => {
+                            match mi.ident_str() {
+                                Some("since") => if !get(mi, &mut since) { continue 'outer },
+                                Some("note") => if !get(mi, &mut note) { continue 'outer },
                                 _ => {
                                     handle_errors(
                                         sess,
-                                        meta.span,
-                                        AttrError::UnknownMetaItem(mi.name(), &["since", "note"]),
+                                        meta.span(),
+                                        AttrError::UnknownMetaItem(mi.path.to_string(),
+                                                                   &["since", "note"]),
                                     );
                                     continue 'outer
                                 }
                             }
                         }
-                        NestedMetaItemKind::Literal(lit) => {
+                        NestedMetaItem::Literal(lit) => {
                             handle_errors(
                                 sess,
                                 lit.span,
@@ -714,7 +718,7 @@ pub fn find_repr_attrs(sess: &ParseSess, attr: &Attribute) -> Vec<ReprAttr> {
                 if !item.is_meta_item() {
                     handle_errors(
                         sess,
-                        item.span,
+                        item.span(),
                         AttrError::UnsupportedLiteral(
                             "meta item in `repr` must be an identifier",
                             false,
@@ -724,19 +728,13 @@ pub fn find_repr_attrs(sess: &ParseSess, attr: &Attribute) -> Vec<ReprAttr> {
                 }
 
                 let mut recognised = false;
-                if let Some(mi) = item.word() {
-                    let word = &*mi.name().as_str();
-                    let hint = match word {
-                        "C" => Some(ReprC),
-                        "packed" => Some(ReprPacked(1)),
-                        "simd" => Some(ReprSimd),
-                        "transparent" => Some(ReprTransparent),
-                        _ => match int_type_of_word(word) {
-                            Some(ity) => Some(ReprInt(ity)),
-                            None => {
-                                None
-                            }
-                        }
+                if item.is_word() {
+                    let hint = match item.ident_str() {
+                        Some("C") => Some(ReprC),
+                        Some("packed") => Some(ReprPacked(1)),
+                        Some("simd") => Some(ReprSimd),
+                        Some("transparent") => Some(ReprTransparent),
+                        name => name.and_then(|name| int_type_of_word(name)).map(ReprInt),
                     };
 
                     if let Some(h) = hint {
@@ -777,20 +775,20 @@ pub fn find_repr_attrs(sess: &ParseSess, attr: &Attribute) -> Vec<ReprAttr> {
                         };
                     }
                     if let Some(literal_error) = literal_error {
-                        span_err!(diagnostic, item.span, E0589,
+                        span_err!(diagnostic, item.span(), E0589,
                                   "invalid `repr(align)` attribute: {}", literal_error);
                     }
                 } else {
                     if let Some(meta_item) = item.meta_item() {
-                        if meta_item.name() == "align" {
+                        if meta_item.check_name("align") {
                             if let MetaItemKind::NameValue(ref value) = meta_item.node {
                                 recognised = true;
-                                let mut err = struct_span_err!(diagnostic, item.span, E0693,
+                                let mut err = struct_span_err!(diagnostic, item.span(), E0693,
                                     "incorrect `repr(align)` attribute format");
                                 match value.node {
                                     ast::LitKind::Int(int, ast::LitIntType::Unsuffixed) => {
                                         err.span_suggestion(
-                                            item.span,
+                                            item.span(),
                                             "use parentheses instead",
                                             format!("align({})", int),
                                             Applicability::MachineApplicable
@@ -798,7 +796,7 @@ pub fn find_repr_attrs(sess: &ParseSess, attr: &Attribute) -> Vec<ReprAttr> {
                                     }
                                     ast::LitKind::Str(s, _) => {
                                         err.span_suggestion(
-                                            item.span,
+                                            item.span(),
                                             "use parentheses instead",
                                             format!("align({})", s),
                                             Applicability::MachineApplicable
@@ -813,7 +811,7 @@ pub fn find_repr_attrs(sess: &ParseSess, attr: &Attribute) -> Vec<ReprAttr> {
                 }
                 if !recognised {
                     // Not a word we recognize
-                    span_err!(diagnostic, item.span, E0552,
+                    span_err!(diagnostic, item.span(), E0552,
                               "unrecognized representation hint");
                 }
             }
diff --git a/src/libsyntax/attr/mod.rs b/src/libsyntax/attr/mod.rs
index b5fc8507314..1a8faa43fff 100644
--- a/src/libsyntax/attr/mod.rs
+++ b/src/libsyntax/attr/mod.rs
@@ -13,7 +13,7 @@ pub use StabilityLevel::*;
 
 use crate::ast;
 use crate::ast::{AttrId, Attribute, AttrStyle, Name, Ident, Path, PathSegment};
-use crate::ast::{MetaItem, MetaItemKind, NestedMetaItem, NestedMetaItemKind};
+use crate::ast::{MetaItem, MetaItemKind, NestedMetaItem};
 use crate::ast::{Lit, LitKind, Expr, ExprKind, Item, Local, Stmt, StmtKind, GenericParam};
 use crate::mut_visit::visit_clobber;
 use crate::source_map::{BytePos, Spanned, respan, dummy_spanned};
@@ -64,36 +64,33 @@ pub fn is_known_lint_tool(m_item: Ident) -> bool {
 }
 
 impl NestedMetaItem {
-    /// Returns the MetaItem if self is a NestedMetaItemKind::MetaItem.
+    /// Returns the MetaItem if self is a NestedMetaItem::MetaItem.
     pub fn meta_item(&self) -> Option<&MetaItem> {
-        match self.node {
-            NestedMetaItemKind::MetaItem(ref item) => Some(item),
+        match *self {
+            NestedMetaItem::MetaItem(ref item) => Some(item),
             _ => None
         }
     }
 
-    /// Returns the Lit if self is a NestedMetaItemKind::Literal.
+    /// Returns the Lit if self is a NestedMetaItem::Literal.
     pub fn literal(&self) -> Option<&Lit> {
-        match self.node {
-            NestedMetaItemKind::Literal(ref lit) => Some(lit),
+        match *self {
+            NestedMetaItem::Literal(ref lit) => Some(lit),
             _ => None
         }
     }
 
-    /// Returns the Span for `self`.
-    pub fn span(&self) -> Span {
-        self.span
-    }
-
     /// Returns `true` if this list item is a MetaItem with a name of `name`.
     pub fn check_name(&self, name: &str) -> bool {
         self.meta_item().map_or(false, |meta_item| meta_item.check_name(name))
     }
 
-    /// 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()))
+    /// For a single-segment meta-item returns its name, otherwise returns `None`.
+    pub fn ident(&self) -> Option<Ident> {
+        self.meta_item().and_then(|meta_item| meta_item.ident())
+    }
+    pub fn ident_str(&self) -> Option<&str> {
+        self.ident().map(|name| name.as_str().get())
     }
 
     /// Gets the string value if self is a MetaItem and the MetaItem is a
@@ -108,25 +105,14 @@ impl NestedMetaItem {
             |meta_item| meta_item.meta_item_list().and_then(
                 |meta_item_list| {
                     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()))
-                        } else {
-                            None
+                        if let Some(ident) = meta_item.ident() {
+                            if let Some(lit) = meta_item_list[0].literal() {
+                                return Some((ident.name, lit));
+                            }
                         }
                     }
-                    else {
-                        None
-                    }}))
-    }
-
-    /// Returns a MetaItem if self is a MetaItem with Kind Word.
-    pub fn word(&self) -> Option<&MetaItem> {
-        self.meta_item().and_then(|meta_item| if meta_item.is_word() {
-            Some(meta_item)
-        } else {
-            None
-        })
+                    None
+                }))
     }
 
     /// Gets a list of inner meta items from a list MetaItem type.
@@ -146,7 +132,7 @@ impl NestedMetaItem {
 
     /// Returns `true` if self is a MetaItem and the meta item is a word.
     pub fn is_word(&self) -> bool {
-        self.word().is_some()
+        self.meta_item().map_or(false, |meta_item| meta_item.is_word())
     }
 
     /// Returns `true` if self is a MetaItem and the meta item is a ValueString.
@@ -160,10 +146,6 @@ impl NestedMetaItem {
     }
 }
 
-fn name_from_path(path: &Path) -> Name {
-    path.segments.last().expect("empty path in attribute").ident.name
-}
-
 impl Attribute {
     /// Returns `true` if the attribute's path matches the argument. If it matches, then the
     /// attribute is marked as used.
@@ -177,10 +159,16 @@ impl Attribute {
         matches
     }
 
-    /// Returns the **last** segment of the name of this attribute.
-    /// e.g., `foo` for `#[foo]`, `skip` for `#[rustfmt::skip]`.
-    pub fn name(&self) -> Name {
-        name_from_path(&self.path)
+    /// For a single-segment attribute returns its name, otherwise returns `None`.
+    pub fn ident(&self) -> Option<Ident> {
+        if self.path.segments.len() == 1 {
+            Some(self.path.segments[0].ident)
+        } else {
+            None
+        }
+    }
+    pub fn ident_str(&self) -> Option<&str> {
+        self.ident().map(|name| name.as_str().get())
     }
 
     pub fn value_str(&self) -> Option<Symbol> {
@@ -195,11 +183,7 @@ impl Attribute {
     }
 
     pub fn is_word(&self) -> bool {
-        self.path.segments.len() == 1 && self.tokens.is_empty()
-    }
-
-    pub fn span(&self) -> Span {
-        self.span
+        self.tokens.is_empty()
     }
 
     pub fn is_meta_item_list(&self) -> bool {
@@ -213,8 +197,16 @@ impl Attribute {
 }
 
 impl MetaItem {
-    pub fn name(&self) -> Name {
-        name_from_path(&self.ident)
+    /// For a single-segment meta-item returns its name, otherwise returns `None`.
+    pub fn ident(&self) -> Option<Ident> {
+        if self.path.segments.len() == 1 {
+            Some(self.path.segments[0].ident)
+        } else {
+            None
+        }
+    }
+    pub fn ident_str(&self) -> Option<&str> {
+        self.ident().map(|name| name.as_str().get())
     }
 
     // #[attribute(name = "value")]
@@ -252,10 +244,8 @@ impl MetaItem {
         }
     }
 
-    pub fn span(&self) -> Span { self.span }
-
     pub fn check_name(&self, name: &str) -> bool {
-        self.name() == name
+        self.path == name
     }
 
     pub fn is_value_str(&self) -> bool {
@@ -265,14 +255,6 @@ impl MetaItem {
     pub fn is_meta_item_list(&self) -> bool {
         self.meta_item_list().is_some()
     }
-
-    pub fn is_scoped(&self) -> Option<Ident> {
-        if self.ident.segments.len() > 1 {
-            Some(self.ident.segments[0].ident)
-        } else {
-            None
-        }
-    }
 }
 
 impl Attribute {
@@ -280,7 +262,7 @@ impl Attribute {
     pub fn meta(&self) -> Option<MetaItem> {
         let mut tokens = self.tokens.trees().peekable();
         Some(MetaItem {
-            ident: self.path.clone(),
+            path: self.path.clone(),
             node: if let Some(node) = MetaItemKind::from_tokens(&mut tokens) {
                 if tokens.peek().is_some() {
                     return None;
@@ -326,7 +308,7 @@ impl Attribute {
 
     pub fn parse_meta<'a>(&self, sess: &'a ParseSess) -> PResult<'a, MetaItem> {
         Ok(MetaItem {
-            ident: self.path.clone(),
+            path: self.path.clone(),
             node: self.parse(sess, |parser| parser.parse_meta_item_kind())?,
             span: self.span,
         })
@@ -364,19 +346,19 @@ pub fn mk_name_value_item_str(ident: Ident, value: Spanned<Symbol>) -> MetaItem
 }
 
 pub fn mk_name_value_item(span: Span, ident: Ident, value: ast::Lit) -> MetaItem {
-    MetaItem { ident: Path::from_ident(ident), span, node: MetaItemKind::NameValue(value) }
+    MetaItem { path: Path::from_ident(ident), span, node: MetaItemKind::NameValue(value) }
 }
 
 pub fn mk_list_item(span: Span, ident: Ident, items: Vec<NestedMetaItem>) -> MetaItem {
-    MetaItem { ident: Path::from_ident(ident), span, node: MetaItemKind::List(items) }
+    MetaItem { path: Path::from_ident(ident), span, node: MetaItemKind::List(items) }
 }
 
 pub fn mk_word_item(ident: Ident) -> MetaItem {
-    MetaItem { ident: Path::from_ident(ident), span: ident.span, node: MetaItemKind::Word }
+    MetaItem { path: Path::from_ident(ident), span: ident.span, node: MetaItemKind::Word }
 }
 
 pub fn mk_nested_word_item(ident: Ident) -> NestedMetaItem {
-    respan(ident.span, NestedMetaItemKind::MetaItem(mk_word_item(ident)))
+    NestedMetaItem::MetaItem(mk_word_item(ident))
 }
 
 pub fn mk_attr_id() -> AttrId {
@@ -400,7 +382,7 @@ pub fn mk_spanned_attr_inner(sp: Span, id: AttrId, item: MetaItem) -> Attribute
     Attribute {
         id,
         style: ast::AttrStyle::Inner,
-        path: item.ident,
+        path: item.path,
         tokens: item.node.tokens(item.span),
         is_sugared_doc: false,
         span: sp,
@@ -417,7 +399,7 @@ pub fn mk_spanned_attr_outer(sp: Span, id: AttrId, item: MetaItem) -> Attribute
     Attribute {
         id,
         style: ast::AttrStyle::Outer,
-        path: item.ident,
+        path: item.path,
         tokens: item.node.tokens(item.span),
         is_sugared_doc: false,
         span: sp,
@@ -468,7 +450,7 @@ impl MetaItem {
     fn tokens(&self) -> TokenStream {
         let mut idents = vec![];
         let mut last_pos = BytePos(0 as u32);
-        for (i, segment) in self.ident.segments.iter().enumerate() {
+        for (i, segment) in self.path.segments.iter().enumerate() {
             let is_first = i == 0;
             if !is_first {
                 let mod_sep_span = Span::new(last_pos,
@@ -488,7 +470,7 @@ impl MetaItem {
         where I: Iterator<Item = TokenTree>,
     {
         // FIXME: Share code with `parse_path`.
-        let ident = match tokens.next() {
+        let path = match tokens.next() {
             Some(TokenTree::Token(span, token @ Token::Ident(..))) |
             Some(TokenTree::Token(span, token @ Token::ModSep)) => 'arm: {
                 let mut segments = if let Token::Ident(ident, _) = token {
@@ -529,11 +511,11 @@ impl MetaItem {
         let node = MetaItemKind::from_tokens(tokens)?;
         let hi = match node {
             MetaItemKind::NameValue(ref lit) => lit.span.hi(),
-            MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(ident.span.hi()),
-            _ => ident.span.hi(),
+            MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(path.span.hi()),
+            _ => path.span.hi(),
         };
-        let span = ident.span.with_hi(hi);
-        Some(MetaItem { ident, node, span })
+        let span = path.span.with_hi(hi);
+        Some(MetaItem { path, node, span })
     }
 }
 
@@ -552,7 +534,7 @@ impl MetaItemKind {
                     if i > 0 {
                         tokens.push(TokenTree::Token(span, Token::Comma).into());
                     }
-                    item.node.tokens().append_to_tree_and_joint_vec(&mut tokens);
+                    item.tokens().append_to_tree_and_joint_vec(&mut tokens);
                 }
                 TokenTree::Delimited(
                     DelimSpan::from_single(span),
@@ -586,8 +568,8 @@ impl MetaItemKind {
         let mut tokens = delimited.into_trees().peekable();
         let mut result = Vec::new();
         while let Some(..) = tokens.peek() {
-            let item = NestedMetaItemKind::from_tokens(&mut tokens)?;
-            result.push(respan(item.span(), item));
+            let item = NestedMetaItem::from_tokens(&mut tokens)?;
+            result.push(item);
             match tokens.next() {
                 None | Some(TokenTree::Token(_, Token::Comma)) => {}
                 _ => return None,
@@ -597,32 +579,32 @@ impl MetaItemKind {
     }
 }
 
-impl NestedMetaItemKind {
-    fn span(&self) -> Span {
+impl NestedMetaItem {
+    pub fn span(&self) -> Span {
         match *self {
-            NestedMetaItemKind::MetaItem(ref item) => item.span,
-            NestedMetaItemKind::Literal(ref lit) => lit.span,
+            NestedMetaItem::MetaItem(ref item) => item.span,
+            NestedMetaItem::Literal(ref lit) => lit.span,
         }
     }
 
     fn tokens(&self) -> TokenStream {
         match *self {
-            NestedMetaItemKind::MetaItem(ref item) => item.tokens(),
-            NestedMetaItemKind::Literal(ref lit) => lit.tokens(),
+            NestedMetaItem::MetaItem(ref item) => item.tokens(),
+            NestedMetaItem::Literal(ref lit) => lit.tokens(),
         }
     }
 
-    fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<NestedMetaItemKind>
+    fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<NestedMetaItem>
         where I: Iterator<Item = TokenTree>,
     {
         if let Some(TokenTree::Token(span, token)) = tokens.peek().cloned() {
             if let Some(node) = LitKind::from_token(token) {
                 tokens.next();
-                return Some(NestedMetaItemKind::Literal(respan(span, node)));
+                return Some(NestedMetaItem::Literal(respan(span, node)));
             }
         }
 
-        MetaItem::from_tokens(tokens).map(NestedMetaItemKind::MetaItem)
+        MetaItem::from_tokens(tokens).map(NestedMetaItem::MetaItem)
     }
 }
 
diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs
index 2abb7e407eb..c300ffc2d61 100644
--- a/src/libsyntax/config.rs
+++ b/src/libsyntax/config.rs
@@ -181,13 +181,13 @@ impl<'a> StripUnconfigured<'a> {
             if nested_meta_items.is_empty() {
                 return error(meta_item.span, "`cfg` predicate is not specified", "");
             } else if nested_meta_items.len() > 1 {
-                return error(nested_meta_items.last().unwrap().span,
+                return error(nested_meta_items.last().unwrap().span(),
                              "multiple `cfg` predicates are specified", "");
             }
 
             match nested_meta_items[0].meta_item() {
                 Some(meta_item) => attr::cfg_matches(meta_item, self.sess, self.features),
-                None => error(nested_meta_items[0].span,
+                None => error(nested_meta_items[0].span(),
                               "`cfg` predicate key cannot be a literal", ""),
             }
         })
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index b805213bb1a..82358679c0e 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -601,7 +601,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 res
             }
             ProcMacroDerive(..) | BuiltinDerive(..) => {
-                self.cx.span_err(attr.span, &format!("`{}` is a derive mode", attr.path));
+                self.cx.span_err(attr.span, &format!("`{}` is a derive macro", attr.path));
                 self.cx.trace_macros_diag();
                 invoc.fragment_kind.dummy(attr.span)
             }
@@ -822,7 +822,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
             }
 
             ProcMacroDerive(..) | BuiltinDerive(..) => {
-                self.cx.span_err(path.span, &format!("`{}` is a derive mode", path));
+                self.cx.span_err(path.span, &format!("`{}` is a derive macro", path));
                 self.cx.trace_macros_diag();
                 kind.dummy(span)
             }
@@ -929,7 +929,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
-                    ident: Path::from_ident(keywords::Invalid.ident()),
+                    path: Path::from_ident(keywords::Invalid.ident()),
                     span: DUMMY_SP,
                     node: ast::MetaItemKind::Word,
                 };
@@ -1520,23 +1520,23 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
                             self.cx.source_map().new_source_file(filename.into(), src);
 
                             let include_info = vec![
-                                dummy_spanned(ast::NestedMetaItemKind::MetaItem(
+                                ast::NestedMetaItem::MetaItem(
                                     attr::mk_name_value_item_str(
                                         Ident::from_str("file"),
                                         dummy_spanned(file),
                                     ),
-                                )),
-                                dummy_spanned(ast::NestedMetaItemKind::MetaItem(
+                                ),
+                                ast::NestedMetaItem::MetaItem(
                                     attr::mk_name_value_item_str(
                                         Ident::from_str("contents"),
                                         dummy_spanned(src_interned),
                                     ),
-                                )),
+                                ),
                             ];
 
                             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)));
+                            items.push(ast::NestedMetaItem::MetaItem(item));
                         }
                         Err(e) => {
                             let lit = it
@@ -1569,7 +1569,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
                     }
                 } else {
                     let mut err = self.cx.struct_span_err(
-                        it.span,
+                        it.span(),
                         &format!("expected path to external documentation"),
                     );
 
@@ -1590,7 +1590,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
                     };
 
                     err.span_suggestion(
-                        it.span,
+                        it.span(),
                         "provide a file path with `=`",
                         format!("include = \"{}\"", path),
                         applicability,
diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs
index bd64bb01021..12912044e4e 100644
--- a/src/libsyntax/ext/tt/macro_rules.rs
+++ b/src/libsyntax/ext/tt/macro_rules.rs
@@ -380,9 +380,14 @@ pub fn compile(
             .map(|attr| attr
                 .meta_item_list()
                 .map(|list| list.iter()
-                    .map(|it| it.name().unwrap_or_else(|| sess.span_diagnostic.span_bug(
-                        it.span, "allow internal unstable expects feature names",
-                    )))
+                    .filter_map(|it| {
+                        let name = it.ident().map(|ident| ident.name);
+                        if name.is_none() {
+                            sess.span_diagnostic.span_err(it.span(),
+                                "allow internal unstable expects feature names")
+                        }
+                        name
+                    })
                     .collect::<Vec<Symbol>>().into()
                 )
                 .unwrap_or_else(|| {
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 280b17da0be..9beaabb0cd5 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -1289,9 +1289,8 @@ pub struct GatedCfg {
 
 impl GatedCfg {
     pub fn gate(cfg: &ast::MetaItem) -> Option<GatedCfg> {
-        let name = cfg.name().as_str();
         GATED_CFGS.iter()
-                  .position(|info| info.0 == name)
+                  .position(|info| cfg.check_name(info.0))
                   .map(|idx| {
                       GatedCfg {
                           span: cfg.span,
@@ -1342,16 +1341,16 @@ macro_rules! gate_feature {
 impl<'a> Context<'a> {
     fn check_attribute(&self, attr: &ast::Attribute, is_macro: bool) {
         debug!("check_attribute(attr = {:?})", attr);
-        let name = attr.name().as_str();
+        let name = attr.ident_str();
         for &(n, ty, _template, ref gateage) in BUILTIN_ATTRIBUTES {
-            if name == n {
+            if name == Some(n) {
                 if let Gated(_, name, desc, ref has_feature) = *gateage {
                     if !attr.span.allows_unstable(name) {
                         gate_feature_fn!(
                             self, has_feature, attr.span, name, desc, GateStrength::Hard
                         );
                     }
-                } else if name == "doc" {
+                } else if n == "doc" {
                     if let Some(content) = attr.meta_item_list() {
                         if content.iter().any(|c| c.check_name("include")) {
                             gate_feature!(self, external_doc, attr.span,
@@ -1374,7 +1373,7 @@ impl<'a> Context<'a> {
             }
         }
         if !attr::is_known(attr) {
-            if name.starts_with("rustc_") {
+            if name.map_or(false, |name| name.starts_with("rustc_")) {
                 let msg = "unless otherwise specified, attributes with the prefix `rustc_` \
                            are reserved for internal compiler diagnostics";
                 gate_feature!(self, rustc_attrs, attr.span, msg);
@@ -2055,15 +2054,14 @@ 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()
-            } else {
-                continue
+            let name = match mi.ident_str() {
+                Some(name) if mi.is_word() => name,
+                _ => continue,
             };
 
-            if incomplete_features.iter().any(|f| *f == name.as_str()) {
+            if incomplete_features.iter().any(|f| *f == name) {
                 span_handler.struct_span_warn(
-                    mi.span,
+                    mi.span(),
                     &format!(
                         "the feature `{}` is incomplete and may cause the compiler to crash",
                         name
@@ -2101,18 +2099,19 @@ 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()
-            } else {
-                span_err!(span_handler, mi.span, E0556,
-                          "malformed feature, expected just one word");
-                continue
+            let name = match mi.ident() {
+                Some(ident) if mi.is_word() => ident.name,
+                _ => {
+                    span_err!(span_handler, mi.span(), E0556,
+                            "malformed feature, expected just one word");
+                    continue
+                }
             };
 
             if let Some(edition) = edition_enabled_features.get(&name) {
                 struct_span_warn!(
                     span_handler,
-                    mi.span,
+                    mi.span(),
                     E0705,
                     "the feature `{}` is included in the Rust {} edition",
                     name,
@@ -2129,32 +2128,32 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute],
             if let Some((.., set)) = ACTIVE_FEATURES.iter().find(|f| name == f.0) {
                 if let Some(allowed) = allow_features.as_ref() {
                     if allowed.iter().find(|f| *f == name.as_str()).is_none() {
-                        span_err!(span_handler, mi.span, E0725,
+                        span_err!(span_handler, mi.span(), E0725,
                                   "the feature `{}` is not in the list of allowed features",
                                   name);
                         continue;
                     }
                 }
 
-                set(&mut features, mi.span);
-                features.declared_lang_features.push((name, mi.span, None));
+                set(&mut features, mi.span());
+                features.declared_lang_features.push((name, mi.span(), None));
                 continue
             }
 
             let removed = REMOVED_FEATURES.iter().find(|f| name == f.0);
             let stable_removed = STABLE_REMOVED_FEATURES.iter().find(|f| name == f.0);
             if let Some((.., reason)) = removed.or(stable_removed) {
-                feature_removed(span_handler, mi.span, *reason);
+                feature_removed(span_handler, mi.span(), *reason);
                 continue
             }
 
             if let Some((_, since, ..)) = ACCEPTED_FEATURES.iter().find(|f| name == f.0) {
                 let since = Some(Symbol::intern(since));
-                features.declared_lang_features.push((name, mi.span, since));
+                features.declared_lang_features.push((name, mi.span(), since));
                 continue
             }
 
-            features.declared_lib_features.push((name, mi.span));
+            features.declared_lib_features.push((name, mi.span()));
         }
     }
 
diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs
index 802b7808695..462346df7d7 100644
--- a/src/libsyntax/mut_visit.rs
+++ b/src/libsyntax/mut_visit.rs
@@ -539,16 +539,14 @@ pub fn noop_visit_macro_def<T: MutVisitor>(macro_def: &mut MacroDef, vis: &mut T
 }
 
 pub fn noop_visit_meta_list_item<T: MutVisitor>(li: &mut NestedMetaItem, vis: &mut T) {
-    let Spanned { node, span } = li;
-    match node {
-        NestedMetaItemKind::MetaItem(mi) => vis.visit_meta_item(mi),
-        NestedMetaItemKind::Literal(_lit) => {}
+    match li {
+        NestedMetaItem::MetaItem(mi) => vis.visit_meta_item(mi),
+        NestedMetaItem::Literal(_lit) => {}
     }
-    vis.visit_span(span);
 }
 
 pub fn noop_visit_meta_item<T: MutVisitor>(mi: &mut MetaItem, vis: &mut T) {
-    let MetaItem { ident: _, node, span } = mi;
+    let MetaItem { path: _, node, span } = mi;
     match node {
         MetaItemKind::Word => {}
         MetaItemKind::List(mis) => visit_vec(mis, |mi| vis.visit_meta_list_item(mi)),
@@ -1340,4 +1338,3 @@ mod tests {
         })
     }
 }
-
diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs
index e93e15f9012..4211268f33e 100644
--- a/src/libsyntax/parse/attr.rs
+++ b/src/libsyntax/parse/attr.rs
@@ -1,6 +1,5 @@
 use crate::attr;
 use crate::ast;
-use crate::source_map::respan;
 use crate::parse::{SeqSep, PResult};
 use crate::parse::token::{self, Nonterminal, DelimToken};
 use crate::parse::parser::{Parser, TokenType, PathStyle};
@@ -149,7 +148,7 @@ impl<'a> Parser<'a> {
         };
         Ok(if let Some(meta) = meta {
             self.bump();
-            (meta.ident, meta.node.tokens(meta.span))
+            (meta.path, meta.node.tokens(meta.span))
         } else {
             let path = self.parse_path(PathStyle::Mod)?;
             let tokens = if self.check(&token::OpenDelim(DelimToken::Paren)) ||
@@ -250,10 +249,10 @@ impl<'a> Parser<'a> {
         }
 
         let lo = self.span;
-        let ident = self.parse_path(PathStyle::Mod)?;
+        let path = self.parse_path(PathStyle::Mod)?;
         let node = self.parse_meta_item_kind()?;
         let span = lo.to(self.prev_span);
-        Ok(ast::MetaItem { ident, node, span })
+        Ok(ast::MetaItem { path, node, span })
     }
 
     crate fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> {
@@ -268,18 +267,16 @@ impl<'a> Parser<'a> {
 
     /// matches meta_item_inner : (meta_item | UNSUFFIXED_LIT) ;
     fn parse_meta_item_inner(&mut self) -> PResult<'a, ast::NestedMetaItem> {
-        let lo = self.span;
-
         match self.parse_unsuffixed_lit() {
             Ok(lit) => {
-                return Ok(respan(lo.to(self.prev_span), ast::NestedMetaItemKind::Literal(lit)))
+                return Ok(ast::NestedMetaItem::Literal(lit))
             }
             Err(ref mut err) => self.diagnostic().cancel(err)
         }
 
         match self.parse_meta_item() {
             Ok(mi) => {
-                return Ok(respan(lo.to(self.prev_span), ast::NestedMetaItemKind::MetaItem(mi)))
+                return Ok(ast::NestedMetaItem::MetaItem(mi))
             }
             Err(ref mut err) => self.diagnostic().cancel(err)
         }
diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs
index 01e3b292903..bcd53dbfeb2 100644
--- a/src/libsyntax/parse/lexer/mod.rs
+++ b/src/libsyntax/parse/lexer/mod.rs
@@ -1,7 +1,7 @@
 use crate::ast::{self, Ident};
 use crate::source_map::{SourceMap, FilePathMapping};
 use crate::parse::{token, ParseSess};
-use crate::symbol::{Symbol, keywords};
+use crate::symbol::Symbol;
 
 use errors::{Applicability, FatalError, Diagnostic, DiagnosticBuilder};
 use syntax_pos::{BytePos, CharPos, Pos, Span, NO_EXPANSION};
@@ -1249,15 +1249,11 @@ impl<'a> StringReader<'a> {
                     // FIXME: perform NFKC normalization here. (Issue #2253)
                     let ident = self.mk_ident(string);
 
-                    if is_raw_ident && (ident.is_path_segment_keyword() ||
-                                        ident.name == keywords::Underscore.name()) {
-                        self.fatal_span_(raw_start, self.pos,
-                            &format!("`r#{}` is not currently supported.", ident.name)
-                        ).raise();
-                    }
-
                     if is_raw_ident {
                         let span = self.mk_sp(raw_start, self.pos);
+                        if !ident.can_be_raw() {
+                            self.err_span(span, &format!("`{}` cannot be a raw identifier", ident));
+                        }
                         self.sess.raw_identifier_spans.borrow_mut().push(span);
                     }
 
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 22af7d47fd0..aa70c54a1ef 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -895,9 +895,7 @@ impl<'a> Parser<'a> {
                                            &format!("expected identifier, found {}",
                                                     self.this_token_descr()));
         if let token::Ident(ident, false) = &self.token {
-            if ident.is_reserved() && !ident.is_path_segment_keyword() &&
-                ident.name != keywords::Underscore.name()
-            {
+            if ident.is_raw_guess() {
                 err.span_suggestion(
                     self.span,
                     "you can escape reserved keywords to use them as identifiers",
@@ -2335,7 +2333,7 @@ impl<'a> Parser<'a> {
         let meta_ident = match self.token {
             token::Interpolated(ref nt) => match **nt {
                 token::NtMeta(ref meta) => match meta.node {
-                    ast::MetaItemKind::Word => Some(meta.ident.clone()),
+                    ast::MetaItemKind::Word => Some(meta.path.clone()),
                     _ => None,
                 },
                 _ => None,
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 49e3fad4af0..e04e127ccf1 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -768,11 +768,11 @@ pub trait PrintState<'a> {
     }
 
     fn print_meta_list_item(&mut self, item: &ast::NestedMetaItem) -> io::Result<()> {
-        match item.node {
-            ast::NestedMetaItemKind::MetaItem(ref mi) => {
+        match item {
+            ast::NestedMetaItem::MetaItem(ref mi) => {
                 self.print_meta_item(mi)
             },
-            ast::NestedMetaItemKind::Literal(ref lit) => {
+            ast::NestedMetaItem::Literal(ref lit) => {
                 self.print_literal(lit)
             }
         }
@@ -781,15 +781,15 @@ pub trait PrintState<'a> {
     fn print_meta_item(&mut self, item: &ast::MetaItem) -> io::Result<()> {
         self.ibox(INDENT_UNIT)?;
         match item.node {
-            ast::MetaItemKind::Word => self.print_attribute_path(&item.ident)?,
+            ast::MetaItemKind::Word => self.print_attribute_path(&item.path)?,
             ast::MetaItemKind::NameValue(ref value) => {
-                self.print_attribute_path(&item.ident)?;
+                self.print_attribute_path(&item.path)?;
                 self.writer().space()?;
                 self.word_space("=")?;
                 self.print_literal(value)?;
             }
             ast::MetaItemKind::List(ref items) => {
-                self.print_attribute_path(&item.ident)?;
+                self.print_attribute_path(&item.path)?;
                 self.popen()?;
                 self.commasep(Consistent,
                               &items[..],
diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs
index 56290fa771b..6f03c585403 100644
--- a/src/libsyntax/test.rs
+++ b/src/libsyntax/test.rs
@@ -435,9 +435,12 @@ fn get_test_runner(sd: &errors::Handler, krate: &ast::Crate) -> Option<ast::Path
     let test_attr = attr::find_by_name(&krate.attrs, "test_runner")?;
     test_attr.meta_item_list().map(|meta_list| {
         if meta_list.len() != 1 {
-            sd.span_fatal(test_attr.span(),
+            sd.span_fatal(test_attr.span,
                 "#![test_runner(..)] accepts exactly 1 argument").raise()
         }
-        meta_list[0].word().as_ref().unwrap().ident.clone()
+        match meta_list[0].meta_item() {
+            Some(meta_item) if meta_item.is_word() => meta_item.path.clone(),
+            _ => sd.span_fatal(test_attr.span, "`test_runner` argument must be a path").raise()
+        }
     })
 }
diff --git a/src/libsyntax_ext/deriving/custom.rs b/src/libsyntax_ext/deriving/custom.rs
index cfc3c931598..e73110717e9 100644
--- a/src/libsyntax_ext/deriving/custom.rs
+++ b/src/libsyntax_ext/deriving/custom.rs
@@ -17,9 +17,11 @@ struct MarkAttrs<'a>(&'a [ast::Name]);
 
 impl<'a> Visitor<'a> for MarkAttrs<'a> {
     fn visit_attribute(&mut self, attr: &Attribute) {
-        if self.0.contains(&attr.name()) {
-            mark_used(attr);
-            mark_known(attr);
+        if let Some(ident) = attr.ident() {
+            if self.0.contains(&ident.name) {
+                mark_used(attr);
+                mark_known(attr);
+            }
         }
     }
 
diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs
index b8f96c5bc0e..2bb98c1bf62 100644
--- a/src/libsyntax_ext/deriving/generic/mod.rs
+++ b/src/libsyntax_ext/deriving/generic/mod.rs
@@ -463,12 +463,9 @@ impl<'a> TraitDef<'a> {
                 let mut attrs = newitem.attrs.clone();
                 attrs.extend(item.attrs
                     .iter()
-                    .filter(|a| {
-                        match &*a.name().as_str() {
-                            "allow" | "warn" | "deny" | "forbid" | "stable" | "unstable" => true,
-                            _ => false,
-                        }
-                    })
+                    .filter(|a| a.ident_str().map_or(false, |name| {
+                        ["allow", "warn", "deny", "forbid", "stable", "unstable"].contains(&name)
+                    }))
                     .cloned());
                 push(Annotatable::Item(P(ast::Item { attrs: attrs, ..(*newitem).clone() })))
             }
diff --git a/src/libsyntax_ext/proc_macro_decls.rs b/src/libsyntax_ext/proc_macro_decls.rs
index d8f8decef39..d5f37aff222 100644
--- a/src/libsyntax_ext/proc_macro_decls.rs
+++ b/src/libsyntax_ext/proc_macro_decls.rs
@@ -109,52 +109,67 @@ impl<'a> CollectProcMacros<'a> {
             None => return,
         };
         if list.len() != 1 && list.len() != 2 {
-            self.handler.span_err(attr.span(),
+            self.handler.span_err(attr.span,
                                   "attribute must have either one or two arguments");
             return
         }
-        let trait_attr = &list[0];
-        let attributes_attr = list.get(1);
-        let trait_name = match trait_attr.name() {
-            Some(name) => name,
+        let trait_attr = match list[0].meta_item() {
+            Some(meta_item) => meta_item,
             _ => {
-                self.handler.span_err(trait_attr.span(), "not a meta item");
+                self.handler.span_err(list[0].span(), "not a meta item");
+                return
+            }
+        };
+        let trait_ident = match trait_attr.ident() {
+            Some(trait_ident) if trait_attr.is_word() => trait_ident,
+            _ => {
+                self.handler.span_err(trait_attr.span, "must only be one word");
                 return
             }
         };
-        if !trait_attr.is_word() {
-            self.handler.span_err(trait_attr.span(), "must only be one word");
-        }
 
-        if deriving::is_builtin_trait(trait_name) {
-            self.handler.span_err(trait_attr.span(),
-                                  "cannot override a built-in #[derive] mode");
+        if !trait_ident.can_be_raw() {
+            self.handler.span_err(trait_attr.span,
+                                  &format!("`{}` cannot be a name of derive macro", trait_ident));
+        }
+        if deriving::is_builtin_trait(trait_ident.name) {
+            self.handler.span_err(trait_attr.span,
+                                  "cannot override a built-in derive macro");
         }
 
+        let attributes_attr = list.get(1);
         let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr {
             if !attr.check_name("attributes") {
                 self.handler.span_err(attr.span(), "second argument must be `attributes`")
             }
             attr.meta_item_list().unwrap_or_else(|| {
                 self.handler.span_err(attr.span(),
-                                      "attribute must be of form: \
-                                       `attributes(foo, bar)`");
+                                      "attribute must be of form: `attributes(foo, bar)`");
                 &[]
             }).into_iter().filter_map(|attr| {
-                let name = match attr.name() {
-                    Some(name) => name,
+                let attr = match attr.meta_item() {
+                    Some(meta_item) => meta_item,
                     _ => {
                         self.handler.span_err(attr.span(), "not a meta item");
                         return None;
-                    },
+                    }
                 };
 
-                if !attr.is_word() {
-                    self.handler.span_err(attr.span(), "must only be one word");
-                    return None;
+                let ident = match attr.ident() {
+                    Some(ident) if attr.is_word() => ident,
+                    _ => {
+                        self.handler.span_err(attr.span, "must only be one word");
+                        return None;
+                    }
+                };
+                if !ident.can_be_raw() {
+                    self.handler.span_err(
+                        attr.span,
+                        &format!("`{}` cannot be a name of derive helper attribute", ident),
+                    );
                 }
 
-                Some(name)
+                Some(ident.name)
             }).collect()
         } else {
             Vec::new()
@@ -163,7 +178,7 @@ impl<'a> CollectProcMacros<'a> {
         if self.in_root && item.vis.node.is_pub() {
             self.derives.push(ProcMacroDerive {
                 span: item.span,
-                trait_name,
+                trait_name: trait_ident.name,
                 function_name: item.ident,
                 attrs: proc_attrs,
             });
@@ -247,8 +262,8 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
                                 to the same function", attr.path, prev_attr.path)
                     };
 
-                    self.handler.struct_span_err(attr.span(), &msg)
-                        .span_note(prev_attr.span(), "Previous attribute here")
+                    self.handler.struct_span_err(attr.span, &msg)
+                        .span_note(prev_attr.span, "Previous attribute here")
                         .emit();
 
                     return;
@@ -273,7 +288,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
             let msg = format!("the `#[{}]` attribute may only be used on bare functions",
                               attr.path);
 
-            self.handler.span_err(attr.span(), &msg);
+            self.handler.span_err(attr.span, &msg);
             return;
         }
 
@@ -285,7 +300,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
             let msg = format!("the `#[{}]` attribute is only usable with crates of the \
                               `proc-macro` crate type", attr.path);
 
-            self.handler.span_err(attr.span(), &msg);
+            self.handler.span_err(attr.span, &msg);
             return;
         }
 
diff --git a/src/libsyntax_ext/proc_macro_server.rs b/src/libsyntax_ext/proc_macro_server.rs
index a7ac95ba9ef..c0a9dfe6189 100644
--- a/src/libsyntax_ext/proc_macro_server.rs
+++ b/src/libsyntax_ext/proc_macro_server.rs
@@ -340,12 +340,8 @@ impl Ident {
         if !Self::is_valid(string) {
             panic!("`{:?}` is not a valid identifier", string)
         }
-        if is_raw {
-            let normalized_sym = Symbol::intern(string);
-            if normalized_sym == keywords::Underscore.name() ||
-               ast::Ident::with_empty_ctxt(normalized_sym).is_path_segment_keyword() {
-                panic!("`{:?}` is not a valid raw identifier", string)
-            }
+        if is_raw && !ast::Ident::from_str(string).can_be_raw() {
+            panic!("`{}` cannot be a raw identifier", string);
         }
         Ident { sym, is_raw, span }
     }
diff --git a/src/libsyntax_ext/test.rs b/src/libsyntax_ext/test.rs
index f4b625f8ea2..0dbcb7ce0b7 100644
--- a/src/libsyntax_ext/test.rs
+++ b/src/libsyntax_ext/test.rs
@@ -227,7 +227,7 @@ fn should_panic(cx: &ExtCtxt<'_>, i: &ast::Item) -> ShouldPanic {
                         .and_then(|mi| mi.value_str());
                     if list.len() != 1 || msg.is_none() {
                         sd.struct_span_warn(
-                            attr.span(),
+                            attr.span,
                             "argument must be of the form: \
                              `expected = \"error message\"`"
                         ).note("Errors in this attribute were erroneously \
diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs
index c5301f9f174..e8d215a562e 100644
--- a/src/libsyntax_pos/symbol.rs
+++ b/src/libsyntax_pos/symbol.rs
@@ -484,11 +484,16 @@ impl Ident {
         self.name == keywords::DollarCrate.name()
     }
 
-    // We see this identifier in a normal identifier position, like variable name or a type.
-    // How was it written originally? Did it use the raw form? Let's try to guess.
-    pub fn is_raw_guess(self) -> bool {
+    /// This identifier can be a raw identifier.
+    pub fn can_be_raw(self) -> bool {
         self.name != keywords::Invalid.name() && self.name != keywords::Underscore.name() &&
-        self.is_reserved() && !self.is_path_segment_keyword()
+        !self.is_path_segment_keyword()
+    }
+
+    /// We see this identifier in a normal identifier position, like variable name or a type.
+    /// How was it written originally? Did it use the raw form? Let's try to guess.
+    pub fn is_raw_guess(self) -> bool {
+        self.can_be_raw() && self.is_reserved()
     }
 }
 
diff --git a/src/test/ui/parser/raw/raw-literal-self.rs b/src/test/ui/parser/raw/raw-literal-self.rs
index d7b9553d032..123a11b6f85 100644
--- a/src/test/ui/parser/raw/raw-literal-self.rs
+++ b/src/test/ui/parser/raw/raw-literal-self.rs
@@ -1,3 +1,4 @@
-fn self_test(r#self: u32) {
-    //~^ ERROR `r#self` is not currently supported.
+fn main() {
+    let r#self;
+    //~^ ERROR `self` cannot be a raw identifier
 }
diff --git a/src/test/ui/parser/raw/raw-literal-self.stderr b/src/test/ui/parser/raw/raw-literal-self.stderr
index e64332785cc..9a330fcdf2a 100644
--- a/src/test/ui/parser/raw/raw-literal-self.stderr
+++ b/src/test/ui/parser/raw/raw-literal-self.stderr
@@ -1,8 +1,8 @@
-error: `r#self` is not currently supported.
-  --> $DIR/raw-literal-self.rs:1:14
+error: `self` cannot be a raw identifier
+  --> $DIR/raw-literal-self.rs:2:9
    |
-LL | fn self_test(r#self: u32) {
-   |              ^^^^^^
+LL |     let r#self;
+   |         ^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/raw/raw-literal-underscore.rs b/src/test/ui/parser/raw/raw-literal-underscore.rs
index bbedd395202..6d15f1e7f0a 100644
--- a/src/test/ui/parser/raw/raw-literal-underscore.rs
+++ b/src/test/ui/parser/raw/raw-literal-underscore.rs
@@ -1,3 +1,4 @@
-fn underscore_test(r#_: u32) {
-    //~^ ERROR `r#_` is not currently supported.
+fn main() {
+    let r#_;
+    //~^ ERROR `_` cannot be a raw identifier
 }
diff --git a/src/test/ui/parser/raw/raw-literal-underscore.stderr b/src/test/ui/parser/raw/raw-literal-underscore.stderr
index 9427b33afd8..d96b14f55a3 100644
--- a/src/test/ui/parser/raw/raw-literal-underscore.stderr
+++ b/src/test/ui/parser/raw/raw-literal-underscore.stderr
@@ -1,8 +1,8 @@
-error: `r#_` is not currently supported.
-  --> $DIR/raw-literal-underscore.rs:1:20
+error: `_` cannot be a raw identifier
+  --> $DIR/raw-literal-underscore.rs:2:9
    |
-LL | fn underscore_test(r#_: u32) {
-   |                    ^^^
+LL |     let r#_;
+   |         ^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/proc-macro/attribute.rs b/src/test/ui/proc-macro/attribute.rs
index a0b982d75f5..ac7d0b4c2b6 100644
--- a/src/test/ui/proc-macro/attribute.rs
+++ b/src/test/ui/proc-macro/attribute.rs
@@ -4,53 +4,73 @@
 #![crate_type = "proc-macro"]
 
 extern crate proc_macro;
+use proc_macro::*;
 
 #[proc_macro_derive]
 //~^ ERROR: attribute must be of the form
-pub fn foo1(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
-    input
-}
+pub fn foo1(input: TokenStream) -> TokenStream { input }
 
-#[proc_macro_derive = "foo"]
+#[proc_macro_derive = ""]
 //~^ ERROR: attribute must be of the form
-pub fn foo2(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
-    input
-}
-
-#[proc_macro_derive(
-    a = "b"
-)]
-//~^^ ERROR: must only be one word
-pub fn foo3(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
-    input
-}
-
-#[proc_macro_derive(b, c, d)]
+pub fn foo2(input: TokenStream) -> TokenStream { input }
+
+#[proc_macro_derive(d3, a, b)]
+//~^ ERROR: attribute must have either one or two arguments
+pub fn foo3(input: TokenStream) -> TokenStream { input }
+
+#[proc_macro_derive(d4, attributes(a), b)]
 //~^ ERROR: attribute must have either one or two arguments
-pub fn foo4(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
-    input
-}
+pub fn foo4(input: TokenStream) -> TokenStream { input }
+
+#[proc_macro_derive("a")]
+//~^ ERROR: not a meta item
+pub fn foo5(input: TokenStream) -> TokenStream { input }
 
-#[proc_macro_derive(d(e))]
+#[proc_macro_derive(d6 = "")]
 //~^ ERROR: must only be one word
-pub fn foo5(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
-    input
-}
+pub fn foo6(input: TokenStream) -> TokenStream { input }
 
-#[proc_macro_derive(f, attributes(g = "h"))]
+#[proc_macro_derive(m::d7)]
 //~^ ERROR: must only be one word
-pub fn foo6(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
-    input
-}
+pub fn foo7(input: TokenStream) -> TokenStream { input }
 
-#[proc_macro_derive(i, attributes(j(k)))]
+#[proc_macro_derive(d8(a))]
 //~^ ERROR: must only be one word
-pub fn foo7(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
-    input
-}
+pub fn foo8(input: TokenStream) -> TokenStream { input }
 
-#[proc_macro_derive(l, attributes(m), n)]
-//~^ ERROR: attribute must have either one or two arguments
-pub fn foo8(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
-    input
-}
+#[proc_macro_derive(self)]
+//~^ ERROR: `self` cannot be a name of derive macro
+pub fn foo9(input: TokenStream) -> TokenStream { input }
+
+#[proc_macro_derive(PartialEq)]
+//~^ ERROR: cannot override a built-in derive macro
+pub fn foo10(input: TokenStream) -> TokenStream { input }
+
+#[proc_macro_derive(d11, a)]
+//~^ ERROR: second argument must be `attributes`
+//~| ERROR: attribute must be of form: `attributes(foo, bar)`
+pub fn foo11(input: TokenStream) -> TokenStream { input }
+
+#[proc_macro_derive(d12, attributes)]
+//~^ ERROR: attribute must be of form: `attributes(foo, bar)`
+pub fn foo12(input: TokenStream) -> TokenStream { input }
+
+#[proc_macro_derive(d13, attributes("a"))]
+//~^ ERROR: not a meta item
+pub fn foo13(input: TokenStream) -> TokenStream { input }
+
+#[proc_macro_derive(d14, attributes(a = ""))]
+//~^ ERROR: must only be one word
+pub fn foo14(input: TokenStream) -> TokenStream { input }
+
+#[proc_macro_derive(d15, attributes(m::a))]
+//~^ ERROR: must only be one word
+pub fn foo15(input: TokenStream) -> TokenStream { input }
+
+#[proc_macro_derive(d16, attributes(a(b)))]
+//~^ ERROR: must only be one word
+pub fn foo16(input: TokenStream) -> TokenStream { input }
+
+#[proc_macro_derive(d17, attributes(self))]
+//~^ ERROR: `self` cannot be a name of derive helper attribute
+pub fn foo17(input: TokenStream) -> TokenStream { input }
diff --git a/src/test/ui/proc-macro/attribute.stderr b/src/test/ui/proc-macro/attribute.stderr
index 231eb1f1068..cc17d383569 100644
--- a/src/test/ui/proc-macro/attribute.stderr
+++ b/src/test/ui/proc-macro/attribute.stderr
@@ -1,50 +1,110 @@
-error: must only be one word
-  --> $DIR/attribute.rs:21:5
+error: attribute must have either one or two arguments
+  --> $DIR/attribute.rs:17:1
    |
-LL |     a = "b"
-   |     ^^^^^^^
+LL | #[proc_macro_derive(d3, a, b)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: attribute must have either one or two arguments
-  --> $DIR/attribute.rs:28:1
+  --> $DIR/attribute.rs:21:1
+   |
+LL | #[proc_macro_derive(d4, attributes(a), b)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: not a meta item
+  --> $DIR/attribute.rs:25:21
+   |
+LL | #[proc_macro_derive("a")]
+   |                     ^^^
+
+error: must only be one word
+  --> $DIR/attribute.rs:29:21
    |
-LL | #[proc_macro_derive(b, c, d)]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | #[proc_macro_derive(d6 = "")]
+   |                     ^^^^^^^
 
 error: must only be one word
-  --> $DIR/attribute.rs:34:21
+  --> $DIR/attribute.rs:33:21
    |
-LL | #[proc_macro_derive(d(e))]
+LL | #[proc_macro_derive(m::d7)]
+   |                     ^^^^^
+
+error: must only be one word
+  --> $DIR/attribute.rs:37:21
+   |
+LL | #[proc_macro_derive(d8(a))]
+   |                     ^^^^^
+
+error: `self` cannot be a name of derive macro
+  --> $DIR/attribute.rs:41:21
+   |
+LL | #[proc_macro_derive(self)]
    |                     ^^^^
 
+error: cannot override a built-in derive macro
+  --> $DIR/attribute.rs:45:21
+   |
+LL | #[proc_macro_derive(PartialEq)]
+   |                     ^^^^^^^^^
+
+error: second argument must be `attributes`
+  --> $DIR/attribute.rs:49:26
+   |
+LL | #[proc_macro_derive(d11, a)]
+   |                          ^
+
+error: attribute must be of form: `attributes(foo, bar)`
+  --> $DIR/attribute.rs:49:26
+   |
+LL | #[proc_macro_derive(d11, a)]
+   |                          ^
+
+error: attribute must be of form: `attributes(foo, bar)`
+  --> $DIR/attribute.rs:54:26
+   |
+LL | #[proc_macro_derive(d12, attributes)]
+   |                          ^^^^^^^^^^
+
+error: not a meta item
+  --> $DIR/attribute.rs:58:37
+   |
+LL | #[proc_macro_derive(d13, attributes("a"))]
+   |                                     ^^^
+
 error: must only be one word
-  --> $DIR/attribute.rs:40:35
+  --> $DIR/attribute.rs:62:37
    |
-LL | #[proc_macro_derive(f, attributes(g = "h"))]
-   |                                   ^^^^^^^
+LL | #[proc_macro_derive(d14, attributes(a = ""))]
+   |                                     ^^^^^^
 
 error: must only be one word
-  --> $DIR/attribute.rs:46:35
+  --> $DIR/attribute.rs:66:37
    |
-LL | #[proc_macro_derive(i, attributes(j(k)))]
-   |                                   ^^^^
+LL | #[proc_macro_derive(d15, attributes(m::a))]
+   |                                     ^^^^
 
-error: attribute must have either one or two arguments
-  --> $DIR/attribute.rs:52:1
+error: must only be one word
+  --> $DIR/attribute.rs:70:37
+   |
+LL | #[proc_macro_derive(d16, attributes(a(b)))]
+   |                                     ^^^^
+
+error: `self` cannot be a name of derive helper attribute
+  --> $DIR/attribute.rs:74:37
    |
-LL | #[proc_macro_derive(l, attributes(m), n)]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | #[proc_macro_derive(d17, attributes(self))]
+   |                                     ^^^^
 
 error: attribute must be of the form `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
-  --> $DIR/attribute.rs:8:1
+  --> $DIR/attribute.rs:9:1
    |
 LL | #[proc_macro_derive]
    | ^^^^^^^^^^^^^^^^^^^^
 
 error: attribute must be of the form `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
-  --> $DIR/attribute.rs:14:1
+  --> $DIR/attribute.rs:13:1
    |
-LL | #[proc_macro_derive = "foo"]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | #[proc_macro_derive = ""]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 8 previous errors
+error: aborting due to 18 previous errors
 
diff --git a/src/test/ui/proc-macro/invalid-punct-ident-3.stderr b/src/test/ui/proc-macro/invalid-punct-ident-3.stderr
index 6ff47e3752c..24371f3a2a6 100644
--- a/src/test/ui/proc-macro/invalid-punct-ident-3.stderr
+++ b/src/test/ui/proc-macro/invalid-punct-ident-3.stderr
@@ -4,7 +4,7 @@ error: proc macro panicked
 LL | invalid_raw_ident!();
    | ^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: message: `"self"` is not a valid raw identifier
+   = help: message: `self` cannot be a raw identifier
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/proc-macro/shadow-builtin.rs b/src/test/ui/proc-macro/shadow-builtin.rs
deleted file mode 100644
index afcc0ebc346..00000000000
--- a/src/test/ui/proc-macro/shadow-builtin.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-// force-host
-// no-prefer-dynamic
-
-#![crate_type = "proc-macro"]
-
-extern crate proc_macro;
-
-use proc_macro::TokenStream;
-
-#[proc_macro_derive(PartialEq)]
-//~^ ERROR: cannot override a built-in #[derive] mode
-pub fn foo(input: TokenStream) -> TokenStream {
-    input
-}
diff --git a/src/test/ui/proc-macro/shadow-builtin.stderr b/src/test/ui/proc-macro/shadow-builtin.stderr
deleted file mode 100644
index 668579509dc..00000000000
--- a/src/test/ui/proc-macro/shadow-builtin.stderr
+++ /dev/null
@@ -1,8 +0,0 @@
-error: cannot override a built-in #[derive] mode
-  --> $DIR/shadow-builtin.rs:10:21
-   |
-LL | #[proc_macro_derive(PartialEq)]
-   |                     ^^^^^^^^^
-
-error: aborting due to previous error
-