diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs
index d9963f23a1593..bb6329dad4b9c 100644
--- a/src/librustc/hir/intravisit.rs
+++ b/src/librustc/hir/intravisit.rs
@@ -41,7 +41,7 @@
 //! This order consistency is required in a few places in rustc, for
 //! example generator inference, and possibly also HIR borrowck.
 
-use syntax::ast::{NodeId, CRATE_NODE_ID, Ident, Name, Attribute};
+use syntax::ast::{NodeId, CRATE_NODE_ID, Ident, Name};
 use syntax_pos::Span;
 use hir::*;
 use hir::def::Def;
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index dc8baa112bb59..0733d5c6e176d 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -483,7 +483,7 @@ impl<'a> LoweringContext<'a> {
         visit::walk_crate(&mut ItemLowerer { lctx: &mut self }, c);
 
         let module = self.lower_mod(&c.module);
-        let attrs = self.lower_attrs(&c.attrs);
+        let attrs = self.lower_attrs(&c.attrs).into();
         let body_ids = body_ids(&self.bodies);
 
         self.resolver
@@ -1057,21 +1057,32 @@ impl<'a> LoweringContext<'a> {
         }
     }
 
-    fn lower_attrs(&mut self, attrs: &[Attribute]) -> hir::HirVec<Attribute> {
+    fn lower_attrs(&mut self, attrs: &[Attribute]) -> Vec<hir::Attribute> {
         attrs
             .iter()
             .map(|a| self.lower_attr(a))
             .collect()
     }
 
-    fn lower_attr(&mut self, attr: &Attribute) -> Attribute {
-        // Note that we explicitly do not walk the path. Since we don't really
-        // lower attributes (we use the AST version) there is nowhere to keep
-        // the HirIds. We don't actually need HIR version of attributes anyway.
-        Attribute {
+    fn lower_attr(&mut self, attr: &Attribute) -> hir::Attribute {
+        hir::Attribute {
             id: attr.id,
             style: attr.style,
-            path: attr.path.clone(),
+            // HACK(eddyb) manual conversion because `lower_path(_extra)`
+            // use `lower_path_segment` and that allocates `ItemLocalId`s.
+            path: hir::Path {
+                def: Def::Err,
+                segments: attr.path.segments.iter().map(|segment| {
+                    hir::PathSegment::new(
+                        segment.ident,
+                        None,
+                        None,
+                        hir::GenericArgs::none(),
+                        false,
+                    )
+                }).collect(),
+                span: attr.path.span,
+            },
             tokens: self.lower_token_stream(attr.tokens.clone()),
             is_sugared_doc: attr.is_sugared_doc,
             span: attr.span,
@@ -1110,7 +1121,7 @@ impl<'a> LoweringContext<'a> {
 
     fn lower_arm(&mut self, arm: &Arm) -> hir::Arm {
         hir::Arm {
-            attrs: self.lower_attrs(&arm.attrs),
+            attrs: self.lower_attrs(&arm.attrs).into(),
             pats: arm.pats.iter().map(|x| self.lower_pat(x)).collect(),
             guard: match arm.guard {
                 Some(Guard::If(ref x)) => Some(hir::Guard::If(P(self.lower_expr(x)))),
@@ -1576,7 +1587,7 @@ impl<'a> LoweringContext<'a> {
         Spanned {
             node: hir::VariantKind {
                 name: v.node.ident.name,
-                attrs: self.lower_attrs(&v.node.attrs),
+                attrs: self.lower_attrs(&v.node.attrs).into(),
                 data: self.lower_variant_data(&v.node.data),
                 disr_expr: v.node.disr_expr.as_ref().map(|e| self.lower_anon_const(e)),
             },
@@ -1967,7 +1978,7 @@ impl<'a> LoweringContext<'a> {
             pat: self.lower_pat(&l.pat),
             init: l.init.as_ref().map(|e| P(self.lower_expr(e))),
             span: l.span,
-            attrs: l.attrs.clone(),
+            attrs: self.lower_attrs(&l.attrs).into(),
             source: hir::LocalSource::Normal,
         }), ids)
     }
@@ -2410,7 +2421,7 @@ impl<'a> LoweringContext<'a> {
                     name: param_name,
                     span: lt.span,
                     pure_wrt_drop: attr::contains_name(&param.attrs, "may_dangle"),
-                    attrs: self.lower_attrs(&param.attrs),
+                    attrs: self.lower_attrs(&param.attrs).into(),
                     bounds,
                     kind: hir::GenericParamKind::Lifetime {
                         kind: hir::LifetimeParamKind::Explicit,
@@ -2443,7 +2454,7 @@ impl<'a> LoweringContext<'a> {
                     id: self.lower_node_id(param.id).node_id,
                     name: hir::ParamName::Plain(ident),
                     pure_wrt_drop: attr::contains_name(&param.attrs, "may_dangle"),
-                    attrs: self.lower_attrs(&param.attrs),
+                    attrs: self.lower_attrs(&param.attrs).into(),
                     bounds,
                     span: ident.span,
                     kind: hir::GenericParamKind::Type {
@@ -2667,7 +2678,7 @@ impl<'a> LoweringContext<'a> {
             },
             vis: self.lower_visibility(&f.vis, None),
             ty: self.lower_ty(&f.ty, ImplTraitContext::disallowed()),
-            attrs: self.lower_attrs(&f.attrs),
+            attrs: self.lower_attrs(&f.attrs).into(),
         }
     }
 
@@ -2750,7 +2761,7 @@ impl<'a> LoweringContext<'a> {
         &mut self,
         id: NodeId,
         name: &mut Name,
-        attrs: &hir::HirVec<Attribute>,
+        attrs: &hir::HirVec<hir::Attribute>,
         vis: &mut hir::Visibility,
         i: &ItemKind,
     ) -> hir::ItemKind {
@@ -2956,7 +2967,7 @@ impl<'a> LoweringContext<'a> {
         id: NodeId,
         vis: &mut hir::Visibility,
         name: &mut Name,
-        attrs: &hir::HirVec<Attribute>,
+        attrs: &hir::HirVec<hir::Attribute>,
     ) -> hir::ItemKind {
         debug!("lower_use_tree(tree={:?})", tree);
         debug!("lower_use_tree: vis = {:?}", vis);
@@ -3257,7 +3268,7 @@ impl<'a> LoweringContext<'a> {
             id: node_id,
             hir_id,
             ident: i.ident,
-            attrs: self.lower_attrs(&i.attrs),
+            attrs: self.lower_attrs(&i.attrs).into(),
             generics,
             node,
             span: i.span,
@@ -3333,7 +3344,7 @@ impl<'a> LoweringContext<'a> {
             id: node_id,
             hir_id,
             ident: i.ident,
-            attrs: self.lower_attrs(&i.attrs),
+            attrs: self.lower_attrs(&i.attrs).into(),
             generics,
             vis: self.lower_visibility(&i.vis, None),
             defaultness: self.lower_defaultness(i.defaultness, true /* [1] */),
@@ -3427,7 +3438,7 @@ impl<'a> LoweringContext<'a> {
     pub fn lower_item(&mut self, i: &Item) -> Option<hir::Item> {
         let mut name = i.ident.name;
         let mut vis = self.lower_visibility(&i.vis, None);
-        let attrs = self.lower_attrs(&i.attrs);
+        let attrs = self.lower_attrs(&i.attrs).into();
         if let ItemKind::MacroDef(ref def) = i.node {
             if !def.legacy || attr::contains_name(&i.attrs, "macro_export") ||
                               attr::contains_name(&i.attrs, "rustc_doc_only_macro") {
@@ -3466,7 +3477,7 @@ impl<'a> LoweringContext<'a> {
         hir::ForeignItem {
             id: node_id,
             name: i.ident.name,
-            attrs: self.lower_attrs(&i.attrs),
+            attrs: self.lower_attrs(&i.attrs).into(),
             node: match i.node {
                 ForeignItemKind::Fn(ref fdec, ref generics) => {
                     let (generics, (fn_dec, fn_args)) = self.add_in_band_defs(
@@ -4025,7 +4036,7 @@ impl<'a> LoweringContext<'a> {
                         hir::ExprKind::Struct(struct_path, fields, None)
                     },
                     span: e.span,
-                    attrs: e.attrs.clone(),
+                    attrs: self.lower_attrs(&e.attrs).into(),
                 };
             }
             ExprKind::Path(ref qself, ref path) => {
@@ -4111,7 +4122,7 @@ impl<'a> LoweringContext<'a> {
                     ex.span = e.span;
                 }
                 // merge attributes into the inner expression.
-                let mut attrs = e.attrs.clone();
+                let mut attrs: ThinVec<_> = self.lower_attrs(&e.attrs).into();
                 attrs.extend::<Vec<_>>(ex.attrs.into());
                 ex.attrs = attrs;
                 return ex;
@@ -4394,7 +4405,8 @@ impl<'a> LoweringContext<'a> {
                 let result = P(self.expr_ident(e.span, result_ident, let_stmt_binding));
                 let block = P(self.block_all(e.span, hir_vec![let_stmt], Some(result)));
                 // add the attributes to the outer returned expr node
-                return self.expr_block(block, e.attrs.clone());
+                let attrs = self.lower_attrs(&e.attrs).into();
+                return self.expr_block(block, attrs);
             }
 
             // Desugar ExprKind::Try
@@ -4507,7 +4519,7 @@ impl<'a> LoweringContext<'a> {
             hir_id,
             node: kind,
             span: e.span,
-            attrs: e.attrs.clone(),
+            attrs: self.lower_attrs(&e.attrs).into(),
         }
     }
 
@@ -4685,7 +4697,7 @@ impl<'a> LoweringContext<'a> {
         }
     }
 
-    fn expr_break(&mut self, span: Span, attrs: ThinVec<Attribute>) -> P<hir::Expr> {
+    fn expr_break(&mut self, span: Span, attrs: ThinVec<hir::Attribute>) -> P<hir::Expr> {
         let expr_break = hir::ExprKind::Break(self.lower_loop_destination(None), None);
         P(self.expr(span, expr_break, attrs))
     }
@@ -4708,7 +4720,7 @@ impl<'a> LoweringContext<'a> {
         span: Span,
         ident: Ident,
         binding: NodeId,
-        attrs: ThinVec<Attribute>,
+        attrs: ThinVec<hir::Attribute>,
     ) -> hir::Expr {
         let expr_path = hir::ExprKind::Path(hir::QPath::Resolved(
             None,
@@ -4731,7 +4743,7 @@ impl<'a> LoweringContext<'a> {
         span: Span,
         components: &[&str],
         params: Option<P<hir::GenericArgs>>,
-        attrs: ThinVec<Attribute>,
+        attrs: ThinVec<hir::Attribute>,
     ) -> hir::Expr {
         let path = self.std_path(span, components, params, true);
         self.expr(
@@ -4751,7 +4763,7 @@ impl<'a> LoweringContext<'a> {
         self.expr(span, hir::ExprKind::Match(arg, arms, source), ThinVec::new())
     }
 
-    fn expr_block(&mut self, b: P<hir::Block>, attrs: ThinVec<Attribute>) -> hir::Expr {
+    fn expr_block(&mut self, b: P<hir::Block>, attrs: ThinVec<hir::Attribute>) -> hir::Expr {
         self.expr(b.span, hir::ExprKind::Block(b, None), attrs)
     }
 
@@ -4759,7 +4771,12 @@ impl<'a> LoweringContext<'a> {
         P(self.expr(sp, hir::ExprKind::Tup(exprs), ThinVec::new()))
     }
 
-    fn expr(&mut self, span: Span, node: hir::ExprKind, attrs: ThinVec<Attribute>) -> hir::Expr {
+    fn expr(
+        &mut self,
+        span: Span,
+        node: hir::ExprKind,
+        attrs: ThinVec<hir::Attribute>,
+    ) -> hir::Expr {
         let LoweredNodeId { node_id, hir_id } = self.next_id();
         hir::Expr {
             id: node_id,
diff --git a/src/librustc/hir/map/blocks.rs b/src/librustc/hir/map/blocks.rs
index 1ab1c7d3fc5c4..5cb0b4017458a 100644
--- a/src/librustc/hir/map/blocks.rs
+++ b/src/librustc/hir/map/blocks.rs
@@ -23,9 +23,9 @@
 
 use hir as ast;
 use hir::map;
-use hir::{Expr, FnDecl, Node};
+use hir::{Attribute, Expr, FnDecl, Node};
 use hir::intravisit::FnKind;
-use syntax::ast::{Attribute, Ident, Name, NodeId};
+use syntax::ast::{Ident, Name, NodeId};
 use syntax_pos::Span;
 
 /// An FnLikeNode is a Node that is like a fn, in that it has a decl
diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs
index ef777abfbc41a..e3dac03ca2b38 100644
--- a/src/librustc/hir/map/mod.rs
+++ b/src/librustc/hir/map/mod.rs
@@ -504,7 +504,7 @@ impl<'hir> Map<'hir> {
     /// Get the attributes on the krate. This is preferable to
     /// invoking `krate.attrs` because it registers a tighter
     /// dep-graph access.
-    pub fn krate_attrs(&self) -> &'hir [ast::Attribute] {
+    pub fn krate_attrs(&self) -> &'hir [Attribute] {
         let def_path_hash = self.definitions.def_path_hash(CRATE_DEF_INDEX);
 
         self.dep_graph.read(def_path_hash.to_dep_node(DepKind::Hir));
@@ -834,7 +834,7 @@ impl<'hir> Map<'hir> {
 
     /// Given a node ID, get a list of attributes associated with the AST
     /// corresponding to the Node ID
-    pub fn attrs(&self, id: NodeId) -> &'hir [ast::Attribute] {
+    pub fn attrs(&self, id: NodeId) -> &'hir [Attribute] {
         self.read(id); // reveals attributes on the node
         let attrs = match self.find(id) {
             Some(Node::Item(i)) => Some(&i.attrs[..]),
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index 1674320165e65..194c09386d1ec 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -27,8 +27,8 @@ use syntax_pos::{Span, DUMMY_SP, symbol::InternedString};
 use syntax::source_map::{self, Spanned};
 use rustc_target::spec::abi::Abi;
 use syntax::ast::{self, CrateSugar, Ident, Name, NodeId, DUMMY_NODE_ID, AsmDialect};
-use syntax::ast::{Attribute, Lit, StrStyle, FloatTy, IntTy, UintTy};
-use syntax::attr::InlineAttr;
+use syntax::ast::{Lit, StrStyle, FloatTy, IntTy, UintTy};
+use syntax::attr::{self, InlineAttr};
 use syntax::ext::hygiene::SyntaxContext;
 use syntax::ptr::P;
 use syntax::symbol::{Symbol, keywords};
@@ -150,6 +150,12 @@ pub const DUMMY_HIR_ID: HirId = HirId {
 
 pub const DUMMY_ITEM_LOCAL_ID: ItemLocalId = ItemLocalId::MAX;
 
+pub type Attribute = ast::Attribute<Path>;
+pub type MetaItem = ast::MetaItem<Path>;
+pub type MetaItemKind = ast::MetaItemKind<Path>;
+pub type NestedMetaItem = ast::NestedMetaItem<Path>;
+pub type NestedMetaItemKind = ast::NestedMetaItemKind<Path>;
+
 #[derive(Clone, RustcEncodable, RustcDecodable, Copy)]
 pub struct Label {
     pub ident: Ident,
@@ -327,6 +333,34 @@ impl fmt::Display for Path {
     }
 }
 
+impl attr::Path for Path {
+    type Segment = PathSegment;
+
+    fn from_span_and_segments(span: Span, segments: Vec<Self::Segment>) -> Self {
+        Self {
+            span,
+            def: Def::Err,
+            segments: HirVec::from(segments),
+        }
+    }
+
+    fn from_nt(
+        _: &::syntax::parse::token::Nonterminal,
+    ) -> Result<Self, Option<ast::MetaItem<Self>>> {
+        bug!("interpolated tokens should not be present in the HIR")
+    }
+
+    #[inline]
+    fn span(&self) -> Span {
+        self.span
+    }
+
+    #[inline]
+    fn segments(&self) -> &[Self::Segment] {
+        &self.segments
+    }
+}
+
 /// A segment of a path: an identifier, an optional lifetime, and a set of
 /// types.
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
@@ -401,6 +435,17 @@ impl PathSegment {
     }
 }
 
+impl attr::PathSegment for PathSegment {
+    fn from_ident(ident: Ident) -> Self {
+        Self::from_ident(ident)
+    }
+
+    #[inline]
+    fn ident(&self) -> Ident {
+        self.ident
+    }
+}
+
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub enum GenericArg {
     Lifetime(Lifetime),
diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs
index 9a0ceddcf1b4a..985d1e8583d25 100644
--- a/src/librustc/hir/print.rs
+++ b/src/librustc/hir/print.rs
@@ -339,7 +339,7 @@ impl<'a> State<'a> {
         self.commasep_cmnt(b, exprs, |s, e| s.print_expr(&e), |e| e.span)
     }
 
-    pub fn print_mod(&mut self, _mod: &hir::Mod, attrs: &[ast::Attribute]) -> io::Result<()> {
+    pub fn print_mod(&mut self, _mod: &hir::Mod, attrs: &[hir::Attribute]) -> io::Result<()> {
         self.print_inner_attributes(attrs)?;
         for &item_id in &_mod.item_ids {
             self.ann.nested(self, Nested::Item(item_id))?;
@@ -349,7 +349,7 @@ impl<'a> State<'a> {
 
     pub fn print_foreign_mod(&mut self,
                              nmod: &hir::ForeignMod,
-                             attrs: &[ast::Attribute])
+                             attrs: &[hir::Attribute])
                              -> io::Result<()> {
         self.print_inner_attributes(attrs)?;
         for item in &nmod.items {
@@ -1035,7 +1035,7 @@ impl<'a> State<'a> {
 
     pub fn print_block_with_attrs(&mut self,
                                   blk: &hir::Block,
-                                  attrs: &[ast::Attribute])
+                                  attrs: &[hir::Attribute])
                                   -> io::Result<()> {
         self.print_block_maybe_unclosed(blk, indent_unit, attrs, true)
     }
@@ -1043,7 +1043,7 @@ impl<'a> State<'a> {
     pub fn print_block_maybe_unclosed(&mut self,
                                       blk: &hir::Block,
                                       indented: usize,
-                                      attrs: &[ast::Attribute],
+                                      attrs: &[hir::Attribute],
                                       close_box: bool)
                                       -> io::Result<()> {
         match blk.rules {
diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs
index ae0d78d2958ad..f8c13c92a275c 100644
--- a/src/librustc/ich/impls_hir.rs
+++ b/src/librustc/ich/impls_hir.rs
@@ -17,6 +17,7 @@ use hir::def_id::{DefId, LocalDefId, CrateNum, CRATE_DEF_INDEX};
 use ich::{StableHashingContext, NodeIdHashingMode, Fingerprint};
 use rustc_data_structures::stable_hasher::{HashStable, ToStableHashKey,
                                            StableHasher, StableHasherResult};
+use smallvec::SmallVec;
 use std::mem;
 use syntax::ast;
 use syntax::attr;
@@ -1197,3 +1198,91 @@ impl_stable_hash_for!(struct hir::Freevar {
     def,
     span
 });
+
+impl<'a> HashStable<StableHashingContext<'a>> for [hir::Attribute] {
+    fn hash_stable<W: StableHasherResult>(&self,
+                                          hcx: &mut StableHashingContext<'a>,
+                                          hasher: &mut StableHasher<W>) {
+        if self.len() == 0 {
+            self.len().hash_stable(hcx, hasher);
+            return
+        }
+
+        // Some attributes are always ignored during hashing.
+        let filtered: SmallVec<[&hir::Attribute; 8]> = self
+            .iter()
+            .filter(|attr| {
+                !attr.is_sugared_doc && !hcx.is_ignored_attr(attr.name())
+            })
+            .collect();
+
+        filtered.len().hash_stable(hcx, hasher);
+        for attr in filtered {
+            attr.hash_stable(hcx, hasher);
+        }
+    }
+}
+
+impl<'a> HashStable<StableHashingContext<'a>> for hir::Attribute {
+    fn hash_stable<W: StableHasherResult>(&self,
+                                          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.is_sugared_doc);
+
+        let hir::Attribute {
+            id: _,
+            style,
+            ref path,
+            ref tokens,
+            is_sugared_doc: _,
+            span,
+        } = *self;
+
+        style.hash_stable(hcx, hasher);
+        path.hash_stable(hcx, hasher);
+        for tt in tokens.trees() {
+            tt.hash_stable(hcx, hasher);
+        }
+        span.hash_stable(hcx, hasher);
+    }
+}
+impl_stable_hash_for_spanned!(hir::NestedMetaItemKind);
+
+// HACK(eddyb) need manual impl because `hir::NestedMetaItemKind` is an alias.
+impl<'a> HashStable<StableHashingContext<'a>> for hir::NestedMetaItemKind {
+    fn hash_stable<W: StableHasherResult>(&self,
+                                          hcx: &mut StableHashingContext<'a>,
+                                          hasher: &mut StableHasher<W>) {
+        use syntax::ast::NestedMetaItemKind::*;
+
+        mem::discriminant(self).hash_stable(hcx, hasher);
+        match self {
+            MetaItem(meta_item) => meta_item.hash_stable(hcx, hasher),
+            Literal(lit) => lit.hash_stable(hcx, hasher),
+        }
+    }
+}
+
+impl_stable_hash_for!(struct hir::MetaItem {
+    ident,
+    node,
+    span
+});
+
+// HACK(eddyb) need manual impl because `hir::MetaItemKind` is an alias.
+impl<'a> HashStable<StableHashingContext<'a>> for hir::MetaItemKind {
+    fn hash_stable<W: StableHasherResult>(&self,
+                                          hcx: &mut StableHashingContext<'a>,
+                                          hasher: &mut StableHasher<W>) {
+        use syntax::ast::MetaItemKind::*;
+
+        mem::discriminant(self).hash_stable(hcx, hasher);
+        match self {
+            Word => {}
+            List(nested_items) => nested_items.hash_stable(hcx, hasher),
+            NameValue(lit) => lit.hash_stable(hcx, hasher),
+        }
+    }
+}
diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs
index 4be467c01aac1..353da1aed7bd1 100644
--- a/src/librustc/ich/impls_syntax.rs
+++ b/src/librustc/ich/impls_syntax.rs
@@ -25,7 +25,6 @@ use syntax_pos::SourceFile;
 
 use hir::def_id::{DefId, CrateNum, CRATE_DEF_INDEX};
 
-use smallvec::SmallVec;
 use rustc_data_structures::stable_hasher::{HashStable, ToStableHashKey,
                                            StableHasher, StableHasherResult};
 
@@ -197,67 +196,6 @@ impl_stable_hash_for!(struct ::syntax::ast::Lifetime { id, ident });
 impl_stable_hash_for!(enum ::syntax::ast::StrStyle { Cooked, Raw(pounds) });
 impl_stable_hash_for!(enum ::syntax::ast::AttrStyle { Outer, Inner });
 
-impl<'a> HashStable<StableHashingContext<'a>> for [ast::Attribute] {
-    fn hash_stable<W: StableHasherResult>(&self,
-                                          hcx: &mut StableHashingContext<'a>,
-                                          hasher: &mut StableHasher<W>) {
-        if self.len() == 0 {
-            self.len().hash_stable(hcx, hasher);
-            return
-        }
-
-        // Some attributes are always ignored during hashing.
-        let filtered: SmallVec<[&ast::Attribute; 8]> = self
-            .iter()
-            .filter(|attr| {
-                !attr.is_sugared_doc && !hcx.is_ignored_attr(attr.name())
-            })
-            .collect();
-
-        filtered.len().hash_stable(hcx, hasher);
-        for attr in filtered {
-            attr.hash_stable(hcx, hasher);
-        }
-    }
-}
-
-impl<'a> HashStable<StableHashingContext<'a>> for ast::Path {
-    fn hash_stable<W: StableHasherResult>(&self,
-                                          hcx: &mut StableHashingContext<'a>,
-                                          hasher: &mut StableHasher<W>) {
-        self.segments.len().hash_stable(hcx, hasher);
-        for segment in &self.segments {
-            segment.ident.name.hash_stable(hcx, hasher);
-        }
-    }
-}
-
-impl<'a> HashStable<StableHashingContext<'a>> for ast::Attribute {
-    fn hash_stable<W: StableHasherResult>(&self,
-                                          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.is_sugared_doc);
-
-        let ast::Attribute {
-            id: _,
-            style,
-            ref path,
-            ref tokens,
-            is_sugared_doc: _,
-            span,
-        } = *self;
-
-        style.hash_stable(hcx, hasher);
-        path.hash_stable(hcx, hasher);
-        for tt in tokens.trees() {
-            tt.hash_stable(hcx, hasher);
-        }
-        span.hash_stable(hcx, hasher);
-    }
-}
-
 impl<'a> HashStable<StableHashingContext<'a>>
 for tokenstream::TokenTree {
     fn hash_stable<W: StableHasherResult>(&self,
@@ -372,25 +310,6 @@ fn hash_token<'a, 'gcx, W: StableHasherResult>(
     }
 }
 
-impl_stable_hash_for_spanned!(::syntax::ast::NestedMetaItemKind);
-
-impl_stable_hash_for!(enum ::syntax::ast::NestedMetaItemKind {
-    MetaItem(meta_item),
-    Literal(lit)
-});
-
-impl_stable_hash_for!(struct ::syntax::ast::MetaItem {
-    ident,
-    node,
-    span
-});
-
-impl_stable_hash_for!(enum ::syntax::ast::MetaItemKind {
-    Word,
-    List(nested_items),
-    NameValue(lit)
-});
-
 impl_stable_hash_for!(struct ::syntax_pos::hygiene::ExpnInfo {
     call_site,
     def_site,
diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs
index 469d77403a3d8..f7d20747104b8 100644
--- a/src/librustc/lint/context.rs
+++ b/src/librustc/lint/context.rs
@@ -549,13 +549,14 @@ impl LintPassObject for LateLintPassObject {}
 
 pub trait LintContext<'tcx>: Sized {
     type PassObject: LintPassObject;
+    type Path;
 
     fn sess(&self) -> &Session;
     fn lints(&self) -> &LintStore;
     fn lint_sess(&self) -> &LintSession<'tcx, Self::PassObject>;
     fn lint_sess_mut(&mut self) -> &mut LintSession<'tcx, Self::PassObject>;
-    fn enter_attrs(&mut self, attrs: &'tcx [ast::Attribute]);
-    fn exit_attrs(&mut self, attrs: &'tcx [ast::Attribute]);
+    fn enter_attrs(&mut self, attrs: &'tcx [ast::Attribute<Self::Path>]);
+    fn exit_attrs(&mut self, attrs: &'tcx [ast::Attribute<Self::Path>]);
 
     fn lookup_and_emit<S: Into<MultiSpan>>(&self,
                                            lint: &'static Lint,
@@ -624,7 +625,7 @@ pub trait LintContext<'tcx>: Sized {
     /// lints in effect to their previous state.
     fn with_lint_attrs<F>(&mut self,
                           id: ast::NodeId,
-                          attrs: &'tcx [ast::Attribute],
+                          attrs: &'tcx [ast::Attribute<Self::Path>],
                           f: F)
         where F: FnOnce(&mut Self);
 }
@@ -661,6 +662,7 @@ impl<'a> EarlyContext<'a> {
 
 impl<'a, 'tcx> LintContext<'tcx> for LateContext<'a, 'tcx> {
     type PassObject = LateLintPassObject;
+    type Path = hir::Path;
 
     /// Get the overall compiler `Session` object.
     fn sess(&self) -> &Session {
@@ -679,12 +681,12 @@ impl<'a, 'tcx> LintContext<'tcx> for LateContext<'a, 'tcx> {
         &mut self.lint_sess
     }
 
-    fn enter_attrs(&mut self, attrs: &'tcx [ast::Attribute]) {
+    fn enter_attrs(&mut self, attrs: &'tcx [hir::Attribute]) {
         debug!("late context: enter_attrs({:?})", attrs);
         run_lints!(self, enter_lint_attrs, attrs);
     }
 
-    fn exit_attrs(&mut self, attrs: &'tcx [ast::Attribute]) {
+    fn exit_attrs(&mut self, attrs: &'tcx [hir::Attribute]) {
         debug!("late context: exit_attrs({:?})", attrs);
         run_lints!(self, exit_lint_attrs, attrs);
     }
@@ -703,7 +705,7 @@ impl<'a, 'tcx> LintContext<'tcx> for LateContext<'a, 'tcx> {
 
     fn with_lint_attrs<F>(&mut self,
                           id: ast::NodeId,
-                          attrs: &'tcx [ast::Attribute],
+                          attrs: &'tcx [hir::Attribute],
                           f: F)
         where F: FnOnce(&mut Self)
     {
@@ -718,6 +720,7 @@ impl<'a, 'tcx> LintContext<'tcx> for LateContext<'a, 'tcx> {
 
 impl<'a> LintContext<'a> for EarlyContext<'a> {
     type PassObject = EarlyLintPassObject;
+    type Path = ast::Path;
 
     /// Get the overall compiler `Session` object.
     fn sess(&self) -> &Session {
@@ -997,7 +1000,7 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> {
         hir_visit::walk_path(self, p);
     }
 
-    fn visit_attribute(&mut self, attr: &'tcx ast::Attribute) {
+    fn visit_attribute(&mut self, attr: &'tcx hir::Attribute) {
         run_lints!(self, check_attribute, attr);
     }
 }
diff --git a/src/librustc/lint/levels.rs b/src/librustc/lint/levels.rs
index cfb9f04c4c6d1..351a432ea09ba 100644
--- a/src/librustc/lint/levels.rs
+++ b/src/librustc/lint/levels.rs
@@ -195,7 +195,7 @@ impl<'a> LintLevelsBuilder<'a> {
     ///   #[allow]
     ///
     /// Don't forget to call `pop`!
-    pub fn push(&mut self, attrs: &[ast::Attribute]) -> BuilderPush {
+    pub fn push(&mut self, attrs: &[ast::Attribute<impl attr::Path>]) -> BuilderPush {
         let mut specs = FxHashMap::default();
         let store = self.sess.lint_store.borrow();
         let sess = self.sess;
@@ -233,7 +233,7 @@ impl<'a> LintLevelsBuilder<'a> {
                     ast::MetaItemKind::Word => {}  // actual lint names handled later
                     ast::MetaItemKind::NameValue(ref name_value) => {
                         let gate_reasons = !self.sess.features_untracked().lint_reasons;
-                        if item.ident == "reason" {
+                        if item.check_name_correct("reason") {
                             // found reason, reslice meta list to exclude it
                             metas = &metas[0..metas.len()-1];
                             // FIXME (#55112): issue unused-attributes lint if we thereby
@@ -274,7 +274,7 @@ impl<'a> LintLevelsBuilder<'a> {
                         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.check_name_correct("reason") {
                                     err.help("reason in lint attribute must come last");
                                 }
                             }
diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs
index 4b878b862526b..38e378e24d887 100644
--- a/src/librustc/lint/mod.rs
+++ b/src/librustc/lint/mod.rs
@@ -243,14 +243,14 @@ macro_rules! late_lint_methods {
             fn check_variant_post(a: &$hir hir::Variant, b: &$hir hir::Generics);
             fn check_lifetime(a: &$hir hir::Lifetime);
             fn check_path(a: &$hir hir::Path, b: hir::HirId);
-            fn check_attribute(a: &$hir ast::Attribute);
+            fn check_attribute(a: &$hir hir::Attribute);
 
             /// Called when entering a syntax node that can have lint attributes such
             /// as `#[allow(...)]`. Called with *all* the attributes of that node.
-            fn enter_lint_attrs(a: &$hir [ast::Attribute]);
+            fn enter_lint_attrs(a: &$hir [hir::Attribute]);
 
             /// Counterpart to `enter_lint_attrs`.
-            fn exit_lint_attrs(a: &$hir [ast::Attribute]);
+            fn exit_lint_attrs(a: &$hir [hir::Attribute]);
         ]);
     )
 }
@@ -660,7 +660,7 @@ struct LintLevelMapBuilder<'a, 'tcx: 'a> {
 impl<'a, 'tcx> LintLevelMapBuilder<'a, 'tcx> {
     fn with_lint_attrs<F>(&mut self,
                           id: ast::NodeId,
-                          attrs: &[ast::Attribute],
+                          attrs: &[hir::Attribute],
                           f: F)
         where F: FnOnce(&mut Self)
     {
diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs
index 4720bb2954963..9bc6e31910c7f 100644
--- a/src/librustc/middle/cstore.rs
+++ b/src/librustc/middle/cstore.rs
@@ -22,6 +22,7 @@
 //! are *mostly* used as a part of that interface, but these should
 //! probably get a better home if someone can find one.
 
+use hir;
 use hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
 use hir::map as hir_map;
 use hir::map::definitions::{DefKey, DefPathTable};
@@ -121,7 +122,7 @@ pub enum NativeLibraryKind {
 pub struct NativeLibrary {
     pub kind: NativeLibraryKind,
     pub name: Option<Symbol>,
-    pub cfg: Option<ast::MetaItem>,
+    pub cfg: Option<hir::MetaItem>,
     pub foreign_module: Option<DefId>,
     pub wasm_import_module: Option<Symbol>,
 }
diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs
index 282b5d13e2c61..15c5a58c4f23c 100644
--- a/src/librustc/middle/dead.rs
+++ b/src/librustc/middle/dead.rs
@@ -286,7 +286,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> {
 
 fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt<'_, '_, '_>,
                                     id: ast::NodeId,
-                                    attrs: &[ast::Attribute]) -> bool {
+                                    attrs: &[hir::Attribute]) -> bool {
     if attr::contains_name(attrs, "lang") {
         return true;
     }
diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs
index e7a8baf738395..e45203ace03eb 100644
--- a/src/librustc/middle/lang_items.rs
+++ b/src/librustc/middle/lang_items.rs
@@ -27,7 +27,6 @@ use ty::{self, TyCtxt};
 use middle::weak_lang_items;
 use util::nodemap::FxHashMap;
 
-use syntax::ast;
 use syntax::symbol::Symbol;
 use syntax_pos::Span;
 use hir::itemlikevisit::ItemLikeVisitor;
@@ -198,7 +197,7 @@ impl<'a, 'tcx> LanguageItemCollector<'a, 'tcx> {
     }
 }
 
-pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> {
+pub fn extract(attrs: &[hir::Attribute]) -> Option<(Symbol, Span)> {
     for attribute in attrs {
         if attribute.check_name("lang") {
             if let Some(value) = attribute.value_str() {
diff --git a/src/librustc/middle/lib_features.rs b/src/librustc/middle/lib_features.rs
index 7d65d412e01d7..ff580ccdf7706 100644
--- a/src/librustc/middle/lib_features.rs
+++ b/src/librustc/middle/lib_features.rs
@@ -16,8 +16,9 @@
 
 use ty::TyCtxt;
 use syntax::symbol::Symbol;
-use syntax::ast::{Attribute, MetaItem, MetaItemKind};
+use syntax::ast::{MetaItem, MetaItemKind};
 use syntax_pos::Span;
+use hir::Attribute;
 use hir::intravisit::{self, NestedVisitorMap, Visitor};
 use rustc_data_structures::fx::{FxHashSet, FxHashMap};
 use errors::DiagnosticId;
diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs
index 543d1053b55e6..4a1dc43ce5aee 100644
--- a/src/librustc/middle/stability.rs
+++ b/src/librustc/middle/stability.rs
@@ -22,13 +22,13 @@ use session::{DiagnosticMessageId, Session};
 use syntax::symbol::Symbol;
 use syntax_pos::{Span, MultiSpan};
 use syntax::ast;
-use syntax::ast::{NodeId, Attribute};
+use syntax::ast::NodeId;
 use syntax::feature_gate::{GateIssue, emit_feature_err};
 use syntax::attr::{self, Stability, Deprecation};
 use util::nodemap::{FxHashSet, FxHashMap};
 
 use hir;
-use hir::{Item, Generics, StructField, Variant, HirId};
+use hir::{Attribute, Item, Generics, StructField, Variant, HirId};
 use hir::intravisit::{self, Visitor, NestedVisitorMap};
 
 use std::mem::replace;
diff --git a/src/librustc/middle/weak_lang_items.rs b/src/librustc/middle/weak_lang_items.rs
index 0d407765c9e10..ebf01660ff7b3 100644
--- a/src/librustc/middle/weak_lang_items.rs
+++ b/src/librustc/middle/weak_lang_items.rs
@@ -15,7 +15,6 @@ use middle::lang_items;
 
 use rustc_data_structures::fx::FxHashSet;
 use rustc_target::spec::PanicStrategy;
-use syntax::ast;
 use syntax::symbol::Symbol;
 use syntax_pos::Span;
 use hir::def_id::DefId;
@@ -54,7 +53,7 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     verify(tcx, items);
 }
 
-pub fn link_name(attrs: &[ast::Attribute]) -> Option<Symbol> {
+pub fn link_name(attrs: &[hir::Attribute]) -> Option<Symbol> {
     lang_items::extract(attrs).and_then(|(name, _)| {
         $(if name == stringify!($name) {
             Some(Symbol::intern(stringify!($sym)))
diff --git a/src/librustc/traits/on_unimplemented.rs b/src/librustc/traits/on_unimplemented.rs
index dcbddc0308091..2a35aff9265d3 100644
--- a/src/librustc/traits/on_unimplemented.rs
+++ b/src/librustc/traits/on_unimplemented.rs
@@ -15,7 +15,7 @@ use ty::{self, TyCtxt, GenericParamDefKind};
 use util::common::ErrorReported;
 use util::nodemap::FxHashMap;
 
-use syntax::ast::{MetaItem, NestedMetaItem};
+use hir::{MetaItem, NestedMetaItem};
 use syntax::attr;
 use syntax_pos::Span;
 use syntax_pos::symbol::LocalInternedString;
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 4633ab1166347..399c576ccd0a9 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -2602,14 +2602,14 @@ impl BorrowKind {
 
 #[derive(Debug, Clone)]
 pub enum Attributes<'gcx> {
-    Owned(Lrc<[ast::Attribute]>),
-    Borrowed(&'gcx [ast::Attribute])
+    Owned(Lrc<[hir::Attribute]>),
+    Borrowed(&'gcx [hir::Attribute])
 }
 
 impl<'gcx> ::std::ops::Deref for Attributes<'gcx> {
-    type Target = [ast::Attribute];
+    type Target = [hir::Attribute];
 
-    fn deref(&self) -> &[ast::Attribute] {
+    fn deref(&self) -> &[hir::Attribute] {
         match self {
             &Attributes::Owned(ref data) => &data,
             &Attributes::Borrowed(data) => data
diff --git a/src/librustc/ty/query/mod.rs b/src/librustc/ty/query/mod.rs
index 699c2d111c639..fc6ef43ddbc6b 100644
--- a/src/librustc/ty/query/mod.rs
+++ b/src/librustc/ty/query/mod.rs
@@ -341,7 +341,7 @@ define_queries! { <'tcx>
         [] fn def_span: DefSpan(DefId) -> Span,
         [] fn lookup_stability: LookupStability(DefId) -> Option<&'tcx attr::Stability>,
         [] fn lookup_deprecation_entry: LookupDeprecationEntry(DefId) -> Option<DeprecationEntry>,
-        [] fn item_attrs: ItemAttrs(DefId) -> Lrc<[ast::Attribute]>,
+        [] fn item_attrs: ItemAttrs(DefId) -> Lrc<[hir::Attribute]>,
     },
 
     Codegen {
diff --git a/src/librustc_codegen_utils/link.rs b/src/librustc_codegen_utils/link.rs
index b11aa687326f2..0430fc04cd482 100644
--- a/src/librustc_codegen_utils/link.rs
+++ b/src/librustc_codegen_utils/link.rs
@@ -48,7 +48,7 @@ fn is_writeable(p: &Path) -> bool {
 }
 
 pub fn find_crate_name(sess: Option<&Session>,
-                       attrs: &[ast::Attribute],
+                       attrs: &[ast::Attribute<impl attr::Path>],
                        input: &Input) -> String {
     let validate = |s: String, span: Option<Span>| {
         ::rustc_metadata::validate_crate_name(sess, &s, span);
diff --git a/src/librustc_incremental/assert_dep_graph.rs b/src/librustc_incremental/assert_dep_graph.rs
index 22a0cc983daab..e0cedd648174e 100644
--- a/src/librustc_incremental/assert_dep_graph.rs
+++ b/src/librustc_incremental/assert_dep_graph.rs
@@ -106,7 +106,7 @@ struct IfThisChanged<'a, 'tcx:'a> {
 }
 
 impl<'a, 'tcx> IfThisChanged<'a, 'tcx> {
-    fn argument(&self, attr: &ast::Attribute) -> Option<ast::Name> {
+    fn argument(&self, attr: &hir::Attribute) -> Option<ast::Name> {
         let mut value = None;
         for list_item in attr.meta_item_list().unwrap_or_default() {
             match list_item.word() {
@@ -120,7 +120,7 @@ impl<'a, 'tcx> IfThisChanged<'a, 'tcx> {
         value
     }
 
-    fn process_attrs(&mut self, node_id: ast::NodeId, attrs: &[ast::Attribute]) {
+    fn process_attrs(&mut self, node_id: ast::NodeId, attrs: &[hir::Attribute]) {
         let def_id = self.tcx.hir.local_def_id(node_id);
         let def_path_hash = self.tcx.def_path_hash(def_id);
         for attr in attrs {
diff --git a/src/librustc_incremental/assert_module_sources.rs b/src/librustc_incremental/assert_module_sources.rs
index 4ff2529b26d93..fe364d03d9937 100644
--- a/src/librustc_incremental/assert_module_sources.rs
+++ b/src/librustc_incremental/assert_module_sources.rs
@@ -31,6 +31,7 @@
 //! allows for doing a more fine-grained check to see if pre- or post-lto data
 //! was re-used.
 
+use rustc::hir;
 use rustc::hir::def_id::LOCAL_CRATE;
 use rustc::dep_graph::cgu_reuse_tracker::*;
 use rustc::mir::mono::CodegenUnitNameBuilder;
@@ -74,7 +75,7 @@ struct AssertModuleSource<'a, 'tcx: 'a> {
 }
 
 impl<'a, 'tcx> AssertModuleSource<'a, 'tcx> {
-    fn check_attr(&self, attr: &ast::Attribute) {
+    fn check_attr(&self, attr: &hir::Attribute) {
         let (expected_reuse, comp_kind) = if attr.check_name(ATTR_PARTITION_REUSED) {
             (CguReuse::PreLto, ComparisonKind::AtLeast)
         } else if attr.check_name(ATTR_PARTITION_CODEGENED) {
@@ -156,7 +157,7 @@ impl<'a, 'tcx> AssertModuleSource<'a, 'tcx> {
                                                         comp_kind);
     }
 
-    fn field(&self, attr: &ast::Attribute, name: &str) -> ast::Name {
+    fn field(&self, attr: &hir::Attribute, name: &str) -> ast::Name {
         for item in attr.meta_item_list().unwrap_or_else(Vec::new) {
             if item.check_name(name) {
                 if let Some(value) = item.value_str() {
@@ -176,7 +177,7 @@ impl<'a, 'tcx> AssertModuleSource<'a, 'tcx> {
 
     /// Scan for a `cfg="foo"` attribute and check whether we have a
     /// cfg flag called `foo`.
-    fn check_config(&self, attr: &ast::Attribute) -> bool {
+    fn check_config(&self, attr: &hir::Attribute) -> bool {
         let config = &self.tcx.sess.parse_sess.config;
         let value = self.field(attr, CFG);
         debug!("check_config(config={:?}, value={:?})", config, value);
diff --git a/src/librustc_incremental/persist/dirty_clean.rs b/src/librustc_incremental/persist/dirty_clean.rs
index f76086139ed7e..ed71ffe068d31 100644
--- a/src/librustc_incremental/persist/dirty_clean.rs
+++ b/src/librustc_incremental/persist/dirty_clean.rs
@@ -28,13 +28,13 @@ use std::iter::FromIterator;
 use std::vec::Vec;
 use rustc::dep_graph::{DepNode, label_strs};
 use rustc::hir;
-use rustc::hir::{ItemKind as HirItem, ImplItemKind, TraitItemKind};
+use rustc::hir::{Attribute, NestedMetaItem, ItemKind as HirItem, ImplItemKind, TraitItemKind};
 use rustc::hir::Node as HirNode;
 use rustc::hir::def_id::DefId;
 use rustc::hir::itemlikevisit::ItemLikeVisitor;
 use rustc::hir::intravisit;
 use rustc::ich::{ATTR_DIRTY, ATTR_CLEAN};
-use syntax::ast::{self, Attribute, NestedMetaItem};
+use syntax::ast;
 use rustc_data_structures::fx::FxHashSet;
 use syntax_pos::Span;
 use rustc::ty::TyCtxt;
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 7dd1ca3493e9d..22938ac6ef87f 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -322,7 +322,7 @@ impl MissingDoc {
     fn check_missing_docs_attrs(&self,
                                 cx: &LateContext,
                                 id: Option<ast::NodeId>,
-                                attrs: &[ast::Attribute],
+                                attrs: &[hir::Attribute],
                                 sp: Span,
                                 desc: &'static str) {
         // If we're building a test harness, then warning about
@@ -345,7 +345,7 @@ impl MissingDoc {
             }
         }
 
-        fn has_doc(attr: &ast::Attribute) -> bool {
+        fn has_doc(attr: &hir::Attribute) -> bool {
             if !attr.check_name("doc") {
                 return false;
             }
@@ -381,7 +381,7 @@ impl LintPass for MissingDoc {
 }
 
 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc {
-    fn enter_lint_attrs(&mut self, _: &LateContext, attrs: &[ast::Attribute]) {
+    fn enter_lint_attrs(&mut self, _: &LateContext, attrs: &[hir::Attribute]) {
         let doc_hidden = self.doc_hidden() ||
                          attrs.iter().any(|attr| {
             attr.check_name("doc") &&
@@ -393,7 +393,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc {
         self.doc_hidden_stack.push(doc_hidden);
     }
 
-    fn exit_lint_attrs(&mut self, _: &LateContext, _attrs: &[ast::Attribute]) {
+    fn exit_lint_attrs(&mut self, _: &LateContext, _attrs: &[hir::Attribute]) {
         self.doc_hidden_stack.pop().expect("empty doc_hidden_stack");
     }
 
@@ -1043,7 +1043,7 @@ impl LintPass for UnstableFeatures {
 }
 
 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnstableFeatures {
-    fn check_attribute(&mut self, ctx: &LateContext, attr: &ast::Attribute) {
+    fn check_attribute(&mut self, ctx: &LateContext, attr: &hir::Attribute) {
         if attr.check_name("feature") {
             if let Some(items) = attr.meta_item_list() {
                 for item in items {
diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs
index fab618d9c8ec8..9055208a83ca8 100644
--- a/src/librustc_lint/unused.rs
+++ b/src/librustc_lint/unused.rs
@@ -239,7 +239,7 @@ impl LintPass for UnusedAttributes {
 }
 
 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes {
-    fn check_attribute(&mut self, cx: &LateContext, attr: &ast::Attribute) {
+    fn check_attribute(&mut self, cx: &LateContext, attr: &hir::Attribute) {
         debug!("checking attribute: {:?}", attr);
         // Note that check_name() marks the attribute as used if it matches.
         for &(ref name, ty, _) in BUILTIN_ATTRIBUTES {
diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs
index d0fa63a6163db..74bb319cc97fc 100644
--- a/src/librustc_metadata/cstore_impl.rs
+++ b/src/librustc_metadata/cstore_impl.rs
@@ -463,7 +463,33 @@ impl cstore::CStore {
             ident: ast::Ident::from_str(&name.as_str()),
             id: ast::DUMMY_NODE_ID,
             span: local_span,
-            attrs: attrs.iter().cloned().collect(),
+            attrs: attrs.iter().map(|attr| {
+                // HACK(eddyb) convert from a `hir::Attribute` to an `ast::Attribute`
+                // (ideally we wouldn't need to create a fake `ast::Item`)
+                let ast::Attribute {
+                    id,
+                    style,
+                    ref path,
+                    ref tokens,
+                    is_sugared_doc,
+                    span,
+                } = *attr;
+                ast::Attribute {
+                    id,
+                    style,
+                    path: ast::Path {
+                        span: path.span,
+                        segments: path.segments.iter().map(|segment| ast::PathSegment {
+                            ident: segment.ident,
+                            id: ast::DUMMY_NODE_ID,
+                            args: None,
+                        }).collect(),
+                    },
+                    tokens: tokens.clone(),
+                    is_sugared_doc,
+                    span,
+                }
+            }).collect(),
             node: ast::ItemKind::MacroDef(ast::MacroDef {
                 tokens: body.into(),
                 legacy: def.legacy,
diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs
index 1c7e3c95d1470..7f652745a988b 100644
--- a/src/librustc_metadata/decoder.rs
+++ b/src/librustc_metadata/decoder.rs
@@ -926,7 +926,7 @@ impl<'a, 'tcx> CrateMetadata {
         }
     }
 
-    pub fn get_item_attrs(&self, node_id: DefIndex, sess: &Session) -> Lrc<[ast::Attribute]> {
+    pub fn get_item_attrs(&self, node_id: DefIndex, sess: &Session) -> Lrc<[hir::Attribute]> {
         if self.is_proc_macro(node_id) {
             return Lrc::new([]);
         }
@@ -953,7 +953,7 @@ impl<'a, 'tcx> CrateMetadata {
             .collect()
     }
 
-    fn get_attributes(&self, item: &Entry<'tcx>, sess: &Session) -> Vec<ast::Attribute> {
+    fn get_attributes(&self, item: &Entry<'tcx>, sess: &Session) -> Vec<hir::Attribute> {
         item.attributes
             .decode((self, sess))
             .map(|mut attr| {
diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs
index 2736c60ffb6fa..0f53a970a4468 100644
--- a/src/librustc_metadata/encoder.rs
+++ b/src/librustc_metadata/encoder.rs
@@ -632,7 +632,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
 
     fn encode_info_for_mod(&mut self,
                            FromId(id, (md, attrs, vis)): FromId<(&hir::Mod,
-                                                                 &[ast::Attribute],
+                                                                 &[hir::Attribute],
                                                                  &hir::Visibility)>)
                            -> Entry<'tcx> {
         let tcx = self.tcx;
@@ -1407,7 +1407,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> {
         }
     }
 
-    fn encode_attributes(&mut self, attrs: &[ast::Attribute]) -> LazySeq<ast::Attribute> {
+    fn encode_attributes(&mut self, attrs: &[hir::Attribute]) -> LazySeq<hir::Attribute> {
         // NOTE: This must use lazy_seq_from_slice(), not lazy_seq() because
         //       we rely on the HashStable specialization for [Attribute]
         //       to properly filter things out.
diff --git a/src/librustc_metadata/link_args.rs b/src/librustc_metadata/link_args.rs
index 008e1e363ff53..fa7f191f9f55e 100644
--- a/src/librustc_metadata/link_args.rs
+++ b/src/librustc_metadata/link_args.rs
@@ -20,7 +20,7 @@ pub fn collect<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Vec<String> {
     tcx.hir.krate().visit_all_item_likes(&mut collector);
 
     for attr in tcx.hir.krate().attrs.iter() {
-        if attr.path == "link_args" {
+        if attr.check_name("link_args") {
             if let Some(linkarg) = attr.value_str() {
                 collector.add_link_args(&linkarg.as_str());
             }
diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs
index e91d15b78c075..133b036bdea94 100644
--- a/src/librustc_metadata/schema.rs
+++ b/src/librustc_metadata/schema.rs
@@ -265,7 +265,7 @@ pub struct Entry<'tcx> {
     pub kind: EntryKind<'tcx>,
     pub visibility: Lazy<ty::Visibility>,
     pub span: Lazy<Span>,
-    pub attributes: LazySeq<ast::Attribute>,
+    pub attributes: LazySeq<hir::Attribute>,
     pub children: LazySeq<DefIndex>,
     pub stability: Option<Lazy<attr::Stability>>,
     pub deprecation: Option<Lazy<attr::Deprecation>>,
diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs
index c19145636e6da..0fbe225496bb1 100644
--- a/src/librustc_mir/dataflow/mod.rs
+++ b/src/librustc_mir/dataflow/mod.rs
@@ -8,12 +8,13 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use syntax::ast::{self, MetaItem};
+use syntax::ast;
 
 use rustc_data_structures::bit_set::{BitSet, BitSetOperator, HybridBitSet};
 use rustc_data_structures::indexed_vec::Idx;
 use rustc_data_structures::work_queue::WorkQueue;
 
+use rustc::hir;
 use rustc::ty::{self, TyCtxt};
 use rustc::mir::{self, Mir, BasicBlock, BasicBlockData, Location, Statement, Terminator};
 use rustc::mir::traversal;
@@ -100,7 +101,7 @@ impl<'a, 'tcx: 'a, BD> Dataflow<BD> for DataflowBuilder<'a, 'tcx, BD> where BD:
     fn propagate(&mut self) { self.flow_state.propagate(); }
 }
 
-pub(crate) fn has_rustc_mir_with(attrs: &[ast::Attribute], name: &str) -> Option<MetaItem> {
+pub(crate) fn has_rustc_mir_with(attrs: &[hir::Attribute], name: &str) -> Option<hir::MetaItem> {
     for attr in attrs {
         if attr.check_name("rustc_mir") {
             let items = attr.meta_item_list();
@@ -123,7 +124,7 @@ pub struct MoveDataParamEnv<'gcx, 'tcx> {
 pub(crate) fn do_dataflow<'a, 'gcx, 'tcx, BD, P>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
                                                  mir: &'a Mir<'tcx>,
                                                  node_id: ast::NodeId,
-                                                 attributes: &[ast::Attribute],
+                                                 attributes: &[hir::Attribute],
                                                  dead_unwinds: &BitSet<BasicBlock>,
                                                  bd: BD,
                                                  p: P)
@@ -140,11 +141,11 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> where BD: BitD
     pub(crate) fn run<P>(self,
                          tcx: TyCtxt<'a, 'gcx, 'tcx>,
                          node_id: ast::NodeId,
-                         attributes: &[ast::Attribute],
+                         attributes: &[hir::Attribute],
                          p: P) -> DataflowResults<BD>
         where P: Fn(&BD, BD::Idx) -> DebugFormatted
     {
-        let name_found = |sess: &Session, attrs: &[ast::Attribute], name| -> Option<String> {
+        let name_found = |sess: &Session, attrs: &[hir::Attribute], name| -> Option<String> {
             if let Some(item) = has_rustc_mir_with(attrs, name) {
                 if let Some(s) = item.value_str() {
                     return Some(s.to_string())
diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs
index f852195b8351a..a92ead0e59121 100644
--- a/src/librustc_mir/transform/rustc_peek.rs
+++ b/src/librustc_mir/transform/rustc_peek.rs
@@ -12,6 +12,7 @@ use rustc_target::spec::abi::{Abi};
 use syntax::ast;
 use syntax_pos::Span;
 
+use rustc::hir;
 use rustc::ty::{self, TyCtxt};
 use rustc::mir::{self, Mir, Location};
 use rustc_data_structures::bit_set::BitSet;
@@ -94,7 +95,7 @@ impl MirPass for SanityCheck {
 pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                                 mir: &Mir<'tcx>,
                                                 id: ast::NodeId,
-                                                _attributes: &[ast::Attribute],
+                                                _attributes: &[hir::Attribute],
                                                 results: &DataflowResults<O>)
     where O: BitDenotation<Idx=MovePathIndex> + HasMoveData<'tcx>
 {
diff --git a/src/librustc_passes/hir_stats.rs b/src/librustc_passes/hir_stats.rs
index 019fb9565f413..eb6654524d937 100644
--- a/src/librustc_passes/hir_stats.rs
+++ b/src/librustc_passes/hir_stats.rs
@@ -253,7 +253,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
         hir_visit::walk_assoc_type_binding(self, type_binding)
     }
 
-    fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
+    fn visit_attribute(&mut self, attr: &'v hir::Attribute) {
         self.record("Attribute", Id::Attr(attr.id), attr);
     }
 
diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs
index 82c4795a29d4b..80ecb1e9e2029 100644
--- a/src/librustc_save_analysis/lib.rs
+++ b/src/librustc_save_analysis/lib.rs
@@ -57,6 +57,7 @@ use std::env;
 use std::fs::File;
 use std::path::{Path, PathBuf};
 
+use syntax::attr;
 use syntax::ast::{self, Attribute, DUMMY_NODE_ID, NodeId, PatKind};
 use syntax::source_map::Spanned;
 use syntax::parse::lexer::comments::strip_doc_comment_decoration;
@@ -892,7 +893,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
         }
     }
 
-    fn docs_for_attrs(&self, attrs: &[Attribute]) -> String {
+    fn docs_for_attrs(&self, attrs: &[Attribute<impl attr::Path>]) -> String {
         let mut result = String::new();
 
         for attr in attrs {
@@ -1202,10 +1203,13 @@ fn null_id() -> rls_data::Id {
     }
 }
 
-fn lower_attributes(attrs: Vec<Attribute>, scx: &SaveContext) -> Vec<rls_data::Attribute> {
+fn lower_attributes(
+    attrs: Vec<Attribute<impl attr::Path>>,
+    scx: &SaveContext,
+) -> Vec<rls_data::Attribute> {
     attrs.into_iter()
     // Only retain real attributes. Doc comments are lowered separately.
-    .filter(|attr| attr.path != "doc")
+    .filter(|attr| !attr.check_name("doc"))
     .map(|mut attr| {
         // Remove the surrounding '#[..]' or '#![..]' of the pretty printed
         // attribute. First normalize all inner attribute (#![..]) to outer
diff --git a/src/librustc_traits/lowering/mod.rs b/src/librustc_traits/lowering/mod.rs
index 2d8e5b48aac7b..f34236024078f 100644
--- a/src/librustc_traits/lowering/mod.rs
+++ b/src/librustc_traits/lowering/mod.rs
@@ -625,7 +625,7 @@ struct ClauseDumper<'a, 'tcx: 'a> {
 }
 
 impl<'a, 'tcx> ClauseDumper<'a, 'tcx> {
-    fn process_attrs(&mut self, node_id: ast::NodeId, attrs: &[ast::Attribute]) {
+    fn process_attrs(&mut self, node_id: ast::NodeId, attrs: &[hir::Attribute]) {
         let def_id = self.tcx.hir.local_def_id(node_id);
         for attr in attrs {
             let mut clauses = None;
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index a1bb0b53f1fce..537f3082de9e3 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -44,7 +44,7 @@ use rustc_target::spec::abi;
 
 use syntax::ast;
 use syntax::ast::MetaItemKind;
-use syntax::attr::{InlineAttr, list_contains_name, mark_used};
+use syntax::attr::{InlineAttr, list_contains_name};
 use syntax::source_map::Spanned;
 use syntax::feature_gate;
 use syntax::symbol::{keywords, Symbol};
@@ -2137,7 +2137,7 @@ fn is_foreign_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool
 fn from_target_feature(
     tcx: TyCtxt,
     id: DefId,
-    attr: &ast::Attribute,
+    attr: &hir::Attribute,
     whitelist: &FxHashMap<String, Option<String>>,
     target_features: &mut Vec<Symbol>,
 ) {
@@ -2290,48 +2290,41 @@ fn codegen_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> Codegen
         } else if attr.check_name("thread_local") {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL;
         } else if attr.check_name("inline") {
-            codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| {
-                if attr.path != "inline" {
-                    return ia;
+            let meta = match attr.meta() {
+                Some(meta) => meta.node,
+                None => continue,
+            };
+            match meta {
+                MetaItemKind::Word => {
+                    codegen_fn_attrs.inline = InlineAttr::Hint;
                 }
-                let meta = match attr.meta() {
-                    Some(meta) => meta.node,
-                    None => return ia,
-                };
-                match meta {
-                    MetaItemKind::Word => {
-                        mark_used(attr);
-                        InlineAttr::Hint
-                    }
-                    MetaItemKind::List(ref items) => {
-                        mark_used(attr);
-                        inline_span = Some(attr.span);
-                        if items.len() != 1 {
-                            span_err!(
-                                tcx.sess.diagnostic(),
-                                attr.span,
-                                E0534,
-                                "expected one argument"
-                            );
-                            InlineAttr::None
-                        } else if list_contains_name(&items[..], "always") {
-                            InlineAttr::Always
-                        } else if list_contains_name(&items[..], "never") {
-                            InlineAttr::Never
-                        } else {
-                            span_err!(
-                                tcx.sess.diagnostic(),
-                                items[0].span,
-                                E0535,
-                                "invalid argument"
-                            );
+                MetaItemKind::List(ref items) => {
+                    inline_span = Some(attr.span);
+                    codegen_fn_attrs.inline = if items.len() != 1 {
+                        span_err!(
+                            tcx.sess.diagnostic(),
+                            attr.span,
+                            E0534,
+                            "expected one argument"
+                        );
+                        InlineAttr::None
+                    } else if list_contains_name(&items[..], "always") {
+                        InlineAttr::Always
+                    } else if list_contains_name(&items[..], "never") {
+                        InlineAttr::Never
+                    } else {
+                        span_err!(
+                            tcx.sess.diagnostic(),
+                            items[0].span,
+                            E0535,
+                            "invalid argument"
+                        );
 
-                            InlineAttr::None
-                        }
-                    }
-                    _ => ia,
+                        InlineAttr::None
+                    };
                 }
-            });
+                _ => {}
+            }
         } else if attr.check_name("export_name") {
             if let Some(s) = attr.value_str() {
                 if s.as_str().contains("\0") {
diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs
index f90f1e54da22c..42deb84cb8e6d 100644
--- a/src/librustdoc/clean/cfg.rs
+++ b/src/librustdoc/clean/cfg.rs
@@ -18,6 +18,7 @@ use std::ops;
 
 use syntax::symbol::Symbol;
 use syntax::ast::{MetaItem, MetaItemKind, NestedMetaItem, NestedMetaItemKind, LitKind};
+use syntax::attr;
 use syntax::parse::ParseSess;
 use syntax::feature_gate::Features;
 
@@ -49,7 +50,7 @@ pub struct InvalidCfgError {
 
 impl Cfg {
     /// Parses a `NestedMetaItem` into a `Cfg`.
-    fn parse_nested(nested_cfg: &NestedMetaItem) -> Result<Cfg, InvalidCfgError> {
+    fn parse_nested(nested_cfg: &NestedMetaItem<impl attr::Path>) -> Result<Cfg, InvalidCfgError> {
         match nested_cfg.node {
             NestedMetaItemKind::MetaItem(ref cfg) => Cfg::parse(cfg),
             NestedMetaItemKind::Literal(ref lit) => Err(InvalidCfgError {
@@ -66,7 +67,7 @@ impl Cfg {
     ///
     /// If the content is not properly formatted, it will return an error indicating what and where
     /// the error is.
-    pub fn parse(cfg: &MetaItem) -> Result<Cfg, InvalidCfgError> {
+    pub fn parse(cfg: &MetaItem<impl attr::Path>) -> Result<Cfg, InvalidCfgError> {
         let name = cfg.name();
         match cfg.node {
             MetaItemKind::Word => Ok(Cfg::Cfg(name, None)),
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index fd8f70b19e7ec..d946dedfa5053 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -639,13 +639,13 @@ impl Clean<Item> for doctree::Module {
 }
 
 pub struct ListAttributesIter<'a> {
-    attrs: slice::Iter<'a, ast::Attribute>,
-    current_list: vec::IntoIter<ast::NestedMetaItem>,
+    attrs: slice::Iter<'a, hir::Attribute>,
+    current_list: vec::IntoIter<hir::NestedMetaItem>,
     name: &'a str
 }
 
 impl<'a> Iterator for ListAttributesIter<'a> {
-    type Item = ast::NestedMetaItem;
+    type Item = hir::NestedMetaItem;
 
     fn next(&mut self) -> Option<Self::Item> {
         if let Some(nested) = self.current_list.next() {
@@ -677,7 +677,7 @@ pub trait AttributesExt {
     fn lists<'a>(&'a self, name: &'a str) -> ListAttributesIter<'a>;
 }
 
-impl AttributesExt for [ast::Attribute] {
+impl AttributesExt for [hir::Attribute] {
     fn lists<'a>(&'a self, name: &'a str) -> ListAttributesIter<'a> {
         ListAttributesIter {
             attrs: self.iter(),
@@ -692,7 +692,7 @@ pub trait NestedAttributesExt {
     fn has_word(self, word: &str) -> bool;
 }
 
-impl<I: IntoIterator<Item=ast::NestedMetaItem>> NestedAttributesExt for I {
+impl<I: IntoIterator<Item=hir::NestedMetaItem>> NestedAttributesExt for I {
     fn has_word(self, word: &str) -> bool {
         self.into_iter().any(|attr| attr.is_word() && attr.check_name(word))
     }
@@ -761,7 +761,7 @@ impl<'a> FromIterator<&'a DocFragment> for String {
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug, Default)]
 pub struct Attributes {
     pub doc_strings: Vec<DocFragment>,
-    pub other_attrs: Vec<ast::Attribute>,
+    pub other_attrs: Vec<hir::Attribute>,
     pub cfg: Option<Arc<Cfg>>,
     pub span: Option<syntax_pos::Span>,
     /// map from Rust paths to resolved defs and potential URL fragments
@@ -771,7 +771,7 @@ pub struct Attributes {
 
 impl Attributes {
     /// Extracts the content from an attribute `#[doc(cfg(content))]`.
-    fn extract_cfg(mi: &ast::MetaItem) -> Option<&ast::MetaItem> {
+    fn extract_cfg(mi: &hir::MetaItem) -> Option<&hir::MetaItem> {
         use syntax::ast::NestedMetaItemKind::MetaItem;
 
         if let ast::MetaItemKind::List(ref nmis) = mi.node {
@@ -796,7 +796,7 @@ impl Attributes {
     /// Reads a `MetaItem` from within an attribute, looks for whether it is a
     /// `#[doc(include="file")]`, and returns the filename and contents of the file as loaded from
     /// its expansion.
-    fn extract_include(mi: &ast::MetaItem)
+    fn extract_include(mi: &hir::MetaItem)
         -> Option<(String, String)>
     {
         mi.meta_item_list().and_then(|list| {
@@ -849,7 +849,7 @@ impl Attributes {
     }
 
     pub fn from_ast(diagnostic: &::errors::Handler,
-                    attrs: &[ast::Attribute]) -> Attributes {
+                    attrs: &[hir::Attribute]) -> Attributes {
         let mut doc_strings = vec![];
         let mut sp = None;
         let mut cfg = Cfg::True;
@@ -902,8 +902,9 @@ impl Attributes {
         for attr in attrs.lists("target_feature") {
             if attr.check_name("enable") {
                 if let Some(feat) = attr.value_str() {
-                    let meta = attr::mk_name_value_item_str(Ident::from_str("target_feature"),
-                                                            dummy_spanned(feat));
+                    let meta: hir::MetaItem =
+                        attr::mk_name_value_item_str(Ident::from_str("target_feature"),
+                                                     dummy_spanned(feat));
                     if let Ok(feat_cfg) = Cfg::parse(&meta) {
                         cfg &= feat_cfg;
                     }
@@ -1015,7 +1016,7 @@ impl AttributesExt for Attributes {
     }
 }
 
-impl Clean<Attributes> for [ast::Attribute] {
+impl Clean<Attributes> for [hir::Attribute] {
     fn clean(&self, cx: &DocContext) -> Attributes {
         Attributes::from_ast(cx.sess().diagnostic(), self)
     }
diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs
index 4a6a4ee09ea1a..10fa8a647ff1b 100644
--- a/src/librustdoc/doctree.rs
+++ b/src/librustdoc/doctree.rs
@@ -25,7 +25,7 @@ use rustc::hir::def_id::CrateNum;
 
 pub struct Module {
     pub name: Option<Name>,
-    pub attrs: hir::HirVec<ast::Attribute>,
+    pub attrs: hir::HirVec<hir::Attribute>,
     pub where_outer: Span,
     pub where_inner: Span,
     pub extern_crates: Vec<ExternCrate>,
@@ -101,7 +101,7 @@ pub struct Struct {
     pub struct_type: StructType,
     pub name: Name,
     pub generics: hir::Generics,
-    pub attrs: hir::HirVec<ast::Attribute>,
+    pub attrs: hir::HirVec<hir::Attribute>,
     pub fields: hir::HirVec<hir::StructField>,
     pub whence: Span,
 }
@@ -114,7 +114,7 @@ pub struct Union {
     pub struct_type: StructType,
     pub name: Name,
     pub generics: hir::Generics,
-    pub attrs: hir::HirVec<ast::Attribute>,
+    pub attrs: hir::HirVec<hir::Attribute>,
     pub fields: hir::HirVec<hir::StructField>,
     pub whence: Span,
 }
@@ -125,7 +125,7 @@ pub struct Enum {
     pub depr: Option<attr::Deprecation>,
     pub variants: hir::HirVec<Variant>,
     pub generics: hir::Generics,
-    pub attrs: hir::HirVec<ast::Attribute>,
+    pub attrs: hir::HirVec<hir::Attribute>,
     pub id: NodeId,
     pub whence: Span,
     pub name: Name,
@@ -133,7 +133,7 @@ pub struct Enum {
 
 pub struct Variant {
     pub name: Name,
-    pub attrs: hir::HirVec<ast::Attribute>,
+    pub attrs: hir::HirVec<hir::Attribute>,
     pub def: hir::VariantData,
     pub stab: Option<attr::Stability>,
     pub depr: Option<attr::Deprecation>,
@@ -142,7 +142,7 @@ pub struct Variant {
 
 pub struct Function {
     pub decl: hir::FnDecl,
-    pub attrs: hir::HirVec<ast::Attribute>,
+    pub attrs: hir::HirVec<hir::Attribute>,
     pub id: NodeId,
     pub name: Name,
     pub vis: hir::Visibility,
@@ -159,7 +159,7 @@ pub struct Typedef {
     pub gen: hir::Generics,
     pub name: Name,
     pub id: ast::NodeId,
-    pub attrs: hir::HirVec<ast::Attribute>,
+    pub attrs: hir::HirVec<hir::Attribute>,
     pub whence: Span,
     pub vis: hir::Visibility,
     pub stab: Option<attr::Stability>,
@@ -170,7 +170,7 @@ pub struct Existential {
     pub exist_ty: hir::ExistTy,
     pub name: Name,
     pub id: ast::NodeId,
-    pub attrs: hir::HirVec<ast::Attribute>,
+    pub attrs: hir::HirVec<hir::Attribute>,
     pub whence: Span,
     pub vis: hir::Visibility,
     pub stab: Option<attr::Stability>,
@@ -183,7 +183,7 @@ pub struct Static {
     pub mutability: hir::Mutability,
     pub expr: hir::BodyId,
     pub name: Name,
-    pub attrs: hir::HirVec<ast::Attribute>,
+    pub attrs: hir::HirVec<hir::Attribute>,
     pub vis: hir::Visibility,
     pub stab: Option<attr::Stability>,
     pub depr: Option<attr::Deprecation>,
@@ -195,7 +195,7 @@ pub struct Constant {
     pub type_: P<hir::Ty>,
     pub expr: hir::BodyId,
     pub name: Name,
-    pub attrs: hir::HirVec<ast::Attribute>,
+    pub attrs: hir::HirVec<hir::Attribute>,
     pub vis: hir::Visibility,
     pub stab: Option<attr::Stability>,
     pub depr: Option<attr::Deprecation>,
@@ -210,7 +210,7 @@ pub struct Trait {
     pub items: hir::HirVec<hir::TraitItem>,
     pub generics: hir::Generics,
     pub bounds: hir::HirVec<hir::GenericBound>,
-    pub attrs: hir::HirVec<ast::Attribute>,
+    pub attrs: hir::HirVec<hir::Attribute>,
     pub id: ast::NodeId,
     pub whence: Span,
     pub vis: hir::Visibility,
@@ -227,7 +227,7 @@ pub struct Impl {
     pub trait_: Option<hir::TraitRef>,
     pub for_: P<hir::Ty>,
     pub items: hir::HirVec<hir::ImplItem>,
-    pub attrs: hir::HirVec<ast::Attribute>,
+    pub attrs: hir::HirVec<hir::Attribute>,
     pub whence: Span,
     pub vis: hir::Visibility,
     pub stab: Option<attr::Stability>,
@@ -240,7 +240,7 @@ pub struct Impl {
 pub struct Macro {
     pub name: Name,
     pub def_id: hir::def_id::DefId,
-    pub attrs: hir::HirVec<ast::Attribute>,
+    pub attrs: hir::HirVec<hir::Attribute>,
     pub whence: Span,
     pub matchers: hir::HirVec<Span>,
     pub stab: Option<attr::Stability>,
@@ -253,7 +253,7 @@ pub struct ExternCrate {
     pub cnum: CrateNum,
     pub path: Option<String>,
     pub vis: hir::Visibility,
-    pub attrs: hir::HirVec<ast::Attribute>,
+    pub attrs: hir::HirVec<hir::Attribute>,
     pub whence: Span,
 }
 
@@ -261,7 +261,7 @@ pub struct Import {
     pub name: Name,
     pub id: NodeId,
     pub vis: hir::Visibility,
-    pub attrs: hir::HirVec<ast::Attribute>,
+    pub attrs: hir::HirVec<hir::Attribute>,
     pub path: hir::Path,
     pub glob: bool,
     pub whence: Span,
@@ -272,7 +272,7 @@ pub struct ProcMacro {
     pub id: NodeId,
     pub kind: MacroKind,
     pub helpers: Vec<Name>,
-    pub attrs: hir::HirVec<ast::Attribute>,
+    pub attrs: hir::HirVec<hir::Attribute>,
     pub whence: Span,
     pub stab: Option<attr::Stability>,
     pub depr: Option<attr::Deprecation>,
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index b46efb20d8f6b..ff77d1bccd57e 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -54,7 +54,6 @@ use std::rc::Rc;
 
 use errors;
 use serialize::json::{ToJson, Json, as_json};
-use syntax::ast;
 use syntax::ext::base::MacroKind;
 use syntax::source_map::FileName;
 use syntax::feature_gate::UnstableFeatures;
@@ -3495,7 +3494,7 @@ fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
     Ok(())
 }
 
-fn render_attribute(attr: &ast::MetaItem) -> Option<String> {
+fn render_attribute(attr: &hir::MetaItem) -> Option<String> {
     let name = attr.name();
 
     if attr.is_word() {
diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs
index d9bab91fd0c78..7e9f2216ef4c7 100644
--- a/src/librustdoc/test.rs
+++ b/src/librustdoc/test.rs
@@ -736,7 +736,7 @@ struct HirCollector<'a, 'hir: 'a> {
 impl<'a, 'hir> HirCollector<'a, 'hir> {
     fn visit_testable<F: FnOnce(&mut Self)>(&mut self,
                                             name: String,
-                                            attrs: &[ast::Attribute],
+                                            attrs: &[hir::Attribute],
                                             nested: F) {
         let mut attrs = Attributes::from_ast(self.sess.diagnostic(), attrs);
         if let Some(ref cfg) = attrs.cfg {
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index 5d221d3006f3e..c312d45501946 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -41,7 +41,7 @@ use doctree::*;
 
 pub struct RustdocVisitor<'a, 'tcx: 'a, 'rcx: 'a, 'cstore: 'rcx> {
     pub module: Module,
-    pub attrs: hir::HirVec<ast::Attribute>,
+    pub attrs: hir::HirVec<hir::Attribute>,
     pub cx: &'a core::DocContext<'a, 'tcx, 'rcx, 'cstore>,
     view_item_stack: FxHashSet<ast::NodeId>,
     inlining: bool,
@@ -241,7 +241,7 @@ impl<'a, 'tcx, 'rcx, 'cstore> RustdocVisitor<'a, 'tcx, 'rcx, 'cstore> {
         }
     }
 
-    pub fn visit_mod_contents(&mut self, span: Span, attrs: hir::HirVec<ast::Attribute>,
+    pub fn visit_mod_contents(&mut self, span: Span, attrs: hir::HirVec<hir::Attribute>,
                               vis: hir::Visibility, id: ast::NodeId,
                               m: &hir::Mod,
                               name: Option<ast::Name>) -> Module {
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 227017a9073fe..523aac5e6e7d9 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -410,15 +410,15 @@ pub struct Crate {
 }
 
 /// A spanned compile-time attribute list item.
-pub type NestedMetaItem = Spanned<NestedMetaItemKind>;
+pub type NestedMetaItem<Path = self::Path> = Spanned<NestedMetaItemKind<Path>>;
 
 /// Possible values inside of compile-time attribute lists.
 ///
 /// E.g. the '..' in `#[name(..)]`.
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
-pub enum NestedMetaItemKind {
+pub enum NestedMetaItemKind<Path = self::Path> {
     /// A full MetaItem, for recursive meta items.
-    MetaItem(MetaItem),
+    MetaItem(MetaItem<Path>),
     /// A literal.
     ///
     /// E.g. "foo", 64, true
@@ -429,9 +429,9 @@ pub enum NestedMetaItemKind {
 ///
 /// E.g. `#[test]`, `#[derive(..)]`, `#[rustfmt::skip]` or `#[feature = "foo"]`
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
-pub struct MetaItem {
+pub struct MetaItem<Path = self::Path> {
     pub ident: Path,
-    pub node: MetaItemKind,
+    pub node: MetaItemKind<Path>,
     pub span: Span,
 }
 
@@ -439,7 +439,7 @@ pub struct MetaItem {
 ///
 /// E.g. `#[test]`, `#[derive(..)]` or `#[feature = "foo"]`
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
-pub enum MetaItemKind {
+pub enum MetaItemKind<Path = self::Path> {
     /// Word meta item.
     ///
     /// E.g. `test` as in `#[test]`
@@ -447,7 +447,7 @@ pub enum MetaItemKind {
     /// List meta item.
     ///
     /// E.g. `derive(..)` as in `#[derive(..)]`
-    List(Vec<NestedMetaItem>),
+    List(Vec<NestedMetaItem<Path>>),
     /// Name value meta item.
     ///
     /// E.g. `feature = "foo"` as in `#[feature = "foo"]`
@@ -1985,7 +1985,7 @@ impl Idx for AttrId {
 /// Meta-data associated with an item
 /// Doc-comments are promoted to attributes that have is_sugared_doc = true
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
-pub struct Attribute {
+pub struct Attribute<Path = self::Path> {
     pub id: AttrId,
     pub style: AttrStyle,
     pub path: Path,
diff --git a/src/libsyntax/attr/builtin.rs b/src/libsyntax/attr/builtin.rs
index 1bbc1accc07d6..b47dabf440ac3 100644
--- a/src/libsyntax/attr/builtin.rs
+++ b/src/libsyntax/attr/builtin.rs
@@ -16,7 +16,7 @@ use feature_gate::{Features, GatedCfg};
 use parse::ParseSess;
 use syntax_pos::{symbol::Symbol, Span};
 
-use super::{list_contains_name, mark_used, MetaItemKind};
+use super::{list_contains_name, mark_used, MetaItemKind, Path};
 
 enum AttrError {
     MultipleItem(Name),
@@ -80,8 +80,11 @@ pub enum UnwindAttr {
 }
 
 /// Determine what `#[unwind]` attribute is present in `attrs`, if any.
-pub fn find_unwind_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> Option<UnwindAttr> {
-    let syntax_error = |attr: &Attribute| {
+pub fn find_unwind_attr(
+    diagnostic: Option<&Handler>,
+    attrs: &[Attribute<impl Path>],
+) -> Option<UnwindAttr> {
+    let syntax_error = |attr: &Attribute<_>| {
         mark_used(attr);
         diagnostic.map(|d| {
             span_err!(d, attr.span, E0633, "malformed `#[unwind]` attribute");
@@ -90,7 +93,7 @@ pub fn find_unwind_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> Op
     };
 
     attrs.iter().fold(None, |ia, attr| {
-        if attr.path != "unwind" {
+        if !attr.check_name_no_mark_used("unwind") {
             return ia;
         }
         let meta = match attr.meta() {
@@ -165,7 +168,7 @@ pub struct RustcDeprecation {
 
 /// Check if `attrs` contains an attribute like `#![feature(feature_name)]`.
 /// This will not perform any "sanity checks" on the form of the attributes.
-pub fn contains_feature_attr(attrs: &[Attribute], feature_name: &str) -> bool {
+pub fn contains_feature_attr(attrs: &[Attribute<impl Path>], feature_name: &str) -> bool {
     attrs.iter().any(|item| {
         item.check_name("feature") &&
         item.meta_item_list().map(|list| {
@@ -178,17 +181,16 @@ pub fn contains_feature_attr(attrs: &[Attribute], feature_name: &str) -> bool {
 }
 
 /// Find the first stability attribute. `None` if none exists.
-pub fn find_stability(sess: &ParseSess, attrs: &[Attribute],
+pub fn find_stability(sess: &ParseSess, attrs: &[Attribute<impl Path>],
                       item_sp: Span) -> Option<Stability> {
     find_stability_generic(sess, attrs.iter(), item_sp)
 }
 
-fn find_stability_generic<'a, I>(sess: &ParseSess,
-                                 attrs_iter: I,
-                                 item_sp: Span)
-                                 -> Option<Stability>
-    where I: Iterator<Item = &'a Attribute>
-{
+fn find_stability_generic<'a>(
+    sess: &ParseSess,
+    attrs_iter: impl Iterator<Item = &'a Attribute<impl Path>>,
+    item_sp: Span,
+) -> Option<Stability> {
     use self::StabilityLevel::*;
 
     let mut stab: Option<Stability> = None;
@@ -204,7 +206,7 @@ fn find_stability_generic<'a, I>(sess: &ParseSess,
             "unstable",
             "stable",
             "rustc_promotable",
-        ].iter().any(|&s| attr.path == s) {
+        ].iter().any(|&s| attr.check_name_no_mark_used(s)) {
             continue // not a stability level
         }
 
@@ -212,13 +214,13 @@ fn find_stability_generic<'a, I>(sess: &ParseSess,
 
         let meta = attr.meta();
 
-        if attr.path == "rustc_promotable" {
+        if attr.check_name_no_mark_used("rustc_promotable") {
             promotable = true;
         }
         // attributes with data
         else if let Some(MetaItem { node: MetaItemKind::List(ref metas), .. }) = meta {
             let meta = meta.as_ref().unwrap();
-            let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
+            let get = |meta: &MetaItem<_>, item: &mut Option<Symbol>| {
                 if item.is_some() {
                     handle_errors(sess, meta.span, AttrError::MultipleItem(meta.name()));
                     return false
@@ -488,19 +490,23 @@ fn find_stability_generic<'a, I>(sess: &ParseSess,
     stab
 }
 
-pub fn find_crate_name(attrs: &[Attribute]) -> Option<Symbol> {
+pub fn find_crate_name(attrs: &[Attribute<impl Path>]) -> Option<Symbol> {
     super::first_attr_value_str_by_name(attrs, "crate_name")
 }
 
 /// Tests if a cfg-pattern matches the cfg set
-pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) -> bool {
+pub fn cfg_matches(
+    cfg: &ast::MetaItem<impl Path>,
+    sess: &ParseSess,
+    features: Option<&Features>,
+) -> bool {
     eval_condition(cfg, sess, &mut |cfg| {
         if let (Some(feats), Some(gated_cfg)) = (features, GatedCfg::gate(cfg)) {
             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.ident.segments().len() != 1 {
+            return error(cfg.ident.span(), "`cfg` predicate key must be an identifier");
         }
         match &cfg.node {
             MetaItemKind::List(..) => {
@@ -526,10 +532,11 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat
 
 /// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
 /// evaluate individual items.
-pub fn eval_condition<F>(cfg: &ast::MetaItem, sess: &ParseSess, eval: &mut F)
-                         -> bool
-    where F: FnMut(&ast::MetaItem) -> bool
-{
+pub fn eval_condition<Path: self::Path>(
+    cfg: &ast::MetaItem<Path>,
+    sess: &ParseSess,
+    eval: &mut impl FnMut(&ast::MetaItem<Path>) -> bool,
+) -> bool {
     match cfg.node {
         ast::MetaItemKind::List(ref mis) => {
             for mi in mis.iter() {
@@ -583,22 +590,21 @@ pub struct Deprecation {
 }
 
 /// Find the deprecation attribute. `None` if none exists.
-pub fn find_deprecation(sess: &ParseSess, attrs: &[Attribute],
+pub fn find_deprecation(sess: &ParseSess, attrs: &[Attribute<impl Path>],
                         item_sp: Span) -> Option<Deprecation> {
     find_deprecation_generic(sess, attrs.iter(), item_sp)
 }
 
-fn find_deprecation_generic<'a, I>(sess: &ParseSess,
-                                   attrs_iter: I,
-                                   item_sp: Span)
-                                   -> Option<Deprecation>
-    where I: Iterator<Item = &'a Attribute>
-{
+fn find_deprecation_generic<'a>(
+    sess: &ParseSess,
+    attrs_iter: impl Iterator<Item = &'a Attribute<impl Path>>,
+    item_sp: Span,
+) -> Option<Deprecation> {
     let mut depr: Option<Deprecation> = None;
     let diagnostic = &sess.span_diagnostic;
 
     'outer: for attr in attrs_iter {
-        if attr.path != "deprecated" {
+        if !attr.check_name_no_mark_used("deprecated") {
             continue
         }
 
@@ -610,7 +616,7 @@ fn find_deprecation_generic<'a, I>(sess: &ParseSess,
         }
 
         depr = if let Some(metas) = attr.meta_item_list() {
-            let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
+            let get = |meta: &MetaItem<_>, item: &mut Option<Symbol>| {
                 if item.is_some() {
                     handle_errors(sess, meta.span, AttrError::MultipleItem(meta.name()));
                     return false
@@ -713,12 +719,12 @@ impl IntType {
 /// the same discriminant size that the corresponding C enum would or C
 /// structure layout, `packed` to remove padding, and `transparent` to elegate representation
 /// concerns to the only non-ZST field.
-pub fn find_repr_attrs(sess: &ParseSess, attr: &Attribute) -> Vec<ReprAttr> {
+pub fn find_repr_attrs(sess: &ParseSess, attr: &Attribute<impl Path>) -> Vec<ReprAttr> {
     use self::ReprAttr::*;
 
     let mut acc = Vec::new();
     let diagnostic = &sess.span_diagnostic;
-    if attr.path == "repr" {
+    if attr.check_name_no_mark_used("repr") {
         if let Some(items) = attr.meta_item_list() {
             mark_used(attr);
             for item in items {
diff --git a/src/libsyntax/attr/mod.rs b/src/libsyntax/attr/mod.rs
index 6487665947729..f30066e4f9a14 100644
--- a/src/libsyntax/attr/mod.rs
+++ b/src/libsyntax/attr/mod.rs
@@ -22,7 +22,7 @@ pub use self::ReprAttr::*;
 pub use self::StabilityLevel::*;
 
 use ast;
-use ast::{AttrId, Attribute, AttrStyle, Name, Ident, Path, PathSegment};
+use ast::{AttrId, Attribute, AttrStyle, Name, Ident};
 use ast::{MetaItem, MetaItemKind, NestedMetaItem, NestedMetaItemKind};
 use ast::{Lit, LitKind, Expr, ExprKind, Item, Local, Stmt, StmtKind, GenericParam};
 use source_map::{BytePos, Spanned, respan, dummy_spanned};
@@ -37,29 +37,85 @@ use ThinVec;
 use tokenstream::{TokenStream, TokenTree, Delimited, DelimSpan};
 use GLOBALS;
 
+use std::fmt;
 use std::iter;
 
-pub fn mark_used(attr: &Attribute) {
+pub trait Path: Clone + fmt::Debug + fmt::Display + 'static {
+    type Segment: PathSegment;
+
+    fn from_ident(ident: Ident) -> Self {
+        Self::from_span_and_segments(ident.span, vec![Self::Segment::from_ident(ident)])
+    }
+    fn from_span_and_segments(span: Span, segments: Vec<Self::Segment>) -> Self;
+    fn from_nt(nt: &token::Nonterminal) -> Result<Self, Option<MetaItem<Self>>>;
+    fn span(&self) -> Span;
+    fn segments(&self) -> &[Self::Segment];
+}
+
+pub trait PathSegment: 'static {
+    fn from_ident(ident: Ident) -> Self;
+    fn ident(&self) -> Ident;
+}
+
+impl Path for ast::Path {
+    type Segment = ast::PathSegment;
+
+    fn from_span_and_segments(span: Span, segments: Vec<Self::Segment>) -> Self {
+        Self { span, segments }
+    }
+
+    fn from_nt(nt: &token::Nonterminal) -> Result<Self, Option<MetaItem<Self>>> {
+        match nt {
+            &token::Nonterminal::NtIdent(ident, _) => Ok(Self::from_ident(ident)),
+            token::Nonterminal::NtPath(path) => Ok(path.clone()),
+            token::Nonterminal::NtMeta(meta) => Err(Some(meta.clone())),
+            _ => Err(None),
+        }
+    }
+
+    #[inline]
+    fn span(&self) -> Span {
+        self.span
+    }
+
+    #[inline]
+    fn segments(&self) -> &[Self::Segment] {
+        &self.segments
+    }
+}
+
+impl PathSegment for ast::PathSegment {
+    fn from_ident(ident: Ident) -> Self {
+        Self::from_ident(ident)
+    }
+
+    #[inline]
+    fn ident(&self) -> Ident {
+        self.ident
+    }
+}
+
+pub fn mark_used(attr: &Attribute<impl Path>) {
     debug!("Marking {:?} as used.", attr);
     GLOBALS.with(|globals| {
         globals.used_attrs.lock().insert(attr.id);
     });
 }
 
-pub fn is_used(attr: &Attribute) -> bool {
+pub fn is_used(attr: &Attribute<impl Path>) -> bool {
     GLOBALS.with(|globals| {
         globals.used_attrs.lock().contains(attr.id)
     })
 }
 
-pub fn mark_known(attr: &Attribute) {
+pub fn mark_known(attr: &Attribute<impl Path>) {
     debug!("Marking {:?} as known.", attr);
     GLOBALS.with(|globals| {
         globals.known_attrs.lock().insert(attr.id);
     });
 }
 
-pub fn is_known(attr: &Attribute) -> bool {
+pub fn is_known(attr: &Attribute<impl Path>) -> bool {
     GLOBALS.with(|globals| {
         globals.known_attrs.lock().contains(attr.id)
     })
@@ -69,9 +125,9 @@ pub fn is_known_lint_tool(m_item: Ident) -> bool {
     ["clippy"].contains(&m_item.as_str().as_ref())
 }
 
-impl NestedMetaItem {
+impl<Path: self::Path> NestedMetaItem<Path> {
     /// Returns the MetaItem if self is a NestedMetaItemKind::MetaItem.
-    pub fn meta_item(&self) -> Option<&MetaItem> {
+    pub fn meta_item(&self) -> Option<&MetaItem<Path>> {
         match self.node {
             NestedMetaItemKind::MetaItem(ref item) => Some(item),
             _ => None
@@ -127,7 +183,7 @@ impl NestedMetaItem {
     }
 
     /// Returns a MetaItem if self is a MetaItem with Kind Word.
-    pub fn word(&self) -> Option<&MetaItem> {
+    pub fn word(&self) -> Option<&MetaItem<Path>> {
         self.meta_item().and_then(|meta_item| if meta_item.is_word() {
             Some(meta_item)
         } else {
@@ -136,7 +192,7 @@ impl NestedMetaItem {
     }
 
     /// Gets a list of inner meta items from a list MetaItem type.
-    pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
+    pub fn meta_item_list(&self) -> Option<&[NestedMetaItem<Path>]> {
         self.meta_item().and_then(|meta_item| meta_item.meta_item_list())
     }
 
@@ -166,13 +222,18 @@ impl NestedMetaItem {
     }
 }
 
-fn name_from_path(path: &Path) -> Name {
-    path.segments.last().expect("empty path in attribute").ident.name
+fn name_from_path(path: &impl Path) -> Name {
+    path.segments().last().expect("empty path in attribute").ident().name
 }
 
-impl Attribute {
+impl<Path: self::Path> Attribute<Path> {
+    fn check_name_no_mark_used(&self, name: &str) -> bool {
+        self.path.segments().len() == 1 &&
+        self.path.segments()[0].ident().name == name
+    }
+
     pub fn check_name(&self, name: &str) -> bool {
-        let matches = self.path == name;
+        let matches = self.check_name_no_mark_used(name);
         if matches {
             mark_used(self);
         }
@@ -189,7 +250,7 @@ impl Attribute {
         self.meta().and_then(|meta| meta.value_str())
     }
 
-    pub fn meta_item_list(&self) -> Option<Vec<NestedMetaItem>> {
+    pub fn meta_item_list(&self) -> Option<Vec<NestedMetaItem<Path>>> {
         match self.meta() {
             Some(MetaItem { node: MetaItemKind::List(list), .. }) => Some(list),
             _ => None
@@ -197,7 +258,7 @@ impl Attribute {
     }
 
     pub fn is_word(&self) -> bool {
-        self.path.segments.len() == 1 && self.tokens.is_empty()
+        self.path.segments().len() == 1 && self.tokens.is_empty()
     }
 
     pub fn span(&self) -> Span {
@@ -214,7 +275,14 @@ impl Attribute {
     }
 }
 
-impl MetaItem {
+impl<Path: self::Path> MetaItem<Path> {
+    // FIXME(eddyb, petrochenkov) figure out whether
+    // the older `check_name` has been misused.
+    pub fn check_name_correct(&self, name: &str) -> bool {
+        self.ident.segments().len() == 1 &&
+        self.ident.segments()[0].ident().name == name
+    }
+
     pub fn name(&self) -> Name {
         name_from_path(&self.ident)
     }
@@ -240,7 +308,7 @@ impl MetaItem {
         }
     }
 
-    pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
+    pub fn meta_item_list(&self) -> Option<&[NestedMetaItem<Path>]> {
         match self.node {
             MetaItemKind::List(ref l) => Some(&l[..]),
             _ => None
@@ -269,17 +337,17 @@ impl MetaItem {
     }
 
     pub fn is_scoped(&self) -> Option<Ident> {
-        if self.ident.segments.len() > 1 {
-            Some(self.ident.segments[0].ident)
+        if self.ident.segments().len() > 1 {
+            Some(self.ident.segments()[0].ident())
         } else {
             None
         }
     }
 }
 
-impl Attribute {
+impl<Path: self::Path> Attribute<Path> {
     /// Extract the MetaItem from inside this Attribute.
-    pub fn meta(&self) -> Option<MetaItem> {
+    pub fn meta(&self) -> Option<MetaItem<Path>> {
         let mut tokens = self.tokens.trees().peekable();
         Some(MetaItem {
             ident: self.path.clone(),
@@ -326,20 +394,10 @@ impl Attribute {
         })
     }
 
-    pub fn parse_meta<'a>(&self, sess: &'a ParseSess) -> PResult<'a, MetaItem> {
-        Ok(MetaItem {
-            ident: self.path.clone(),
-            node: self.parse(sess, |parser| parser.parse_meta_item_kind())?,
-            span: self.span,
-        })
-    }
-
     /// Convert self to a normal #[doc="foo"] comment, if it is a
     /// comment like `///` or `/** */`. (Returns self unchanged for
     /// non-sugared doc attributes.)
-    pub fn with_desugared_doc<T, F>(&self, f: F) -> T where
-        F: FnOnce(&Attribute) -> T,
-    {
+    pub fn with_desugared_doc<T>(&self, f: impl FnOnce(&Self) -> T) -> T {
         if self.is_sugared_doc {
             let comment = self.value_str().unwrap();
             let meta = mk_name_value_item_str(
@@ -358,26 +416,47 @@ impl Attribute {
     }
 }
 
+impl Attribute {
+    pub fn parse_meta<'a>(&self, sess: &'a ParseSess) -> PResult<'a, MetaItem> {
+        Ok(MetaItem {
+            ident: self.path.clone(),
+            node: self.parse(sess, |parser| parser.parse_meta_item_kind())?,
+            span: self.span,
+        })
+    }
+}
+
 /* Constructors */
 
-pub fn mk_name_value_item_str(ident: Ident, value: Spanned<Symbol>) -> MetaItem {
+pub fn mk_name_value_item_str<Path: self::Path>(
+    ident: Ident,
+    value: Spanned<Symbol>,
+) -> MetaItem<Path> {
     let value = respan(value.span, LitKind::Str(value.node, ast::StrStyle::Cooked));
     mk_name_value_item(ident.span.to(value.span), ident, value)
 }
 
-pub fn mk_name_value_item(span: Span, ident: Ident, value: ast::Lit) -> MetaItem {
+pub fn mk_name_value_item<Path: self::Path>(
+    span: Span,
+    ident: Ident,
+    value: ast::Lit,
+) -> MetaItem<Path> {
     MetaItem { ident: Path::from_ident(ident), span, node: MetaItemKind::NameValue(value) }
 }
 
-pub fn mk_list_item(span: Span, ident: Ident, items: Vec<NestedMetaItem>) -> MetaItem {
+pub fn mk_list_item<Path: self::Path>(
+    span: Span,
+    ident: Ident,
+    items: Vec<NestedMetaItem<Path>>,
+) -> MetaItem<Path> {
     MetaItem { ident: Path::from_ident(ident), span, node: MetaItemKind::List(items) }
 }
 
-pub fn mk_word_item(ident: Ident) -> MetaItem {
+pub fn mk_word_item<Path: self::Path>(ident: Ident) -> MetaItem<Path> {
     MetaItem { ident: Path::from_ident(ident), span: ident.span, node: MetaItemKind::Word }
 }
 
-pub fn mk_nested_word_item(ident: Ident) -> NestedMetaItem {
+pub fn mk_nested_word_item<Path: self::Path>(ident: Ident) -> NestedMetaItem<Path> {
     respan(ident.span, NestedMetaItemKind::MetaItem(mk_word_item(ident)))
 }
 
@@ -393,12 +472,20 @@ pub fn mk_attr_id() -> AttrId {
 }
 
 /// Returns an inner attribute with the given value.
-pub fn mk_attr_inner(span: Span, id: AttrId, item: MetaItem) -> Attribute {
+pub fn mk_attr_inner<Path: self::Path>(
+    span: Span,
+    id: AttrId,
+    item: MetaItem<Path>,
+) -> Attribute<Path> {
     mk_spanned_attr_inner(span, id, item)
 }
 
 /// Returns an inner attribute with the given value and span.
-pub fn mk_spanned_attr_inner(sp: Span, id: AttrId, item: MetaItem) -> Attribute {
+pub fn mk_spanned_attr_inner<Path: self::Path>(
+    sp: Span,
+    id: AttrId,
+    item: MetaItem<Path>,
+) -> Attribute<Path> {
     Attribute {
         id,
         style: ast::AttrStyle::Inner,
@@ -410,12 +497,20 @@ pub fn mk_spanned_attr_inner(sp: Span, id: AttrId, item: MetaItem) -> Attribute
 }
 
 /// Returns an outer attribute with the given value.
-pub fn mk_attr_outer(span: Span, id: AttrId, item: MetaItem) -> Attribute {
+pub fn mk_attr_outer<Path: self::Path>(
+    span: Span,
+    id: AttrId,
+    item: MetaItem<Path>,
+) -> Attribute<Path> {
     mk_spanned_attr_outer(span, id, item)
 }
 
 /// Returns an outer attribute with the given value and span.
-pub fn mk_spanned_attr_outer(sp: Span, id: AttrId, item: MetaItem) -> Attribute {
+pub fn mk_spanned_attr_outer<Path: self::Path>(
+    sp: Span,
+    id: AttrId,
+    item: MetaItem<Path>,
+) -> Attribute<Path> {
     Attribute {
         id,
         style: ast::AttrStyle::Outer,
@@ -432,73 +527,80 @@ pub fn mk_sugared_doc_attr(id: AttrId, text: Symbol, span: Span) -> Attribute {
     Attribute {
         id,
         style,
-        path: Path::from_ident(Ident::from_str("doc").with_span_pos(span)),
-        tokens: MetaItemKind::NameValue(lit).tokens(span),
+        path: ast::Path::from_ident(Ident::from_str("doc").with_span_pos(span)),
+        tokens: MetaItemKind::NameValue::<ast::Path>(lit).tokens(span),
         is_sugared_doc: true,
         span,
     }
 }
 
-pub fn list_contains_name(items: &[NestedMetaItem], name: &str) -> bool {
+pub fn list_contains_name(items: &[NestedMetaItem<impl Path>], name: &str) -> bool {
     items.iter().any(|item| {
         item.check_name(name)
     })
 }
 
-pub fn contains_name(attrs: &[Attribute], name: &str) -> bool {
+pub fn contains_name(attrs: &[Attribute<impl Path>], name: &str) -> bool {
     attrs.iter().any(|item| {
         item.check_name(name)
     })
 }
 
-pub fn find_by_name<'a>(attrs: &'a [Attribute], name: &str) -> Option<&'a Attribute> {
+pub fn find_by_name<'a, Path: self::Path>(
+    attrs: &'a [Attribute<Path>],
+    name: &str,
+) -> Option<&'a Attribute<Path>> {
     attrs.iter().find(|attr| attr.check_name(name))
 }
 
-pub fn filter_by_name<'a>(attrs: &'a [Attribute], name: &'a str)
-    -> impl Iterator<Item = &'a Attribute> {
+pub fn filter_by_name<'a, Path: self::Path>(
+    attrs: &'a [Attribute<Path>],
+    name: &'a str,
+) -> impl Iterator<Item = &'a Attribute<Path>> {
     attrs.iter().filter(move |attr| attr.check_name(name))
 }
 
-pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: &str) -> Option<Symbol> {
+pub fn first_attr_value_str_by_name(attrs: &[Attribute<impl Path>], name: &str) -> Option<Symbol> {
     attrs.iter()
         .find(|at| at.check_name(name))
         .and_then(|at| at.value_str())
 }
 
-impl MetaItem {
+impl<Path: self::Path> MetaItem<Path> {
     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.ident.segments().iter().enumerate() {
             let is_first = i == 0;
             if !is_first {
                 let mod_sep_span = Span::new(last_pos,
-                                             segment.ident.span.lo(),
-                                             segment.ident.span.ctxt());
+                                             segment.ident().span.lo(),
+                                             segment.ident().span.ctxt());
                 idents.push(TokenTree::Token(mod_sep_span, Token::ModSep).into());
             }
-            idents.push(TokenTree::Token(segment.ident.span,
-                                         Token::from_ast_ident(segment.ident)).into());
-            last_pos = segment.ident.span.hi();
+            idents.push(TokenTree::Token(segment.ident().span,
+                                         Token::from_ast_ident(segment.ident())).into());
+            last_pos = segment.ident().span.hi();
         }
         idents.push(self.node.tokens(self.span));
         TokenStream::concat(idents)
     }
 
-    fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
+    fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<Self>
         where I: Iterator<Item = TokenTree>,
     {
         // FIXME: Share code with `parse_path`.
         let ident = match tokens.next() {
             Some(TokenTree::Token(span, Token::Ident(ident, _))) => {
                 if let Some(TokenTree::Token(_, Token::ModSep)) = tokens.peek() {
-                    let mut segments = vec![PathSegment::from_ident(ident.with_span_pos(span))];
+                    let mut segments = vec![
+                        Path::Segment::from_ident(ident.with_span_pos(span)),
+                    ];
                     tokens.next();
                     loop {
                         if let Some(TokenTree::Token(span,
                                                      Token::Ident(ident, _))) = tokens.next() {
-                            segments.push(PathSegment::from_ident(ident.with_span_pos(span)));
+                            segments.push(Path::Segment::from_ident(ident.with_span_pos(span)));
                         } else {
                             return None;
                         }
@@ -508,33 +610,33 @@ impl MetaItem {
                             break;
                         }
                     }
-                    let span = span.with_hi(segments.last().unwrap().ident.span.hi());
-                    Path { span, segments }
+                    let span = span.with_hi(segments.last().unwrap().ident().span.hi());
+                    Path::from_span_and_segments(span, segments)
                 } else {
                     Path::from_ident(ident.with_span_pos(span))
                 }
             }
-            Some(TokenTree::Token(_, Token::Interpolated(ref nt))) => match nt.0 {
-                token::Nonterminal::NtIdent(ident, _) => Path::from_ident(ident),
-                token::Nonterminal::NtMeta(ref meta) => return Some(meta.clone()),
-                token::Nonterminal::NtPath(ref path) => path.clone(),
-                _ => return None,
-            },
+            Some(TokenTree::Token(_, Token::Interpolated(ref nt))) => {
+                match Path::from_nt(&nt.0) {
+                    Ok(path) => path,
+                    Err(opt_meta) => return opt_meta,
+                }
+            }
             _ => return None,
         };
         let list_closing_paren_pos = tokens.peek().map(|tt| tt.span().hi());
         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(ident.span().hi()),
+            _ => ident.span().hi(),
         };
-        let span = ident.span.with_hi(hi);
+        let span = ident.span().with_hi(hi);
         Some(MetaItem { ident, node, span })
     }
 }
 
-impl MetaItemKind {
+impl<Path: self::Path> MetaItemKind<Path> {
     pub fn tokens(&self, span: Span) -> TokenStream {
         match *self {
             MetaItemKind::Word => TokenStream::empty(),
@@ -557,7 +659,7 @@ impl MetaItemKind {
         }
     }
 
-    fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItemKind>
+    fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<Self>
         where I: Iterator<Item = TokenTree>,
     {
         let delimited = match tokens.peek().cloned() {
@@ -591,7 +693,7 @@ impl MetaItemKind {
     }
 }
 
-impl NestedMetaItemKind {
+impl<Path: self::Path> NestedMetaItemKind<Path> {
     fn span(&self) -> Span {
         match *self {
             NestedMetaItemKind::MetaItem(ref item) => item.span,
@@ -606,7 +708,7 @@ impl NestedMetaItemKind {
         }
     }
 
-    fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<NestedMetaItemKind>
+    fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<Self>
         where I: Iterator<Item = TokenTree>,
     {
         if let Some(TokenTree::Token(span, token)) = tokens.peek().cloned() {
@@ -695,46 +797,73 @@ impl LitKind {
 }
 
 pub trait HasAttrs: Sized {
-    fn attrs(&self) -> &[ast::Attribute];
-    fn map_attrs<F: FnOnce(Vec<ast::Attribute>) -> Vec<ast::Attribute>>(self, f: F) -> Self;
+    type Path: Path;
+
+    fn attrs(&self) -> &[Attribute<Self::Path>];
+    fn map_attrs(
+        self,
+        f: impl FnOnce(Vec<Attribute<Self::Path>>) -> Vec<Attribute<Self::Path>>,
+    ) -> Self;
 }
 
 impl<T: HasAttrs> HasAttrs for Spanned<T> {
-    fn attrs(&self) -> &[ast::Attribute] { self.node.attrs() }
-    fn map_attrs<F: FnOnce(Vec<ast::Attribute>) -> Vec<ast::Attribute>>(self, f: F) -> Self {
+    type Path = T::Path;
+
+    fn attrs(&self) -> &[Attribute<Self::Path>] { self.node.attrs() }
+    fn map_attrs(
+        self,
+        f: impl FnOnce(Vec<Attribute<Self::Path>>) -> Vec<Attribute<Self::Path>>,
+    ) -> Self {
         respan(self.span, self.node.map_attrs(f))
     }
 }
 
-impl HasAttrs for Vec<Attribute> {
-    fn attrs(&self) -> &[Attribute] {
+impl<Path: self::Path> HasAttrs for Vec<Attribute<Path>> {
+    type Path = Path;
+
+    fn attrs(&self) -> &[Attribute<Self::Path>] {
         self
     }
-    fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self {
+    fn map_attrs(
+        self,
+        f: impl FnOnce(Vec<Attribute<Self::Path>>) -> Vec<Attribute<Self::Path>>,
+    ) -> Self {
         f(self)
     }
 }
 
-impl HasAttrs for ThinVec<Attribute> {
-    fn attrs(&self) -> &[Attribute] {
+impl<Path: self::Path> HasAttrs for ThinVec<Attribute<Path>> {
+    type Path = Path;
+
+    fn attrs(&self) -> &[Attribute<Self::Path>] {
         self
     }
-    fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self {
+    fn map_attrs(
+        self,
+        f: impl FnOnce(Vec<Attribute<Self::Path>>) -> Vec<Attribute<Self::Path>>,
+    ) -> Self {
         f(self.into()).into()
     }
 }
 
 impl<T: HasAttrs + 'static> HasAttrs for P<T> {
-    fn attrs(&self) -> &[Attribute] {
+    type Path = T::Path;
+
+    fn attrs(&self) -> &[Attribute<Self::Path>] {
         (**self).attrs()
     }
-    fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self {
+    fn map_attrs(
+        self,
+        f: impl FnOnce(Vec<Attribute<Self::Path>>) -> Vec<Attribute<Self::Path>>,
+    ) -> Self {
         self.map(|t| t.map_attrs(f))
     }
 }
 
 impl HasAttrs for StmtKind {
-    fn attrs(&self) -> &[Attribute] {
+    type Path = ast::Path;
+
+    fn attrs(&self) -> &[Attribute<Self::Path>] {
         match *self {
             StmtKind::Local(ref local) => local.attrs(),
             StmtKind::Item(..) => &[],
@@ -746,7 +875,10 @@ impl HasAttrs for StmtKind {
         }
     }
 
-    fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self {
+    fn map_attrs(
+        self,
+        f: impl FnOnce(Vec<Attribute<Self::Path>>) -> Vec<Attribute<Self::Path>>,
+    ) -> Self {
         match self {
             StmtKind::Local(local) => StmtKind::Local(local.map_attrs(f)),
             StmtKind::Item(..) => self,
@@ -760,18 +892,28 @@ impl HasAttrs for StmtKind {
 }
 
 impl HasAttrs for Stmt {
-    fn attrs(&self) -> &[ast::Attribute] { self.node.attrs() }
-    fn map_attrs<F: FnOnce(Vec<ast::Attribute>) -> Vec<ast::Attribute>>(self, f: F) -> Self {
+    type Path = ast::Path;
+
+    fn attrs(&self) -> &[Attribute<Self::Path>] { self.node.attrs() }
+    fn map_attrs(
+        self,
+        f: impl FnOnce(Vec<Attribute<Self::Path>>) -> Vec<Attribute<Self::Path>>,
+    ) -> Self {
         Stmt { id: self.id, node: self.node.map_attrs(f), span: self.span }
     }
 }
 
 impl HasAttrs for GenericParam {
-    fn attrs(&self) -> &[ast::Attribute] {
+    type Path = ast::Path;
+
+    fn attrs(&self) -> &[Attribute<Self::Path>] {
         &self.attrs
     }
 
-    fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(mut self, f: F) -> Self {
+    fn map_attrs(
+        mut self,
+        f: impl FnOnce(Vec<Attribute<Self::Path>>) -> Vec<Attribute<Self::Path>>,
+    ) -> Self {
         self.attrs = self.attrs.map_attrs(f);
         self
     }
@@ -780,13 +922,16 @@ impl HasAttrs for GenericParam {
 macro_rules! derive_has_attrs {
     ($($ty:path),*) => { $(
         impl HasAttrs for $ty {
-            fn attrs(&self) -> &[Attribute] {
+            type Path = ast::Path;
+
+            fn attrs(&self) -> &[Attribute<Self::Path>] {
                 &self.attrs
             }
 
-            fn map_attrs<F>(mut self, f: F) -> Self
-                where F: FnOnce(Vec<Attribute>) -> Vec<Attribute>,
-            {
+            fn map_attrs(
+                mut self,
+                f: impl FnOnce(Vec<Attribute<Self::Path>>) -> Vec<Attribute<Self::Path>>,
+            ) -> Self {
                 self.attrs = self.attrs.map_attrs(f);
                 self
             }
diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs
index d8fb20d425008..9196537ba2331 100644
--- a/src/libsyntax/config.rs
+++ b/src/libsyntax/config.rs
@@ -75,7 +75,7 @@ macro_rules! configure {
 }
 
 impl<'a> StripUnconfigured<'a> {
-    pub fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> {
+    pub fn configure<T: HasAttrs<Path = ast::Path>>(&mut self, node: T) -> Option<T> {
         let node = self.process_cfg_attrs(node);
         if self.in_cfg(node.attrs()) { Some(node) } else { None }
     }
@@ -86,7 +86,7 @@ impl<'a> StripUnconfigured<'a> {
     /// Gives compiler warnigns if any `cfg_attr` does not contain any
     /// attributes and is in the original source code. Gives compiler errors if
     /// the syntax of any `cfg_attr` is incorrect.
-    pub fn process_cfg_attrs<T: HasAttrs>(&mut self, node: T) -> T {
+    pub fn process_cfg_attrs<T: HasAttrs<Path = ast::Path>>(&mut self, node: T) -> T {
         node.map_attrs(|attrs| {
             attrs.into_iter().flat_map(|attr| self.process_cfg_attr(attr)).collect()
         })
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index bb927b62a181e..342ef01490509 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -46,6 +46,8 @@ pub enum Annotatable {
 }
 
 impl HasAttrs for Annotatable {
+    type Path = ast::Path;
+
     fn attrs(&self) -> &[Attribute] {
         match *self {
             Annotatable::Item(ref item) => &item.attrs,
@@ -57,7 +59,7 @@ impl HasAttrs for Annotatable {
         }
     }
 
-    fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self {
+    fn map_attrs(self, f: impl FnOnce(Vec<Attribute>) -> Vec<Attribute>) -> Self {
         match self {
             Annotatable::Item(item) => Annotatable::Item(item.map_attrs(f)),
             Annotatable::TraitItem(trait_item) => Annotatable::TraitItem(trait_item.map_attrs(f)),
diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs
index 684cee3887463..f5310e8658c7c 100644
--- a/src/libsyntax/ext/derive.rs
+++ b/src/libsyntax/ext/derive.rs
@@ -46,7 +46,7 @@ pub fn collect_derives(cx: &mut ExtCtxt, attrs: &mut Vec<ast::Attribute>) -> Vec
 }
 
 pub fn add_derived_markers<T>(cx: &mut ExtCtxt, span: Span, traits: &[ast::Path], item: T) -> T
-    where T: HasAttrs,
+    where T: HasAttrs<Path = ast::Path>,
 {
     let (mut names, mut pretty_name) = (FxHashSet::default(), "derive(".to_owned());
     for (i, path) in traits.iter().enumerate() {
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 68a96293891a0..915f7289e2490 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -1129,7 +1129,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
     /// If `item` is an attr invocation, remove and return the macro attribute and derive traits.
     fn classify_item<T>(&mut self, mut item: T)
                         -> (Option<ast::Attribute>, Vec<Path>, T, /* after_derive */ bool)
-        where T: HasAttrs,
+        where T: HasAttrs<Path = ast::Path>,
     {
         let (mut attr, mut traits, mut after_derive) = (None, Vec::new(), false);
 
@@ -1151,8 +1151,10 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
     /// Alternative of `classify_item()` that ignores `#[derive]` so invocations fallthrough
     /// to the unused-attributes lint (making it an error on statements and expressions
     /// is a breaking change)
-    fn classify_nonitem<T: HasAttrs>(&mut self, mut item: T)
-                                     -> (Option<ast::Attribute>, T, /* after_derive */ bool) {
+    fn classify_nonitem<T: HasAttrs<Path = ast::Path>>(
+        &mut self,
+        mut item: T,
+    ) -> (Option<ast::Attribute>, T, /* after_derive */ bool) {
         let (mut attr, mut after_derive) = (None, false);
 
         item = item.map_attrs(|mut attrs| {
@@ -1169,7 +1171,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
         (attr, item, after_derive)
     }
 
-    fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> {
+    fn configure<T: HasAttrs<Path = ast::Path>>(&mut self, node: T) -> Option<T> {
         self.cfg.configure(node)
     }
 
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index d0c4d1c7dce0a..f199d5ddd4e9e 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -1198,7 +1198,7 @@ pub struct GatedCfg {
 }
 
 impl GatedCfg {
-    pub fn gate(cfg: &ast::MetaItem) -> Option<GatedCfg> {
+    pub fn gate(cfg: &ast::MetaItem<impl attr::Path>) -> Option<GatedCfg> {
         let name = cfg.name().as_str();
         GATED_CFGS.iter()
                   .position(|info| info.0 == name)
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 14ad4b5c6f815..3c7515d147a59 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -104,12 +104,14 @@ pub fn print_crate<'a>(cm: &'a SourceMap,
         // #![feature(prelude_import)]
         let pi_nested = attr::mk_nested_word_item(ast::Ident::from_str("prelude_import"));
         let list = attr::mk_list_item(DUMMY_SP, ast::Ident::from_str("feature"), vec![pi_nested]);
-        let fake_attr = attr::mk_attr_inner(DUMMY_SP, attr::mk_attr_id(), list);
+        let fake_attr: ast::Attribute =
+            attr::mk_attr_inner(DUMMY_SP, attr::mk_attr_id(), list);
         s.print_attribute(&fake_attr)?;
 
         // #![no_std]
         let no_std_meta = attr::mk_word_item(ast::Ident::from_str("no_std"));
-        let fake_attr = attr::mk_attr_inner(DUMMY_SP, attr::mk_attr_id(), no_std_meta);
+        let fake_attr: ast::Attribute =
+            attr::mk_attr_inner(DUMMY_SP, attr::mk_attr_id(), no_std_meta);
         s.print_attribute(&fake_attr)?;
     }
 
@@ -401,15 +403,15 @@ pub fn block_to_string(blk: &ast::Block) -> String {
     })
 }
 
-pub fn meta_list_item_to_string(li: &ast::NestedMetaItem) -> String {
+pub fn meta_list_item_to_string(li: &ast::NestedMetaItem<impl attr::Path>) -> String {
     to_string(|s| s.print_meta_list_item(li))
 }
 
-pub fn meta_item_to_string(mi: &ast::MetaItem) -> String {
+pub fn meta_item_to_string(mi: &ast::MetaItem<impl attr::Path>) -> String {
     to_string(|s| s.print_meta_item(mi))
 }
 
-pub fn attribute_to_string(attr: &ast::Attribute) -> String {
+pub fn attribute_to_string(attr: &ast::Attribute<impl attr::Path>) -> String {
     to_string(|s| s.print_attribute(attr))
 }
 
@@ -674,33 +676,33 @@ pub trait PrintState<'a> {
     }
 
     fn print_inner_attributes(&mut self,
-                              attrs: &[ast::Attribute]) -> io::Result<()> {
+                              attrs: &[ast::Attribute<impl attr::Path>]) -> io::Result<()> {
         self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, true)
     }
 
     fn print_inner_attributes_no_trailing_hardbreak(&mut self,
-                                                   attrs: &[ast::Attribute])
+                                                   attrs: &[ast::Attribute<impl attr::Path>])
                                                    -> io::Result<()> {
         self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, false)
     }
 
     fn print_outer_attributes(&mut self,
-                              attrs: &[ast::Attribute]) -> io::Result<()> {
+                              attrs: &[ast::Attribute<impl attr::Path>]) -> io::Result<()> {
         self.print_either_attributes(attrs, ast::AttrStyle::Outer, false, true)
     }
 
     fn print_inner_attributes_inline(&mut self,
-                                     attrs: &[ast::Attribute]) -> io::Result<()> {
+                                     attrs: &[ast::Attribute<impl attr::Path>]) -> io::Result<()> {
         self.print_either_attributes(attrs, ast::AttrStyle::Inner, true, true)
     }
 
     fn print_outer_attributes_inline(&mut self,
-                                     attrs: &[ast::Attribute]) -> io::Result<()> {
+                                     attrs: &[ast::Attribute<impl attr::Path>]) -> io::Result<()> {
         self.print_either_attributes(attrs, ast::AttrStyle::Outer, true, true)
     }
 
     fn print_either_attributes(&mut self,
-                              attrs: &[ast::Attribute],
+                              attrs: &[ast::Attribute<impl attr::Path>],
                               kind: ast::AttrStyle,
                               is_inline: bool,
                               trailing_hardbreak: bool) -> io::Result<()> {
@@ -720,27 +722,29 @@ pub trait PrintState<'a> {
         Ok(())
     }
 
-    fn print_attribute_path(&mut self, path: &ast::Path) -> io::Result<()> {
-        for (i, segment) in path.segments.iter().enumerate() {
+    fn print_attribute_path(&mut self, path: &impl attr::Path) -> io::Result<()> {
+        use attr::PathSegment;
+
+        for (i, segment) in path.segments().iter().enumerate() {
             if i > 0 {
                 self.writer().word("::")?
             }
-            if segment.ident.name != keywords::CrateRoot.name() &&
-               segment.ident.name != keywords::DollarCrate.name()
+            if segment.ident().name != keywords::CrateRoot.name() &&
+               segment.ident().name != keywords::DollarCrate.name()
             {
-                self.writer().word(segment.ident.as_str().get())?;
-            } else if segment.ident.name == keywords::DollarCrate.name() {
-                self.print_dollar_crate(segment.ident.span.ctxt())?;
+                self.writer().word(segment.ident().as_str().get())?;
+            } else if segment.ident().name == keywords::DollarCrate.name() {
+                self.print_dollar_crate(segment.ident().span.ctxt())?;
             }
         }
         Ok(())
     }
 
-    fn print_attribute(&mut self, attr: &ast::Attribute) -> io::Result<()> {
+    fn print_attribute(&mut self, attr: &ast::Attribute<impl attr::Path>) -> io::Result<()> {
         self.print_attribute_inline(attr, false)
     }
 
-    fn print_attribute_inline(&mut self, attr: &ast::Attribute,
+    fn print_attribute_inline(&mut self, attr: &ast::Attribute<impl attr::Path>,
                               is_inline: bool) -> io::Result<()> {
         if !is_inline {
             self.hardbreak_if_not_bol()?;
@@ -765,7 +769,10 @@ pub trait PrintState<'a> {
         }
     }
 
-    fn print_meta_list_item(&mut self, item: &ast::NestedMetaItem) -> io::Result<()> {
+    fn print_meta_list_item(
+        &mut self,
+        item: &ast::NestedMetaItem<impl attr::Path>,
+    ) -> io::Result<()> {
         match item.node {
             ast::NestedMetaItemKind::MetaItem(ref mi) => {
                 self.print_meta_item(mi)
@@ -776,7 +783,7 @@ pub trait PrintState<'a> {
         }
     }
 
-    fn print_meta_item(&mut self, item: &ast::MetaItem) -> io::Result<()> {
+    fn print_meta_item(&mut self, item: &ast::MetaItem<impl attr::Path>) -> io::Result<()> {
         self.ibox(INDENT_UNIT)?;
         match item.node {
             ast::MetaItemKind::Word => self.print_attribute_path(&item.ident)?,