about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorBaoshan <pangbw@gmail.com>2019-09-01 17:52:09 -0700
committerGitHub <noreply@github.com>2019-09-01 17:52:09 -0700
commitd5ef9df032ec32c48d6c59050735352c48ff16f8 (patch)
tree31a87971a84ffc967645099773910d6498d0cb0b /src/libsyntax
parent7726b54c059db0759e9725a58e222118eacec6d9 (diff)
parentdfd43f0fdd4e6969c7d82c0670d70bf305fbccf8 (diff)
downloadrust-d5ef9df032ec32c48d6c59050735352c48ff16f8.tar.gz
rust-d5ef9df032ec32c48d6c59050735352c48ff16f8.zip
Merge pull request #13 from rust-lang/master
sync with rust-lang/rust
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ast.rs22
-rw-r--r--src/libsyntax/attr/mod.rs2
-rw-r--r--src/libsyntax/diagnostics/metadata.rs93
-rw-r--r--src/libsyntax/diagnostics/plugin.rs34
-rw-r--r--src/libsyntax/ext/base.rs145
-rw-r--r--src/libsyntax/ext/build.rs12
-rw-r--r--src/libsyntax/ext/expand.rs144
-rw-r--r--src/libsyntax/ext/proc_macro.rs6
-rw-r--r--src/libsyntax/ext/proc_macro_server.rs14
-rw-r--r--src/libsyntax/ext/tt/macro_rules.rs88
-rw-r--r--src/libsyntax/ext/tt/transcribe.rs44
-rw-r--r--src/libsyntax/feature_gate.rs2495
-rw-r--r--src/libsyntax/feature_gate/accepted.rs248
-rw-r--r--src/libsyntax/feature_gate/active.rs549
-rw-r--r--src/libsyntax/feature_gate/builtin_attrs.rs573
-rw-r--r--src/libsyntax/feature_gate/check.rs958
-rw-r--r--src/libsyntax/feature_gate/mod.rs64
-rw-r--r--src/libsyntax/feature_gate/removed.rs105
-rw-r--r--src/libsyntax/json.rs2
-rw-r--r--src/libsyntax/lib.rs1
-rw-r--r--src/libsyntax/mut_visit.rs126
-rw-r--r--src/libsyntax/parse/attr.rs7
-rw-r--r--src/libsyntax/parse/diagnostics.rs34
-rw-r--r--src/libsyntax/parse/lexer/tests.rs78
-rw-r--r--src/libsyntax/parse/literal.rs4
-rw-r--r--src/libsyntax/parse/mod.rs32
-rw-r--r--src/libsyntax/parse/parser.rs114
-rw-r--r--src/libsyntax/parse/parser/expr.rs37
-rw-r--r--src/libsyntax/parse/parser/item.rs11
-rw-r--r--src/libsyntax/parse/parser/pat.rs443
-rw-r--r--src/libsyntax/parse/parser/path.rs2
-rw-r--r--src/libsyntax/parse/parser/stmt.rs3
-rw-r--r--src/libsyntax/parse/parser/ty.rs2
-rw-r--r--src/libsyntax/parse/token.rs43
-rw-r--r--src/libsyntax/print/pprust.rs32
-rw-r--r--src/libsyntax/tests.rs13
-rw-r--r--src/libsyntax/tokenstream.rs9
-rw-r--r--src/libsyntax/util/node_count.rs7
-rw-r--r--src/libsyntax/visit.rs68
39 files changed, 3434 insertions, 3230 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 50e428ea0cc..6be00bcef45 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -1789,11 +1789,11 @@ pub struct InlineAsm {
     pub dialect: AsmDialect,
 }
 
-/// An argument in a function header.
+/// A parameter in a function header.
 ///
 /// E.g., `bar: usize` as in `fn foo(bar: usize)`.
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
-pub struct Arg {
+pub struct Param {
     pub attrs: ThinVec<Attribute>,
     pub ty: P<Ty>,
     pub pat: P<Pat>,
@@ -1816,7 +1816,7 @@ pub enum SelfKind {
 
 pub type ExplicitSelf = Spanned<SelfKind>;
 
-impl Arg {
+impl Param {
     pub fn to_self(&self) -> Option<ExplicitSelf> {
         if let PatKind::Ident(BindingMode::ByValue(mutbl), ident, _) = self.pat.node {
             if ident.name == kw::SelfLower {
@@ -1843,14 +1843,14 @@ impl Arg {
         }
     }
 
-    pub fn from_self(attrs: ThinVec<Attribute>, eself: ExplicitSelf, eself_ident: Ident) -> Arg {
+    pub fn from_self(attrs: ThinVec<Attribute>, eself: ExplicitSelf, eself_ident: Ident) -> Param {
         let span = eself.span.to(eself_ident.span);
         let infer_ty = P(Ty {
             id: DUMMY_NODE_ID,
             node: TyKind::ImplicitSelf,
             span,
         });
-        let arg = |mutbl, ty| Arg {
+        let param = |mutbl, ty| Param {
             attrs,
             pat: P(Pat {
                 id: DUMMY_NODE_ID,
@@ -1862,9 +1862,9 @@ impl Arg {
             id: DUMMY_NODE_ID,
         };
         match eself.node {
-            SelfKind::Explicit(ty, mutbl) => arg(mutbl, ty),
-            SelfKind::Value(mutbl) => arg(mutbl, infer_ty),
-            SelfKind::Region(lt, mutbl) => arg(
+            SelfKind::Explicit(ty, mutbl) => param(mutbl, ty),
+            SelfKind::Value(mutbl) => param(mutbl, infer_ty),
+            SelfKind::Region(lt, mutbl) => param(
                 Mutability::Immutable,
                 P(Ty {
                     id: DUMMY_NODE_ID,
@@ -1887,17 +1887,17 @@ impl Arg {
 /// E.g., `fn foo(bar: baz)`.
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub struct FnDecl {
-    pub inputs: Vec<Arg>,
+    pub inputs: Vec<Param>,
     pub output: FunctionRetTy,
     pub c_variadic: bool,
 }
 
 impl FnDecl {
     pub fn get_self(&self) -> Option<ExplicitSelf> {
-        self.inputs.get(0).and_then(Arg::to_self)
+        self.inputs.get(0).and_then(Param::to_self)
     }
     pub fn has_self(&self) -> bool {
-        self.inputs.get(0).map(Arg::is_self).unwrap_or(false)
+        self.inputs.get(0).map(Param::is_self).unwrap_or(false)
     }
 }
 
diff --git a/src/libsyntax/attr/mod.rs b/src/libsyntax/attr/mod.rs
index bcf03b5237a..0e5cfa73a9e 100644
--- a/src/libsyntax/attr/mod.rs
+++ b/src/libsyntax/attr/mod.rs
@@ -714,7 +714,7 @@ macro_rules! derive_has_attrs {
 
 derive_has_attrs! {
     Item, Expr, Local, ast::ForeignItem, ast::StructField, ast::ImplItem, ast::TraitItem, ast::Arm,
-    ast::Field, ast::FieldPat, ast::Variant, ast::Arg
+    ast::Field, ast::FieldPat, ast::Variant, ast::Param
 }
 
 pub fn inject(mut krate: ast::Crate, parse_sess: &ParseSess, attrs: &[String]) -> ast::Crate {
diff --git a/src/libsyntax/diagnostics/metadata.rs b/src/libsyntax/diagnostics/metadata.rs
deleted file mode 100644
index 53f37bb10bd..00000000000
--- a/src/libsyntax/diagnostics/metadata.rs
+++ /dev/null
@@ -1,93 +0,0 @@
-//! This module contains utilities for outputting metadata for diagnostic errors.
-//!
-//! Each set of errors is mapped to a metadata file by a name, which is
-//! currently always a crate name.
-
-use std::collections::BTreeMap;
-use std::env;
-use std::fs::{remove_file, create_dir_all, File};
-use std::io::Write;
-use std::path::PathBuf;
-use std::error::Error;
-use rustc_serialize::json::as_json;
-
-use syntax_pos::{Span, FileName};
-
-use crate::ext::base::ExtCtxt;
-use crate::diagnostics::plugin::{ErrorMap, ErrorInfo};
-
-/// JSON encodable/decodable version of `ErrorInfo`.
-#[derive(PartialEq, RustcDecodable, RustcEncodable)]
-pub struct ErrorMetadata {
-    pub description: Option<String>,
-    pub use_site: Option<ErrorLocation>
-}
-
-/// Mapping from error codes to metadata that can be (de)serialized.
-pub type ErrorMetadataMap = BTreeMap<String, ErrorMetadata>;
-
-/// JSON encodable error location type with filename and line number.
-#[derive(PartialEq, RustcDecodable, RustcEncodable)]
-pub struct ErrorLocation {
-    pub filename: FileName,
-    pub line: usize
-}
-
-impl ErrorLocation {
-    /// Creates an error location from a span.
-    pub fn from_span(ecx: &ExtCtxt<'_>, sp: Span) -> ErrorLocation {
-        let loc = ecx.source_map().lookup_char_pos(sp.lo());
-        ErrorLocation {
-            filename: loc.file.name.clone(),
-            line: loc.line
-        }
-    }
-}
-
-/// Gets the directory where metadata for a given `prefix` should be stored.
-///
-/// See `output_metadata`.
-pub fn get_metadata_dir(prefix: &str) -> PathBuf {
-    env::var_os("RUSTC_ERROR_METADATA_DST")
-        .map(PathBuf::from)
-        .expect("env var `RUSTC_ERROR_METADATA_DST` isn't set")
-        .join(prefix)
-}
-
-/// Map `name` to a path in the given directory: <directory>/<name>.json
-fn get_metadata_path(directory: PathBuf, name: &str) -> PathBuf {
-    directory.join(format!("{}.json", name))
-}
-
-/// Write metadata for the errors in `err_map` to disk, to a file corresponding to `prefix/name`.
-///
-/// For our current purposes the prefix is the target architecture and the name is a crate name.
-/// If an error occurs steps will be taken to ensure that no file is created.
-pub fn output_metadata(ecx: &ExtCtxt<'_>, prefix: &str, name: &str, err_map: &ErrorMap)
-    -> Result<(), Box<dyn Error>>
-{
-    // Create the directory to place the file in.
-    let metadata_dir = get_metadata_dir(prefix);
-    create_dir_all(&metadata_dir)?;
-
-    // Open the metadata file.
-    let metadata_path = get_metadata_path(metadata_dir, name);
-    let mut metadata_file = File::create(&metadata_path)?;
-
-    // Construct a serializable map.
-    let json_map = err_map.iter().map(|(k, &ErrorInfo { description, use_site })| {
-        let key = k.as_str().to_string();
-        let value = ErrorMetadata {
-            description: description.map(|n| n.as_str().to_string()),
-            use_site: use_site.map(|sp| ErrorLocation::from_span(ecx, sp))
-        };
-        (key, value)
-    }).collect::<ErrorMetadataMap>();
-
-    // Write the data to the file, deleting it if the write fails.
-    let result = write!(&mut metadata_file, "{}", as_json(&json_map));
-    if result.is_err() {
-        remove_file(&metadata_path)?;
-    }
-    Ok(result?)
-}
diff --git a/src/libsyntax/diagnostics/plugin.rs b/src/libsyntax/diagnostics/plugin.rs
index 9618b5acfb0..e9a55af52e8 100644
--- a/src/libsyntax/diagnostics/plugin.rs
+++ b/src/libsyntax/diagnostics/plugin.rs
@@ -1,5 +1,4 @@
 use std::collections::BTreeMap;
-use std::env;
 
 use crate::ast::{self, Ident, Name};
 use crate::source_map;
@@ -12,8 +11,6 @@ use crate::tokenstream::{TokenTree};
 use smallvec::smallvec;
 use syntax_pos::Span;
 
-use crate::diagnostics::metadata::output_metadata;
-
 pub use errors::*;
 
 // Maximum width of any line in an extended error description (inclusive).
@@ -127,36 +124,13 @@ pub fn expand_build_diagnostic_array<'cx>(ecx: &'cx mut ExtCtxt<'_>,
                                           token_tree: &[TokenTree])
                                           -> Box<dyn MacResult+'cx> {
     assert_eq!(token_tree.len(), 3);
-    let (crate_name, ident) = match (&token_tree[0], &token_tree[2]) {
-        (
-            // Crate name.
-            &TokenTree::Token(Token { kind: token::Ident(crate_name, _), .. }),
-            // DIAGNOSTICS ident.
-            &TokenTree::Token(Token { kind: token::Ident(name, _), span })
-        ) => (crate_name, Ident::new(name, span)),
+    let ident = match &token_tree[2] {
+        // DIAGNOSTICS ident.
+        &TokenTree::Token(Token { kind: token::Ident(name, _), span })
+        => Ident::new(name, span),
         _ => unreachable!()
     };
 
-    // Output error metadata to `tmp/extended-errors/<target arch>/<crate name>.json`
-    if let Ok(target_triple) = env::var("CFG_COMPILER_HOST_TRIPLE") {
-        ecx.parse_sess.registered_diagnostics.with_lock(|diagnostics| {
-            if let Err(e) = output_metadata(ecx,
-                                            &target_triple,
-                                            &crate_name.as_str(),
-                                            diagnostics) {
-                ecx.span_bug(span, &format!(
-                    "error writing metadata for triple `{}` and crate `{}`, error: {}, \
-                     cause: {:?}",
-                    target_triple, crate_name, e.description(), e.source()
-                ));
-            }
-        });
-    } else {
-        ecx.span_err(span, &format!(
-            "failed to write metadata for crate `{}` because $CFG_COMPILER_HOST_TRIPLE is not set",
-            crate_name));
-    }
-
     // Construct the output expression.
     let (count, expr) =
         ecx.parse_sess.registered_diagnostics.with_lock(|diagnostics| {
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index b0a4a6af983..5d68983d7cb 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -1,16 +1,17 @@
 use crate::ast::{self, NodeId, Attribute, Name, PatKind};
-use crate::attr::{HasAttrs, Stability, Deprecation};
+use crate::attr::{self, HasAttrs, Stability, Deprecation};
 use crate::source_map::SourceMap;
 use crate::edition::Edition;
 use crate::ext::expand::{self, AstFragment, Invocation};
-use crate::ext::hygiene::{ExpnId, SyntaxContext, Transparency};
+use crate::ext::hygiene::{ExpnId, Transparency};
 use crate::mut_visit::{self, MutVisitor};
-use crate::parse::{self, parser, DirectoryOwnership};
+use crate::parse::{self, parser, ParseSess, DirectoryOwnership};
 use crate::parse::token;
 use crate::ptr::P;
 use crate::symbol::{kw, sym, Ident, Symbol};
 use crate::{ThinVec, MACRO_ARGUMENTS};
 use crate::tokenstream::{self, TokenStream, TokenTree};
+use crate::visit::Visitor;
 
 use errors::{DiagnosticBuilder, DiagnosticId};
 use smallvec::{smallvec, SmallVec};
@@ -72,6 +73,17 @@ impl Annotatable {
         }
     }
 
+    pub fn visit_with<'a, V: Visitor<'a>>(&'a self, visitor: &mut V) {
+        match self {
+            Annotatable::Item(item) => visitor.visit_item(item),
+            Annotatable::TraitItem(trait_item) => visitor.visit_trait_item(trait_item),
+            Annotatable::ImplItem(impl_item) => visitor.visit_impl_item(impl_item),
+            Annotatable::ForeignItem(foreign_item) => visitor.visit_foreign_item(foreign_item),
+            Annotatable::Stmt(stmt) => visitor.visit_stmt(stmt),
+            Annotatable::Expr(expr) => visitor.visit_expr(expr),
+        }
+    }
+
     pub fn expect_item(self) -> P<ast::Item> {
         match self {
             Annotatable::Item(i) => i,
@@ -549,8 +561,6 @@ pub struct SyntaxExtension {
     pub kind: SyntaxExtensionKind,
     /// Span of the macro definition.
     pub span: Span,
-    /// Hygienic properties of spans produced by this macro by default.
-    pub default_transparency: Transparency,
     /// Whitelist of unstable features that are treated as stable inside this macro.
     pub allow_internal_unstable: Option<Lrc<[Symbol]>>,
     /// Suppresses the `unsafe_code` lint for code produced by this macro.
@@ -572,22 +582,6 @@ pub struct SyntaxExtension {
     pub is_derive_copy: bool,
 }
 
-impl SyntaxExtensionKind {
-    /// When a syntax extension is constructed,
-    /// its transparency can often be inferred from its kind.
-    fn default_transparency(&self) -> Transparency {
-        match self {
-            SyntaxExtensionKind::Bang(..) |
-            SyntaxExtensionKind::Attr(..) |
-            SyntaxExtensionKind::Derive(..) |
-            SyntaxExtensionKind::NonMacroAttr { .. } => Transparency::Opaque,
-            SyntaxExtensionKind::LegacyBang(..) |
-            SyntaxExtensionKind::LegacyAttr(..) |
-            SyntaxExtensionKind::LegacyDerive(..) => Transparency::SemiTransparent,
-        }
-    }
-}
-
 impl SyntaxExtension {
     /// Returns which kind of macro calls this syntax extension.
     pub fn macro_kind(&self) -> MacroKind {
@@ -606,7 +600,6 @@ impl SyntaxExtension {
     pub fn default(kind: SyntaxExtensionKind, edition: Edition) -> SyntaxExtension {
         SyntaxExtension {
             span: DUMMY_SP,
-            default_transparency: kind.default_transparency(),
             allow_internal_unstable: None,
             allow_internal_unsafe: false,
             local_inner_macros: false,
@@ -620,6 +613,69 @@ impl SyntaxExtension {
         }
     }
 
+    /// Constructs a syntax extension with the given properties
+    /// and other properties converted from attributes.
+    pub fn new(
+        sess: &ParseSess,
+        kind: SyntaxExtensionKind,
+        span: Span,
+        helper_attrs: Vec<Symbol>,
+        edition: Edition,
+        name: Name,
+        attrs: &[ast::Attribute],
+    ) -> SyntaxExtension {
+        let allow_internal_unstable =
+            attr::find_by_name(attrs, sym::allow_internal_unstable).map(|attr| {
+                attr.meta_item_list()
+                    .map(|list| {
+                        list.iter()
+                            .filter_map(|it| {
+                                let name = it.ident().map(|ident| ident.name);
+                                if name.is_none() {
+                                    sess.span_diagnostic.span_err(
+                                        it.span(), "allow internal unstable expects feature names"
+                                    )
+                                }
+                                name
+                            })
+                            .collect::<Vec<Symbol>>()
+                            .into()
+                    })
+                    .unwrap_or_else(|| {
+                        sess.span_diagnostic.span_warn(
+                            attr.span,
+                            "allow_internal_unstable expects list of feature names. In the future \
+                             this will become a hard error. Please use `allow_internal_unstable(\
+                             foo, bar)` to only allow the `foo` and `bar` features",
+                        );
+                        vec![sym::allow_internal_unstable_backcompat_hack].into()
+                    })
+            });
+
+        let mut local_inner_macros = false;
+        if let Some(macro_export) = attr::find_by_name(attrs, sym::macro_export) {
+            if let Some(l) = macro_export.meta_item_list() {
+                local_inner_macros = attr::list_contains_name(&l, sym::local_inner_macros);
+            }
+        }
+
+        let is_builtin = attr::contains_name(attrs, sym::rustc_builtin_macro);
+
+        SyntaxExtension {
+            kind,
+            span,
+            allow_internal_unstable,
+            allow_internal_unsafe: attr::contains_name(attrs, sym::allow_internal_unsafe),
+            local_inner_macros,
+            stability: attr::find_stability(&sess, attrs, span),
+            deprecation: attr::find_deprecation(&sess, attrs, span),
+            helper_attrs,
+            edition,
+            is_builtin,
+            is_derive_copy: is_builtin && name == sym::Copy,
+        }
+    }
+
     pub fn dummy_bang(edition: Edition) -> SyntaxExtension {
         fn expander<'cx>(_: &'cx mut ExtCtxt<'_>, span: Span, _: &[TokenTree])
                          -> Box<dyn MacResult + 'cx> {
@@ -646,7 +702,6 @@ impl SyntaxExtension {
             parent,
             call_site,
             def_site: self.span,
-            default_transparency: self.default_transparency,
             allow_internal_unstable: self.allow_internal_unstable.clone(),
             allow_internal_unsafe: self.allow_internal_unsafe,
             local_inner_macros: self.local_inner_macros,
@@ -657,6 +712,12 @@ impl SyntaxExtension {
 
 pub type NamedSyntaxExtension = (Name, SyntaxExtension);
 
+/// Result of resolving a macro invocation.
+pub enum InvocationRes {
+    Single(Lrc<SyntaxExtension>),
+    DeriveContainer(Vec<Lrc<SyntaxExtension>>),
+}
+
 /// Error type that denotes indeterminacy.
 pub struct Indeterminate;
 
@@ -682,8 +743,9 @@ pub trait Resolver {
 
     fn resolve_imports(&mut self);
 
-    fn resolve_macro_invocation(&mut self, invoc: &Invocation, invoc_id: ExpnId, force: bool)
-                                -> Result<Option<Lrc<SyntaxExtension>>, Indeterminate>;
+    fn resolve_macro_invocation(
+        &mut self, invoc: &Invocation, eager_expansion_root: ExpnId, force: bool
+    ) -> Result<InvocationRes, Indeterminate>;
 
     fn check_unused_macros(&self);
 
@@ -759,23 +821,39 @@ impl<'a> ExtCtxt<'a> {
     pub fn call_site(&self) -> Span {
         self.current_expansion.id.expn_data().call_site
     }
-    pub fn backtrace(&self) -> SyntaxContext {
-        SyntaxContext::root().apply_mark(self.current_expansion.id)
+
+    /// Equivalent of `Span::def_site` from the proc macro API,
+    /// except that the location is taken from the span passed as an argument.
+    pub fn with_def_site_ctxt(&self, span: Span) -> Span {
+        span.with_ctxt_from_mark(self.current_expansion.id, Transparency::Opaque)
+    }
+
+    /// Equivalent of `Span::call_site` from the proc macro API,
+    /// except that the location is taken from the span passed as an argument.
+    pub fn with_call_site_ctxt(&self, span: Span) -> Span {
+        span.with_ctxt_from_mark(self.current_expansion.id, Transparency::Transparent)
+    }
+
+    /// Span with a context reproducing `macro_rules` hygiene (hygienic locals, unhygienic items).
+    /// FIXME: This should be eventually replaced either with `with_def_site_ctxt` (preferably),
+    /// or with `with_call_site_ctxt` (where necessary).
+    pub fn with_legacy_ctxt(&self, span: Span) -> Span {
+        span.with_ctxt_from_mark(self.current_expansion.id, Transparency::SemiTransparent)
     }
 
     /// Returns span for the macro which originally caused the current expansion to happen.
     ///
     /// Stops backtracing at include! boundary.
     pub fn expansion_cause(&self) -> Option<Span> {
-        let mut ctxt = self.backtrace();
+        let mut expn_id = self.current_expansion.id;
         let mut last_macro = None;
         loop {
-            let expn_data = ctxt.outer_expn_data();
+            let expn_data = expn_id.expn_data();
             // Stop going up the backtrace once include! is encountered
             if expn_data.is_root() || expn_data.kind.descr() == sym::include {
                 break;
             }
-            ctxt = expn_data.call_site.ctxt();
+            expn_id = expn_data.call_site.ctxt().outer_expn();
             last_macro = Some(expn_data.call_site);
         }
         last_macro
@@ -864,7 +942,7 @@ impl<'a> ExtCtxt<'a> {
         ast::Ident::from_str(st)
     }
     pub fn std_path(&self, components: &[Symbol]) -> Vec<ast::Ident> {
-        let def_site = DUMMY_SP.apply_mark(self.current_expansion.id);
+        let def_site = self.with_def_site_ctxt(DUMMY_SP);
         iter::once(Ident::new(kw::DollarCrate, def_site))
             .chain(components.iter().map(|&s| Ident::with_dummy_span(s)))
             .collect()
@@ -908,12 +986,9 @@ impl<'a> ExtCtxt<'a> {
 /// compilation on error, merely emits a non-fatal error and returns `None`.
 pub fn expr_to_spanned_string<'a>(
     cx: &'a mut ExtCtxt<'_>,
-    mut expr: P<ast::Expr>,
+    expr: P<ast::Expr>,
     err_msg: &str,
 ) -> Result<(Symbol, ast::StrStyle, Span), Option<DiagnosticBuilder<'a>>> {
-    // Update `expr.span`'s ctxt now in case expr is an `include!` macro invocation.
-    expr.span = expr.span.apply_mark(cx.current_expansion.id);
-
     // Perform eager expansion on the expression.
     // We want to be able to handle e.g., `concat!("foo", "bar")`.
     let expr = cx.expander().fully_expand_fragment(AstFragment::Expr(expr)).make_expr();
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index e2ac4d573a1..e894fd17ff5 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -655,7 +655,7 @@ impl<'a> ExtCtxt<'a> {
               body: P<ast::Expr>)
               -> P<ast::Expr> {
         let fn_decl = self.fn_decl(
-            ids.iter().map(|id| self.arg(span, *id, self.ty_infer(span))).collect(),
+            ids.iter().map(|id| self.param(span, *id, self.ty_infer(span))).collect(),
             ast::FunctionRetTy::Default(span));
 
         // FIXME -- We are using `span` as the span of the `|...|`
@@ -693,9 +693,9 @@ impl<'a> ExtCtxt<'a> {
         self.lambda1(span, self.expr_block(self.block(span, stmts)), ident)
     }
 
-    pub fn arg(&self, span: Span, ident: ast::Ident, ty: P<ast::Ty>) -> ast::Arg {
+    pub fn param(&self, span: Span, ident: ast::Ident, ty: P<ast::Ty>) -> ast::Param {
         let arg_pat = self.pat_ident(span, ident);
-        ast::Arg {
+        ast::Param {
             attrs: ThinVec::default(),
             id: ast::DUMMY_NODE_ID,
             pat: arg_pat,
@@ -705,7 +705,7 @@ impl<'a> ExtCtxt<'a> {
     }
 
     // FIXME: unused `self`
-    pub fn fn_decl(&self, inputs: Vec<ast::Arg>, output: ast::FunctionRetTy) -> P<ast::FnDecl> {
+    pub fn fn_decl(&self, inputs: Vec<ast::Param>, output: ast::FunctionRetTy) -> P<ast::FnDecl> {
         P(ast::FnDecl {
             inputs,
             output,
@@ -731,7 +731,7 @@ impl<'a> ExtCtxt<'a> {
     pub fn item_fn_poly(&self,
                     span: Span,
                     name: Ident,
-                    inputs: Vec<ast::Arg> ,
+                    inputs: Vec<ast::Param> ,
                     output: P<ast::Ty>,
                     generics: Generics,
                     body: P<ast::Block>) -> P<ast::Item> {
@@ -752,7 +752,7 @@ impl<'a> ExtCtxt<'a> {
     pub fn item_fn(&self,
                span: Span,
                name: Ident,
-               inputs: Vec<ast::Arg> ,
+               inputs: Vec<ast::Param> ,
                output: P<ast::Ty>,
                body: P<ast::Block>
               ) -> P<ast::Item> {
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index c1d52c97455..7b4a5167446 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -4,7 +4,7 @@ use crate::attr::{self, HasAttrs};
 use crate::source_map::respan;
 use crate::config::StripUnconfigured;
 use crate::ext::base::*;
-use crate::ext::proc_macro::collect_derives;
+use crate::ext::proc_macro::{collect_derives, MarkAttrs};
 use crate::ext::hygiene::{ExpnId, SyntaxContext, ExpnData, ExpnKind};
 use crate::ext::tt::macro_rules::annotate_err_with_kind;
 use crate::ext::placeholders::{placeholder, PlaceholderExpander};
@@ -305,10 +305,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 continue
             };
 
-            let scope =
+            let eager_expansion_root =
                 if self.monotonic { invoc.expansion_data.id } else { orig_expansion_data.id };
-            let ext = match self.cx.resolver.resolve_macro_invocation(&invoc, scope, force) {
-                Ok(ext) => ext,
+            let res = match self.cx.resolver.resolve_macro_invocation(
+                &invoc, eager_expansion_root, force
+            ) {
+                Ok(res) => res,
                 Err(Indeterminate) => {
                     undetermined_invocations.push(invoc);
                     continue
@@ -318,57 +320,74 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
             progress = true;
             let ExpansionData { depth, id: expn_id, .. } = invoc.expansion_data;
             self.cx.current_expansion = invoc.expansion_data.clone();
-            self.cx.current_expansion.id = scope;
 
             // FIXME(jseyfried): Refactor out the following logic
-            let (expanded_fragment, new_invocations) = if let Some(ext) = ext {
-                let fragment = self.expand_invoc(invoc, &ext.kind);
-                self.collect_invocations(fragment, &[])
-            } else if let InvocationKind::DeriveContainer { derives: traits, item } = invoc.kind {
-                if !item.derive_allowed() {
-                    let attr = attr::find_by_name(item.attrs(), sym::derive)
-                        .expect("`derive` attribute should exist");
-                    let span = attr.span;
-                    let mut err = self.cx.mut_span_err(span,
-                                                        "`derive` may only be applied to \
-                                                        structs, enums and unions");
-                    if let ast::AttrStyle::Inner = attr.style {
-                        let trait_list = traits.iter()
-                            .map(|t| t.to_string()).collect::<Vec<_>>();
-                        let suggestion = format!("#[derive({})]", trait_list.join(", "));
-                        err.span_suggestion(
-                            span, "try an outer attribute", suggestion,
-                            // We don't 𝑘𝑛𝑜𝑤 that the following item is an ADT
-                            Applicability::MaybeIncorrect
-                        );
-                    }
-                    err.emit();
+            let (expanded_fragment, new_invocations) = match res {
+                InvocationRes::Single(ext) => {
+                    let fragment = self.expand_invoc(invoc, &ext.kind);
+                    self.collect_invocations(fragment, &[])
                 }
+                InvocationRes::DeriveContainer(exts) => {
+                    let (derives, item) = match invoc.kind {
+                        InvocationKind::DeriveContainer { derives, item } => (derives, item),
+                        _ => unreachable!(),
+                    };
+                    if !item.derive_allowed() {
+                        let attr = attr::find_by_name(item.attrs(), sym::derive)
+                            .expect("`derive` attribute should exist");
+                        let span = attr.span;
+                        let mut err = self.cx.mut_span_err(span,
+                            "`derive` may only be applied to structs, enums and unions");
+                        if let ast::AttrStyle::Inner = attr.style {
+                            let trait_list = derives.iter()
+                                .map(|t| t.to_string()).collect::<Vec<_>>();
+                            let suggestion = format!("#[derive({})]", trait_list.join(", "));
+                            err.span_suggestion(
+                                span, "try an outer attribute", suggestion,
+                                // We don't 𝑘𝑛𝑜𝑤 that the following item is an ADT
+                                Applicability::MaybeIncorrect
+                            );
+                        }
+                        err.emit();
+                    }
 
-                let mut item = self.fully_configure(item);
-                item.visit_attrs(|attrs| attrs.retain(|a| a.path != sym::derive));
-                let derive_placeholders =
-                    all_derive_placeholders.entry(invoc.expansion_data.id).or_default();
-
-                derive_placeholders.reserve(traits.len());
-                invocations.reserve(traits.len());
-                for path in traits {
-                    let expn_id = ExpnId::fresh(None);
-                    derive_placeholders.push(NodeId::placeholder_from_expn_id(expn_id));
-                    invocations.push(Invocation {
-                        kind: InvocationKind::Derive { path, item: item.clone() },
-                        fragment_kind: invoc.fragment_kind,
-                        expansion_data: ExpansionData {
-                            id: expn_id,
-                            ..invoc.expansion_data.clone()
-                        },
-                    });
+                    let mut item = self.fully_configure(item);
+                    item.visit_attrs(|attrs| attrs.retain(|a| a.path != sym::derive));
+                    let mut helper_attrs = Vec::new();
+                    let mut has_copy = false;
+                    for ext in exts {
+                        helper_attrs.extend(&ext.helper_attrs);
+                        has_copy |= ext.is_derive_copy;
+                    }
+                    // Mark derive helpers inside this item as known and used.
+                    // FIXME: This is a hack, derive helpers should be integrated with regular name
+                    // resolution instead. For example, helpers introduced by a derive container
+                    // can be in scope for all code produced by that container's expansion.
+                    item.visit_with(&mut MarkAttrs(&helper_attrs));
+                    if has_copy {
+                        self.cx.resolver.add_derives(invoc.expansion_data.id, SpecialDerives::COPY);
+                    }
+
+                    let derive_placeholders =
+                        all_derive_placeholders.entry(invoc.expansion_data.id).or_default();
+                    derive_placeholders.reserve(derives.len());
+                    invocations.reserve(derives.len());
+                    for path in derives {
+                        let expn_id = ExpnId::fresh(None);
+                        derive_placeholders.push(NodeId::placeholder_from_expn_id(expn_id));
+                        invocations.push(Invocation {
+                            kind: InvocationKind::Derive { path, item: item.clone() },
+                            fragment_kind: invoc.fragment_kind,
+                            expansion_data: ExpansionData {
+                                id: expn_id,
+                                ..invoc.expansion_data.clone()
+                            },
+                        });
+                    }
+                    let fragment = invoc.fragment_kind
+                        .expect_from_annotatables(::std::iter::once(item));
+                    self.collect_invocations(fragment, derive_placeholders)
                 }
-                let fragment = invoc.fragment_kind
-                    .expect_from_annotatables(::std::iter::once(item));
-                self.collect_invocations(fragment, derive_placeholders)
-            } else {
-                unreachable!()
             };
 
             if expanded_fragments.len() < depth {
@@ -564,7 +583,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                         return fragment_kind.dummy(span);
                     }
                     let meta = ast::MetaItem { node: ast::MetaItemKind::Word, span, path };
-                    let span = span.with_ctxt(self.cx.backtrace());
                     let items = expander.expand(self.cx, span, &meta, item);
                     fragment_kind.expect_from_annotatables(items)
                 }
@@ -1209,9 +1227,13 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
         }
     }
 
-    fn visit_generic_params(&mut self, params: &mut Vec<ast::GenericParam>) {
-        self.cfg.configure_generic_params(params);
-        noop_visit_generic_params(params, self);
+   fn flat_map_generic_param(
+       &mut self,
+       param: ast::GenericParam
+    ) -> SmallVec<[ast::GenericParam; 1]>
+    {
+        let param = configure!(self, param);
+        noop_flat_map_generic_param(param, self)
     }
 
     fn visit_attribute(&mut self, at: &mut ast::Attribute) {
@@ -1388,17 +1410,3 @@ impl<'feat> ExpansionConfig<'feat> {
         self.features.map_or(false, |features| features.custom_inner_attributes)
     }
 }
-
-// A Marker adds the given mark to the syntax context.
-#[derive(Debug)]
-pub struct Marker(pub ExpnId);
-
-impl MutVisitor for Marker {
-    fn visit_span(&mut self, span: &mut Span) {
-        *span = span.apply_mark(self.0)
-    }
-
-    fn visit_mac(&mut self, mac: &mut ast::Mac) {
-        noop_visit_mac(mac, self)
-    }
-}
diff --git a/src/libsyntax/ext/proc_macro.rs b/src/libsyntax/ext/proc_macro.rs
index c17b6f6b424..4a44c9a9f1f 100644
--- a/src/libsyntax/ext/proc_macro.rs
+++ b/src/libsyntax/ext/proc_macro.rs
@@ -78,7 +78,6 @@ pub struct ProcMacroDerive {
     pub client: proc_macro::bridge::client::Client<
         fn(proc_macro::TokenStream) -> proc_macro::TokenStream,
     >,
-    pub attrs: Vec<ast::Name>,
 }
 
 impl MultiItemModifier for ProcMacroDerive {
@@ -111,9 +110,6 @@ impl MultiItemModifier for ProcMacroDerive {
             }
         }
 
-        // Mark attributes as known, and used.
-        MarkAttrs(&self.attrs).visit_item(&item);
-
         let token = token::Interpolated(Lrc::new(token::NtItem(item)));
         let input = tokenstream::TokenTree::token(token, DUMMY_SP).into();
 
@@ -164,7 +160,7 @@ impl MultiItemModifier for ProcMacroDerive {
     }
 }
 
-struct MarkAttrs<'a>(&'a [ast::Name]);
+crate struct MarkAttrs<'a>(crate &'a [ast::Name]);
 
 impl<'a> Visitor<'a> for MarkAttrs<'a> {
     fn visit_attribute(&mut self, attr: &Attribute) {
diff --git a/src/libsyntax/ext/proc_macro_server.rs b/src/libsyntax/ext/proc_macro_server.rs
index 1619fa69941..1a26b17dac7 100644
--- a/src/libsyntax/ext/proc_macro_server.rs
+++ b/src/libsyntax/ext/proc_macro_server.rs
@@ -7,7 +7,6 @@ use crate::tokenstream::{self, DelimSpan, IsJoint::*, TokenStream, TreeAndJoint}
 use errors::{Diagnostic, DiagnosticBuilder};
 use rustc_data_structures::sync::Lrc;
 use syntax_pos::{BytePos, FileName, MultiSpan, Pos, SourceFile, Span};
-use syntax_pos::hygiene::{SyntaxContext, Transparency};
 use syntax_pos::symbol::{kw, sym, Symbol};
 
 use proc_macro::{Delimiter, Level, LineColumn, Spacing};
@@ -361,18 +360,11 @@ pub(crate) struct Rustc<'a> {
 
 impl<'a> Rustc<'a> {
     pub fn new(cx: &'a ExtCtxt<'_>) -> Self {
-        // No way to determine def location for a proc macro right now, so use call location.
-        let location = cx.current_expansion.id.expn_data().call_site;
-        let to_span = |transparency| {
-            location.with_ctxt(
-                SyntaxContext::root()
-                    .apply_mark_with_transparency(cx.current_expansion.id, transparency),
-            )
-        };
+        let expn_data = cx.current_expansion.id.expn_data();
         Rustc {
             sess: cx.parse_sess,
-            def_site: to_span(Transparency::Opaque),
-            call_site: to_span(Transparency::Transparent),
+            def_site: cx.with_def_site_ctxt(expn_data.def_site),
+            call_site: cx.with_call_site_ctxt(expn_data.call_site),
         }
     }
 
diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs
index b057a9ad44d..46ffa52f7f5 100644
--- a/src/libsyntax/ext/tt/macro_rules.rs
+++ b/src/libsyntax/ext/tt/macro_rules.rs
@@ -1,3 +1,5 @@
+use crate::ast;
+use crate::attr::{self, TransparencyError};
 use crate::edition::Edition;
 use crate::ext::base::{DummyResult, ExtCtxt, MacResult, TTMacroExpander};
 use crate::ext::base::{SyntaxExtension, SyntaxExtensionKind};
@@ -15,10 +17,10 @@ use crate::parse::token::{self, NtTT, Token};
 use crate::parse::{Directory, ParseSess};
 use crate::symbol::{kw, sym, Symbol};
 use crate::tokenstream::{DelimSpan, TokenStream, TokenTree};
-use crate::{ast, attr, attr::TransparencyError};
 
 use errors::{DiagnosticBuilder, FatalError};
 use log::debug;
+use syntax_pos::hygiene::Transparency;
 use syntax_pos::Span;
 
 use rustc_data_structures::fx::FxHashMap;
@@ -128,6 +130,7 @@ impl<'a> ParserAnyMacro<'a> {
 struct MacroRulesMacroExpander {
     name: ast::Ident,
     span: Span,
+    transparency: Transparency,
     lhses: Vec<quoted::TokenTree>,
     rhses: Vec<quoted::TokenTree>,
     valid: bool,
@@ -143,7 +146,9 @@ impl TTMacroExpander for MacroRulesMacroExpander {
         if !self.valid {
             return DummyResult::any(sp);
         }
-        generic_extension(cx, sp, self.span, self.name, input, &self.lhses, &self.rhses)
+        generic_extension(
+            cx, sp, self.span, self.name, self.transparency, input, &self.lhses, &self.rhses
+        )
     }
 }
 
@@ -158,6 +163,7 @@ fn generic_extension<'cx>(
     sp: Span,
     def_span: Span,
     name: ast::Ident,
+    transparency: Transparency,
     arg: TokenStream,
     lhses: &[quoted::TokenTree],
     rhses: &[quoted::TokenTree],
@@ -187,7 +193,7 @@ fn generic_extension<'cx>(
 
                 let rhs_spans = rhs.iter().map(|t| t.span()).collect::<Vec<_>>();
                 // rhs has holes ( `$id` and `$(...)` that need filled)
-                let mut tts = transcribe(cx, &named_matches, rhs);
+                let mut tts = transcribe(cx, &named_matches, rhs, transparency);
 
                 // Replace all the tokens for the corresponding positions in the macro, to maintain
                 // proper positions in error reporting, while maintaining the macro_backtrace.
@@ -285,6 +291,7 @@ pub fn compile(
     def: &ast::Item,
     edition: Edition,
 ) -> SyntaxExtension {
+    let diag = &sess.span_diagnostic;
     let lhs_nm = ast::Ident::new(sym::lhs, def.span);
     let rhs_nm = ast::Ident::new(sym::rhs, def.span);
     let tt_spec = ast::Ident::new(sym::tt, def.span);
@@ -415,75 +422,28 @@ pub fn compile(
     // that is not lint-checked and trigger the "failed to process buffered lint here" bug.
     valid &= macro_check::check_meta_variables(sess, ast::CRATE_NODE_ID, def.span, &lhses, &rhses);
 
-    let expander: Box<_> =
-        Box::new(MacroRulesMacroExpander { name: def.ident, span: def.span, lhses, rhses, valid });
-
-    let (default_transparency, transparency_error) =
-        attr::find_transparency(&def.attrs, body.legacy);
+    let (transparency, transparency_error) = attr::find_transparency(&def.attrs, body.legacy);
     match transparency_error {
         Some(TransparencyError::UnknownTransparency(value, span)) =>
-            sess.span_diagnostic.span_err(
-                span, &format!("unknown macro transparency: `{}`", value)
-            ),
+            diag.span_err(span, &format!("unknown macro transparency: `{}`", value)),
         Some(TransparencyError::MultipleTransparencyAttrs(old_span, new_span)) =>
-            sess.span_diagnostic.span_err(
-                vec![old_span, new_span], "multiple macro transparency attributes"
-            ),
+            diag.span_err(vec![old_span, new_span], "multiple macro transparency attributes"),
         None => {}
     }
 
-    let allow_internal_unstable =
-        attr::find_by_name(&def.attrs, sym::allow_internal_unstable).map(|attr| {
-            attr.meta_item_list()
-                .map(|list| {
-                    list.iter()
-                        .filter_map(|it| {
-                            let name = it.ident().map(|ident| ident.name);
-                            if name.is_none() {
-                                sess.span_diagnostic.span_err(
-                                    it.span(),
-                                    "allow internal unstable expects feature names",
-                                )
-                            }
-                            name
-                        })
-                        .collect::<Vec<Symbol>>()
-                        .into()
-                })
-                .unwrap_or_else(|| {
-                    sess.span_diagnostic.span_warn(
-                        attr.span,
-                        "allow_internal_unstable expects list of feature names. In the \
-                         future this will become a hard error. Please use `allow_internal_unstable(\
-                         foo, bar)` to only allow the `foo` and `bar` features",
-                    );
-                    vec![sym::allow_internal_unstable_backcompat_hack].into()
-                })
-        });
-
-    let mut local_inner_macros = false;
-    if let Some(macro_export) = attr::find_by_name(&def.attrs, sym::macro_export) {
-        if let Some(l) = macro_export.meta_item_list() {
-            local_inner_macros = attr::list_contains_name(&l, sym::local_inner_macros);
-        }
-    }
+    let expander: Box<_> = Box::new(MacroRulesMacroExpander {
+        name: def.ident, span: def.span, transparency, lhses, rhses, valid
+    });
 
-    let is_builtin = attr::contains_name(&def.attrs, sym::rustc_builtin_macro);
-
-    SyntaxExtension {
-        kind: SyntaxExtensionKind::LegacyBang(expander),
-        span: def.span,
-        default_transparency,
-        allow_internal_unstable,
-        allow_internal_unsafe: attr::contains_name(&def.attrs, sym::allow_internal_unsafe),
-        local_inner_macros,
-        stability: attr::find_stability(&sess, &def.attrs, def.span),
-        deprecation: attr::find_deprecation(&sess, &def.attrs, def.span),
-        helper_attrs: Vec::new(),
+    SyntaxExtension::new(
+        sess,
+        SyntaxExtensionKind::LegacyBang(expander),
+        def.span,
+        Vec::new(),
         edition,
-        is_builtin,
-        is_derive_copy: is_builtin && def.ident.name == sym::Copy,
-    }
+        def.ident.name,
+        &def.attrs,
+    )
 }
 
 fn check_lhs_nt_follows(
diff --git a/src/libsyntax/ext/tt/transcribe.rs b/src/libsyntax/ext/tt/transcribe.rs
index 214e721fd15..30d5df13dce 100644
--- a/src/libsyntax/ext/tt/transcribe.rs
+++ b/src/libsyntax/ext/tt/transcribe.rs
@@ -1,9 +1,8 @@
-use crate::ast::Ident;
+use crate::ast::{Ident, Mac};
 use crate::ext::base::ExtCtxt;
-use crate::ext::expand::Marker;
 use crate::ext::tt::macro_parser::{MatchedNonterminal, MatchedSeq, NamedMatch};
 use crate::ext::tt::quoted;
-use crate::mut_visit::noop_visit_tt;
+use crate::mut_visit::{self, MutVisitor};
 use crate::parse::token::{self, NtTT, Token};
 use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint};
 
@@ -11,8 +10,31 @@ use smallvec::{smallvec, SmallVec};
 
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::Lrc;
+use syntax_pos::hygiene::{ExpnId, Transparency};
+use syntax_pos::Span;
+
 use std::mem;
 
+// A Marker adds the given mark to the syntax context.
+struct Marker(ExpnId, Transparency);
+
+impl MutVisitor for Marker {
+    fn visit_span(&mut self, span: &mut Span) {
+        *span = span.apply_mark(self.0, self.1)
+    }
+
+    fn visit_mac(&mut self, mac: &mut Mac) {
+        mut_visit::noop_visit_mac(mac, self)
+    }
+}
+
+impl Marker {
+    fn visit_delim_span(&mut self, dspan: &mut DelimSpan) {
+        self.visit_span(&mut dspan.open);
+        self.visit_span(&mut dspan.close);
+    }
+}
+
 /// An iterator over the token trees in a delimited token tree (`{ ... }`) or a sequence (`$(...)`).
 enum Frame {
     Delimited { forest: Lrc<quoted::Delimited>, idx: usize, span: DelimSpan },
@@ -68,6 +90,7 @@ pub(super) fn transcribe(
     cx: &ExtCtxt<'_>,
     interp: &FxHashMap<Ident, NamedMatch>,
     src: Vec<quoted::TokenTree>,
+    transparency: Transparency,
 ) -> TokenStream {
     // Nothing for us to transcribe...
     if src.is_empty() {
@@ -96,6 +119,7 @@ pub(super) fn transcribe(
     // again, and we are done transcribing.
     let mut result: Vec<TreeAndJoint> = Vec::new();
     let mut result_stack = Vec::new();
+    let mut marker = Marker(cx.current_expansion.id, transparency);
 
     loop {
         // Look at the last frame on the stack.
@@ -207,7 +231,7 @@ pub(super) fn transcribe(
             }
 
             // Replace the meta-var with the matched token tree from the invocation.
-            quoted::TokenTree::MetaVar(mut sp, ident) => {
+            quoted::TokenTree::MetaVar(mut sp, mut ident) => {
                 // Find the matched nonterminal from the macro invocation, and use it to replace
                 // the meta-var.
                 if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) {
@@ -218,7 +242,7 @@ pub(super) fn transcribe(
                         if let NtTT(ref tt) = **nt {
                             result.push(tt.clone().into());
                         } else {
-                            sp = sp.apply_mark(cx.current_expansion.id);
+                            marker.visit_span(&mut sp);
                             let token = TokenTree::token(token::Interpolated(nt.clone()), sp);
                             result.push(token.into());
                         }
@@ -232,9 +256,8 @@ pub(super) fn transcribe(
                 } else {
                     // If we aren't able to match the meta-var, we push it back into the result but
                     // with modified syntax context. (I believe this supports nested macros).
-                    let ident =
-                        Ident::new(ident.name, ident.span.apply_mark(cx.current_expansion.id));
-                    sp = sp.apply_mark(cx.current_expansion.id);
+                    marker.visit_span(&mut sp);
+                    marker.visit_ident(&mut ident);
                     result.push(TokenTree::token(token::Dollar, sp).into());
                     result.push(TokenTree::Token(Token::from_ast_ident(ident)).into());
                 }
@@ -246,7 +269,7 @@ pub(super) fn transcribe(
             // jump back out of the Delimited, pop the result_stack and add the new results back to
             // the previous results (from outside the Delimited).
             quoted::TokenTree::Delimited(mut span, delimited) => {
-                span = span.apply_mark(cx.current_expansion.id);
+                marker.visit_delim_span(&mut span);
                 stack.push(Frame::Delimited { forest: delimited, idx: 0, span });
                 result_stack.push(mem::take(&mut result));
             }
@@ -254,9 +277,8 @@ pub(super) fn transcribe(
             // Nothing much to do here. Just push the token to the result, being careful to
             // preserve syntax context.
             quoted::TokenTree::Token(token) => {
-                let mut marker = Marker(cx.current_expansion.id);
                 let mut tt = TokenTree::Token(token);
-                noop_visit_tt(&mut tt, &mut marker);
+                marker.visit_tt(&mut tt);
                 result.push(tt.into());
             }
 
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
deleted file mode 100644
index bce0b07db1c..00000000000
--- a/src/libsyntax/feature_gate.rs
+++ /dev/null
@@ -1,2495 +0,0 @@
-//! # Feature gating
-//!
-//! This module implements the gating necessary for preventing certain compiler
-//! features from being used by default. This module will crawl a pre-expanded
-//! AST to ensure that there are no features which are used that are not
-//! enabled.
-//!
-//! Features are enabled in programs via the crate-level attributes of
-//! `#![feature(...)]` with a comma-separated list of features.
-//!
-//! For the purpose of future feature-tracking, once code for detection of feature
-//! gate usage is added, *do not remove it again* even once the feature
-//! becomes stable.
-
-use AttributeType::*;
-use AttributeGate::*;
-
-use crate::ast::{
-    self, AssocTyConstraint, AssocTyConstraintKind, NodeId, GenericParam, GenericParamKind,
-    PatKind, RangeEnd,
-};
-use crate::attr::{self, check_builtin_attribute, AttributeTemplate};
-use crate::source_map::Spanned;
-use crate::edition::{ALL_EDITIONS, Edition};
-use crate::visit::{self, FnKind, Visitor};
-use crate::parse::{token, ParseSess};
-use crate::parse::parser::Parser;
-use crate::symbol::{Symbol, sym};
-use crate::tokenstream::TokenTree;
-
-use errors::{Applicability, DiagnosticBuilder, Handler};
-use rustc_data_structures::fx::FxHashMap;
-use rustc_target::spec::abi::Abi;
-use syntax_pos::{Span, DUMMY_SP, MultiSpan};
-use log::debug;
-use lazy_static::lazy_static;
-
-use std::env;
-
-macro_rules! set {
-    ($field: ident) => {{
-        fn f(features: &mut Features, _: Span) {
-            features.$field = true;
-        }
-        f as fn(&mut Features, Span)
-    }}
-}
-
-macro_rules! declare_features {
-    ($((active, $feature: ident, $ver: expr, $issue: expr, $edition: expr),)+) => {
-        /// Represents active features that are currently being implemented or
-        /// currently being considered for addition/removal.
-        const ACTIVE_FEATURES:
-            &[(Symbol, &str, Option<u32>, Option<Edition>, fn(&mut Features, Span))] =
-            &[$((sym::$feature, $ver, $issue, $edition, set!($feature))),+];
-
-        /// A set of features to be used by later passes.
-        #[derive(Clone)]
-        pub struct Features {
-            /// `#![feature]` attrs for language features, for error reporting
-            pub declared_lang_features: Vec<(Symbol, Span, Option<Symbol>)>,
-            /// `#![feature]` attrs for non-language (library) features
-            pub declared_lib_features: Vec<(Symbol, Span)>,
-            $(pub $feature: bool),+
-        }
-
-        impl Features {
-            pub fn new() -> Features {
-                Features {
-                    declared_lang_features: Vec::new(),
-                    declared_lib_features: Vec::new(),
-                    $($feature: false),+
-                }
-            }
-
-            pub fn walk_feature_fields<F>(&self, mut f: F)
-                where F: FnMut(&str, bool)
-            {
-                $(f(stringify!($feature), self.$feature);)+
-            }
-        }
-    };
-
-    ($((removed, $feature: ident, $ver: expr, $issue: expr, None, $reason: expr),)+) => {
-        /// Represents unstable features which have since been removed (it was once Active)
-        const REMOVED_FEATURES: &[(Symbol, &str, Option<u32>, Option<&str>)] = &[
-            $((sym::$feature, $ver, $issue, $reason)),+
-        ];
-    };
-
-    ($((stable_removed, $feature: ident, $ver: expr, $issue: expr, None),)+) => {
-        /// Represents stable features which have since been removed (it was once Accepted)
-        const STABLE_REMOVED_FEATURES: &[(Symbol, &str, Option<u32>, Option<&str>)] = &[
-            $((sym::$feature, $ver, $issue, None)),+
-        ];
-    };
-
-    ($((accepted, $feature: ident, $ver: expr, $issue: expr, None),)+) => {
-        /// Those language feature has since been Accepted (it was once Active)
-        const ACCEPTED_FEATURES: &[(Symbol, &str, Option<u32>, Option<&str>)] = &[
-            $((sym::$feature, $ver, $issue, None)),+
-        ];
-    }
-}
-
-// If you change this, please modify `src/doc/unstable-book` as well.
-//
-// Don't ever remove anything from this list; set them to 'Removed'.
-//
-// The version numbers here correspond to the version in which the current status
-// was set. This is most important for knowing when a particular feature became
-// stable (active).
-//
-// Note that the features are grouped into internal/user-facing and then
-// sorted by version inside those groups. This is inforced with tidy.
-//
-// N.B., `tools/tidy/src/features.rs` parses this information directly out of the
-// source, so take care when modifying it.
-
-declare_features! (
-    // -------------------------------------------------------------------------
-    // feature-group-start: internal feature gates
-    // -------------------------------------------------------------------------
-
-    // no-tracking-issue-start
-
-    // Allows using compiler's own crates.
-    (active, rustc_private, "1.0.0", Some(27812), None),
-
-    // Allows using the `rust-intrinsic`'s "ABI".
-    (active, intrinsics, "1.0.0", None, None),
-
-    // Allows using `#[lang = ".."]` attribute for linking items to special compiler logic.
-    (active, lang_items, "1.0.0", None, None),
-
-    // Allows using the `#[stable]` and `#[unstable]` attributes.
-    (active, staged_api, "1.0.0", None, None),
-
-    // Allows using `#[allow_internal_unstable]`. This is an
-    // attribute on `macro_rules!` and can't use the attribute handling
-    // below (it has to be checked before expansion possibly makes
-    // macros disappear).
-    (active, allow_internal_unstable, "1.0.0", None, None),
-
-    // Allows using `#[allow_internal_unsafe]`. This is an
-    // attribute on `macro_rules!` and can't use the attribute handling
-    // below (it has to be checked before expansion possibly makes
-    // macros disappear).
-    (active, allow_internal_unsafe, "1.0.0", None, None),
-
-    // Allows using the macros:
-    // + `__diagnostic_used`
-    // + `__register_diagnostic`
-    // +`__build_diagnostic_array`
-    (active, rustc_diagnostic_macros, "1.0.0", None, None),
-
-    // Allows using `#[rustc_const_unstable(feature = "foo", ..)]` which
-    // lets a function to be `const` when opted into with `#![feature(foo)]`.
-    (active, rustc_const_unstable, "1.0.0", None, None),
-
-    // no-tracking-issue-end
-
-    // Allows using `#[link_name="llvm.*"]`.
-    (active, link_llvm_intrinsics, "1.0.0", Some(29602), None),
-
-    // Allows using `rustc_*` attributes (RFC 572).
-    (active, rustc_attrs, "1.0.0", Some(29642), None),
-
-    // Allows using `#[on_unimplemented(..)]` on traits.
-    (active, on_unimplemented, "1.0.0", Some(29628), None),
-
-    // Allows using the `box $expr` syntax.
-    (active, box_syntax, "1.0.0", Some(49733), None),
-
-    // Allows using `#[main]` to replace the entrypoint `#[lang = "start"]` calls.
-    (active, main, "1.0.0", Some(29634), None),
-
-    // Allows using `#[start]` on a function indicating that it is the program entrypoint.
-    (active, start, "1.0.0", Some(29633), None),
-
-    // Allows using the `#[fundamental]` attribute.
-    (active, fundamental, "1.0.0", Some(29635), None),
-
-    // Allows using the `rust-call` ABI.
-    (active, unboxed_closures, "1.0.0", Some(29625), None),
-
-    // Allows using the `#[linkage = ".."]` attribute.
-    (active, linkage, "1.0.0", Some(29603), None),
-
-    // Allows features specific to OIBIT (auto traits).
-    (active, optin_builtin_traits, "1.0.0", Some(13231), None),
-
-    // Allows using `box` in patterns (RFC 469).
-    (active, box_patterns, "1.0.0", Some(29641), None),
-
-    // no-tracking-issue-start
-
-    // Allows using `#[prelude_import]` on glob `use` items.
-    (active, prelude_import, "1.2.0", None, None),
-
-    // no-tracking-issue-end
-
-    // no-tracking-issue-start
-
-    // Allows using `#[omit_gdb_pretty_printer_section]`.
-    (active, omit_gdb_pretty_printer_section, "1.5.0", None, None),
-
-    // Allows using the `vectorcall` ABI.
-    (active, abi_vectorcall, "1.7.0", None, None),
-
-    // no-tracking-issue-end
-
-    // Allows using `#[structural_match]` which indicates that a type is structurally matchable.
-    (active, structural_match, "1.8.0", Some(31434), None),
-
-    // Allows using the `may_dangle` attribute (RFC 1327).
-    (active, dropck_eyepatch, "1.10.0", Some(34761), None),
-
-    // Allows using the `#![panic_runtime]` attribute.
-    (active, panic_runtime, "1.10.0", Some(32837), None),
-
-    // Allows declaring with `#![needs_panic_runtime]` that a panic runtime is needed.
-    (active, needs_panic_runtime, "1.10.0", Some(32837), None),
-
-    // no-tracking-issue-start
-
-    // Allows identifying the `compiler_builtins` crate.
-    (active, compiler_builtins, "1.13.0", None, None),
-
-    // Allows using the `unadjusted` ABI; perma-unstable.
-    (active, abi_unadjusted, "1.16.0", None, None),
-
-    // Allows identifying crates that contain sanitizer runtimes.
-    (active, sanitizer_runtime, "1.17.0", None, None),
-
-    // Used to identify crates that contain the profiler runtime.
-    (active, profiler_runtime, "1.18.0", None, None),
-
-    // Allows using the `thiscall` ABI.
-    (active, abi_thiscall, "1.19.0", None, None),
-
-    // Allows using `#![needs_allocator]`, an implementation detail of `#[global_allocator]`.
-    (active, allocator_internals, "1.20.0", None, None),
-
-    // no-tracking-issue-end
-
-    // Added for testing E0705; perma-unstable.
-    (active, test_2018_feature, "1.31.0", Some(0), Some(Edition::Edition2018)),
-
-    // -------------------------------------------------------------------------
-    // feature-group-end: internal feature gates
-    // -------------------------------------------------------------------------
-
-    // -------------------------------------------------------------------------
-    // feature-group-start: actual feature gates (target features)
-    // -------------------------------------------------------------------------
-
-    // FIXME: Document these and merge with the list below.
-
-    // Unstable `#[target_feature]` directives.
-    (active, arm_target_feature, "1.27.0", Some(44839), None),
-    (active, aarch64_target_feature, "1.27.0", Some(44839), None),
-    (active, hexagon_target_feature, "1.27.0", Some(44839), None),
-    (active, powerpc_target_feature, "1.27.0", Some(44839), None),
-    (active, mips_target_feature, "1.27.0", Some(44839), None),
-    (active, avx512_target_feature, "1.27.0", Some(44839), None),
-    (active, mmx_target_feature, "1.27.0", Some(44839), None),
-    (active, sse4a_target_feature, "1.27.0", Some(44839), None),
-    (active, tbm_target_feature, "1.27.0", Some(44839), None),
-    (active, wasm_target_feature, "1.30.0", Some(44839), None),
-    (active, adx_target_feature, "1.32.0", Some(44839), None),
-    (active, cmpxchg16b_target_feature, "1.32.0", Some(44839), None),
-    (active, movbe_target_feature, "1.34.0", Some(44839), None),
-    (active, rtm_target_feature, "1.35.0", Some(44839), None),
-    (active, f16c_target_feature, "1.36.0", Some(44839), None),
-
-    // -------------------------------------------------------------------------
-    // feature-group-end: actual feature gates (target features)
-    // -------------------------------------------------------------------------
-
-    // -------------------------------------------------------------------------
-    // feature-group-start: actual feature gates
-    // -------------------------------------------------------------------------
-
-    // Allows using the `#[link_args]` attribute.
-    (active, link_args, "1.0.0", Some(29596), None),
-
-    // Allows defining identifiers beyond ASCII.
-    (active, non_ascii_idents, "1.0.0", Some(55467), None),
-
-    // Allows using `#[plugin_registrar]` on functions.
-    (active, plugin_registrar, "1.0.0", Some(29597), None),
-
-    // Allows using `#![plugin(myplugin)]`.
-    (active, plugin, "1.0.0", Some(29597), None),
-
-    // Allows using `#[thread_local]` on `static` items.
-    (active, thread_local, "1.0.0", Some(29594), None),
-
-    // Allows the use of SIMD types in functions declared in `extern` blocks.
-    (active, simd_ffi, "1.0.0", Some(27731), None),
-
-    // Allows using custom attributes (RFC 572).
-    (active, custom_attribute, "1.0.0", Some(29642), None),
-
-    // Allows using non lexical lifetimes (RFC 2094).
-    (active, nll, "1.0.0", Some(43234), None),
-
-    // Allows using slice patterns.
-    (active, slice_patterns, "1.0.0", Some(62254), None),
-
-    // Allows the definition of `const` functions with some advanced features.
-    (active, const_fn, "1.2.0", Some(57563), None),
-
-    // Allows associated type defaults.
-    (active, associated_type_defaults, "1.2.0", Some(29661), None),
-
-    // Allows `#![no_core]`.
-    (active, no_core, "1.3.0", Some(29639), None),
-
-    // Allows default type parameters to influence type inference.
-    (active, default_type_parameter_fallback, "1.3.0", Some(27336), None),
-
-    // Allows `repr(simd)` and importing the various simd intrinsics.
-    (active, repr_simd, "1.4.0", Some(27731), None),
-
-    // Allows `extern "platform-intrinsic" { ... }`.
-    (active, platform_intrinsics, "1.4.0", Some(27731), None),
-
-    // Allows `#[unwind(..)]`.
-    //
-    // Permits specifying whether a function should permit unwinding or abort on unwind.
-    (active, unwind_attributes, "1.4.0", Some(58760), None),
-
-    // Allows `#[no_debug]`.
-    (active, no_debug, "1.5.0", Some(29721), None),
-
-    // Allows attributes on expressions and non-item statements.
-    (active, stmt_expr_attributes, "1.6.0", Some(15701), None),
-
-    // Allows the use of type ascription in expressions.
-    (active, type_ascription, "1.6.0", Some(23416), None),
-
-    // Allows `cfg(target_thread_local)`.
-    (active, cfg_target_thread_local, "1.7.0", Some(29594), None),
-
-    // Allows specialization of implementations (RFC 1210).
-    (active, specialization, "1.7.0", Some(31844), None),
-
-    // Allows using `#[naked]` on functions.
-    (active, naked_functions, "1.9.0", Some(32408), None),
-
-    // Allows `cfg(target_has_atomic = "...")`.
-    (active, cfg_target_has_atomic, "1.9.0", Some(32976), None),
-
-    // Allows `X..Y` patterns.
-    (active, exclusive_range_pattern, "1.11.0", Some(37854), None),
-
-    // Allows the `!` type. Does not imply 'exhaustive_patterns' (below) any more.
-    (active, never_type, "1.13.0", Some(35121), None),
-
-    // Allows exhaustive pattern matching on types that contain uninhabited types.
-    (active, exhaustive_patterns, "1.13.0", Some(51085), None),
-
-    // Allows untagged unions `union U { ... }`.
-    (active, untagged_unions, "1.13.0", Some(32836), None),
-
-    // Allows `#[link(..., cfg(..))]`.
-    (active, link_cfg, "1.14.0", Some(37406), None),
-
-    // Allows `extern "ptx-*" fn()`.
-    (active, abi_ptx, "1.15.0", Some(38788), None),
-
-    // Allows the `#[repr(i128)]` attribute for enums.
-    (active, repr128, "1.16.0", Some(35118), None),
-
-    // Allows `#[link(kind="static-nobundle"...)]`.
-    (active, static_nobundle, "1.16.0", Some(37403), None),
-
-    // Allows `extern "msp430-interrupt" fn()`.
-    (active, abi_msp430_interrupt, "1.16.0", Some(38487), None),
-
-    // Allows declarative macros 2.0 (`macro`).
-    (active, decl_macro, "1.17.0", Some(39412), None),
-
-    // Allows `extern "x86-interrupt" fn()`.
-    (active, abi_x86_interrupt, "1.17.0", Some(40180), None),
-
-    // Allows overlapping impls of marker traits.
-    (active, overlapping_marker_traits, "1.18.0", Some(29864), None),
-
-    // Allows a test to fail without failing the whole suite.
-    (active, allow_fail, "1.19.0", Some(46488), None),
-
-    // Allows unsized tuple coercion.
-    (active, unsized_tuple_coercion, "1.20.0", Some(42877), None),
-
-    // Allows defining generators.
-    (active, generators, "1.21.0", Some(43122), None),
-
-    // Allows `#[doc(cfg(...))]`.
-    (active, doc_cfg, "1.21.0", Some(43781), None),
-
-    // Allows `#[doc(masked)]`.
-    (active, doc_masked, "1.21.0", Some(44027), None),
-
-    // Allows `#[doc(spotlight)]`.
-    (active, doc_spotlight, "1.22.0", Some(45040), None),
-
-    // Allows `#[doc(include = "some-file")]`.
-    (active, external_doc, "1.22.0", Some(44732), None),
-
-    // Allows future-proofing enums/structs with the `#[non_exhaustive]` attribute (RFC 2008).
-    (active, non_exhaustive, "1.22.0", Some(44109), None),
-
-    // Allows using `crate` as visibility modifier, synonymous with `pub(crate)`.
-    (active, crate_visibility_modifier, "1.23.0", Some(53120), None),
-
-    // Allows defining `extern type`s.
-    (active, extern_types, "1.23.0", Some(43467), None),
-
-    // Allows trait methods with arbitrary self types.
-    (active, arbitrary_self_types, "1.23.0", Some(44874), None),
-
-    // Allows in-band quantification of lifetime bindings (e.g., `fn foo(x: &'a u8) -> &'a u8`).
-    (active, in_band_lifetimes, "1.23.0", Some(44524), None),
-
-    // Allows associated types to be generic, e.g., `type Foo<T>;` (RFC 1598).
-    (active, generic_associated_types, "1.23.0", Some(44265), None),
-
-    // Allows defining `trait X = A + B;` alias items.
-    (active, trait_alias, "1.24.0", Some(41517), None),
-
-    // Allows infering `'static` outlives requirements (RFC 2093).
-    (active, infer_static_outlives_requirements, "1.26.0", Some(54185), None),
-
-    // Allows macro invocations in `extern {}` blocks.
-    (active, macros_in_extern, "1.27.0", Some(49476), None),
-
-    // Allows accessing fields of unions inside `const` functions.
-    (active, const_fn_union, "1.27.0", Some(51909), None),
-
-    // Allows casting raw pointers to `usize` during const eval.
-    (active, const_raw_ptr_to_usize_cast, "1.27.0", Some(51910), None),
-
-    // Allows dereferencing raw pointers during const eval.
-    (active, const_raw_ptr_deref, "1.27.0", Some(51911), None),
-
-    // Allows comparing raw pointers during const eval.
-    (active, const_compare_raw_pointers, "1.27.0", Some(53020), None),
-
-    // Allows `#[doc(alias = "...")]`.
-    (active, doc_alias, "1.27.0", Some(50146), None),
-
-    // Allows inconsistent bounds in where clauses.
-    (active, trivial_bounds, "1.28.0", Some(48214), None),
-
-    // Allows `'a: { break 'a; }`.
-    (active, label_break_value, "1.28.0", Some(48594), None),
-
-    // Allows using `#[doc(keyword = "...")]`.
-    (active, doc_keyword, "1.28.0", Some(51315), None),
-
-    // Allows reinterpretation of the bits of a value of one type as another type during const eval.
-    (active, const_transmute, "1.29.0", Some(53605), None),
-
-    // Allows using `try {...}` expressions.
-    (active, try_blocks, "1.29.0", Some(31436), None),
-
-    // Allows defining an `#[alloc_error_handler]`.
-    (active, alloc_error_handler, "1.29.0", Some(51540), None),
-
-    // Allows using the `amdgpu-kernel` ABI.
-    (active, abi_amdgpu_kernel, "1.29.0", Some(51575), None),
-
-    // Allows panicking during const eval (producing compile-time errors).
-    (active, const_panic, "1.30.0", Some(51999), None),
-
-    // Allows `#[marker]` on certain traits allowing overlapping implementations.
-    (active, marker_trait_attr, "1.30.0", Some(29864), None),
-
-    // Allows macro invocations on modules expressions and statements and
-    // procedural macros to expand to non-items.
-    (active, proc_macro_hygiene, "1.30.0", Some(54727), None),
-
-    // Allows unsized rvalues at arguments and parameters.
-    (active, unsized_locals, "1.30.0", Some(48055), None),
-
-    // Allows custom test frameworks with `#![test_runner]` and `#[test_case]`.
-    (active, custom_test_frameworks, "1.30.0", Some(50297), None),
-
-    // Allows non-builtin attributes in inner attribute position.
-    (active, custom_inner_attributes, "1.30.0", Some(54726), None),
-
-    // Allows mixing bind-by-move in patterns and references to those identifiers in guards.
-    (active, bind_by_move_pattern_guards, "1.30.0", Some(15287), None),
-
-    // Allows `impl Trait` in bindings (`let`, `const`, `static`).
-    (active, impl_trait_in_bindings, "1.30.0", Some(63065), None),
-
-    // Allows using `reason` in lint attributes and the `#[expect(lint)]` lint check.
-    (active, lint_reasons, "1.31.0", Some(54503), None),
-
-    // Allows exhaustive integer pattern matching on `usize` and `isize`.
-    (active, precise_pointer_size_matching, "1.32.0", Some(56354), None),
-
-    // Allows relaxing the coherence rules such that
-    // `impl<T> ForeignTrait<LocalType> for ForeignType<T> is permitted.
-    (active, re_rebalance_coherence, "1.32.0", Some(55437), None),
-
-    // Allows using `#[ffi_returns_twice]` on foreign functions.
-    (active, ffi_returns_twice, "1.34.0", Some(58314), None),
-
-    // Allows const generic types (e.g. `struct Foo<const N: usize>(...);`).
-    (active, const_generics, "1.34.0", Some(44580), None),
-
-    // Allows using `#[optimize(X)]`.
-    (active, optimize_attribute, "1.34.0", Some(54882), None),
-
-    // Allows using C-variadics.
-    (active, c_variadic, "1.34.0", Some(44930), None),
-
-    // Allows the user of associated type bounds.
-    (active, associated_type_bounds, "1.34.0", Some(52662), None),
-
-    // Attributes on formal function params.
-    (active, param_attrs, "1.36.0", Some(60406), None),
-
-    // Allows calling constructor functions in `const fn`.
-    (active, const_constructor, "1.37.0", Some(61456), None),
-
-    // Allows `if/while p && let q = r && ...` chains.
-    (active, let_chains, "1.37.0", Some(53667), None),
-
-    // Allows #[repr(transparent)] on enums (RFC 2645).
-    (active, transparent_enums, "1.37.0", Some(60405), None),
-
-    // Allows #[repr(transparent)] on unions (RFC 2645).
-    (active, transparent_unions, "1.37.0", Some(60405), None),
-
-    // Allows explicit discriminants on non-unit enum variants.
-    (active, arbitrary_enum_discriminant, "1.37.0", Some(60553), None),
-
-    // Allows `impl Trait` with multiple unrelated lifetimes.
-    (active, member_constraints, "1.37.0", Some(61977), None),
-
-    // Allows `async || body` closures.
-    (active, async_closure, "1.37.0", Some(62290), None),
-
-    // Allows the use of `#[cfg(doctest)]`, set when rustdoc is collecting doctests
-    (active, cfg_doctest, "1.37.0", Some(62210), None),
-
-    // Allows `[x; N]` where `x` is a constant (RFC 2203).
-    (active, const_in_array_repeat_expressions, "1.37.0", Some(49147), None),
-
-    // Allows `impl Trait` to be used inside type aliases (RFC 2515).
-    (active, type_alias_impl_trait, "1.38.0", Some(63063), None),
-
-    // Allows the use of or-patterns, e.g. `0 | 1`.
-    (active, or_patterns, "1.38.0", Some(54883), None),
-
-    // -------------------------------------------------------------------------
-    // feature-group-end: actual feature gates
-    // -------------------------------------------------------------------------
-);
-
-/// Some features are known to be incomplete and using them is likely to have
-/// unanticipated results, such as compiler crashes. We warn the user about these
-/// to alert them.
-pub const INCOMPLETE_FEATURES: &[Symbol] = &[
-    sym::impl_trait_in_bindings,
-    sym::generic_associated_types,
-    sym::const_generics,
-    sym::or_patterns,
-    sym::let_chains,
-];
-
-declare_features! (
-    // -------------------------------------------------------------------------
-    // feature-group-start: removed features
-    // -------------------------------------------------------------------------
-
-    (removed, import_shadowing, "1.0.0", None, None, None),
-    (removed, managed_boxes, "1.0.0", None, None, None),
-    // Allows use of unary negate on unsigned integers, e.g., -e for e: u8
-    (removed, negate_unsigned, "1.0.0", Some(29645), None, None),
-    (removed, reflect, "1.0.0", Some(27749), None, None),
-    // A way to temporarily opt out of opt in copy. This will *never* be accepted.
-    (removed, opt_out_copy, "1.0.0", None, None, None),
-    (removed, quad_precision_float, "1.0.0", None, None, None),
-    (removed, struct_inherit, "1.0.0", None, None, None),
-    (removed, test_removed_feature, "1.0.0", None, None, None),
-    (removed, visible_private_types, "1.0.0", None, None, None),
-    (removed, unsafe_no_drop_flag, "1.0.0", None, None, None),
-    // Allows using items which are missing stability attributes
-    (removed, unmarked_api, "1.0.0", None, None, None),
-    (removed, allocator, "1.0.0", None, None, None),
-    (removed, simd, "1.0.0", Some(27731), None,
-     Some("removed in favor of `#[repr(simd)]`")),
-    (removed, advanced_slice_patterns, "1.0.0", Some(62254), None,
-     Some("merged into `#![feature(slice_patterns)]`")),
-    (removed, macro_reexport, "1.0.0", Some(29638), None,
-     Some("subsumed by `pub use`")),
-    (removed, pushpop_unsafe, "1.2.0", None, None, None),
-    (removed, needs_allocator, "1.4.0", Some(27389), None,
-     Some("subsumed by `#![feature(allocator_internals)]`")),
-    (removed, proc_macro_mod, "1.27.0", Some(54727), None,
-     Some("subsumed by `#![feature(proc_macro_hygiene)]`")),
-    (removed, proc_macro_expr, "1.27.0", Some(54727), None,
-     Some("subsumed by `#![feature(proc_macro_hygiene)]`")),
-    (removed, proc_macro_non_items, "1.27.0", Some(54727), None,
-     Some("subsumed by `#![feature(proc_macro_hygiene)]`")),
-    (removed, proc_macro_gen, "1.27.0", Some(54727), None,
-     Some("subsumed by `#![feature(proc_macro_hygiene)]`")),
-    (removed, panic_implementation, "1.28.0", Some(44489), None,
-     Some("subsumed by `#[panic_handler]`")),
-    // Allows the use of `#[derive(Anything)]` as sugar for `#[derive_Anything]`.
-    (removed, custom_derive, "1.32.0", Some(29644), None,
-     Some("subsumed by `#[proc_macro_derive]`")),
-    // Paths of the form: `extern::foo::bar`
-    (removed, extern_in_paths, "1.33.0", Some(55600), None,
-     Some("subsumed by `::foo::bar` paths")),
-    (removed, quote, "1.33.0", Some(29601), None, None),
-    // Allows using `#[unsafe_destructor_blind_to_params]` (RFC 1238).
-    (removed, dropck_parametricity, "1.38.0", Some(28498), None, None),
-    (removed, await_macro, "1.38.0", Some(50547), None,
-     Some("subsumed by `.await` syntax")),
-    // Allows defining `existential type`s.
-    (removed, existential_type, "1.38.0", Some(63063), None,
-     Some("removed in favor of `#![feature(type_alias_impl_trait)]`")),
-
-    // -------------------------------------------------------------------------
-    // feature-group-end: removed features
-    // -------------------------------------------------------------------------
-);
-
-declare_features! (
-    (stable_removed, no_stack_check, "1.0.0", None, None),
-);
-
-declare_features! (
-    // -------------------------------------------------------------------------
-    // feature-group-start: for testing purposes
-    // -------------------------------------------------------------------------
-
-    // A temporary feature gate used to enable parser extensions needed
-    // to bootstrap fix for #5723.
-    (accepted, issue_5723_bootstrap, "1.0.0", None, None),
-    // These are used to test this portion of the compiler,
-    // they don't actually mean anything.
-    (accepted, test_accepted_feature, "1.0.0", None, None),
-
-    // -------------------------------------------------------------------------
-    // feature-group-end: for testing purposes
-    // -------------------------------------------------------------------------
-
-    // -------------------------------------------------------------------------
-    // feature-group-start: accepted features
-    // -------------------------------------------------------------------------
-
-    // Allows using associated `type`s in `trait`s.
-    (accepted, associated_types, "1.0.0", None, None),
-    // Allows using assigning a default type to type parameters in algebraic data type definitions.
-    (accepted, default_type_params, "1.0.0", None, None),
-    // FIXME: explain `globs`.
-    (accepted, globs, "1.0.0", None, None),
-    // Allows `macro_rules!` items.
-    (accepted, macro_rules, "1.0.0", None, None),
-    // Allows use of `&foo[a..b]` as a slicing syntax.
-    (accepted, slicing_syntax, "1.0.0", None, None),
-    // Allows struct variants `Foo { baz: u8, .. }` in enums (RFC 418).
-    (accepted, struct_variant, "1.0.0", None, None),
-    // Allows indexing tuples.
-    (accepted, tuple_indexing, "1.0.0", None, None),
-    // Allows the use of `if let` expressions.
-    (accepted, if_let, "1.0.0", None, None),
-    // Allows the use of `while let` expressions.
-    (accepted, while_let, "1.0.0", None, None),
-    // Allows using `#![no_std]`.
-    (accepted, no_std, "1.6.0", None, None),
-    // Allows overloading augmented assignment operations like `a += b`.
-    (accepted, augmented_assignments, "1.8.0", Some(28235), None),
-    // Allows empty structs and enum variants with braces.
-    (accepted, braced_empty_structs, "1.8.0", Some(29720), None),
-    // Allows `#[deprecated]` attribute.
-    (accepted, deprecated, "1.9.0", Some(29935), None),
-    // Allows macros to appear in the type position.
-    (accepted, type_macros, "1.13.0", Some(27245), None),
-    // Allows use of the postfix `?` operator in expressions.
-    (accepted, question_mark, "1.13.0", Some(31436), None),
-    // Allows `..` in tuple (struct) patterns.
-    (accepted, dotdot_in_tuple_patterns, "1.14.0", Some(33627), None),
-    // Allows some increased flexibility in the name resolution rules,
-    // especially around globs and shadowing (RFC 1560).
-    (accepted, item_like_imports, "1.15.0", Some(35120), None),
-    // Allows using `Self` and associated types in struct expressions and patterns.
-    (accepted, more_struct_aliases, "1.16.0", Some(37544), None),
-    // Allows elision of `'static` lifetimes in `static`s and `const`s.
-    (accepted, static_in_const, "1.17.0", Some(35897), None),
-    // Allows field shorthands (`x` meaning `x: x`) in struct literal expressions.
-    (accepted, field_init_shorthand, "1.17.0", Some(37340), None),
-    // Allows the definition recursive static items.
-    (accepted, static_recursion, "1.17.0", Some(29719), None),
-    // Allows `pub(restricted)` visibilities (RFC 1422).
-    (accepted, pub_restricted, "1.18.0", Some(32409), None),
-    // Allows `#![windows_subsystem]`.
-    (accepted, windows_subsystem, "1.18.0", Some(37499), None),
-    // Allows `break {expr}` with a value inside `loop`s.
-    (accepted, loop_break_value, "1.19.0", Some(37339), None),
-    // Allows numeric fields in struct expressions and patterns.
-    (accepted, relaxed_adts, "1.19.0", Some(35626), None),
-    // Allows coercing non capturing closures to function pointers.
-    (accepted, closure_to_fn_coercion, "1.19.0", Some(39817), None),
-    // Allows attributes on struct literal fields.
-    (accepted, struct_field_attributes, "1.20.0", Some(38814), None),
-    // Allows the definition of associated constants in `trait` or `impl` blocks.
-    (accepted, associated_consts, "1.20.0", Some(29646), None),
-    // Allows usage of the `compile_error!` macro.
-    (accepted, compile_error, "1.20.0", Some(40872), None),
-    // Allows code like `let x: &'static u32 = &42` to work (RFC 1414).
-    (accepted, rvalue_static_promotion, "1.21.0", Some(38865), None),
-    // Allows `Drop` types in constants (RFC 1440).
-    (accepted, drop_types_in_const, "1.22.0", Some(33156), None),
-    // Allows the sysV64 ABI to be specified on all platforms
-    // instead of just the platforms on which it is the C ABI.
-    (accepted, abi_sysv64, "1.24.0", Some(36167), None),
-    // Allows `repr(align(16))` struct attribute (RFC 1358).
-    (accepted, repr_align, "1.25.0", Some(33626), None),
-    // Allows '|' at beginning of match arms (RFC 1925).
-    (accepted, match_beginning_vert, "1.25.0", Some(44101), None),
-    // Allows nested groups in `use` items (RFC 2128).
-    (accepted, use_nested_groups, "1.25.0", Some(44494), None),
-    // Allows indexing into constant arrays.
-    (accepted, const_indexing, "1.26.0", Some(29947), None),
-    // Allows using `a..=b` and `..=b` as inclusive range syntaxes.
-    (accepted, inclusive_range_syntax, "1.26.0", Some(28237), None),
-    // Allows `..=` in patterns (RFC 1192).
-    (accepted, dotdoteq_in_patterns, "1.26.0", Some(28237), None),
-    // Allows `fn main()` with return types which implements `Termination` (RFC 1937).
-    (accepted, termination_trait, "1.26.0", Some(43301), None),
-    // Allows implementing `Clone` for closures where possible (RFC 2132).
-    (accepted, clone_closures, "1.26.0", Some(44490), None),
-    // Allows implementing `Copy` for closures where possible (RFC 2132).
-    (accepted, copy_closures, "1.26.0", Some(44490), None),
-    // Allows `impl Trait` in function arguments.
-    (accepted, universal_impl_trait, "1.26.0", Some(34511), None),
-    // Allows `impl Trait` in function return types.
-    (accepted, conservative_impl_trait, "1.26.0", Some(34511), None),
-    // Allows using the `u128` and `i128` types.
-    (accepted, i128_type, "1.26.0", Some(35118), None),
-    // Allows default match binding modes (RFC 2005).
-    (accepted, match_default_bindings, "1.26.0", Some(42640), None),
-    // Allows `'_` placeholder lifetimes.
-    (accepted, underscore_lifetimes, "1.26.0", Some(44524), None),
-    // Allows attributes on lifetime/type formal parameters in generics (RFC 1327).
-    (accepted, generic_param_attrs, "1.27.0", Some(48848), None),
-    // Allows `cfg(target_feature = "...")`.
-    (accepted, cfg_target_feature, "1.27.0", Some(29717), None),
-    // Allows `#[target_feature(...)]`.
-    (accepted, target_feature, "1.27.0", None, None),
-    // Allows using `dyn Trait` as a syntax for trait objects.
-    (accepted, dyn_trait, "1.27.0", Some(44662), None),
-    // Allows `#[must_use]` on functions, and introduces must-use operators (RFC 1940).
-    (accepted, fn_must_use, "1.27.0", Some(43302), None),
-    // Allows use of the `:lifetime` macro fragment specifier.
-    (accepted, macro_lifetime_matcher, "1.27.0", Some(34303), None),
-    // Allows `#[test]` functions where the return type implements `Termination` (RFC 1937).
-    (accepted, termination_trait_test, "1.27.0", Some(48854), None),
-    // Allows the `#[global_allocator]` attribute.
-    (accepted, global_allocator, "1.28.0", Some(27389), None),
-    // Allows `#[repr(transparent)]` attribute on newtype structs.
-    (accepted, repr_transparent, "1.28.0", Some(43036), None),
-    // Allows procedural macros in `proc-macro` crates.
-    (accepted, proc_macro, "1.29.0", Some(38356), None),
-    // Allows `foo.rs` as an alternative to `foo/mod.rs`.
-    (accepted, non_modrs_mods, "1.30.0", Some(44660), None),
-    // Allows use of the `:vis` macro fragment specifier
-    (accepted, macro_vis_matcher, "1.30.0", Some(41022), None),
-    // Allows importing and reexporting macros with `use`,
-    // enables macro modularization in general.
-    (accepted, use_extern_macros, "1.30.0", Some(35896), None),
-    // Allows keywords to be escaped for use as identifiers.
-    (accepted, raw_identifiers, "1.30.0", Some(48589), None),
-    // Allows attributes scoped to tools.
-    (accepted, tool_attributes, "1.30.0", Some(44690), None),
-    // Allows multi-segment paths in attributes and derives.
-    (accepted, proc_macro_path_invoc, "1.30.0", Some(38356), None),
-    // Allows all literals in attribute lists and values of key-value pairs.
-    (accepted, attr_literals, "1.30.0", Some(34981), None),
-    // Allows inferring outlives requirements (RFC 2093).
-    (accepted, infer_outlives_requirements, "1.30.0", Some(44493), None),
-    // Allows annotating functions conforming to `fn(&PanicInfo) -> !` with `#[panic_handler]`.
-    // This defines the behavior of panics.
-    (accepted, panic_handler, "1.30.0", Some(44489), None),
-    // Allows `#[used]` to preserve symbols (see llvm.used).
-    (accepted, used, "1.30.0", Some(40289), None),
-    // Allows `crate` in paths.
-    (accepted, crate_in_paths, "1.30.0", Some(45477), None),
-    // Allows resolving absolute paths as paths from other crates.
-    (accepted, extern_absolute_paths, "1.30.0", Some(44660), None),
-    // Allows access to crate names passed via `--extern` through prelude.
-    (accepted, extern_prelude, "1.30.0", Some(44660), None),
-    // Allows parentheses in patterns.
-    (accepted, pattern_parentheses, "1.31.0", Some(51087), None),
-    // Allows the definition of `const fn` functions.
-    (accepted, min_const_fn, "1.31.0", Some(53555), None),
-    // Allows scoped lints.
-    (accepted, tool_lints, "1.31.0", Some(44690), None),
-    // Allows lifetime elision in `impl` headers. For example:
-    // + `impl<I:Iterator> Iterator for &mut Iterator`
-    // + `impl Debug for Foo<'_>`
-    (accepted, impl_header_lifetime_elision, "1.31.0", Some(15872), None),
-    // Allows `extern crate foo as bar;`. This puts `bar` into extern prelude.
-    (accepted, extern_crate_item_prelude, "1.31.0", Some(55599), None),
-    // Allows use of the `:literal` macro fragment specifier (RFC 1576).
-    (accepted, macro_literal_matcher, "1.32.0", Some(35625), None),
-    // Allows use of `?` as the Kleene "at most one" operator in macros.
-    (accepted, macro_at_most_once_rep, "1.32.0", Some(48075), None),
-    // Allows `Self` struct constructor (RFC 2302).
-    (accepted, self_struct_ctor, "1.32.0", Some(51994), None),
-    // Allows `Self` in type definitions (RFC 2300).
-    (accepted, self_in_typedefs, "1.32.0", Some(49303), None),
-    // Allows `use x::y;` to search `x` in the current scope.
-    (accepted, uniform_paths, "1.32.0", Some(53130), None),
-    // Allows integer match exhaustiveness checking (RFC 2591).
-    (accepted, exhaustive_integer_patterns, "1.33.0", Some(50907), None),
-    // Allows `use path as _;` and `extern crate c as _;`.
-    (accepted, underscore_imports, "1.33.0", Some(48216), None),
-    // Allows `#[repr(packed(N))]` attribute on structs.
-    (accepted, repr_packed, "1.33.0", Some(33158), None),
-    // Allows irrefutable patterns in `if let` and `while let` statements (RFC 2086).
-    (accepted, irrefutable_let_patterns, "1.33.0", Some(44495), None),
-    // Allows calling `const unsafe fn` inside `unsafe` blocks in `const fn` functions.
-    (accepted, min_const_unsafe_fn, "1.33.0", Some(55607), None),
-    // Allows let bindings, assignments and destructuring in `const` functions and constants.
-    // As long as control flow is not implemented in const eval, `&&` and `||` may not be used
-    // at the same time as let bindings.
-    (accepted, const_let, "1.33.0", Some(48821), None),
-    // Allows `#[cfg_attr(predicate, multiple, attributes, here)]`.
-    (accepted, cfg_attr_multi, "1.33.0", Some(54881), None),
-    // Allows top level or-patterns (`p | q`) in `if let` and `while let`.
-    (accepted, if_while_or_patterns, "1.33.0", Some(48215), None),
-    // Allows `cfg(target_vendor = "...")`.
-    (accepted, cfg_target_vendor, "1.33.0", Some(29718), None),
-    // Allows `extern crate self as foo;`.
-    // This puts local crate root into extern prelude under name `foo`.
-    (accepted, extern_crate_self, "1.34.0", Some(56409), None),
-    // Allows arbitrary delimited token streams in non-macro attributes.
-    (accepted, unrestricted_attribute_tokens, "1.34.0", Some(55208), None),
-    // Allows paths to enum variants on type aliases including `Self`.
-    (accepted, type_alias_enum_variants, "1.37.0", Some(49683), None),
-    // Allows using `#[repr(align(X))]` on enums with equivalent semantics
-    // to wrapping an enum in a wrapper struct with `#[repr(align(X))]`.
-    (accepted, repr_align_enum, "1.37.0", Some(57996), None),
-    // Allows `const _: TYPE = VALUE`.
-    (accepted, underscore_const_names, "1.37.0", Some(54912), None),
-    // Allows free and inherent `async fn`s, `async` blocks, and `<expr>.await` expressions.
-    (accepted, async_await, "1.38.0", Some(50547), None),
-
-    // -------------------------------------------------------------------------
-    // feature-group-end: accepted features
-    // -------------------------------------------------------------------------
-);
-
-// If you change this, please modify `src/doc/unstable-book` as well. You must
-// move that documentation into the relevant place in the other docs, and
-// remove the chapter on the flag.
-
-#[derive(Copy, Clone, PartialEq, Debug)]
-pub enum AttributeType {
-    /// Normal, builtin attribute that is consumed
-    /// by the compiler before the unused_attribute check
-    Normal,
-
-    /// Builtin attribute that may not be consumed by the compiler
-    /// before the unused_attribute check. These attributes
-    /// will be ignored by the unused_attribute lint
-    Whitelisted,
-
-    /// Builtin attribute that is only allowed at the crate level
-    CrateLevel,
-}
-
-pub enum AttributeGate {
-    /// Is gated by a given feature gate, reason
-    /// and function to check if enabled
-    Gated(Stability, Symbol, &'static str, fn(&Features) -> bool),
-
-    /// Ungated attribute, can be used on all release channels
-    Ungated,
-}
-
-/// A convenience macro for constructing attribute templates.
-/// E.g., `template!(Word, List: "description")` means that the attribute
-/// supports forms `#[attr]` and `#[attr(description)]`.
-macro_rules! template {
-    (Word) => { template!(@ true, None, None) };
-    (List: $descr: expr) => { template!(@ false, Some($descr), None) };
-    (NameValueStr: $descr: expr) => { template!(@ false, None, Some($descr)) };
-    (Word, List: $descr: expr) => { template!(@ true, Some($descr), None) };
-    (Word, NameValueStr: $descr: expr) => { template!(@ true, None, Some($descr)) };
-    (List: $descr1: expr, NameValueStr: $descr2: expr) => {
-        template!(@ false, Some($descr1), Some($descr2))
-    };
-    (Word, List: $descr1: expr, NameValueStr: $descr2: expr) => {
-        template!(@ true, Some($descr1), Some($descr2))
-    };
-    (@ $word: expr, $list: expr, $name_value_str: expr) => { AttributeTemplate {
-        word: $word, list: $list, name_value_str: $name_value_str
-    } };
-}
-
-impl AttributeGate {
-    fn is_deprecated(&self) -> bool {
-        match *self {
-            Gated(Stability::Deprecated(_, _), ..) => true,
-            _ => false,
-        }
-    }
-}
-
-#[derive(Copy, Clone, Debug)]
-pub enum Stability {
-    Unstable,
-    // First argument is tracking issue link; second argument is an optional
-    // help message, which defaults to "remove this attribute"
-    Deprecated(&'static str, Option<&'static str>),
-}
-
-// fn() is not Debug
-impl std::fmt::Debug for AttributeGate {
-    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match *self {
-            Gated(ref stab, name, expl, _) =>
-                write!(fmt, "Gated({:?}, {}, {})", stab, name, expl),
-            Ungated => write!(fmt, "Ungated")
-        }
-    }
-}
-
-macro_rules! cfg_fn {
-    ($field: ident) => {{
-        fn f(features: &Features) -> bool {
-            features.$field
-        }
-        f as fn(&Features) -> bool
-    }}
-}
-
-pub fn deprecated_attributes() -> Vec<&'static (Symbol, AttributeType,
-                                                AttributeTemplate, AttributeGate)> {
-    BUILTIN_ATTRIBUTES.iter().filter(|(.., gate)| gate.is_deprecated()).collect()
-}
-
-pub fn is_builtin_attr_name(name: ast::Name) -> bool {
-    BUILTIN_ATTRIBUTE_MAP.get(&name).is_some()
-}
-
-pub fn is_builtin_attr(attr: &ast::Attribute) -> bool {
-    attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)).is_some()
-}
-
-/// Attributes that have a special meaning to rustc or rustdoc
-pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
-    // Normal attributes
-
-    (
-        sym::warn,
-        Normal,
-        template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
-        Ungated
-    ),
-    (
-        sym::allow,
-        Normal,
-        template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
-        Ungated
-    ),
-    (
-        sym::forbid,
-        Normal,
-        template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
-        Ungated
-    ),
-    (
-        sym::deny,
-        Normal,
-        template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
-        Ungated
-    ),
-
-    (sym::macro_use, Normal, template!(Word, List: "name1, name2, ..."), Ungated),
-    (sym::macro_export, Normal, template!(Word, List: "local_inner_macros"), Ungated),
-    (sym::plugin_registrar, Normal, template!(Word), Ungated),
-
-    (sym::cfg, Normal, template!(List: "predicate"), Ungated),
-    (sym::cfg_attr, Normal, template!(List: "predicate, attr1, attr2, ..."), Ungated),
-    (sym::main, Normal, template!(Word), Ungated),
-    (sym::start, Normal, template!(Word), Ungated),
-    (sym::repr, Normal, template!(List: "C, packed, ..."), Ungated),
-    (sym::path, Normal, template!(NameValueStr: "file"), Ungated),
-    (sym::automatically_derived, Normal, template!(Word), Ungated),
-    (sym::no_mangle, Whitelisted, template!(Word), Ungated),
-    (sym::no_link, Normal, template!(Word), Ungated),
-    (sym::derive, Normal, template!(List: "Trait1, Trait2, ..."), Ungated),
-    (
-        sym::should_panic,
-        Normal,
-        template!(Word, List: r#"expected = "reason"#, NameValueStr: "reason"),
-        Ungated
-    ),
-    (sym::ignore, Normal, template!(Word, NameValueStr: "reason"), Ungated),
-    (sym::no_implicit_prelude, Normal, template!(Word), Ungated),
-    (sym::reexport_test_harness_main, Normal, template!(NameValueStr: "name"), Ungated),
-    (sym::link_args, Normal, template!(NameValueStr: "args"), Gated(Stability::Unstable,
-                                sym::link_args,
-                                "the `link_args` attribute is experimental and not \
-                                portable across platforms, it is recommended to \
-                                use `#[link(name = \"foo\")] instead",
-                                cfg_fn!(link_args))),
-    (sym::macro_escape, Normal, template!(Word), Ungated),
-
-    // RFC #1445.
-    (sym::structural_match, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                            sym::structural_match,
-                                            "the semantics of constant patterns is \
-                                            not yet settled",
-                                            cfg_fn!(structural_match))),
-
-    // RFC #2008
-    (sym::non_exhaustive, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                        sym::non_exhaustive,
-                                        "non exhaustive is an experimental feature",
-                                        cfg_fn!(non_exhaustive))),
-
-    // RFC #1268
-    (sym::marker, Normal, template!(Word), Gated(Stability::Unstable,
-                            sym::marker_trait_attr,
-                            "marker traits is an experimental feature",
-                            cfg_fn!(marker_trait_attr))),
-
-    (sym::plugin, CrateLevel, template!(List: "name|name(args)"), Gated(Stability::Unstable,
-                                sym::plugin,
-                                "compiler plugins are experimental \
-                                and possibly buggy",
-                                cfg_fn!(plugin))),
-
-    (sym::no_std, CrateLevel, template!(Word), Ungated),
-    (sym::no_core, CrateLevel, template!(Word), Gated(Stability::Unstable,
-                                sym::no_core,
-                                "no_core is experimental",
-                                cfg_fn!(no_core))),
-    (sym::lang, Normal, template!(NameValueStr: "name"), Gated(Stability::Unstable,
-                        sym::lang_items,
-                        "language items are subject to change",
-                        cfg_fn!(lang_items))),
-    (sym::linkage, Whitelisted, template!(NameValueStr: "external|internal|..."),
-                                Gated(Stability::Unstable,
-                                sym::linkage,
-                                "the `linkage` attribute is experimental \
-                                    and not portable across platforms",
-                                cfg_fn!(linkage))),
-    (sym::thread_local, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                        sym::thread_local,
-                                        "`#[thread_local]` is an experimental feature, and does \
-                                         not currently handle destructors",
-                                        cfg_fn!(thread_local))),
-
-    (sym::rustc_on_unimplemented, Whitelisted, template!(List:
-                        r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#,
-                        NameValueStr: "message"),
-                                            Gated(Stability::Unstable,
-                                            sym::on_unimplemented,
-                                            "the `#[rustc_on_unimplemented]` attribute \
-                                            is an experimental feature",
-                                            cfg_fn!(on_unimplemented))),
-    (sym::rustc_const_unstable, Normal, template!(List: r#"feature = "name""#),
-                                            Gated(Stability::Unstable,
-                                            sym::rustc_const_unstable,
-                                            "the `#[rustc_const_unstable]` attribute \
-                                            is an internal feature",
-                                            cfg_fn!(rustc_const_unstable))),
-    (sym::default_lib_allocator, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                            sym::allocator_internals,
-                                            "the `#[default_lib_allocator]` \
-                                            attribute is an experimental feature",
-                                            cfg_fn!(allocator_internals))),
-    (sym::needs_allocator, Normal, template!(Word), Gated(Stability::Unstable,
-                                    sym::allocator_internals,
-                                    "the `#[needs_allocator]` \
-                                    attribute is an experimental \
-                                    feature",
-                                    cfg_fn!(allocator_internals))),
-    (sym::panic_runtime, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                        sym::panic_runtime,
-                                        "the `#[panic_runtime]` attribute is \
-                                        an experimental feature",
-                                        cfg_fn!(panic_runtime))),
-    (sym::needs_panic_runtime, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                            sym::needs_panic_runtime,
-                                            "the `#[needs_panic_runtime]` \
-                                                attribute is an experimental \
-                                                feature",
-                                            cfg_fn!(needs_panic_runtime))),
-    (sym::rustc_outlives, Normal, template!(Word), Gated(Stability::Unstable,
-                                    sym::rustc_attrs,
-                                    "the `#[rustc_outlives]` attribute \
-                                    is just used for rustc unit tests \
-                                    and will never be stable",
-                                    cfg_fn!(rustc_attrs))),
-    (sym::rustc_variance, Normal, template!(Word), Gated(Stability::Unstable,
-                                    sym::rustc_attrs,
-                                    "the `#[rustc_variance]` attribute \
-                                    is just used for rustc unit tests \
-                                    and will never be stable",
-                                    cfg_fn!(rustc_attrs))),
-    (sym::rustc_layout, Normal, template!(List: "field1, field2, ..."),
-    Gated(Stability::Unstable,
-        sym::rustc_attrs,
-        "the `#[rustc_layout]` attribute \
-            is just used for rustc unit tests \
-            and will never be stable",
-        cfg_fn!(rustc_attrs))),
-    (sym::rustc_layout_scalar_valid_range_start, Whitelisted, template!(List: "value"),
-    Gated(Stability::Unstable,
-        sym::rustc_attrs,
-        "the `#[rustc_layout_scalar_valid_range_start]` attribute \
-            is just used to enable niche optimizations in libcore \
-            and will never be stable",
-        cfg_fn!(rustc_attrs))),
-    (sym::rustc_layout_scalar_valid_range_end, Whitelisted, template!(List: "value"),
-    Gated(Stability::Unstable,
-        sym::rustc_attrs,
-        "the `#[rustc_layout_scalar_valid_range_end]` attribute \
-            is just used to enable niche optimizations in libcore \
-            and will never be stable",
-        cfg_fn!(rustc_attrs))),
-    (sym::rustc_nonnull_optimization_guaranteed, Whitelisted, template!(Word),
-    Gated(Stability::Unstable,
-        sym::rustc_attrs,
-        "the `#[rustc_nonnull_optimization_guaranteed]` attribute \
-            is just used to enable niche optimizations in libcore \
-            and will never be stable",
-        cfg_fn!(rustc_attrs))),
-    (sym::rustc_regions, Normal, template!(Word), Gated(Stability::Unstable,
-                                    sym::rustc_attrs,
-                                    "the `#[rustc_regions]` attribute \
-                                    is just used for rustc unit tests \
-                                    and will never be stable",
-                                    cfg_fn!(rustc_attrs))),
-    (sym::rustc_error, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                    sym::rustc_attrs,
-                                    "the `#[rustc_error]` attribute \
-                                        is just used for rustc unit tests \
-                                        and will never be stable",
-                                    cfg_fn!(rustc_attrs))),
-    (sym::rustc_dump_user_substs, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                    sym::rustc_attrs,
-                                    "this attribute \
-                                        is just used for rustc unit tests \
-                                        and will never be stable",
-                                    cfg_fn!(rustc_attrs))),
-    (sym::rustc_if_this_changed, Whitelisted, template!(Word, List: "DepNode"),
-                                                Gated(Stability::Unstable,
-                                                sym::rustc_attrs,
-                                                "the `#[rustc_if_this_changed]` attribute \
-                                                is just used for rustc unit tests \
-                                                and will never be stable",
-                                                cfg_fn!(rustc_attrs))),
-    (sym::rustc_then_this_would_need, Whitelisted, template!(List: "DepNode"),
-                                                    Gated(Stability::Unstable,
-                                                    sym::rustc_attrs,
-                                                    "the `#[rustc_if_this_changed]` attribute \
-                                                    is just used for rustc unit tests \
-                                                    and will never be stable",
-                                                    cfg_fn!(rustc_attrs))),
-    (sym::rustc_dirty, Whitelisted, template!(List: r#"cfg = "...", /*opt*/ label = "...",
-                                                    /*opt*/ except = "...""#),
-                                    Gated(Stability::Unstable,
-                                    sym::rustc_attrs,
-                                    "the `#[rustc_dirty]` attribute \
-                                        is just used for rustc unit tests \
-                                        and will never be stable",
-                                    cfg_fn!(rustc_attrs))),
-    (sym::rustc_clean, Whitelisted, template!(List: r#"cfg = "...", /*opt*/ label = "...",
-                                                    /*opt*/ except = "...""#),
-                                    Gated(Stability::Unstable,
-                                    sym::rustc_attrs,
-                                    "the `#[rustc_clean]` attribute \
-                                        is just used for rustc unit tests \
-                                        and will never be stable",
-                                    cfg_fn!(rustc_attrs))),
-    (
-        sym::rustc_partition_reused,
-        Whitelisted,
-        template!(List: r#"cfg = "...", module = "...""#),
-        Gated(
-            Stability::Unstable,
-            sym::rustc_attrs,
-            "this attribute \
-            is just used for rustc unit tests \
-            and will never be stable",
-            cfg_fn!(rustc_attrs)
-        )
-    ),
-    (
-        sym::rustc_partition_codegened,
-        Whitelisted,
-        template!(List: r#"cfg = "...", module = "...""#),
-        Gated(
-            Stability::Unstable,
-            sym::rustc_attrs,
-            "this attribute \
-            is just used for rustc unit tests \
-            and will never be stable",
-            cfg_fn!(rustc_attrs),
-        )
-    ),
-    (sym::rustc_expected_cgu_reuse, Whitelisted, template!(List: r#"cfg = "...", module = "...",
-                                                            kind = "...""#),
-                                                    Gated(Stability::Unstable,
-                                                    sym::rustc_attrs,
-                                                    "this attribute \
-                                                    is just used for rustc unit tests \
-                                                    and will never be stable",
-                                                    cfg_fn!(rustc_attrs))),
-    (sym::rustc_synthetic, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                                    sym::rustc_attrs,
-                                                    "this attribute \
-                                                    is just used for rustc unit tests \
-                                                    and will never be stable",
-                                                    cfg_fn!(rustc_attrs))),
-    (sym::rustc_symbol_name, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                            sym::rustc_attrs,
-                                            "internal rustc attributes will never be stable",
-                                            cfg_fn!(rustc_attrs))),
-    (sym::rustc_def_path, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                        sym::rustc_attrs,
-                                        "internal rustc attributes will never be stable",
-                                        cfg_fn!(rustc_attrs))),
-    (sym::rustc_mir, Whitelisted, template!(List: "arg1, arg2, ..."), Gated(Stability::Unstable,
-                                    sym::rustc_attrs,
-                                    "the `#[rustc_mir]` attribute \
-                                    is just used for rustc unit tests \
-                                    and will never be stable",
-                                    cfg_fn!(rustc_attrs))),
-    (
-        sym::rustc_inherit_overflow_checks,
-        Whitelisted,
-        template!(Word),
-        Gated(
-            Stability::Unstable,
-            sym::rustc_attrs,
-            "the `#[rustc_inherit_overflow_checks]` \
-            attribute is just used to control \
-            overflow checking behavior of several \
-            libcore functions that are inlined \
-            across crates and will never be stable",
-            cfg_fn!(rustc_attrs),
-        )
-    ),
-
-    (sym::rustc_dump_program_clauses, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                                    sym::rustc_attrs,
-                                                    "the `#[rustc_dump_program_clauses]` \
-                                                    attribute is just used for rustc unit \
-                                                    tests and will never be stable",
-                                                    cfg_fn!(rustc_attrs))),
-    (sym::rustc_dump_env_program_clauses, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                                    sym::rustc_attrs,
-                                                    "the `#[rustc_dump_env_program_clauses]` \
-                                                    attribute is just used for rustc unit \
-                                                    tests and will never be stable",
-                                                    cfg_fn!(rustc_attrs))),
-    (sym::rustc_object_lifetime_default, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                                    sym::rustc_attrs,
-                                                    "the `#[rustc_object_lifetime_default]` \
-                                                    attribute is just used for rustc unit \
-                                                    tests and will never be stable",
-                                                    cfg_fn!(rustc_attrs))),
-    (sym::rustc_test_marker, Normal, template!(Word), Gated(Stability::Unstable,
-                                    sym::rustc_attrs,
-                                    "the `#[rustc_test_marker]` attribute \
-                                    is used internally to track tests",
-                                    cfg_fn!(rustc_attrs))),
-    (sym::rustc_macro_transparency, Whitelisted, template!(NameValueStr:
-                                                           "transparent|semitransparent|opaque"),
-                                                Gated(Stability::Unstable,
-                                                sym::rustc_attrs,
-                                                "used internally for testing macro hygiene",
-                                                    cfg_fn!(rustc_attrs))),
-    (sym::compiler_builtins, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                            sym::compiler_builtins,
-                                            "the `#[compiler_builtins]` attribute is used to \
-                                            identify the `compiler_builtins` crate which \
-                                            contains compiler-rt intrinsics and will never be \
-                                            stable",
-                                        cfg_fn!(compiler_builtins))),
-    (sym::sanitizer_runtime, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                            sym::sanitizer_runtime,
-                                            "the `#[sanitizer_runtime]` attribute is used to \
-                                            identify crates that contain the runtime of a \
-                                            sanitizer and will never be stable",
-                                            cfg_fn!(sanitizer_runtime))),
-    (sym::profiler_runtime, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                            sym::profiler_runtime,
-                                            "the `#[profiler_runtime]` attribute is used to \
-                                            identify the `profiler_builtins` crate which \
-                                            contains the profiler runtime and will never be \
-                                            stable",
-                                            cfg_fn!(profiler_runtime))),
-
-    (sym::allow_internal_unstable, Normal, template!(Word, List: "feat1, feat2, ..."),
-                                            Gated(Stability::Unstable,
-                                            sym::allow_internal_unstable,
-                                            EXPLAIN_ALLOW_INTERNAL_UNSTABLE,
-                                            cfg_fn!(allow_internal_unstable))),
-
-    (sym::allow_internal_unsafe, Normal, template!(Word), Gated(Stability::Unstable,
-                                            sym::allow_internal_unsafe,
-                                            EXPLAIN_ALLOW_INTERNAL_UNSAFE,
-                                            cfg_fn!(allow_internal_unsafe))),
-
-    (sym::fundamental, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                    sym::fundamental,
-                                    "the `#[fundamental]` attribute \
-                                        is an experimental feature",
-                                    cfg_fn!(fundamental))),
-
-    (sym::proc_macro_derive, Normal, template!(List: "TraitName, \
-                                                /*opt*/ attributes(name1, name2, ...)"),
-                                    Ungated),
-
-    (sym::rustc_allocator, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                                sym::rustc_attrs,
-                                                "internal implementation detail",
-                                                cfg_fn!(rustc_attrs))),
-
-    (sym::rustc_allocator_nounwind, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                                sym::rustc_attrs,
-                                                "internal implementation detail",
-                                                cfg_fn!(rustc_attrs))),
-
-    (sym::rustc_builtin_macro, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                                sym::rustc_attrs,
-                                                "internal implementation detail",
-                                                cfg_fn!(rustc_attrs))),
-
-    (sym::rustc_promotable, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                                sym::rustc_attrs,
-                                                "internal implementation detail",
-                                                cfg_fn!(rustc_attrs))),
-
-    (sym::rustc_allow_const_fn_ptr, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                                sym::rustc_attrs,
-                                                "internal implementation detail",
-                                                cfg_fn!(rustc_attrs))),
-
-    (sym::rustc_dummy, Normal, template!(Word /* doesn't matter*/), Gated(Stability::Unstable,
-                                         sym::rustc_attrs,
-                                         "used by the test suite",
-                                         cfg_fn!(rustc_attrs))),
-
-    // FIXME: #14408 whitelist docs since rustdoc looks at them
-    (
-        sym::doc,
-        Whitelisted,
-        template!(List: "hidden|inline|...", NameValueStr: "string"),
-        Ungated
-    ),
-
-    // FIXME: #14406 these are processed in codegen, which happens after the
-    // lint pass
-    (sym::cold, Whitelisted, template!(Word), Ungated),
-    (sym::naked, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                sym::naked_functions,
-                                "the `#[naked]` attribute \
-                                is an experimental feature",
-                                cfg_fn!(naked_functions))),
-    (sym::ffi_returns_twice, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                sym::ffi_returns_twice,
-                                "the `#[ffi_returns_twice]` attribute \
-                                is an experimental feature",
-                                cfg_fn!(ffi_returns_twice))),
-    (sym::target_feature, Whitelisted, template!(List: r#"enable = "name""#), Ungated),
-    (sym::export_name, Whitelisted, template!(NameValueStr: "name"), Ungated),
-    (sym::inline, Whitelisted, template!(Word, List: "always|never"), Ungated),
-    (sym::link, Whitelisted, template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...",
-                                               /*opt*/ cfg = "...""#), Ungated),
-    (sym::link_name, Whitelisted, template!(NameValueStr: "name"), Ungated),
-    (sym::link_section, Whitelisted, template!(NameValueStr: "name"), Ungated),
-    (sym::no_builtins, Whitelisted, template!(Word), Ungated),
-    (sym::no_debug, Whitelisted, template!(Word), Gated(
-        Stability::Deprecated("https://github.com/rust-lang/rust/issues/29721", None),
-        sym::no_debug,
-        "the `#[no_debug]` attribute was an experimental feature that has been \
-        deprecated due to lack of demand",
-        cfg_fn!(no_debug))),
-    (
-        sym::omit_gdb_pretty_printer_section,
-        Whitelisted,
-        template!(Word),
-        Gated(
-            Stability::Unstable,
-            sym::omit_gdb_pretty_printer_section,
-            "the `#[omit_gdb_pretty_printer_section]` \
-                attribute is just used for the Rust test \
-                suite",
-            cfg_fn!(omit_gdb_pretty_printer_section)
-        )
-    ),
-    (sym::may_dangle,
-    Normal,
-    template!(Word),
-    Gated(Stability::Unstable,
-        sym::dropck_eyepatch,
-        "`may_dangle` has unstable semantics and may be removed in the future",
-        cfg_fn!(dropck_eyepatch))),
-    (sym::unwind, Whitelisted, template!(List: "allowed|aborts"), Gated(Stability::Unstable,
-                                sym::unwind_attributes,
-                                "`#[unwind]` is experimental",
-                                cfg_fn!(unwind_attributes))),
-    (sym::used, Whitelisted, template!(Word), Ungated),
-
-    // used in resolve
-    (sym::prelude_import, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                        sym::prelude_import,
-                                        "`#[prelude_import]` is for use by rustc only",
-                                        cfg_fn!(prelude_import))),
-
-    // FIXME: #14407 these are only looked at on-demand so we can't
-    // guarantee they'll have already been checked
-    (
-        sym::rustc_deprecated,
-        Whitelisted,
-        template!(List: r#"since = "version", reason = "...""#),
-        Ungated
-    ),
-    (sym::must_use, Whitelisted, template!(Word, NameValueStr: "reason"), Ungated),
-    (
-        sym::stable,
-        Whitelisted,
-        template!(List: r#"feature = "name", since = "version""#),
-        Ungated
-    ),
-    (
-        sym::unstable,
-        Whitelisted,
-        template!(List: r#"feature = "name", reason = "...", issue = "N""#),
-        Ungated
-    ),
-    (sym::deprecated,
-        Normal,
-        template!(
-            Word,
-            List: r#"/*opt*/ since = "version", /*opt*/ note = "reason""#,
-            NameValueStr: "reason"
-        ),
-        Ungated
-    ),
-
-    (sym::rustc_paren_sugar, Normal, template!(Word), Gated(Stability::Unstable,
-                                        sym::unboxed_closures,
-                                        "unboxed_closures are still evolving",
-                                        cfg_fn!(unboxed_closures))),
-
-    (sym::windows_subsystem, Whitelisted, template!(NameValueStr: "windows|console"), Ungated),
-
-    (sym::proc_macro_attribute, Normal, template!(Word), Ungated),
-    (sym::proc_macro, Normal, template!(Word), Ungated),
-
-    (sym::rustc_proc_macro_decls, Normal, template!(Word), Gated(Stability::Unstable,
-                                            sym::rustc_attrs,
-                                            "used internally by rustc",
-                                            cfg_fn!(rustc_attrs))),
-
-    (sym::allow_fail, Normal, template!(Word), Gated(Stability::Unstable,
-                                sym::allow_fail,
-                                "allow_fail attribute is currently unstable",
-                                cfg_fn!(allow_fail))),
-
-    (sym::rustc_std_internal_symbol, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                    sym::rustc_attrs,
-                                    "this is an internal attribute that will \
-                                    never be stable",
-                                    cfg_fn!(rustc_attrs))),
-
-    // whitelists "identity-like" conversion methods to suggest on type mismatch
-    (sym::rustc_conversion_suggestion, Whitelisted, template!(Word), Gated(Stability::Unstable,
-                                                    sym::rustc_attrs,
-                                                    "this is an internal attribute that will \
-                                                        never be stable",
-                                                    cfg_fn!(rustc_attrs))),
-
-    (
-        sym::rustc_args_required_const,
-        Whitelisted,
-        template!(List: "N"),
-        Gated(Stability::Unstable, sym::rustc_attrs, "never will be stable",
-           cfg_fn!(rustc_attrs))
-    ),
-    // RFC 2070
-    (sym::panic_handler, Normal, template!(Word), Ungated),
-
-    (sym::alloc_error_handler, Normal, template!(Word), Gated(Stability::Unstable,
-                        sym::alloc_error_handler,
-                        "`#[alloc_error_handler]` is an unstable feature",
-                        cfg_fn!(alloc_error_handler))),
-
-    // RFC 2412
-    (sym::optimize, Whitelisted, template!(List: "size|speed"), Gated(Stability::Unstable,
-                            sym::optimize_attribute,
-                            "`#[optimize]` attribute is an unstable feature",
-                            cfg_fn!(optimize_attribute))),
-
-    // Crate level attributes
-    (sym::crate_name, CrateLevel, template!(NameValueStr: "name"), Ungated),
-    (sym::crate_type, CrateLevel, template!(NameValueStr: "bin|lib|..."), Ungated),
-    (sym::crate_id, CrateLevel, template!(NameValueStr: "ignored"), Ungated),
-    (sym::feature, CrateLevel, template!(List: "name1, name1, ..."), Ungated),
-    (sym::no_start, CrateLevel, template!(Word), Ungated),
-    (sym::no_main, CrateLevel, template!(Word), Ungated),
-    (sym::recursion_limit, CrateLevel, template!(NameValueStr: "N"), Ungated),
-    (sym::type_length_limit, CrateLevel, template!(NameValueStr: "N"), Ungated),
-    (sym::test_runner, CrateLevel, template!(List: "path"), Gated(Stability::Unstable,
-                    sym::custom_test_frameworks,
-                    "custom test frameworks are an unstable feature",
-                    cfg_fn!(custom_test_frameworks))),
-];
-
-pub type BuiltinAttribute = (Symbol, AttributeType, AttributeTemplate, AttributeGate);
-
-lazy_static! {
-    pub static ref BUILTIN_ATTRIBUTE_MAP: FxHashMap<Symbol, &'static BuiltinAttribute> = {
-        let mut map = FxHashMap::default();
-        for attr in BUILTIN_ATTRIBUTES.iter() {
-            if map.insert(attr.0, attr).is_some() {
-                panic!("duplicate builtin attribute `{}`", attr.0);
-            }
-        }
-        map
-    };
-}
-
-// cfg(...)'s that are feature gated
-const GATED_CFGS: &[(Symbol, Symbol, fn(&Features) -> bool)] = &[
-    // (name in cfg, feature, function to check if the feature is enabled)
-    (sym::target_thread_local, sym::cfg_target_thread_local, cfg_fn!(cfg_target_thread_local)),
-    (sym::target_has_atomic, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)),
-    (sym::rustdoc, sym::doc_cfg, cfg_fn!(doc_cfg)),
-    (sym::doctest, sym::cfg_doctest, cfg_fn!(cfg_doctest)),
-];
-
-#[derive(Debug)]
-pub struct GatedCfg {
-    span: Span,
-    index: usize,
-}
-
-impl GatedCfg {
-    pub fn gate(cfg: &ast::MetaItem) -> Option<GatedCfg> {
-        GATED_CFGS.iter()
-                  .position(|info| cfg.check_name(info.0))
-                  .map(|idx| {
-                      GatedCfg {
-                          span: cfg.span,
-                          index: idx
-                      }
-                  })
-    }
-
-    pub fn check_and_emit(&self, sess: &ParseSess, features: &Features) {
-        let (cfg, feature, has_feature) = GATED_CFGS[self.index];
-        if !has_feature(features) && !self.span.allows_unstable(feature) {
-            let explain = format!("`cfg({})` is experimental and subject to change", cfg);
-            emit_feature_err(sess, feature, self.span, GateIssue::Language, &explain);
-        }
-    }
-}
-
-struct Context<'a> {
-    features: &'a Features,
-    parse_sess: &'a ParseSess,
-    plugin_attributes: &'a [(Symbol, AttributeType)],
-}
-
-macro_rules! gate_feature_fn {
-    ($cx: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr, $level: expr) => {{
-        let (cx, has_feature, span,
-             name, explain, level) = ($cx, $has_feature, $span, $name, $explain, $level);
-        let has_feature: bool = has_feature(&$cx.features);
-        debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature);
-        if !has_feature && !span.allows_unstable($name) {
-            leveled_feature_err(cx.parse_sess, name, span, GateIssue::Language, explain, level)
-                .emit();
-        }
-    }}
-}
-
-macro_rules! gate_feature {
-    ($cx: expr, $feature: ident, $span: expr, $explain: expr) => {
-        gate_feature_fn!($cx, |x:&Features| x.$feature, $span,
-                         sym::$feature, $explain, GateStrength::Hard)
-    };
-    ($cx: expr, $feature: ident, $span: expr, $explain: expr, $level: expr) => {
-        gate_feature_fn!($cx, |x:&Features| x.$feature, $span,
-                         sym::$feature, $explain, $level)
-    };
-}
-
-impl<'a> Context<'a> {
-    fn check_attribute(
-        &self,
-        attr: &ast::Attribute,
-        attr_info: Option<&BuiltinAttribute>,
-        is_macro: bool
-    ) {
-        debug!("check_attribute(attr = {:?})", attr);
-        if let Some(&(name, ty, _template, ref gateage)) = attr_info {
-            if let Gated(_, name, desc, ref has_feature) = *gateage {
-                if !attr.span.allows_unstable(name) {
-                    gate_feature_fn!(
-                        self, has_feature, attr.span, name, desc, GateStrength::Hard
-                    );
-                }
-            } else if name == sym::doc {
-                if let Some(content) = attr.meta_item_list() {
-                    if content.iter().any(|c| c.check_name(sym::include)) {
-                        gate_feature!(self, external_doc, attr.span,
-                            "`#[doc(include = \"...\")]` is experimental"
-                        );
-                    }
-                }
-            }
-            debug!("check_attribute: {:?} is builtin, {:?}, {:?}", attr.path, ty, gateage);
-            return;
-        } else {
-            for segment in &attr.path.segments {
-                if segment.ident.as_str().starts_with("rustc") {
-                    let msg = "attributes starting with `rustc` are \
-                               reserved for use by the `rustc` compiler";
-                    gate_feature!(self, rustc_attrs, segment.ident.span, msg);
-                }
-            }
-        }
-        for &(n, ty) in self.plugin_attributes {
-            if attr.path == n {
-                // Plugins can't gate attributes, so we don't check for it
-                // unlike the code above; we only use this loop to
-                // short-circuit to avoid the checks below.
-                debug!("check_attribute: {:?} is registered by a plugin, {:?}", attr.path, ty);
-                return;
-            }
-        }
-        if !is_macro && !attr::is_known(attr) {
-            // Only run the custom attribute lint during regular feature gate
-            // checking. Macro gating runs before the plugin attributes are
-            // registered, so we skip this in that case.
-            let msg = format!("the attribute `{}` is currently unknown to the compiler and \
-                               may have meaning added to it in the future", attr.path);
-            gate_feature!(self, custom_attribute, attr.span, &msg);
-        }
-    }
-}
-
-pub fn check_attribute(attr: &ast::Attribute, parse_sess: &ParseSess, features: &Features) {
-    let cx = Context { features, parse_sess, plugin_attributes: &[] };
-    cx.check_attribute(
-        attr,
-        attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name).map(|a| *a)),
-        true
-    );
-}
-
-fn find_lang_feature_issue(feature: Symbol) -> Option<u32> {
-    if let Some(info) = ACTIVE_FEATURES.iter().find(|t| t.0 == feature) {
-        let issue = info.2;
-        // FIXME (#28244): enforce that active features have issue numbers
-        // assert!(issue.is_some())
-        issue
-    } else {
-        // search in Accepted, Removed, or Stable Removed features
-        let found = ACCEPTED_FEATURES.iter().chain(REMOVED_FEATURES).chain(STABLE_REMOVED_FEATURES)
-            .find(|t| t.0 == feature);
-        match found {
-            Some(&(_, _, issue, _)) => issue,
-            None => panic!("Feature `{}` is not declared anywhere", feature),
-        }
-    }
-}
-
-pub enum GateIssue {
-    Language,
-    Library(Option<u32>)
-}
-
-#[derive(Debug, Copy, Clone, PartialEq)]
-pub enum GateStrength {
-    /// A hard error. (Most feature gates should use this.)
-    Hard,
-    /// Only a warning. (Use this only as backwards-compatibility demands.)
-    Soft,
-}
-
-pub fn emit_feature_err(
-    sess: &ParseSess,
-    feature: Symbol,
-    span: Span,
-    issue: GateIssue,
-    explain: &str,
-) {
-    feature_err(sess, feature, span, issue, explain).emit();
-}
-
-pub fn feature_err<'a, S: Into<MultiSpan>>(
-    sess: &'a ParseSess,
-    feature: Symbol,
-    span: S,
-    issue: GateIssue,
-    explain: &str,
-) -> DiagnosticBuilder<'a> {
-    leveled_feature_err(sess, feature, span, issue, explain, GateStrength::Hard)
-}
-
-fn leveled_feature_err<'a, S: Into<MultiSpan>>(
-    sess: &'a ParseSess,
-    feature: Symbol,
-    span: S,
-    issue: GateIssue,
-    explain: &str,
-    level: GateStrength,
-) -> DiagnosticBuilder<'a> {
-    let diag = &sess.span_diagnostic;
-
-    let issue = match issue {
-        GateIssue::Language => find_lang_feature_issue(feature),
-        GateIssue::Library(lib) => lib,
-    };
-
-    let mut err = match level {
-        GateStrength::Hard => {
-            diag.struct_span_err_with_code(span, explain, stringify_error_code!(E0658))
-        }
-        GateStrength::Soft => diag.struct_span_warn(span, explain),
-    };
-
-    match issue {
-        None | Some(0) => {}  // We still accept `0` as a stand-in for backwards compatibility
-        Some(n) => {
-            err.note(&format!(
-                "for more information, see https://github.com/rust-lang/rust/issues/{}",
-                n,
-            ));
-        }
-    }
-
-    // #23973: do not suggest `#![feature(...)]` if we are in beta/stable
-    if sess.unstable_features.is_nightly_build() {
-        err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feature));
-    }
-
-    // If we're on stable and only emitting a "soft" warning, add a note to
-    // clarify that the feature isn't "on" (rather than being on but
-    // warning-worthy).
-    if !sess.unstable_features.is_nightly_build() && level == GateStrength::Soft {
-        err.help("a nightly build of the compiler is required to enable this feature");
-    }
-
-    err
-
-}
-
-const EXPLAIN_BOX_SYNTAX: &str =
-    "box expression syntax is experimental; you can call `Box::new` instead";
-
-pub const EXPLAIN_STMT_ATTR_SYNTAX: &str =
-    "attributes on expressions are experimental";
-
-pub const EXPLAIN_ALLOW_INTERNAL_UNSTABLE: &str =
-    "allow_internal_unstable side-steps feature gating and stability checks";
-pub const EXPLAIN_ALLOW_INTERNAL_UNSAFE: &str =
-    "allow_internal_unsafe side-steps the unsafe_code lint";
-
-pub const EXPLAIN_UNSIZED_TUPLE_COERCION: &str =
-    "unsized tuple coercion is not stable enough for use and is subject to change";
-
-struct PostExpansionVisitor<'a> {
-    context: &'a Context<'a>,
-    builtin_attributes: &'static FxHashMap<Symbol, &'static BuiltinAttribute>,
-}
-
-macro_rules! gate_feature_post {
-    ($cx: expr, $feature: ident, $span: expr, $explain: expr) => {{
-        let (cx, span) = ($cx, $span);
-        if !span.allows_unstable(sym::$feature) {
-            gate_feature!(cx.context, $feature, span, $explain)
-        }
-    }};
-    ($cx: expr, $feature: ident, $span: expr, $explain: expr, $level: expr) => {{
-        let (cx, span) = ($cx, $span);
-        if !span.allows_unstable(sym::$feature) {
-            gate_feature!(cx.context, $feature, span, $explain, $level)
-        }
-    }}
-}
-
-impl<'a> PostExpansionVisitor<'a> {
-    fn check_abi(&self, abi: Abi, span: Span) {
-        match abi {
-            Abi::RustIntrinsic => {
-                gate_feature_post!(&self, intrinsics, span,
-                                   "intrinsics are subject to change");
-            },
-            Abi::PlatformIntrinsic => {
-                gate_feature_post!(&self, platform_intrinsics, span,
-                                   "platform intrinsics are experimental and possibly buggy");
-            },
-            Abi::Vectorcall => {
-                gate_feature_post!(&self, abi_vectorcall, span,
-                                   "vectorcall is experimental and subject to change");
-            },
-            Abi::Thiscall => {
-                gate_feature_post!(&self, abi_thiscall, span,
-                                   "thiscall is experimental and subject to change");
-            },
-            Abi::RustCall => {
-                gate_feature_post!(&self, unboxed_closures, span,
-                                   "rust-call ABI is subject to change");
-            },
-            Abi::PtxKernel => {
-                gate_feature_post!(&self, abi_ptx, span,
-                                   "PTX ABIs are experimental and subject to change");
-            },
-            Abi::Unadjusted => {
-                gate_feature_post!(&self, abi_unadjusted, span,
-                                   "unadjusted ABI is an implementation detail and perma-unstable");
-            },
-            Abi::Msp430Interrupt => {
-                gate_feature_post!(&self, abi_msp430_interrupt, span,
-                                   "msp430-interrupt ABI is experimental and subject to change");
-            },
-            Abi::X86Interrupt => {
-                gate_feature_post!(&self, abi_x86_interrupt, span,
-                                   "x86-interrupt ABI is experimental and subject to change");
-            },
-            Abi::AmdGpuKernel => {
-                gate_feature_post!(&self, abi_amdgpu_kernel, span,
-                                   "amdgpu-kernel ABI is experimental and subject to change");
-            },
-            // Stable
-            Abi::Cdecl |
-            Abi::Stdcall |
-            Abi::Fastcall |
-            Abi::Aapcs |
-            Abi::Win64 |
-            Abi::SysV64 |
-            Abi::Rust |
-            Abi::C |
-            Abi::System => {}
-        }
-    }
-}
-
-impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
-    fn visit_attribute(&mut self, attr: &ast::Attribute) {
-        let attr_info = attr.ident().and_then(|ident| {
-            self.builtin_attributes.get(&ident.name).map(|a| *a)
-        });
-
-        // Check for gated attributes.
-        self.context.check_attribute(attr, attr_info, false);
-
-        if attr.check_name(sym::doc) {
-            if let Some(content) = attr.meta_item_list() {
-                if content.len() == 1 && content[0].check_name(sym::cfg) {
-                    gate_feature_post!(&self, doc_cfg, attr.span,
-                        "`#[doc(cfg(...))]` is experimental"
-                    );
-                } else if content.iter().any(|c| c.check_name(sym::masked)) {
-                    gate_feature_post!(&self, doc_masked, attr.span,
-                        "`#[doc(masked)]` is experimental"
-                    );
-                } else if content.iter().any(|c| c.check_name(sym::spotlight)) {
-                    gate_feature_post!(&self, doc_spotlight, attr.span,
-                        "`#[doc(spotlight)]` is experimental"
-                    );
-                } else if content.iter().any(|c| c.check_name(sym::alias)) {
-                    gate_feature_post!(&self, doc_alias, attr.span,
-                        "`#[doc(alias = \"...\")]` is experimental"
-                    );
-                } else if content.iter().any(|c| c.check_name(sym::keyword)) {
-                    gate_feature_post!(&self, doc_keyword, attr.span,
-                        "`#[doc(keyword = \"...\")]` is experimental"
-                    );
-                }
-            }
-        }
-
-        match attr_info {
-            // `rustc_dummy` doesn't have any restrictions specific to built-in attributes.
-            Some(&(name, _, template, _)) if name != sym::rustc_dummy =>
-                check_builtin_attribute(self.context.parse_sess, attr, name, template),
-            _ => if let Some(TokenTree::Token(token)) = attr.tokens.trees().next() {
-                if token == token::Eq {
-                    // All key-value attributes are restricted to meta-item syntax.
-                    attr.parse_meta(self.context.parse_sess).map_err(|mut err| err.emit()).ok();
-                }
-            }
-        }
-    }
-
-    fn visit_name(&mut self, sp: Span, name: ast::Name) {
-        if !name.as_str().is_ascii() {
-            gate_feature_post!(
-                &self,
-                non_ascii_idents,
-                self.context.parse_sess.source_map().def_span(sp),
-                "non-ascii idents are not fully supported"
-            );
-        }
-    }
-
-    fn visit_item(&mut self, i: &'a ast::Item) {
-        match i.node {
-            ast::ItemKind::ForeignMod(ref foreign_module) => {
-                self.check_abi(foreign_module.abi, i.span);
-            }
-
-            ast::ItemKind::Fn(..) => {
-                if attr::contains_name(&i.attrs[..], sym::plugin_registrar) {
-                    gate_feature_post!(&self, plugin_registrar, i.span,
-                                       "compiler plugins are experimental and possibly buggy");
-                }
-                if attr::contains_name(&i.attrs[..], sym::start) {
-                    gate_feature_post!(&self, start, i.span,
-                                      "a `#[start]` function is an experimental \
-                                       feature whose signature may change \
-                                       over time");
-                }
-                if attr::contains_name(&i.attrs[..], sym::main) {
-                    gate_feature_post!(&self, main, i.span,
-                                       "declaration of a non-standard `#[main]` \
-                                        function may change over time, for now \
-                                        a top-level `fn main()` is required");
-                }
-            }
-
-            ast::ItemKind::Struct(..) => {
-                for attr in attr::filter_by_name(&i.attrs[..], sym::repr) {
-                    for item in attr.meta_item_list().unwrap_or_else(Vec::new) {
-                        if item.check_name(sym::simd) {
-                            gate_feature_post!(&self, repr_simd, attr.span,
-                                               "SIMD types are experimental and possibly buggy");
-                        }
-                    }
-                }
-            }
-
-            ast::ItemKind::Enum(ast::EnumDef{ref variants, ..}, ..) => {
-                for variant in variants {
-                    match (&variant.data, &variant.disr_expr) {
-                        (ast::VariantData::Unit(..), _) => {},
-                        (_, Some(disr_expr)) =>
-                            gate_feature_post!(
-                                &self,
-                                arbitrary_enum_discriminant,
-                                disr_expr.value.span,
-                                "discriminants on non-unit variants are experimental"),
-                        _ => {},
-                    }
-                }
-
-                let has_feature = self.context.features.arbitrary_enum_discriminant;
-                if !has_feature && !i.span.allows_unstable(sym::arbitrary_enum_discriminant) {
-                    Parser::maybe_report_invalid_custom_discriminants(
-                        self.context.parse_sess,
-                        &variants,
-                    );
-                }
-            }
-
-            ast::ItemKind::Impl(_, polarity, defaultness, _, _, _, _) => {
-                if polarity == ast::ImplPolarity::Negative {
-                    gate_feature_post!(&self, optin_builtin_traits,
-                                       i.span,
-                                       "negative trait bounds are not yet fully implemented; \
-                                        use marker types for now");
-                }
-
-                if let ast::Defaultness::Default = defaultness {
-                    gate_feature_post!(&self, specialization,
-                                       i.span,
-                                       "specialization is unstable");
-                }
-            }
-
-            ast::ItemKind::Trait(ast::IsAuto::Yes, ..) => {
-                gate_feature_post!(&self, optin_builtin_traits,
-                                   i.span,
-                                   "auto traits are experimental and possibly buggy");
-            }
-
-            ast::ItemKind::TraitAlias(..) => {
-                gate_feature_post!(
-                    &self,
-                    trait_alias,
-                    i.span,
-                    "trait aliases are experimental"
-                );
-            }
-
-            ast::ItemKind::MacroDef(ast::MacroDef { legacy: false, .. }) => {
-                let msg = "`macro` is experimental";
-                gate_feature_post!(&self, decl_macro, i.span, msg);
-            }
-
-            ast::ItemKind::OpaqueTy(..) => {
-                gate_feature_post!(
-                    &self,
-                    type_alias_impl_trait,
-                    i.span,
-                    "`impl Trait` in type aliases is unstable"
-                );
-            }
-
-            _ => {}
-        }
-
-        visit::walk_item(self, i);
-    }
-
-    fn visit_foreign_item(&mut self, i: &'a ast::ForeignItem) {
-        match i.node {
-            ast::ForeignItemKind::Fn(..) |
-            ast::ForeignItemKind::Static(..) => {
-                let link_name = attr::first_attr_value_str_by_name(&i.attrs, sym::link_name);
-                let links_to_llvm = match link_name {
-                    Some(val) => val.as_str().starts_with("llvm."),
-                    _ => false
-                };
-                if links_to_llvm {
-                    gate_feature_post!(&self, link_llvm_intrinsics, i.span,
-                                       "linking to LLVM intrinsics is experimental");
-                }
-            }
-            ast::ForeignItemKind::Ty => {
-                    gate_feature_post!(&self, extern_types, i.span,
-                                       "extern types are experimental");
-            }
-            ast::ForeignItemKind::Macro(..) => {}
-        }
-
-        visit::walk_foreign_item(self, i)
-    }
-
-    fn visit_ty(&mut self, ty: &'a ast::Ty) {
-        match ty.node {
-            ast::TyKind::BareFn(ref bare_fn_ty) => {
-                self.check_abi(bare_fn_ty.abi, ty.span);
-            }
-            ast::TyKind::Never => {
-                gate_feature_post!(&self, never_type, ty.span,
-                                   "The `!` type is experimental");
-            }
-            _ => {}
-        }
-        visit::walk_ty(self, ty)
-    }
-
-    fn visit_fn_ret_ty(&mut self, ret_ty: &'a ast::FunctionRetTy) {
-        if let ast::FunctionRetTy::Ty(ref output_ty) = *ret_ty {
-            if let ast::TyKind::Never = output_ty.node {
-                // Do nothing.
-            } else {
-                self.visit_ty(output_ty)
-            }
-        }
-    }
-
-    fn visit_expr(&mut self, e: &'a ast::Expr) {
-        match e.node {
-            ast::ExprKind::Box(_) => {
-                gate_feature_post!(&self, box_syntax, e.span, EXPLAIN_BOX_SYNTAX);
-            }
-            ast::ExprKind::Type(..) => {
-                // To avoid noise about type ascription in common syntax errors, only emit if it
-                // is the *only* error.
-                if self.context.parse_sess.span_diagnostic.err_count() == 0 {
-                    gate_feature_post!(&self, type_ascription, e.span,
-                                       "type ascription is experimental");
-                }
-            }
-            ast::ExprKind::TryBlock(_) => {
-                gate_feature_post!(&self, try_blocks, e.span, "`try` expression is experimental");
-            }
-            ast::ExprKind::Block(_, opt_label) => {
-                if let Some(label) = opt_label {
-                    gate_feature_post!(&self, label_break_value, label.ident.span,
-                                    "labels on blocks are unstable");
-                }
-            }
-            _ => {}
-        }
-        visit::walk_expr(self, e)
-    }
-
-    fn visit_arm(&mut self, arm: &'a ast::Arm) {
-        visit::walk_arm(self, arm)
-    }
-
-    fn visit_pat(&mut self, pattern: &'a ast::Pat) {
-        match &pattern.node {
-            PatKind::Slice(pats) => {
-                for pat in &*pats {
-                    let span = pat.span;
-                    let inner_pat = match &pat.node {
-                        PatKind::Ident(.., Some(pat)) => pat,
-                        _ => pat,
-                    };
-                    if inner_pat.is_rest() {
-                        gate_feature_post!(
-                            &self,
-                            slice_patterns,
-                            span,
-                            "subslice patterns are unstable"
-                        );
-                    }
-                }
-            }
-            PatKind::Box(..) => {
-                gate_feature_post!(&self, box_patterns,
-                                  pattern.span,
-                                  "box pattern syntax is experimental");
-            }
-            PatKind::Range(_, _, Spanned { node: RangeEnd::Excluded, .. }) => {
-                gate_feature_post!(&self, exclusive_range_pattern, pattern.span,
-                                   "exclusive range pattern syntax is experimental");
-            }
-            _ => {}
-        }
-        visit::walk_pat(self, pattern)
-    }
-
-    fn visit_fn(&mut self,
-                fn_kind: FnKind<'a>,
-                fn_decl: &'a ast::FnDecl,
-                span: Span,
-                _node_id: NodeId) {
-        if let Some(header) = fn_kind.header() {
-            // Stability of const fn methods are covered in
-            // `visit_trait_item` and `visit_impl_item` below; this is
-            // because default methods don't pass through this point.
-            self.check_abi(header.abi, span);
-        }
-
-        if fn_decl.c_variadic {
-            gate_feature_post!(&self, c_variadic, span, "C-variadic functions are unstable");
-        }
-
-        visit::walk_fn(self, fn_kind, fn_decl, span)
-    }
-
-    fn visit_generic_param(&mut self, param: &'a GenericParam) {
-        match param.kind {
-            GenericParamKind::Const { .. } =>
-                gate_feature_post!(&self, const_generics, param.ident.span,
-                    "const generics are unstable"),
-            _ => {}
-        }
-        visit::walk_generic_param(self, param)
-    }
-
-    fn visit_assoc_ty_constraint(&mut self, constraint: &'a AssocTyConstraint) {
-        match constraint.kind {
-            AssocTyConstraintKind::Bound { .. } =>
-                gate_feature_post!(&self, associated_type_bounds, constraint.span,
-                    "associated type bounds are unstable"),
-            _ => {}
-        }
-        visit::walk_assoc_ty_constraint(self, constraint)
-    }
-
-    fn visit_trait_item(&mut self, ti: &'a ast::TraitItem) {
-        match ti.node {
-            ast::TraitItemKind::Method(ref sig, ref block) => {
-                if block.is_none() {
-                    self.check_abi(sig.header.abi, ti.span);
-                }
-                if sig.decl.c_variadic {
-                    gate_feature_post!(&self, c_variadic, ti.span,
-                                       "C-variadic functions are unstable");
-                }
-                if sig.header.constness.node == ast::Constness::Const {
-                    gate_feature_post!(&self, const_fn, ti.span, "const fn is unstable");
-                }
-            }
-            ast::TraitItemKind::Type(_, ref default) => {
-                // We use three if statements instead of something like match guards so that all
-                // of these errors can be emitted if all cases apply.
-                if default.is_some() {
-                    gate_feature_post!(&self, associated_type_defaults, ti.span,
-                                       "associated type defaults are unstable");
-                }
-                if !ti.generics.params.is_empty() {
-                    gate_feature_post!(&self, generic_associated_types, ti.span,
-                                       "generic associated types are unstable");
-                }
-                if !ti.generics.where_clause.predicates.is_empty() {
-                    gate_feature_post!(&self, generic_associated_types, ti.span,
-                                       "where clauses on associated types are unstable");
-                }
-            }
-            _ => {}
-        }
-        visit::walk_trait_item(self, ti)
-    }
-
-    fn visit_impl_item(&mut self, ii: &'a ast::ImplItem) {
-        if ii.defaultness == ast::Defaultness::Default {
-            gate_feature_post!(&self, specialization,
-                              ii.span,
-                              "specialization is unstable");
-        }
-
-        match ii.node {
-            ast::ImplItemKind::Method(..) => {}
-            ast::ImplItemKind::OpaqueTy(..) => {
-                gate_feature_post!(
-                    &self,
-                    type_alias_impl_trait,
-                    ii.span,
-                    "`impl Trait` in type aliases is unstable"
-                );
-            }
-            ast::ImplItemKind::TyAlias(_) => {
-                if !ii.generics.params.is_empty() {
-                    gate_feature_post!(&self, generic_associated_types, ii.span,
-                                       "generic associated types are unstable");
-                }
-                if !ii.generics.where_clause.predicates.is_empty() {
-                    gate_feature_post!(&self, generic_associated_types, ii.span,
-                                       "where clauses on associated types are unstable");
-                }
-            }
-            _ => {}
-        }
-        visit::walk_impl_item(self, ii)
-    }
-
-    fn visit_vis(&mut self, vis: &'a ast::Visibility) {
-        if let ast::VisibilityKind::Crate(ast::CrateSugar::JustCrate) = vis.node {
-            gate_feature_post!(&self, crate_visibility_modifier, vis.span,
-                               "`crate` visibility modifier is experimental");
-        }
-        visit::walk_vis(self, vis)
-    }
-}
-
-pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute],
-                    crate_edition: Edition, allow_features: &Option<Vec<String>>) -> Features {
-    fn feature_removed(span_handler: &Handler, span: Span, reason: Option<&str>) {
-        let mut err = struct_span_err!(span_handler, span, E0557, "feature has been removed");
-        if let Some(reason) = reason {
-            err.span_note(span, reason);
-        } else {
-            err.span_label(span, "feature has been removed");
-        }
-        err.emit();
-    }
-
-    let mut features = Features::new();
-    let mut edition_enabled_features = FxHashMap::default();
-
-    for &edition in ALL_EDITIONS {
-        if edition <= crate_edition {
-            // The `crate_edition` implies its respective umbrella feature-gate
-            // (i.e., `#![feature(rust_20XX_preview)]` isn't needed on edition 20XX).
-            edition_enabled_features.insert(edition.feature_name(), edition);
-        }
-    }
-
-    for &(name, .., f_edition, set) in ACTIVE_FEATURES {
-        if let Some(f_edition) = f_edition {
-            if f_edition <= crate_edition {
-                set(&mut features, DUMMY_SP);
-                edition_enabled_features.insert(name, crate_edition);
-            }
-        }
-    }
-
-    // Process the edition umbrella feature-gates first, to ensure
-    // `edition_enabled_features` is completed before it's queried.
-    for attr in krate_attrs {
-        if !attr.check_name(sym::feature) {
-            continue
-        }
-
-        let list = match attr.meta_item_list() {
-            Some(list) => list,
-            None => continue,
-        };
-
-        for mi in list {
-            if !mi.is_word() {
-                continue;
-            }
-
-            let name = mi.name_or_empty();
-
-            if let Some(edition) = ALL_EDITIONS.iter().find(|e| name == e.feature_name()) {
-                if *edition <= crate_edition {
-                    continue;
-                }
-
-                for &(name, .., f_edition, set) in ACTIVE_FEATURES {
-                    if let Some(f_edition) = f_edition {
-                        if f_edition <= *edition {
-                            // FIXME(Manishearth) there is currently no way to set
-                            // lib features by edition
-                            set(&mut features, DUMMY_SP);
-                            edition_enabled_features.insert(name, *edition);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    for attr in krate_attrs {
-        if !attr.check_name(sym::feature) {
-            continue
-        }
-
-        let list = match attr.meta_item_list() {
-            Some(list) => list,
-            None => continue,
-        };
-
-        let bad_input = |span| {
-            struct_span_err!(span_handler, span, E0556, "malformed `feature` attribute input")
-        };
-
-        for mi in list {
-            let name = match mi.ident() {
-                Some(ident) if mi.is_word() => ident.name,
-                Some(ident) => {
-                    bad_input(mi.span()).span_suggestion(
-                        mi.span(),
-                        "expected just one word",
-                        format!("{}", ident.name),
-                        Applicability::MaybeIncorrect,
-                    ).emit();
-                    continue
-                }
-                None => {
-                    bad_input(mi.span()).span_label(mi.span(), "expected just one word").emit();
-                    continue
-                }
-            };
-
-            if let Some(edition) = edition_enabled_features.get(&name) {
-                struct_span_warn!(
-                    span_handler,
-                    mi.span(),
-                    E0705,
-                    "the feature `{}` is included in the Rust {} edition",
-                    name,
-                    edition,
-                ).emit();
-                continue;
-            }
-
-            if ALL_EDITIONS.iter().any(|e| name == e.feature_name()) {
-                // Handled in the separate loop above.
-                continue;
-            }
-
-            let removed = REMOVED_FEATURES.iter().find(|f| name == f.0);
-            let stable_removed = STABLE_REMOVED_FEATURES.iter().find(|f| name == f.0);
-            if let Some((.., reason)) = removed.or(stable_removed) {
-                feature_removed(span_handler, mi.span(), *reason);
-                continue;
-            }
-
-            if let Some((_, since, ..)) = ACCEPTED_FEATURES.iter().find(|f| name == f.0) {
-                let since = Some(Symbol::intern(since));
-                features.declared_lang_features.push((name, mi.span(), since));
-                continue;
-            }
-
-            if let Some(allowed) = allow_features.as_ref() {
-                if allowed.iter().find(|f| *f == name.as_str()).is_none() {
-                    span_err!(span_handler, mi.span(), E0725,
-                              "the feature `{}` is not in the list of allowed features",
-                              name);
-                    continue;
-                }
-            }
-
-            if let Some((.., set)) = ACTIVE_FEATURES.iter().find(|f| name == f.0) {
-                set(&mut features, mi.span());
-                features.declared_lang_features.push((name, mi.span(), None));
-                continue;
-            }
-
-            features.declared_lib_features.push((name, mi.span()));
-        }
-    }
-
-    features
-}
-
-pub fn check_crate(krate: &ast::Crate,
-                   sess: &ParseSess,
-                   features: &Features,
-                   plugin_attributes: &[(Symbol, AttributeType)],
-                   unstable: UnstableFeatures) {
-    maybe_stage_features(&sess.span_diagnostic, krate, unstable);
-    let ctx = Context {
-        features,
-        parse_sess: sess,
-        plugin_attributes,
-    };
-
-    macro_rules! gate_all {
-        ($spans:ident, $gate:ident, $msg:literal) => {
-            for span in &*sess.$spans.borrow() { gate_feature!(&ctx, $gate, *span, $msg); }
-        }
-    }
-
-    gate_all!(param_attr_spans, param_attrs, "attributes on function parameters are unstable");
-    gate_all!(let_chains_spans, let_chains, "`let` expressions in this position are experimental");
-    gate_all!(async_closure_spans, async_closure, "async closures are unstable");
-    gate_all!(yield_spans, generators, "yield syntax is experimental");
-    gate_all!(or_pattern_spans, or_patterns, "or-patterns syntax is experimental");
-
-    let visitor = &mut PostExpansionVisitor {
-        context: &ctx,
-        builtin_attributes: &*BUILTIN_ATTRIBUTE_MAP,
-    };
-    visit::walk_crate(visitor, krate);
-}
-
-#[derive(Clone, Copy, Hash)]
-pub enum UnstableFeatures {
-    /// Hard errors for unstable features are active, as on beta/stable channels.
-    Disallow,
-    /// Allow features to be activated, as on nightly.
-    Allow,
-    /// Errors are bypassed for bootstrapping. This is required any time
-    /// during the build that feature-related lints are set to warn or above
-    /// because the build turns on warnings-as-errors and uses lots of unstable
-    /// features. As a result, this is always required for building Rust itself.
-    Cheat
-}
-
-impl UnstableFeatures {
-    pub fn from_environment() -> UnstableFeatures {
-        // Whether this is a feature-staged build, i.e., on the beta or stable channel
-        let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
-        // Whether we should enable unstable features for bootstrapping
-        let bootstrap = env::var("RUSTC_BOOTSTRAP").is_ok();
-        match (disable_unstable_features, bootstrap) {
-            (_, true) => UnstableFeatures::Cheat,
-            (true, _) => UnstableFeatures::Disallow,
-            (false, _) => UnstableFeatures::Allow
-        }
-    }
-
-    pub fn is_nightly_build(&self) -> bool {
-        match *self {
-            UnstableFeatures::Allow | UnstableFeatures::Cheat => true,
-            _ => false,
-        }
-    }
-}
-
-fn maybe_stage_features(span_handler: &Handler, krate: &ast::Crate,
-                        unstable: UnstableFeatures) {
-    let allow_features = match unstable {
-        UnstableFeatures::Allow => true,
-        UnstableFeatures::Disallow => false,
-        UnstableFeatures::Cheat => true
-    };
-    if !allow_features {
-        for attr in &krate.attrs {
-            if attr.check_name(sym::feature) {
-                let release_channel = option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)");
-                span_err!(span_handler, attr.span, E0554,
-                          "`#![feature]` may not be used on the {} release channel",
-                          release_channel);
-            }
-        }
-    }
-}
diff --git a/src/libsyntax/feature_gate/accepted.rs b/src/libsyntax/feature_gate/accepted.rs
new file mode 100644
index 00000000000..6c0b271c6c5
--- /dev/null
+++ b/src/libsyntax/feature_gate/accepted.rs
@@ -0,0 +1,248 @@
+//! List of the accepted feature gates.
+
+use crate::symbol::sym;
+use super::{State, Feature};
+
+macro_rules! declare_features {
+    ($(
+        $(#[doc = $doc:tt])* (accepted, $feature:ident, $ver:expr, $issue:expr, None),
+    )+) => {
+        /// Those language feature has since been Accepted (it was once Active)
+        pub const ACCEPTED_FEATURES: &[Feature] = &[
+            $(
+                Feature {
+                    state: State::Accepted,
+                    name: sym::$feature,
+                    since: $ver,
+                    issue: $issue,
+                    edition: None,
+                    description: concat!($($doc,)*),
+                }
+            ),+
+        ];
+    }
+}
+
+declare_features! (
+    // -------------------------------------------------------------------------
+    // feature-group-start: for testing purposes
+    // -------------------------------------------------------------------------
+
+    /// A temporary feature gate used to enable parser extensions needed
+    /// to bootstrap fix for #5723.
+    (accepted, issue_5723_bootstrap, "1.0.0", None, None),
+    /// These are used to test this portion of the compiler,
+    /// they don't actually mean anything.
+    (accepted, test_accepted_feature, "1.0.0", None, None),
+
+    // -------------------------------------------------------------------------
+    // feature-group-end: for testing purposes
+    // -------------------------------------------------------------------------
+
+    // -------------------------------------------------------------------------
+    // feature-group-start: accepted features
+    // -------------------------------------------------------------------------
+
+    /// Allows using associated `type`s in `trait`s.
+    (accepted, associated_types, "1.0.0", None, None),
+    /// Allows using assigning a default type to type parameters in algebraic data type definitions.
+    (accepted, default_type_params, "1.0.0", None, None),
+    // FIXME: explain `globs`.
+    (accepted, globs, "1.0.0", None, None),
+    /// Allows `macro_rules!` items.
+    (accepted, macro_rules, "1.0.0", None, None),
+    /// Allows use of `&foo[a..b]` as a slicing syntax.
+    (accepted, slicing_syntax, "1.0.0", None, None),
+    /// Allows struct variants `Foo { baz: u8, .. }` in enums (RFC 418).
+    (accepted, struct_variant, "1.0.0", None, None),
+    /// Allows indexing tuples.
+    (accepted, tuple_indexing, "1.0.0", None, None),
+    /// Allows the use of `if let` expressions.
+    (accepted, if_let, "1.0.0", None, None),
+    /// Allows the use of `while let` expressions.
+    (accepted, while_let, "1.0.0", None, None),
+    /// Allows using `#![no_std]`.
+    (accepted, no_std, "1.6.0", None, None),
+    /// Allows overloading augmented assignment operations like `a += b`.
+    (accepted, augmented_assignments, "1.8.0", Some(28235), None),
+    /// Allows empty structs and enum variants with braces.
+    (accepted, braced_empty_structs, "1.8.0", Some(29720), None),
+    /// Allows `#[deprecated]` attribute.
+    (accepted, deprecated, "1.9.0", Some(29935), None),
+    /// Allows macros to appear in the type position.
+    (accepted, type_macros, "1.13.0", Some(27245), None),
+    /// Allows use of the postfix `?` operator in expressions.
+    (accepted, question_mark, "1.13.0", Some(31436), None),
+    /// Allows `..` in tuple (struct) patterns.
+    (accepted, dotdot_in_tuple_patterns, "1.14.0", Some(33627), None),
+    /// Allows some increased flexibility in the name resolution rules,
+    /// especially around globs and shadowing (RFC 1560).
+    (accepted, item_like_imports, "1.15.0", Some(35120), None),
+    /// Allows using `Self` and associated types in struct expressions and patterns.
+    (accepted, more_struct_aliases, "1.16.0", Some(37544), None),
+    /// Allows elision of `'static` lifetimes in `static`s and `const`s.
+    (accepted, static_in_const, "1.17.0", Some(35897), None),
+    /// Allows field shorthands (`x` meaning `x: x`) in struct literal expressions.
+    (accepted, field_init_shorthand, "1.17.0", Some(37340), None),
+    /// Allows the definition recursive static items.
+    (accepted, static_recursion, "1.17.0", Some(29719), None),
+    /// Allows `pub(restricted)` visibilities (RFC 1422).
+    (accepted, pub_restricted, "1.18.0", Some(32409), None),
+    /// Allows `#![windows_subsystem]`.
+    (accepted, windows_subsystem, "1.18.0", Some(37499), None),
+    /// Allows `break {expr}` with a value inside `loop`s.
+    (accepted, loop_break_value, "1.19.0", Some(37339), None),
+    /// Allows numeric fields in struct expressions and patterns.
+    (accepted, relaxed_adts, "1.19.0", Some(35626), None),
+    /// Allows coercing non capturing closures to function pointers.
+    (accepted, closure_to_fn_coercion, "1.19.0", Some(39817), None),
+    /// Allows attributes on struct literal fields.
+    (accepted, struct_field_attributes, "1.20.0", Some(38814), None),
+    /// Allows the definition of associated constants in `trait` or `impl` blocks.
+    (accepted, associated_consts, "1.20.0", Some(29646), None),
+    /// Allows usage of the `compile_error!` macro.
+    (accepted, compile_error, "1.20.0", Some(40872), None),
+    /// Allows code like `let x: &'static u32 = &42` to work (RFC 1414).
+    (accepted, rvalue_static_promotion, "1.21.0", Some(38865), None),
+    /// Allows `Drop` types in constants (RFC 1440).
+    (accepted, drop_types_in_const, "1.22.0", Some(33156), None),
+    /// Allows the sysV64 ABI to be specified on all platforms
+    /// instead of just the platforms on which it is the C ABI.
+    (accepted, abi_sysv64, "1.24.0", Some(36167), None),
+    /// Allows `repr(align(16))` struct attribute (RFC 1358).
+    (accepted, repr_align, "1.25.0", Some(33626), None),
+    /// Allows '|' at beginning of match arms (RFC 1925).
+    (accepted, match_beginning_vert, "1.25.0", Some(44101), None),
+    /// Allows nested groups in `use` items (RFC 2128).
+    (accepted, use_nested_groups, "1.25.0", Some(44494), None),
+    /// Allows indexing into constant arrays.
+    (accepted, const_indexing, "1.26.0", Some(29947), None),
+    /// Allows using `a..=b` and `..=b` as inclusive range syntaxes.
+    (accepted, inclusive_range_syntax, "1.26.0", Some(28237), None),
+    /// Allows `..=` in patterns (RFC 1192).
+    (accepted, dotdoteq_in_patterns, "1.26.0", Some(28237), None),
+    /// Allows `fn main()` with return types which implements `Termination` (RFC 1937).
+    (accepted, termination_trait, "1.26.0", Some(43301), None),
+    /// Allows implementing `Clone` for closures where possible (RFC 2132).
+    (accepted, clone_closures, "1.26.0", Some(44490), None),
+    /// Allows implementing `Copy` for closures where possible (RFC 2132).
+    (accepted, copy_closures, "1.26.0", Some(44490), None),
+    /// Allows `impl Trait` in function arguments.
+    (accepted, universal_impl_trait, "1.26.0", Some(34511), None),
+    /// Allows `impl Trait` in function return types.
+    (accepted, conservative_impl_trait, "1.26.0", Some(34511), None),
+    /// Allows using the `u128` and `i128` types.
+    (accepted, i128_type, "1.26.0", Some(35118), None),
+    /// Allows default match binding modes (RFC 2005).
+    (accepted, match_default_bindings, "1.26.0", Some(42640), None),
+    /// Allows `'_` placeholder lifetimes.
+    (accepted, underscore_lifetimes, "1.26.0", Some(44524), None),
+    /// Allows attributes on lifetime/type formal parameters in generics (RFC 1327).
+    (accepted, generic_param_attrs, "1.27.0", Some(48848), None),
+    /// Allows `cfg(target_feature = "...")`.
+    (accepted, cfg_target_feature, "1.27.0", Some(29717), None),
+    /// Allows `#[target_feature(...)]`.
+    (accepted, target_feature, "1.27.0", None, None),
+    /// Allows using `dyn Trait` as a syntax for trait objects.
+    (accepted, dyn_trait, "1.27.0", Some(44662), None),
+    /// Allows `#[must_use]` on functions, and introduces must-use operators (RFC 1940).
+    (accepted, fn_must_use, "1.27.0", Some(43302), None),
+    /// Allows use of the `:lifetime` macro fragment specifier.
+    (accepted, macro_lifetime_matcher, "1.27.0", Some(34303), None),
+    /// Allows `#[test]` functions where the return type implements `Termination` (RFC 1937).
+    (accepted, termination_trait_test, "1.27.0", Some(48854), None),
+    /// Allows the `#[global_allocator]` attribute.
+    (accepted, global_allocator, "1.28.0", Some(27389), None),
+    /// Allows `#[repr(transparent)]` attribute on newtype structs.
+    (accepted, repr_transparent, "1.28.0", Some(43036), None),
+    /// Allows procedural macros in `proc-macro` crates.
+    (accepted, proc_macro, "1.29.0", Some(38356), None),
+    /// Allows `foo.rs` as an alternative to `foo/mod.rs`.
+    (accepted, non_modrs_mods, "1.30.0", Some(44660), None),
+    /// Allows use of the `:vis` macro fragment specifier
+    (accepted, macro_vis_matcher, "1.30.0", Some(41022), None),
+    /// Allows importing and reexporting macros with `use`,
+    /// enables macro modularization in general.
+    (accepted, use_extern_macros, "1.30.0", Some(35896), None),
+    /// Allows keywords to be escaped for use as identifiers.
+    (accepted, raw_identifiers, "1.30.0", Some(48589), None),
+    /// Allows attributes scoped to tools.
+    (accepted, tool_attributes, "1.30.0", Some(44690), None),
+    /// Allows multi-segment paths in attributes and derives.
+    (accepted, proc_macro_path_invoc, "1.30.0", Some(38356), None),
+    /// Allows all literals in attribute lists and values of key-value pairs.
+    (accepted, attr_literals, "1.30.0", Some(34981), None),
+    /// Allows inferring outlives requirements (RFC 2093).
+    (accepted, infer_outlives_requirements, "1.30.0", Some(44493), None),
+    /// Allows annotating functions conforming to `fn(&PanicInfo) -> !` with `#[panic_handler]`.
+    /// This defines the behavior of panics.
+    (accepted, panic_handler, "1.30.0", Some(44489), None),
+    /// Allows `#[used]` to preserve symbols (see llvm.used).
+    (accepted, used, "1.30.0", Some(40289), None),
+    /// Allows `crate` in paths.
+    (accepted, crate_in_paths, "1.30.0", Some(45477), None),
+    /// Allows resolving absolute paths as paths from other crates.
+    (accepted, extern_absolute_paths, "1.30.0", Some(44660), None),
+    /// Allows access to crate names passed via `--extern` through prelude.
+    (accepted, extern_prelude, "1.30.0", Some(44660), None),
+    /// Allows parentheses in patterns.
+    (accepted, pattern_parentheses, "1.31.0", Some(51087), None),
+    /// Allows the definition of `const fn` functions.
+    (accepted, min_const_fn, "1.31.0", Some(53555), None),
+    /// Allows scoped lints.
+    (accepted, tool_lints, "1.31.0", Some(44690), None),
+    /// Allows lifetime elision in `impl` headers. For example:
+    /// + `impl<I:Iterator> Iterator for &mut Iterator`
+    /// + `impl Debug for Foo<'_>`
+    (accepted, impl_header_lifetime_elision, "1.31.0", Some(15872), None),
+    /// Allows `extern crate foo as bar;`. This puts `bar` into extern prelude.
+    (accepted, extern_crate_item_prelude, "1.31.0", Some(55599), None),
+    /// Allows use of the `:literal` macro fragment specifier (RFC 1576).
+    (accepted, macro_literal_matcher, "1.32.0", Some(35625), None),
+    /// Allows use of `?` as the Kleene "at most one" operator in macros.
+    (accepted, macro_at_most_once_rep, "1.32.0", Some(48075), None),
+    /// Allows `Self` struct constructor (RFC 2302).
+    (accepted, self_struct_ctor, "1.32.0", Some(51994), None),
+    /// Allows `Self` in type definitions (RFC 2300).
+    (accepted, self_in_typedefs, "1.32.0", Some(49303), None),
+    /// Allows `use x::y;` to search `x` in the current scope.
+    (accepted, uniform_paths, "1.32.0", Some(53130), None),
+    /// Allows integer match exhaustiveness checking (RFC 2591).
+    (accepted, exhaustive_integer_patterns, "1.33.0", Some(50907), None),
+    /// Allows `use path as _;` and `extern crate c as _;`.
+    (accepted, underscore_imports, "1.33.0", Some(48216), None),
+    /// Allows `#[repr(packed(N))]` attribute on structs.
+    (accepted, repr_packed, "1.33.0", Some(33158), None),
+    /// Allows irrefutable patterns in `if let` and `while let` statements (RFC 2086).
+    (accepted, irrefutable_let_patterns, "1.33.0", Some(44495), None),
+    /// Allows calling `const unsafe fn` inside `unsafe` blocks in `const fn` functions.
+    (accepted, min_const_unsafe_fn, "1.33.0", Some(55607), None),
+    /// Allows let bindings, assignments and destructuring in `const` functions and constants.
+    /// As long as control flow is not implemented in const eval, `&&` and `||` may not be used
+    /// at the same time as let bindings.
+    (accepted, const_let, "1.33.0", Some(48821), None),
+    /// Allows `#[cfg_attr(predicate, multiple, attributes, here)]`.
+    (accepted, cfg_attr_multi, "1.33.0", Some(54881), None),
+    /// Allows top level or-patterns (`p | q`) in `if let` and `while let`.
+    (accepted, if_while_or_patterns, "1.33.0", Some(48215), None),
+    /// Allows `cfg(target_vendor = "...")`.
+    (accepted, cfg_target_vendor, "1.33.0", Some(29718), None),
+    /// Allows `extern crate self as foo;`.
+    /// This puts local crate root into extern prelude under name `foo`.
+    (accepted, extern_crate_self, "1.34.0", Some(56409), None),
+    /// Allows arbitrary delimited token streams in non-macro attributes.
+    (accepted, unrestricted_attribute_tokens, "1.34.0", Some(55208), None),
+    /// Allows paths to enum variants on type aliases including `Self`.
+    (accepted, type_alias_enum_variants, "1.37.0", Some(49683), None),
+    /// Allows using `#[repr(align(X))]` on enums with equivalent semantics
+    /// to wrapping an enum in a wrapper struct with `#[repr(align(X))]`.
+    (accepted, repr_align_enum, "1.37.0", Some(57996), None),
+    /// Allows `const _: TYPE = VALUE`.
+    (accepted, underscore_const_names, "1.37.0", Some(54912), None),
+    /// Allows free and inherent `async fn`s, `async` blocks, and `<expr>.await` expressions.
+    (accepted, async_await, "1.39.0", Some(50547), None),
+
+    // -------------------------------------------------------------------------
+    // feature-group-end: accepted features
+    // -------------------------------------------------------------------------
+);
diff --git a/src/libsyntax/feature_gate/active.rs b/src/libsyntax/feature_gate/active.rs
new file mode 100644
index 00000000000..c947b09fdcb
--- /dev/null
+++ b/src/libsyntax/feature_gate/active.rs
@@ -0,0 +1,549 @@
+//! List of the active feature gates.
+
+use crate::edition::Edition;
+use crate::symbol::{Symbol, sym};
+use syntax_pos::Span;
+use super::{State, Feature};
+
+macro_rules! set {
+    ($field: ident) => {{
+        fn f(features: &mut Features, _: Span) {
+            features.$field = true;
+        }
+        f as fn(&mut Features, Span)
+    }}
+}
+
+macro_rules! declare_features {
+    ($(
+        $(#[doc = $doc:tt])* (active, $feature:ident, $ver:expr, $issue:expr, $edition:expr),
+    )+) => {
+        /// Represents active features that are currently being implemented or
+        /// currently being considered for addition/removal.
+        pub const ACTIVE_FEATURES:
+            &[Feature] =
+            &[$(
+                // (sym::$feature, $ver, $issue, $edition, set!($feature))
+                Feature {
+                    state: State::Active { set: set!($feature) },
+                    name: sym::$feature,
+                    since: $ver,
+                    issue: $issue,
+                    edition: $edition,
+                    description: concat!($($doc,)*),
+                }
+            ),+];
+
+        /// A set of features to be used by later passes.
+        #[derive(Clone)]
+        pub struct Features {
+            /// `#![feature]` attrs for language features, for error reporting
+            pub declared_lang_features: Vec<(Symbol, Span, Option<Symbol>)>,
+            /// `#![feature]` attrs for non-language (library) features
+            pub declared_lib_features: Vec<(Symbol, Span)>,
+            $(
+                $(#[doc = $doc])*
+                pub $feature: bool
+            ),+
+        }
+
+        impl Features {
+            pub fn new() -> Features {
+                Features {
+                    declared_lang_features: Vec::new(),
+                    declared_lib_features: Vec::new(),
+                    $($feature: false),+
+                }
+            }
+
+            pub fn walk_feature_fields<F>(&self, mut f: F)
+                where F: FnMut(&str, bool)
+            {
+                $(f(stringify!($feature), self.$feature);)+
+            }
+        }
+    };
+}
+
+impl Feature {
+    /// Set this feature in `Features`. Panics if called on a non-active feature.
+    pub fn set(&self, features: &mut Features, span: Span) {
+        match self.state {
+            State::Active { set } => set(features, span),
+            _ => panic!("Called `set` on feature `{}` which is not `active`", self.name)
+        }
+    }
+}
+
+// If you change this, please modify `src/doc/unstable-book` as well.
+//
+// Don't ever remove anything from this list; move them to `removed.rs`.
+//
+// The version numbers here correspond to the version in which the current status
+// was set. This is most important for knowing when a particular feature became
+// stable (active).
+//
+// Note that the features are grouped into internal/user-facing and then
+// sorted by version inside those groups. This is enforced with tidy.
+//
+// N.B., `tools/tidy/src/features.rs` parses this information directly out of the
+// source, so take care when modifying it.
+
+declare_features! (
+    // -------------------------------------------------------------------------
+    // feature-group-start: internal feature gates
+    // -------------------------------------------------------------------------
+
+    // no-tracking-issue-start
+
+    /// Allows using compiler's own crates.
+    (active, rustc_private, "1.0.0", Some(27812), None),
+
+    /// Allows using the `rust-intrinsic`'s "ABI".
+    (active, intrinsics, "1.0.0", None, None),
+
+    /// Allows using `#[lang = ".."]` attribute for linking items to special compiler logic.
+    (active, lang_items, "1.0.0", None, None),
+
+    /// Allows using the `#[stable]` and `#[unstable]` attributes.
+    (active, staged_api, "1.0.0", None, None),
+
+    /// Allows using `#[allow_internal_unstable]`. This is an
+    /// attribute on `macro_rules!` and can't use the attribute handling
+    /// below (it has to be checked before expansion possibly makes
+    /// macros disappear).
+    (active, allow_internal_unstable, "1.0.0", None, None),
+
+    /// Allows using `#[allow_internal_unsafe]`. This is an
+    /// attribute on `macro_rules!` and can't use the attribute handling
+    /// below (it has to be checked before expansion possibly makes
+    /// macros disappear).
+    (active, allow_internal_unsafe, "1.0.0", None, None),
+
+    /// Allows using the macros:
+    /// + `__diagnostic_used`
+    /// + `__register_diagnostic`
+    /// +`__build_diagnostic_array`
+    (active, rustc_diagnostic_macros, "1.0.0", None, None),
+
+    /// Allows using `#[rustc_const_unstable(feature = "foo", ..)]` which
+    /// lets a function to be `const` when opted into with `#![feature(foo)]`.
+    (active, rustc_const_unstable, "1.0.0", None, None),
+
+    /// no-tracking-issue-end
+
+    /// Allows using `#[link_name="llvm.*"]`.
+    (active, link_llvm_intrinsics, "1.0.0", Some(29602), None),
+
+    /// Allows using `rustc_*` attributes (RFC 572).
+    (active, rustc_attrs, "1.0.0", Some(29642), None),
+
+    /// Allows using `#[on_unimplemented(..)]` on traits.
+    (active, on_unimplemented, "1.0.0", Some(29628), None),
+
+    /// Allows using the `box $expr` syntax.
+    (active, box_syntax, "1.0.0", Some(49733), None),
+
+    /// Allows using `#[main]` to replace the entrypoint `#[lang = "start"]` calls.
+    (active, main, "1.0.0", Some(29634), None),
+
+    /// Allows using `#[start]` on a function indicating that it is the program entrypoint.
+    (active, start, "1.0.0", Some(29633), None),
+
+    /// Allows using the `#[fundamental]` attribute.
+    (active, fundamental, "1.0.0", Some(29635), None),
+
+    /// Allows using the `rust-call` ABI.
+    (active, unboxed_closures, "1.0.0", Some(29625), None),
+
+    /// Allows using the `#[linkage = ".."]` attribute.
+    (active, linkage, "1.0.0", Some(29603), None),
+
+    /// Allows features specific to OIBIT (auto traits).
+    (active, optin_builtin_traits, "1.0.0", Some(13231), None),
+
+    /// Allows using `box` in patterns (RFC 469).
+    (active, box_patterns, "1.0.0", Some(29641), None),
+
+    // no-tracking-issue-start
+
+    /// Allows using `#[prelude_import]` on glob `use` items.
+    (active, prelude_import, "1.2.0", None, None),
+
+    // no-tracking-issue-end
+
+    // no-tracking-issue-start
+
+    /// Allows using `#[omit_gdb_pretty_printer_section]`.
+    (active, omit_gdb_pretty_printer_section, "1.5.0", None, None),
+
+    /// Allows using the `vectorcall` ABI.
+    (active, abi_vectorcall, "1.7.0", None, None),
+
+    // no-tracking-issue-end
+
+    /// Allows using `#[structural_match]` which indicates that a type is structurally matchable.
+    (active, structural_match, "1.8.0", Some(31434), None),
+
+    /// Allows using the `may_dangle` attribute (RFC 1327).
+    (active, dropck_eyepatch, "1.10.0", Some(34761), None),
+
+    /// Allows using the `#![panic_runtime]` attribute.
+    (active, panic_runtime, "1.10.0", Some(32837), None),
+
+    /// Allows declaring with `#![needs_panic_runtime]` that a panic runtime is needed.
+    (active, needs_panic_runtime, "1.10.0", Some(32837), None),
+
+    // no-tracking-issue-start
+
+    /// Allows identifying the `compiler_builtins` crate.
+    (active, compiler_builtins, "1.13.0", None, None),
+
+    /// Allows using the `unadjusted` ABI; perma-unstable.
+    (active, abi_unadjusted, "1.16.0", None, None),
+
+    /// Allows identifying crates that contain sanitizer runtimes.
+    (active, sanitizer_runtime, "1.17.0", None, None),
+
+    /// Used to identify crates that contain the profiler runtime.
+    (active, profiler_runtime, "1.18.0", None, None),
+
+    /// Allows using the `thiscall` ABI.
+    (active, abi_thiscall, "1.19.0", None, None),
+
+    /// Allows using `#![needs_allocator]`, an implementation detail of `#[global_allocator]`.
+    (active, allocator_internals, "1.20.0", None, None),
+
+    // no-tracking-issue-end
+
+    /// Added for testing E0705; perma-unstable.
+    (active, test_2018_feature, "1.31.0", Some(0), Some(Edition::Edition2018)),
+
+    // -------------------------------------------------------------------------
+    // feature-group-end: internal feature gates
+    // -------------------------------------------------------------------------
+
+    // -------------------------------------------------------------------------
+    // feature-group-start: actual feature gates (target features)
+    // -------------------------------------------------------------------------
+
+    // FIXME: Document these and merge with the list below.
+
+    // Unstable `#[target_feature]` directives.
+    (active, arm_target_feature, "1.27.0", Some(44839), None),
+    (active, aarch64_target_feature, "1.27.0", Some(44839), None),
+    (active, hexagon_target_feature, "1.27.0", Some(44839), None),
+    (active, powerpc_target_feature, "1.27.0", Some(44839), None),
+    (active, mips_target_feature, "1.27.0", Some(44839), None),
+    (active, avx512_target_feature, "1.27.0", Some(44839), None),
+    (active, mmx_target_feature, "1.27.0", Some(44839), None),
+    (active, sse4a_target_feature, "1.27.0", Some(44839), None),
+    (active, tbm_target_feature, "1.27.0", Some(44839), None),
+    (active, wasm_target_feature, "1.30.0", Some(44839), None),
+    (active, adx_target_feature, "1.32.0", Some(44839), None),
+    (active, cmpxchg16b_target_feature, "1.32.0", Some(44839), None),
+    (active, movbe_target_feature, "1.34.0", Some(44839), None),
+    (active, rtm_target_feature, "1.35.0", Some(44839), None),
+    (active, f16c_target_feature, "1.36.0", Some(44839), None),
+
+    // -------------------------------------------------------------------------
+    // feature-group-end: actual feature gates (target features)
+    // -------------------------------------------------------------------------
+
+    // -------------------------------------------------------------------------
+    // feature-group-start: actual feature gates
+    // -------------------------------------------------------------------------
+
+    /// Allows using the `#[link_args]` attribute.
+    (active, link_args, "1.0.0", Some(29596), None),
+
+    /// Allows defining identifiers beyond ASCII.
+    (active, non_ascii_idents, "1.0.0", Some(55467), None),
+
+    /// Allows using `#[plugin_registrar]` on functions.
+    (active, plugin_registrar, "1.0.0", Some(29597), None),
+
+    /// Allows using `#![plugin(myplugin)]`.
+    (active, plugin, "1.0.0", Some(29597), None),
+
+    /// Allows using `#[thread_local]` on `static` items.
+    (active, thread_local, "1.0.0", Some(29594), None),
+
+    /// Allows the use of SIMD types in functions declared in `extern` blocks.
+    (active, simd_ffi, "1.0.0", Some(27731), None),
+
+    /// Allows using custom attributes (RFC 572).
+    (active, custom_attribute, "1.0.0", Some(29642), None),
+
+    /// Allows using non lexical lifetimes (RFC 2094).
+    (active, nll, "1.0.0", Some(43234), None),
+
+    /// Allows using slice patterns.
+    (active, slice_patterns, "1.0.0", Some(62254), None),
+
+    /// Allows the definition of `const` functions with some advanced features.
+    (active, const_fn, "1.2.0", Some(57563), None),
+
+    /// Allows associated type defaults.
+    (active, associated_type_defaults, "1.2.0", Some(29661), None),
+
+    /// Allows `#![no_core]`.
+    (active, no_core, "1.3.0", Some(29639), None),
+
+    /// Allows default type parameters to influence type inference.
+    (active, default_type_parameter_fallback, "1.3.0", Some(27336), None),
+
+    /// Allows `repr(simd)` and importing the various simd intrinsics.
+    (active, repr_simd, "1.4.0", Some(27731), None),
+
+    /// Allows `extern "platform-intrinsic" { ... }`.
+    (active, platform_intrinsics, "1.4.0", Some(27731), None),
+
+    /// Allows `#[unwind(..)]`.
+    ///
+    /// Permits specifying whether a function should permit unwinding or abort on unwind.
+    (active, unwind_attributes, "1.4.0", Some(58760), None),
+
+    /// Allows `#[no_debug]`.
+    (active, no_debug, "1.5.0", Some(29721), None),
+
+    /// Allows attributes on expressions and non-item statements.
+    (active, stmt_expr_attributes, "1.6.0", Some(15701), None),
+
+    /// Allows the use of type ascription in expressions.
+    (active, type_ascription, "1.6.0", Some(23416), None),
+
+    /// Allows `cfg(target_thread_local)`.
+    (active, cfg_target_thread_local, "1.7.0", Some(29594), None),
+
+    /// Allows specialization of implementations (RFC 1210).
+    (active, specialization, "1.7.0", Some(31844), None),
+
+    /// Allows using `#[naked]` on functions.
+    (active, naked_functions, "1.9.0", Some(32408), None),
+
+    /// Allows `cfg(target_has_atomic = "...")`.
+    (active, cfg_target_has_atomic, "1.9.0", Some(32976), None),
+
+    /// Allows `X..Y` patterns.
+    (active, exclusive_range_pattern, "1.11.0", Some(37854), None),
+
+    /// Allows the `!` type. Does not imply 'exhaustive_patterns' (below) any more.
+    (active, never_type, "1.13.0", Some(35121), None),
+
+    /// Allows exhaustive pattern matching on types that contain uninhabited types.
+    (active, exhaustive_patterns, "1.13.0", Some(51085), None),
+
+    /// Allows untagged unions `union U { ... }`.
+    (active, untagged_unions, "1.13.0", Some(32836), None),
+
+    /// Allows `#[link(..., cfg(..))]`.
+    (active, link_cfg, "1.14.0", Some(37406), None),
+
+    /// Allows `extern "ptx-*" fn()`.
+    (active, abi_ptx, "1.15.0", Some(38788), None),
+
+    /// Allows the `#[repr(i128)]` attribute for enums.
+    (active, repr128, "1.16.0", Some(35118), None),
+
+    /// Allows `#[link(kind="static-nobundle"...)]`.
+    (active, static_nobundle, "1.16.0", Some(37403), None),
+
+    /// Allows `extern "msp430-interrupt" fn()`.
+    (active, abi_msp430_interrupt, "1.16.0", Some(38487), None),
+
+    /// Allows declarative macros 2.0 (`macro`).
+    (active, decl_macro, "1.17.0", Some(39412), None),
+
+    /// Allows `extern "x86-interrupt" fn()`.
+    (active, abi_x86_interrupt, "1.17.0", Some(40180), None),
+
+    /// Allows overlapping impls of marker traits.
+    (active, overlapping_marker_traits, "1.18.0", Some(29864), None),
+
+    /// Allows a test to fail without failing the whole suite.
+    (active, allow_fail, "1.19.0", Some(46488), None),
+
+    /// Allows unsized tuple coercion.
+    (active, unsized_tuple_coercion, "1.20.0", Some(42877), None),
+
+    /// Allows defining generators.
+    (active, generators, "1.21.0", Some(43122), None),
+
+    /// Allows `#[doc(cfg(...))]`.
+    (active, doc_cfg, "1.21.0", Some(43781), None),
+
+    /// Allows `#[doc(masked)]`.
+    (active, doc_masked, "1.21.0", Some(44027), None),
+
+    /// Allows `#[doc(spotlight)]`.
+    (active, doc_spotlight, "1.22.0", Some(45040), None),
+
+    /// Allows `#[doc(include = "some-file")]`.
+    (active, external_doc, "1.22.0", Some(44732), None),
+
+    /// Allows future-proofing enums/structs with the `#[non_exhaustive]` attribute (RFC 2008).
+    (active, non_exhaustive, "1.22.0", Some(44109), None),
+
+    /// Allows using `crate` as visibility modifier, synonymous with `pub(crate)`.
+    (active, crate_visibility_modifier, "1.23.0", Some(53120), None),
+
+    /// Allows defining `extern type`s.
+    (active, extern_types, "1.23.0", Some(43467), None),
+
+    /// Allows trait methods with arbitrary self types.
+    (active, arbitrary_self_types, "1.23.0", Some(44874), None),
+
+    /// Allows in-band quantification of lifetime bindings (e.g., `fn foo(x: &'a u8) -> &'a u8`).
+    (active, in_band_lifetimes, "1.23.0", Some(44524), None),
+
+    /// Allows associated types to be generic, e.g., `type Foo<T>;` (RFC 1598).
+    (active, generic_associated_types, "1.23.0", Some(44265), None),
+
+    /// Allows defining `trait X = A + B;` alias items.
+    (active, trait_alias, "1.24.0", Some(41517), None),
+
+    /// Allows infering `'static` outlives requirements (RFC 2093).
+    (active, infer_static_outlives_requirements, "1.26.0", Some(54185), None),
+
+    /// Allows macro invocations in `extern {}` blocks.
+    (active, macros_in_extern, "1.27.0", Some(49476), None),
+
+    /// Allows accessing fields of unions inside `const` functions.
+    (active, const_fn_union, "1.27.0", Some(51909), None),
+
+    /// Allows casting raw pointers to `usize` during const eval.
+    (active, const_raw_ptr_to_usize_cast, "1.27.0", Some(51910), None),
+
+    /// Allows dereferencing raw pointers during const eval.
+    (active, const_raw_ptr_deref, "1.27.0", Some(51911), None),
+
+    /// Allows comparing raw pointers during const eval.
+    (active, const_compare_raw_pointers, "1.27.0", Some(53020), None),
+
+    /// Allows `#[doc(alias = "...")]`.
+    (active, doc_alias, "1.27.0", Some(50146), None),
+
+    /// Allows inconsistent bounds in where clauses.
+    (active, trivial_bounds, "1.28.0", Some(48214), None),
+
+    /// Allows `'a: { break 'a; }`.
+    (active, label_break_value, "1.28.0", Some(48594), None),
+
+    /// Allows using `#[doc(keyword = "...")]`.
+    (active, doc_keyword, "1.28.0", Some(51315), None),
+
+    /// Allows reinterpretation of the bits of a value of one type as another
+    /// type during const eval.
+    (active, const_transmute, "1.29.0", Some(53605), None),
+
+    /// Allows using `try {...}` expressions.
+    (active, try_blocks, "1.29.0", Some(31436), None),
+
+    /// Allows defining an `#[alloc_error_handler]`.
+    (active, alloc_error_handler, "1.29.0", Some(51540), None),
+
+    /// Allows using the `amdgpu-kernel` ABI.
+    (active, abi_amdgpu_kernel, "1.29.0", Some(51575), None),
+
+    /// Allows panicking during const eval (producing compile-time errors).
+    (active, const_panic, "1.30.0", Some(51999), None),
+
+    /// Allows `#[marker]` on certain traits allowing overlapping implementations.
+    (active, marker_trait_attr, "1.30.0", Some(29864), None),
+
+    /// Allows macro invocations on modules expressions and statements and
+    /// procedural macros to expand to non-items.
+    (active, proc_macro_hygiene, "1.30.0", Some(54727), None),
+
+    /// Allows unsized rvalues at arguments and parameters.
+    (active, unsized_locals, "1.30.0", Some(48055), None),
+
+    /// Allows custom test frameworks with `#![test_runner]` and `#[test_case]`.
+    (active, custom_test_frameworks, "1.30.0", Some(50297), None),
+
+    /// Allows non-builtin attributes in inner attribute position.
+    (active, custom_inner_attributes, "1.30.0", Some(54726), None),
+
+    /// Allows mixing bind-by-move in patterns and references to those identifiers in guards.
+    (active, bind_by_move_pattern_guards, "1.30.0", Some(15287), None),
+
+    /// Allows `impl Trait` in bindings (`let`, `const`, `static`).
+    (active, impl_trait_in_bindings, "1.30.0", Some(63065), None),
+
+    /// Allows using `reason` in lint attributes and the `#[expect(lint)]` lint check.
+    (active, lint_reasons, "1.31.0", Some(54503), None),
+
+    /// Allows exhaustive integer pattern matching on `usize` and `isize`.
+    (active, precise_pointer_size_matching, "1.32.0", Some(56354), None),
+
+    /// Allows relaxing the coherence rules such that
+    /// `impl<T> ForeignTrait<LocalType> for ForeignType<T> is permitted.
+    (active, re_rebalance_coherence, "1.32.0", Some(55437), None),
+
+    /// Allows using `#[ffi_returns_twice]` on foreign functions.
+    (active, ffi_returns_twice, "1.34.0", Some(58314), None),
+
+    /// Allows const generic types (e.g. `struct Foo<const N: usize>(...);`).
+    (active, const_generics, "1.34.0", Some(44580), None),
+
+    /// Allows using `#[optimize(X)]`.
+    (active, optimize_attribute, "1.34.0", Some(54882), None),
+
+    /// Allows using C-variadics.
+    (active, c_variadic, "1.34.0", Some(44930), None),
+
+    /// Allows the user of associated type bounds.
+    (active, associated_type_bounds, "1.34.0", Some(52662), None),
+
+    /// Attributes on formal function params.
+    (active, param_attrs, "1.36.0", Some(60406), None),
+
+    /// Allows calling constructor functions in `const fn`.
+    (active, const_constructor, "1.37.0", Some(61456), None),
+
+    /// Allows `if/while p && let q = r && ...` chains.
+    (active, let_chains, "1.37.0", Some(53667), None),
+
+    /// Allows #[repr(transparent)] on enums (RFC 2645).
+    (active, transparent_enums, "1.37.0", Some(60405), None),
+
+    /// Allows #[repr(transparent)] on unions (RFC 2645).
+    (active, transparent_unions, "1.37.0", Some(60405), None),
+
+    /// Allows explicit discriminants on non-unit enum variants.
+    (active, arbitrary_enum_discriminant, "1.37.0", Some(60553), None),
+
+    /// Allows `impl Trait` with multiple unrelated lifetimes.
+    (active, member_constraints, "1.37.0", Some(61977), None),
+
+    /// Allows `async || body` closures.
+    (active, async_closure, "1.37.0", Some(62290), None),
+
+    /// Allows the use of `#[cfg(doctest)]`, set when rustdoc is collecting doctests
+    (active, cfg_doctest, "1.37.0", Some(62210), None),
+
+    /// Allows `[x; N]` where `x` is a constant (RFC 2203).
+    (active, const_in_array_repeat_expressions, "1.37.0", Some(49147), None),
+
+    /// Allows `impl Trait` to be used inside type aliases (RFC 2515).
+    (active, type_alias_impl_trait, "1.38.0", Some(63063), None),
+
+    /// Allows the use of or-patterns, e.g. `0 | 1`.
+    (active, or_patterns, "1.38.0", Some(54883), None),
+
+    // -------------------------------------------------------------------------
+    // feature-group-end: actual feature gates
+    // -------------------------------------------------------------------------
+);
+
+/// Some features are known to be incomplete and using them is likely to have
+/// unanticipated results, such as compiler crashes. We warn the user about these
+/// to alert them.
+pub const INCOMPLETE_FEATURES: &[Symbol] = &[
+    sym::impl_trait_in_bindings,
+    sym::generic_associated_types,
+    sym::const_generics,
+    sym::or_patterns,
+    sym::let_chains,
+];
diff --git a/src/libsyntax/feature_gate/builtin_attrs.rs b/src/libsyntax/feature_gate/builtin_attrs.rs
new file mode 100644
index 00000000000..ee7ac3b15d9
--- /dev/null
+++ b/src/libsyntax/feature_gate/builtin_attrs.rs
@@ -0,0 +1,573 @@
+//! Built-in attributes and `cfg` flag gating.
+
+use AttributeType::*;
+use AttributeGate::*;
+
+use super::check::{emit_feature_err, GateIssue};
+use super::check::{Stability, EXPLAIN_ALLOW_INTERNAL_UNSAFE, EXPLAIN_ALLOW_INTERNAL_UNSTABLE};
+use super::active::Features;
+
+use crate::ast;
+use crate::attr::AttributeTemplate;
+use crate::symbol::{Symbol, sym};
+use crate::parse::ParseSess;
+
+use syntax_pos::Span;
+use rustc_data_structures::fx::FxHashMap;
+use lazy_static::lazy_static;
+
+type GateFn = fn(&Features) -> bool;
+
+macro_rules! cfg_fn {
+    ($field: ident) => {
+        (|features| { features.$field }) as GateFn
+    }
+}
+
+/// `cfg(...)`'s that are feature gated.
+const GATED_CFGS: &[(Symbol, Symbol, GateFn)] = &[
+    // (name in cfg, feature, function to check if the feature is enabled)
+    (sym::target_thread_local, sym::cfg_target_thread_local, cfg_fn!(cfg_target_thread_local)),
+    (sym::target_has_atomic, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)),
+    (sym::rustdoc, sym::doc_cfg, cfg_fn!(doc_cfg)),
+    (sym::doctest, sym::cfg_doctest, cfg_fn!(cfg_doctest)),
+];
+
+#[derive(Debug)]
+pub struct GatedCfg {
+    span: Span,
+    index: usize,
+}
+
+impl GatedCfg {
+    pub fn gate(cfg: &ast::MetaItem) -> Option<GatedCfg> {
+        GATED_CFGS.iter()
+                  .position(|info| cfg.check_name(info.0))
+                  .map(|idx| {
+                      GatedCfg {
+                          span: cfg.span,
+                          index: idx
+                      }
+                  })
+    }
+
+    pub fn check_and_emit(&self, sess: &ParseSess, features: &Features) {
+        let (cfg, feature, has_feature) = GATED_CFGS[self.index];
+        if !has_feature(features) && !self.span.allows_unstable(feature) {
+            let explain = format!("`cfg({})` is experimental and subject to change", cfg);
+            emit_feature_err(sess, feature, self.span, GateIssue::Language, &explain);
+        }
+    }
+}
+
+// If you change this, please modify `src/doc/unstable-book` as well. You must
+// move that documentation into the relevant place in the other docs, and
+// remove the chapter on the flag.
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum AttributeType {
+    /// Normal, builtin attribute that is consumed
+    /// by the compiler before the unused_attribute check
+    Normal,
+
+    /// Builtin attribute that may not be consumed by the compiler
+    /// before the unused_attribute check. These attributes
+    /// will be ignored by the unused_attribute lint
+    Whitelisted,
+
+    /// Builtin attribute that is only allowed at the crate level
+    CrateLevel,
+}
+
+pub enum AttributeGate {
+    /// Is gated by a given feature gate, reason
+    /// and function to check if enabled
+    Gated(Stability, Symbol, &'static str, fn(&Features) -> bool),
+
+    /// Ungated attribute, can be used on all release channels
+    Ungated,
+}
+
+// fn() is not Debug
+impl std::fmt::Debug for AttributeGate {
+    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match *self {
+            Self::Gated(ref stab, name, expl, _) =>
+                write!(fmt, "Gated({:?}, {}, {})", stab, name, expl),
+            Self::Ungated => write!(fmt, "Ungated")
+        }
+    }
+}
+
+impl AttributeGate {
+    fn is_deprecated(&self) -> bool {
+        match *self {
+            Self::Gated(Stability::Deprecated(_, _), ..) => true,
+            _ => false,
+        }
+    }
+}
+
+/// A convenience macro for constructing attribute templates.
+/// E.g., `template!(Word, List: "description")` means that the attribute
+/// supports forms `#[attr]` and `#[attr(description)]`.
+macro_rules! template {
+    (Word) => { template!(@ true, None, None) };
+    (List: $descr: expr) => { template!(@ false, Some($descr), None) };
+    (NameValueStr: $descr: expr) => { template!(@ false, None, Some($descr)) };
+    (Word, List: $descr: expr) => { template!(@ true, Some($descr), None) };
+    (Word, NameValueStr: $descr: expr) => { template!(@ true, None, Some($descr)) };
+    (List: $descr1: expr, NameValueStr: $descr2: expr) => {
+        template!(@ false, Some($descr1), Some($descr2))
+    };
+    (Word, List: $descr1: expr, NameValueStr: $descr2: expr) => {
+        template!(@ true, Some($descr1), Some($descr2))
+    };
+    (@ $word: expr, $list: expr, $name_value_str: expr) => { AttributeTemplate {
+        word: $word, list: $list, name_value_str: $name_value_str
+    } };
+}
+
+macro_rules! ungated {
+    ($attr:ident, $typ:expr, $tpl:expr $(,)?) => {
+        (sym::$attr, $typ, $tpl, Ungated)
+    };
+}
+
+macro_rules! gated {
+    ($attr:ident, $typ:expr, $tpl:expr, $gate:ident, $msg:expr $(,)?) => {
+        (sym::$attr, $typ, $tpl, Gated(Stability::Unstable, sym::$gate, $msg, cfg_fn!($gate)))
+    };
+    ($attr:ident, $typ:expr, $tpl:expr, $msg:expr $(,)?) => {
+        (sym::$attr, $typ, $tpl, Gated(Stability::Unstable, sym::$attr, $msg, cfg_fn!($attr)))
+    };
+}
+
+macro_rules! rustc_attr {
+    (TEST, $attr:ident, $typ:expr, $tpl:expr $(,)?) => {
+        rustc_attr!(
+            $attr, $typ, $tpl,
+            concat!("the `#[", stringify!($attr), "]` attribute is just used for rustc unit tests \
+                and will never be stable",
+            ),
+        )
+    };
+    ($attr:ident, $typ:expr, $tpl:expr, $msg:expr $(,)?) => {
+        (sym::$attr, $typ, $tpl,
+         Gated(Stability::Unstable, sym::rustc_attrs, $msg, cfg_fn!(rustc_attrs)))
+    };
+}
+
+macro_rules! experimental {
+    ($attr:ident) => {
+        concat!("the `#[", stringify!($attr), "]` attribute is an experimental feature")
+    }
+}
+
+const IMPL_DETAIL: &str = "internal implementation detail";
+const INTERAL_UNSTABLE: &str = "this is an internal attribute that will never be stable";
+
+pub type BuiltinAttribute = (Symbol, AttributeType, AttributeTemplate, AttributeGate);
+
+/// Attributes that have a special meaning to rustc or rustdoc
+pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
+    // ==========================================================================
+    // Stable attributes:
+    // ==========================================================================
+
+    // Condtional compilation:
+    ungated!(cfg, Normal, template!(List: "predicate")),
+    ungated!(cfg_attr, Normal, template!(List: "predicate, attr1, attr2, ...")),
+
+    // Testing:
+    ungated!(ignore, Normal, template!(Word, NameValueStr: "reason")),
+    ungated!(
+        should_panic, Normal,
+        template!(Word, List: r#"expected = "reason"#, NameValueStr: "reason"),
+    ),
+    // FIXME(Centril): This can be used on stable but shouldn't.
+    ungated!(reexport_test_harness_main, Normal, template!(NameValueStr: "name")),
+
+    // Macros:
+    ungated!(derive, Normal, template!(List: "Trait1, Trait2, ...")),
+    ungated!(automatically_derived, Normal, template!(Word)),
+    // FIXME(#14407)
+    ungated!(macro_use, Normal, template!(Word, List: "name1, name2, ...")),
+    ungated!(macro_escape, Normal, template!(Word)), // Deprecated synonym for `macro_use`.
+    ungated!(macro_export, Normal, template!(Word, List: "local_inner_macros")),
+    ungated!(proc_macro, Normal, template!(Word)),
+    ungated!(
+        proc_macro_derive, Normal,
+        template!(List: "TraitName, /*opt*/ attributes(name1, name2, ...)"),
+    ),
+    ungated!(proc_macro_attribute, Normal, template!(Word)),
+
+    // Lints:
+    ungated!(warn, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)),
+    ungated!(allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)),
+    ungated!(forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)),
+    ungated!(deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)),
+    ungated!(must_use, Whitelisted, template!(Word, NameValueStr: "reason")),
+    // FIXME(#14407)
+    ungated!(
+        deprecated, Normal,
+        template!(
+            Word,
+            List: r#"/*opt*/ since = "version", /*opt*/ note = "reason""#,
+            NameValueStr: "reason"
+        ),
+    ),
+
+    // Crate properties:
+    ungated!(crate_name, CrateLevel, template!(NameValueStr: "name")),
+    ungated!(crate_type, CrateLevel, template!(NameValueStr: "bin|lib|...")),
+    ungated!(crate_id, CrateLevel, template!(NameValueStr: "ignored")),
+
+    // ABI, linking, symbols, and FFI
+    ungated!(
+        link, Whitelisted,
+        template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ cfg = "...""#),
+    ),
+    ungated!(link_name, Whitelisted, template!(NameValueStr: "name")),
+    ungated!(no_link, Normal, template!(Word)),
+    ungated!(repr, Normal, template!(List: "C, packed, ...")),
+    ungated!(export_name, Whitelisted, template!(NameValueStr: "name")),
+    ungated!(link_section, Whitelisted, template!(NameValueStr: "name")),
+    ungated!(no_mangle, Whitelisted, template!(Word)),
+    ungated!(used, Whitelisted, template!(Word)),
+
+    // Limits:
+    ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N")),
+    ungated!(type_length_limit, CrateLevel, template!(NameValueStr: "N")),
+
+    // Entry point:
+    ungated!(main, Normal, template!(Word)),
+    ungated!(start, Normal, template!(Word)),
+    ungated!(no_start, CrateLevel, template!(Word)),
+    ungated!(no_main, CrateLevel, template!(Word)),
+
+    // Modules, prelude, and resolution:
+    ungated!(path, Normal, template!(NameValueStr: "file")),
+    ungated!(no_std, CrateLevel, template!(Word)),
+    ungated!(no_implicit_prelude, Normal, template!(Word)),
+
+    // Runtime
+    ungated!(windows_subsystem, Whitelisted, template!(NameValueStr: "windows|console")),
+    ungated!(panic_handler, Normal, template!(Word)), // RFC 2070
+
+    // Code generation:
+    ungated!(inline, Whitelisted, template!(Word, List: "always|never")),
+    ungated!(cold, Whitelisted, template!(Word)),
+    ungated!(no_builtins, Whitelisted, template!(Word)),
+    ungated!(target_feature, Whitelisted, template!(List: r#"enable = "name""#)),
+
+    // FIXME: #14408 whitelist docs since rustdoc looks at them
+    ungated!(doc, Whitelisted, template!(List: "hidden|inline|...", NameValueStr: "string")),
+
+    // ==========================================================================
+    // Unstable attributes:
+    // ==========================================================================
+
+    // Linking:
+    gated!(naked, Whitelisted, template!(Word), naked_functions, experimental!(naked)),
+    gated!(
+        link_args, Normal, template!(NameValueStr: "args"),
+        "the `link_args` attribute is experimental and not portable across platforms, \
+        it is recommended to use `#[link(name = \"foo\")] instead",
+    ),
+
+    // Plugins:
+    ungated!(plugin_registrar, Normal, template!(Word)),
+    gated!(
+        plugin, CrateLevel, template!(List: "name|name(args)"),
+        "compiler plugins are experimental and possibly buggy",
+    ),
+
+    // Testing:
+    gated!(allow_fail, Normal, template!(Word), experimental!(allow_fail)),
+    gated!(
+        test_runner, CrateLevel, template!(List: "path"), custom_test_frameworks,
+        "custom test frameworks are an unstable feature",
+    ),
+
+    // RFC #2008
+    gated!(non_exhaustive, Whitelisted, template!(Word), experimental!(non_exhaustive)),
+    // RFC #1268
+    gated!(marker, Normal, template!(Word), marker_trait_attr, experimental!(marker)),
+    gated!(
+        thread_local, Whitelisted, template!(Word),
+        "`#[thread_local]` is an experimental feature, and does not currently handle destructors",
+    ),
+    gated!(no_core, CrateLevel, template!(Word), experimental!(no_core)),
+    // RFC 2412
+    gated!(
+        optimize, Whitelisted, template!(List: "size|speed"), optimize_attribute,
+        experimental!(optimize),
+    ),
+
+    gated!(ffi_returns_twice, Whitelisted, template!(Word), experimental!(ffi_returns_twice)),
+
+    // ==========================================================================
+    // Internal attributes: Stability, deprecation, and unsafe:
+    // ==========================================================================
+
+    ungated!(feature, CrateLevel, template!(List: "name1, name1, ...")),
+    // FIXME(#14407) -- only looked at on-demand so we can't
+    // guarantee they'll have already been checked.
+    ungated!(
+        rustc_deprecated, Whitelisted,
+        template!(List: r#"since = "version", reason = "...""#)
+    ),
+    // FIXME(#14407)
+    ungated!(stable, Whitelisted, template!(List: r#"feature = "name", since = "version""#)),
+    // FIXME(#14407)
+    ungated!(
+        unstable, Whitelisted,
+        template!(List: r#"feature = "name", reason = "...", issue = "N""#),
+    ),
+    gated!(
+        rustc_const_unstable, Normal, template!(List: r#"feature = "name""#),
+        "the `#[rustc_const_unstable]` attribute is an internal feature",
+    ),
+    gated!(
+        allow_internal_unstable, Normal, template!(Word, List: "feat1, feat2, ..."),
+        EXPLAIN_ALLOW_INTERNAL_UNSTABLE,
+    ),
+    gated!(allow_internal_unsafe, Normal, template!(Word), EXPLAIN_ALLOW_INTERNAL_UNSAFE),
+
+    // ==========================================================================
+    // Internal attributes: Type system related:
+    // ==========================================================================
+
+    gated!(fundamental, Whitelisted, template!(Word), experimental!(fundamental)),
+    gated!(
+        // RFC #1445.
+        structural_match, Whitelisted, template!(Word),
+        "the semantics of constant patterns is not yet settled",
+    ),
+    gated!(
+        may_dangle, Normal, template!(Word), dropck_eyepatch,
+        "`may_dangle` has unstable semantics and may be removed in the future",
+    ),
+
+    // ==========================================================================
+    // Internal attributes: Runtime related:
+    // ==========================================================================
+
+    rustc_attr!(rustc_allocator, Whitelisted, template!(Word), IMPL_DETAIL),
+    rustc_attr!(rustc_allocator_nounwind, Whitelisted, template!(Word), IMPL_DETAIL),
+    gated!(alloc_error_handler, Normal, template!(Word), experimental!(alloc_error_handler)),
+    gated!(
+        default_lib_allocator, Whitelisted, template!(Word), allocator_internals,
+        experimental!(default_lib_allocator),
+    ),
+    gated!(
+        needs_allocator, Normal, template!(Word), allocator_internals,
+        experimental!(needs_allocator),
+    ),
+    gated!(panic_runtime, Whitelisted, template!(Word), experimental!(panic_runtime)),
+    gated!(needs_panic_runtime, Whitelisted, template!(Word), experimental!(needs_panic_runtime)),
+    gated!(
+        unwind, Whitelisted, template!(List: "allowed|aborts"), unwind_attributes,
+        experimental!(unwind),
+    ),
+    gated!(
+        compiler_builtins, Whitelisted, template!(Word),
+        "the `#[compiler_builtins]` attribute is used to identify the `compiler_builtins` crate \
+        which contains compiler-rt intrinsics and will never be stable",
+    ),
+    gated!(
+        sanitizer_runtime, Whitelisted, template!(Word),
+        "the `#[sanitizer_runtime]` attribute is used to identify crates that contain the runtime \
+        of a sanitizer and will never be stable",
+    ),
+    gated!(
+        profiler_runtime, Whitelisted, template!(Word),
+        "the `#[profiler_runtime]` attribute is used to identify the `profiler_builtins` crate \
+        which contains the profiler runtime and will never be stable",
+    ),
+
+    // ==========================================================================
+    // Internal attributes, Linkage:
+    // ==========================================================================
+
+    gated!(
+        linkage, Whitelisted, template!(NameValueStr: "external|internal|..."),
+        "the `linkage` attribute is experimental and not portable across platforms",
+    ),
+    rustc_attr!(rustc_std_internal_symbol, Whitelisted, template!(Word), INTERAL_UNSTABLE),
+
+    // ==========================================================================
+    // Internal attributes, Macro related:
+    // ==========================================================================
+
+    rustc_attr!(rustc_builtin_macro, Whitelisted, template!(Word), IMPL_DETAIL),
+    rustc_attr!(rustc_proc_macro_decls, Normal, template!(Word), INTERAL_UNSTABLE),
+    rustc_attr!(
+        rustc_macro_transparency, Whitelisted,
+        template!(NameValueStr: "transparent|semitransparent|opaque"),
+        "used internally for testing macro hygiene",
+    ),
+
+    // ==========================================================================
+    // Internal attributes, Diagnostics related:
+    // ==========================================================================
+
+    gated!(
+        rustc_on_unimplemented, Whitelisted,
+        template!(
+            List: r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#,
+            NameValueStr: "message"
+        ),
+        on_unimplemented,
+        experimental!(rustc_on_unimplemented),
+    ),
+    // Whitelists "identity-like" conversion methods to suggest on type mismatch.
+    rustc_attr!(rustc_conversion_suggestion, Whitelisted, template!(Word), INTERAL_UNSTABLE),
+
+    // ==========================================================================
+    // Internal attributes, Const related:
+    // ==========================================================================
+
+    rustc_attr!(rustc_promotable, Whitelisted, template!(Word), IMPL_DETAIL),
+    rustc_attr!(rustc_allow_const_fn_ptr, Whitelisted, template!(Word), IMPL_DETAIL),
+    rustc_attr!(rustc_args_required_const, Whitelisted, template!(List: "N"), INTERAL_UNSTABLE),
+
+    // ==========================================================================
+    // Internal attributes, Layout related:
+    // ==========================================================================
+
+    rustc_attr!(
+        rustc_layout_scalar_valid_range_start, Whitelisted, template!(List: "value"),
+        "the `#[rustc_layout_scalar_valid_range_start]` attribute is just used to enable \
+        niche optimizations in libcore and will never be stable",
+    ),
+    rustc_attr!(
+        rustc_layout_scalar_valid_range_end, Whitelisted, template!(List: "value"),
+        "the `#[rustc_layout_scalar_valid_range_end]` attribute is just used to enable \
+        niche optimizations in libcore and will never be stable",
+    ),
+    rustc_attr!(
+        rustc_nonnull_optimization_guaranteed, Whitelisted, template!(Word),
+        "the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to enable \
+        niche optimizations in libcore and will never be stable",
+    ),
+
+    // ==========================================================================
+    // Internal attributes, Misc:
+    // ==========================================================================
+
+    gated!(
+        lang, Normal, template!(NameValueStr: "name"), lang_items,
+        "language items are subject to change",
+    ),
+    (
+        sym::rustc_diagnostic_item,
+        Normal,
+        template!(NameValueStr: "name"),
+        Gated(
+            Stability::Unstable,
+            sym::rustc_attrs,
+            "diagnostic items compiler internal support for linting",
+            cfg_fn!(rustc_attrs),
+        ),
+    ),
+    (
+        sym::no_debug, Whitelisted, template!(Word),
+        Gated(
+            Stability::Deprecated("https://github.com/rust-lang/rust/issues/29721", None),
+            sym::no_debug,
+            "the `#[no_debug]` attribute was an experimental feature that has been \
+            deprecated due to lack of demand",
+            cfg_fn!(no_debug)
+        )
+    ),
+    gated!(
+        // Used in resolve:
+        prelude_import, Whitelisted, template!(Word),
+        "`#[prelude_import]` is for use by rustc only",
+    ),
+    gated!(
+        rustc_paren_sugar, Normal, template!(Word), unboxed_closures,
+        "unboxed_closures are still evolving",
+    ),
+    rustc_attr!(
+        rustc_inherit_overflow_checks, Whitelisted, template!(Word),
+        "the `#[rustc_inherit_overflow_checks]` attribute is just used to control \
+        overflow checking behavior of several libcore functions that are inlined \
+        across crates and will never be stable",
+    ),
+    rustc_attr!(
+        rustc_test_marker, Normal, template!(Word),
+        "the `#[rustc_test_marker]` attribute is used internally to track tests",
+    ),
+
+    // ==========================================================================
+    // Internal attributes, Testing:
+    // ==========================================================================
+
+    rustc_attr!(TEST, rustc_outlives, Normal, template!(Word)),
+    rustc_attr!(TEST, rustc_variance, Normal, template!(Word)),
+    rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ...")),
+    rustc_attr!(TEST, rustc_regions, Normal, template!(Word)),
+    rustc_attr!(TEST, rustc_error, Whitelisted, template!(Word)),
+    rustc_attr!(TEST, rustc_dump_user_substs, Whitelisted, template!(Word)),
+    rustc_attr!(TEST, rustc_if_this_changed, Whitelisted, template!(Word, List: "DepNode")),
+    rustc_attr!(TEST, rustc_then_this_would_need, Whitelisted, template!(List: "DepNode")),
+    rustc_attr!(
+        TEST, rustc_dirty, Whitelisted,
+        template!(List: r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#),
+    ),
+    rustc_attr!(
+        TEST, rustc_clean, Whitelisted,
+        template!(List: r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#),
+    ),
+    rustc_attr!(
+        TEST, rustc_partition_reused, Whitelisted,
+        template!(List: r#"cfg = "...", module = "...""#),
+    ),
+    rustc_attr!(
+        TEST, rustc_partition_codegened, Whitelisted,
+        template!(List: r#"cfg = "...", module = "...""#),
+    ),
+    rustc_attr!(
+        TEST, rustc_expected_cgu_reuse, Whitelisted,
+        template!(List: r#"cfg = "...", module = "...", kind = "...""#),
+    ),
+    rustc_attr!(TEST, rustc_synthetic, Whitelisted, template!(Word)),
+    rustc_attr!(TEST, rustc_symbol_name, Whitelisted, template!(Word)),
+    rustc_attr!(TEST, rustc_def_path, Whitelisted, template!(Word)),
+    rustc_attr!(TEST, rustc_mir, Whitelisted, template!(List: "arg1, arg2, ...")),
+    rustc_attr!(TEST, rustc_dump_program_clauses, Whitelisted, template!(Word)),
+    rustc_attr!(TEST, rustc_dump_env_program_clauses, Whitelisted, template!(Word)),
+    rustc_attr!(TEST, rustc_object_lifetime_default, Whitelisted, template!(Word)),
+    rustc_attr!(TEST, rustc_dummy, Normal, template!(Word /* doesn't matter*/)),
+    gated!(
+        omit_gdb_pretty_printer_section, Whitelisted, template!(Word),
+        "the `#[omit_gdb_pretty_printer_section]` attribute is just used for the Rust test suite",
+    ),
+];
+
+pub fn deprecated_attributes() -> Vec<&'static BuiltinAttribute> {
+    BUILTIN_ATTRIBUTES.iter().filter(|(.., gate)| gate.is_deprecated()).collect()
+}
+
+pub fn is_builtin_attr_name(name: ast::Name) -> bool {
+    BUILTIN_ATTRIBUTE_MAP.get(&name).is_some()
+}
+
+pub fn is_builtin_attr(attr: &ast::Attribute) -> bool {
+    attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)).is_some()
+}
+
+lazy_static! {
+    pub static ref BUILTIN_ATTRIBUTE_MAP: FxHashMap<Symbol, &'static BuiltinAttribute> = {
+        let mut map = FxHashMap::default();
+        for attr in BUILTIN_ATTRIBUTES.iter() {
+            if map.insert(attr.0, attr).is_some() {
+                panic!("duplicate builtin attribute `{}`", attr.0);
+            }
+        }
+        map
+    };
+}
diff --git a/src/libsyntax/feature_gate/check.rs b/src/libsyntax/feature_gate/check.rs
new file mode 100644
index 00000000000..f3a9d135125
--- /dev/null
+++ b/src/libsyntax/feature_gate/check.rs
@@ -0,0 +1,958 @@
+use super::{active::{ACTIVE_FEATURES, Features}, Feature, State as FeatureState};
+use super::accepted::ACCEPTED_FEATURES;
+use super::removed::{REMOVED_FEATURES, STABLE_REMOVED_FEATURES};
+use super::builtin_attrs::{AttributeGate, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
+
+use crate::ast::{
+    self, AssocTyConstraint, AssocTyConstraintKind, NodeId, GenericParam, GenericParamKind,
+    PatKind, RangeEnd,
+};
+use crate::attr::{self, check_builtin_attribute};
+use crate::source_map::Spanned;
+use crate::edition::{ALL_EDITIONS, Edition};
+use crate::visit::{self, FnKind, Visitor};
+use crate::parse::{token, ParseSess};
+use crate::parse::parser::Parser;
+use crate::symbol::{Symbol, sym};
+use crate::tokenstream::TokenTree;
+
+use errors::{Applicability, DiagnosticBuilder, Handler};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_target::spec::abi::Abi;
+use syntax_pos::{Span, DUMMY_SP, MultiSpan};
+use log::debug;
+
+use std::env;
+
+#[derive(Copy, Clone, Debug)]
+pub enum Stability {
+    Unstable,
+    // First argument is tracking issue link; second argument is an optional
+    // help message, which defaults to "remove this attribute"
+    Deprecated(&'static str, Option<&'static str>),
+}
+
+struct Context<'a> {
+    features: &'a Features,
+    parse_sess: &'a ParseSess,
+    plugin_attributes: &'a [(Symbol, AttributeType)],
+}
+
+macro_rules! gate_feature_fn {
+    ($cx: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr, $level: expr) => {{
+        let (cx, has_feature, span,
+             name, explain, level) = ($cx, $has_feature, $span, $name, $explain, $level);
+        let has_feature: bool = has_feature(&$cx.features);
+        debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature);
+        if !has_feature && !span.allows_unstable($name) {
+            leveled_feature_err(cx.parse_sess, name, span, GateIssue::Language, explain, level)
+                .emit();
+        }
+    }}
+}
+
+macro_rules! gate_feature {
+    ($cx: expr, $feature: ident, $span: expr, $explain: expr) => {
+        gate_feature_fn!($cx, |x:&Features| x.$feature, $span,
+                         sym::$feature, $explain, GateStrength::Hard)
+    };
+    ($cx: expr, $feature: ident, $span: expr, $explain: expr, $level: expr) => {
+        gate_feature_fn!($cx, |x:&Features| x.$feature, $span,
+                         sym::$feature, $explain, $level)
+    };
+}
+
+impl<'a> Context<'a> {
+    fn check_attribute(
+        &self,
+        attr: &ast::Attribute,
+        attr_info: Option<&BuiltinAttribute>,
+        is_macro: bool
+    ) {
+        debug!("check_attribute(attr = {:?})", attr);
+        if let Some(&(name, ty, _template, ref gateage)) = attr_info {
+            if let AttributeGate::Gated(_, name, desc, ref has_feature) = *gateage {
+                if !attr.span.allows_unstable(name) {
+                    gate_feature_fn!(
+                        self, has_feature, attr.span, name, desc, GateStrength::Hard
+                    );
+                }
+            } else if name == sym::doc {
+                if let Some(content) = attr.meta_item_list() {
+                    if content.iter().any(|c| c.check_name(sym::include)) {
+                        gate_feature!(self, external_doc, attr.span,
+                            "`#[doc(include = \"...\")]` is experimental"
+                        );
+                    }
+                }
+            }
+            debug!("check_attribute: {:?} is builtin, {:?}, {:?}", attr.path, ty, gateage);
+            return;
+        } else {
+            for segment in &attr.path.segments {
+                if segment.ident.as_str().starts_with("rustc") {
+                    let msg = "attributes starting with `rustc` are \
+                               reserved for use by the `rustc` compiler";
+                    gate_feature!(self, rustc_attrs, segment.ident.span, msg);
+                }
+            }
+        }
+        for &(n, ty) in self.plugin_attributes {
+            if attr.path == n {
+                // Plugins can't gate attributes, so we don't check for it
+                // unlike the code above; we only use this loop to
+                // short-circuit to avoid the checks below.
+                debug!("check_attribute: {:?} is registered by a plugin, {:?}", attr.path, ty);
+                return;
+            }
+        }
+        if !is_macro && !attr::is_known(attr) {
+            // Only run the custom attribute lint during regular feature gate
+            // checking. Macro gating runs before the plugin attributes are
+            // registered, so we skip this in that case.
+            let msg = format!("the attribute `{}` is currently unknown to the compiler and \
+                               may have meaning added to it in the future", attr.path);
+            gate_feature!(self, custom_attribute, attr.span, &msg);
+        }
+    }
+}
+
+pub fn check_attribute(attr: &ast::Attribute, parse_sess: &ParseSess, features: &Features) {
+    let cx = Context { features, parse_sess, plugin_attributes: &[] };
+    cx.check_attribute(
+        attr,
+        attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name).map(|a| *a)),
+        true
+    );
+}
+
+fn find_lang_feature_issue(feature: Symbol) -> Option<u32> {
+    if let Some(info) = ACTIVE_FEATURES.iter().find(|t| t.name == feature) {
+        // FIXME (#28244): enforce that active features have issue numbers
+        // assert!(info.issue.is_some())
+        info.issue
+    } else {
+        // search in Accepted, Removed, or Stable Removed features
+        let found = ACCEPTED_FEATURES.iter().chain(REMOVED_FEATURES).chain(STABLE_REMOVED_FEATURES)
+            .find(|t| t.name == feature);
+        match found {
+            Some(&Feature { issue, .. }) => issue,
+            None => panic!("Feature `{}` is not declared anywhere", feature),
+        }
+    }
+}
+
+pub enum GateIssue {
+    Language,
+    Library(Option<u32>)
+}
+
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub enum GateStrength {
+    /// A hard error. (Most feature gates should use this.)
+    Hard,
+    /// Only a warning. (Use this only as backwards-compatibility demands.)
+    Soft,
+}
+
+pub fn emit_feature_err(
+    sess: &ParseSess,
+    feature: Symbol,
+    span: Span,
+    issue: GateIssue,
+    explain: &str,
+) {
+    feature_err(sess, feature, span, issue, explain).emit();
+}
+
+pub fn feature_err<'a, S: Into<MultiSpan>>(
+    sess: &'a ParseSess,
+    feature: Symbol,
+    span: S,
+    issue: GateIssue,
+    explain: &str,
+) -> DiagnosticBuilder<'a> {
+    leveled_feature_err(sess, feature, span, issue, explain, GateStrength::Hard)
+}
+
+fn leveled_feature_err<'a, S: Into<MultiSpan>>(
+    sess: &'a ParseSess,
+    feature: Symbol,
+    span: S,
+    issue: GateIssue,
+    explain: &str,
+    level: GateStrength,
+) -> DiagnosticBuilder<'a> {
+    let diag = &sess.span_diagnostic;
+
+    let issue = match issue {
+        GateIssue::Language => find_lang_feature_issue(feature),
+        GateIssue::Library(lib) => lib,
+    };
+
+    let mut err = match level {
+        GateStrength::Hard => {
+            diag.struct_span_err_with_code(span, explain, stringify_error_code!(E0658))
+        }
+        GateStrength::Soft => diag.struct_span_warn(span, explain),
+    };
+
+    match issue {
+        None | Some(0) => {}  // We still accept `0` as a stand-in for backwards compatibility
+        Some(n) => {
+            err.note(&format!(
+                "for more information, see https://github.com/rust-lang/rust/issues/{}",
+                n,
+            ));
+        }
+    }
+
+    // #23973: do not suggest `#![feature(...)]` if we are in beta/stable
+    if sess.unstable_features.is_nightly_build() {
+        err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feature));
+    }
+
+    // If we're on stable and only emitting a "soft" warning, add a note to
+    // clarify that the feature isn't "on" (rather than being on but
+    // warning-worthy).
+    if !sess.unstable_features.is_nightly_build() && level == GateStrength::Soft {
+        err.help("a nightly build of the compiler is required to enable this feature");
+    }
+
+    err
+
+}
+
+const EXPLAIN_BOX_SYNTAX: &str =
+    "box expression syntax is experimental; you can call `Box::new` instead";
+
+pub const EXPLAIN_STMT_ATTR_SYNTAX: &str =
+    "attributes on expressions are experimental";
+
+pub const EXPLAIN_ALLOW_INTERNAL_UNSTABLE: &str =
+    "allow_internal_unstable side-steps feature gating and stability checks";
+pub const EXPLAIN_ALLOW_INTERNAL_UNSAFE: &str =
+    "allow_internal_unsafe side-steps the unsafe_code lint";
+
+pub const EXPLAIN_UNSIZED_TUPLE_COERCION: &str =
+    "unsized tuple coercion is not stable enough for use and is subject to change";
+
+struct PostExpansionVisitor<'a> {
+    context: &'a Context<'a>,
+    builtin_attributes: &'static FxHashMap<Symbol, &'static BuiltinAttribute>,
+}
+
+macro_rules! gate_feature_post {
+    ($cx: expr, $feature: ident, $span: expr, $explain: expr) => {{
+        let (cx, span) = ($cx, $span);
+        if !span.allows_unstable(sym::$feature) {
+            gate_feature!(cx.context, $feature, span, $explain)
+        }
+    }};
+    ($cx: expr, $feature: ident, $span: expr, $explain: expr, $level: expr) => {{
+        let (cx, span) = ($cx, $span);
+        if !span.allows_unstable(sym::$feature) {
+            gate_feature!(cx.context, $feature, span, $explain, $level)
+        }
+    }}
+}
+
+impl<'a> PostExpansionVisitor<'a> {
+    fn check_abi(&self, abi: Abi, span: Span) {
+        match abi {
+            Abi::RustIntrinsic => {
+                gate_feature_post!(&self, intrinsics, span,
+                                   "intrinsics are subject to change");
+            },
+            Abi::PlatformIntrinsic => {
+                gate_feature_post!(&self, platform_intrinsics, span,
+                                   "platform intrinsics are experimental and possibly buggy");
+            },
+            Abi::Vectorcall => {
+                gate_feature_post!(&self, abi_vectorcall, span,
+                                   "vectorcall is experimental and subject to change");
+            },
+            Abi::Thiscall => {
+                gate_feature_post!(&self, abi_thiscall, span,
+                                   "thiscall is experimental and subject to change");
+            },
+            Abi::RustCall => {
+                gate_feature_post!(&self, unboxed_closures, span,
+                                   "rust-call ABI is subject to change");
+            },
+            Abi::PtxKernel => {
+                gate_feature_post!(&self, abi_ptx, span,
+                                   "PTX ABIs are experimental and subject to change");
+            },
+            Abi::Unadjusted => {
+                gate_feature_post!(&self, abi_unadjusted, span,
+                                   "unadjusted ABI is an implementation detail and perma-unstable");
+            },
+            Abi::Msp430Interrupt => {
+                gate_feature_post!(&self, abi_msp430_interrupt, span,
+                                   "msp430-interrupt ABI is experimental and subject to change");
+            },
+            Abi::X86Interrupt => {
+                gate_feature_post!(&self, abi_x86_interrupt, span,
+                                   "x86-interrupt ABI is experimental and subject to change");
+            },
+            Abi::AmdGpuKernel => {
+                gate_feature_post!(&self, abi_amdgpu_kernel, span,
+                                   "amdgpu-kernel ABI is experimental and subject to change");
+            },
+            // Stable
+            Abi::Cdecl |
+            Abi::Stdcall |
+            Abi::Fastcall |
+            Abi::Aapcs |
+            Abi::Win64 |
+            Abi::SysV64 |
+            Abi::Rust |
+            Abi::C |
+            Abi::System => {}
+        }
+    }
+}
+
+impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
+    fn visit_attribute(&mut self, attr: &ast::Attribute) {
+        let attr_info = attr.ident().and_then(|ident| {
+            self.builtin_attributes.get(&ident.name).map(|a| *a)
+        });
+
+        // Check for gated attributes.
+        self.context.check_attribute(attr, attr_info, false);
+
+        if attr.check_name(sym::doc) {
+            if let Some(content) = attr.meta_item_list() {
+                if content.len() == 1 && content[0].check_name(sym::cfg) {
+                    gate_feature_post!(&self, doc_cfg, attr.span,
+                        "`#[doc(cfg(...))]` is experimental"
+                    );
+                } else if content.iter().any(|c| c.check_name(sym::masked)) {
+                    gate_feature_post!(&self, doc_masked, attr.span,
+                        "`#[doc(masked)]` is experimental"
+                    );
+                } else if content.iter().any(|c| c.check_name(sym::spotlight)) {
+                    gate_feature_post!(&self, doc_spotlight, attr.span,
+                        "`#[doc(spotlight)]` is experimental"
+                    );
+                } else if content.iter().any(|c| c.check_name(sym::alias)) {
+                    gate_feature_post!(&self, doc_alias, attr.span,
+                        "`#[doc(alias = \"...\")]` is experimental"
+                    );
+                } else if content.iter().any(|c| c.check_name(sym::keyword)) {
+                    gate_feature_post!(&self, doc_keyword, attr.span,
+                        "`#[doc(keyword = \"...\")]` is experimental"
+                    );
+                }
+            }
+        }
+
+        match attr_info {
+            // `rustc_dummy` doesn't have any restrictions specific to built-in attributes.
+            Some(&(name, _, template, _)) if name != sym::rustc_dummy =>
+                check_builtin_attribute(self.context.parse_sess, attr, name, template),
+            _ => if let Some(TokenTree::Token(token)) = attr.tokens.trees().next() {
+                if token == token::Eq {
+                    // All key-value attributes are restricted to meta-item syntax.
+                    attr.parse_meta(self.context.parse_sess).map_err(|mut err| err.emit()).ok();
+                }
+            }
+        }
+    }
+
+    fn visit_name(&mut self, sp: Span, name: ast::Name) {
+        if !name.as_str().is_ascii() {
+            gate_feature_post!(
+                &self,
+                non_ascii_idents,
+                self.context.parse_sess.source_map().def_span(sp),
+                "non-ascii idents are not fully supported"
+            );
+        }
+    }
+
+    fn visit_item(&mut self, i: &'a ast::Item) {
+        match i.node {
+            ast::ItemKind::ForeignMod(ref foreign_module) => {
+                self.check_abi(foreign_module.abi, i.span);
+            }
+
+            ast::ItemKind::Fn(..) => {
+                if attr::contains_name(&i.attrs[..], sym::plugin_registrar) {
+                    gate_feature_post!(&self, plugin_registrar, i.span,
+                                       "compiler plugins are experimental and possibly buggy");
+                }
+                if attr::contains_name(&i.attrs[..], sym::start) {
+                    gate_feature_post!(&self, start, i.span,
+                                      "a `#[start]` function is an experimental \
+                                       feature whose signature may change \
+                                       over time");
+                }
+                if attr::contains_name(&i.attrs[..], sym::main) {
+                    gate_feature_post!(&self, main, i.span,
+                                       "declaration of a non-standard `#[main]` \
+                                        function may change over time, for now \
+                                        a top-level `fn main()` is required");
+                }
+            }
+
+            ast::ItemKind::Struct(..) => {
+                for attr in attr::filter_by_name(&i.attrs[..], sym::repr) {
+                    for item in attr.meta_item_list().unwrap_or_else(Vec::new) {
+                        if item.check_name(sym::simd) {
+                            gate_feature_post!(&self, repr_simd, attr.span,
+                                               "SIMD types are experimental and possibly buggy");
+                        }
+                    }
+                }
+            }
+
+            ast::ItemKind::Enum(ast::EnumDef{ref variants, ..}, ..) => {
+                for variant in variants {
+                    match (&variant.data, &variant.disr_expr) {
+                        (ast::VariantData::Unit(..), _) => {},
+                        (_, Some(disr_expr)) =>
+                            gate_feature_post!(
+                                &self,
+                                arbitrary_enum_discriminant,
+                                disr_expr.value.span,
+                                "discriminants on non-unit variants are experimental"),
+                        _ => {},
+                    }
+                }
+
+                let has_feature = self.context.features.arbitrary_enum_discriminant;
+                if !has_feature && !i.span.allows_unstable(sym::arbitrary_enum_discriminant) {
+                    Parser::maybe_report_invalid_custom_discriminants(
+                        self.context.parse_sess,
+                        &variants,
+                    );
+                }
+            }
+
+            ast::ItemKind::Impl(_, polarity, defaultness, ..) => {
+                if polarity == ast::ImplPolarity::Negative {
+                    gate_feature_post!(&self, optin_builtin_traits,
+                                       i.span,
+                                       "negative trait bounds are not yet fully implemented; \
+                                        use marker types for now");
+                }
+
+                if let ast::Defaultness::Default = defaultness {
+                    gate_feature_post!(&self, specialization,
+                                       i.span,
+                                       "specialization is unstable");
+                }
+            }
+
+            ast::ItemKind::Trait(ast::IsAuto::Yes, ..) => {
+                gate_feature_post!(&self, optin_builtin_traits,
+                                   i.span,
+                                   "auto traits are experimental and possibly buggy");
+            }
+
+            ast::ItemKind::TraitAlias(..) => {
+                gate_feature_post!(
+                    &self,
+                    trait_alias,
+                    i.span,
+                    "trait aliases are experimental"
+                );
+            }
+
+            ast::ItemKind::MacroDef(ast::MacroDef { legacy: false, .. }) => {
+                let msg = "`macro` is experimental";
+                gate_feature_post!(&self, decl_macro, i.span, msg);
+            }
+
+            ast::ItemKind::OpaqueTy(..) => {
+                gate_feature_post!(
+                    &self,
+                    type_alias_impl_trait,
+                    i.span,
+                    "`impl Trait` in type aliases is unstable"
+                );
+            }
+
+            _ => {}
+        }
+
+        visit::walk_item(self, i);
+    }
+
+    fn visit_foreign_item(&mut self, i: &'a ast::ForeignItem) {
+        match i.node {
+            ast::ForeignItemKind::Fn(..) |
+            ast::ForeignItemKind::Static(..) => {
+                let link_name = attr::first_attr_value_str_by_name(&i.attrs, sym::link_name);
+                let links_to_llvm = match link_name {
+                    Some(val) => val.as_str().starts_with("llvm."),
+                    _ => false
+                };
+                if links_to_llvm {
+                    gate_feature_post!(&self, link_llvm_intrinsics, i.span,
+                                       "linking to LLVM intrinsics is experimental");
+                }
+            }
+            ast::ForeignItemKind::Ty => {
+                    gate_feature_post!(&self, extern_types, i.span,
+                                       "extern types are experimental");
+            }
+            ast::ForeignItemKind::Macro(..) => {}
+        }
+
+        visit::walk_foreign_item(self, i)
+    }
+
+    fn visit_ty(&mut self, ty: &'a ast::Ty) {
+        match ty.node {
+            ast::TyKind::BareFn(ref bare_fn_ty) => {
+                self.check_abi(bare_fn_ty.abi, ty.span);
+            }
+            ast::TyKind::Never => {
+                gate_feature_post!(&self, never_type, ty.span,
+                                   "The `!` type is experimental");
+            }
+            _ => {}
+        }
+        visit::walk_ty(self, ty)
+    }
+
+    fn visit_fn_ret_ty(&mut self, ret_ty: &'a ast::FunctionRetTy) {
+        if let ast::FunctionRetTy::Ty(ref output_ty) = *ret_ty {
+            if let ast::TyKind::Never = output_ty.node {
+                // Do nothing.
+            } else {
+                self.visit_ty(output_ty)
+            }
+        }
+    }
+
+    fn visit_expr(&mut self, e: &'a ast::Expr) {
+        match e.node {
+            ast::ExprKind::Box(_) => {
+                gate_feature_post!(&self, box_syntax, e.span, EXPLAIN_BOX_SYNTAX);
+            }
+            ast::ExprKind::Type(..) => {
+                // To avoid noise about type ascription in common syntax errors, only emit if it
+                // is the *only* error.
+                if self.context.parse_sess.span_diagnostic.err_count() == 0 {
+                    gate_feature_post!(&self, type_ascription, e.span,
+                                       "type ascription is experimental");
+                }
+            }
+            ast::ExprKind::TryBlock(_) => {
+                gate_feature_post!(&self, try_blocks, e.span, "`try` expression is experimental");
+            }
+            ast::ExprKind::Block(_, opt_label) => {
+                if let Some(label) = opt_label {
+                    gate_feature_post!(&self, label_break_value, label.ident.span,
+                                    "labels on blocks are unstable");
+                }
+            }
+            _ => {}
+        }
+        visit::walk_expr(self, e)
+    }
+
+    fn visit_arm(&mut self, arm: &'a ast::Arm) {
+        visit::walk_arm(self, arm)
+    }
+
+    fn visit_pat(&mut self, pattern: &'a ast::Pat) {
+        match &pattern.node {
+            PatKind::Slice(pats) => {
+                for pat in &*pats {
+                    let span = pat.span;
+                    let inner_pat = match &pat.node {
+                        PatKind::Ident(.., Some(pat)) => pat,
+                        _ => pat,
+                    };
+                    if inner_pat.is_rest() {
+                        gate_feature_post!(
+                            &self,
+                            slice_patterns,
+                            span,
+                            "subslice patterns are unstable"
+                        );
+                    }
+                }
+            }
+            PatKind::Box(..) => {
+                gate_feature_post!(&self, box_patterns,
+                                  pattern.span,
+                                  "box pattern syntax is experimental");
+            }
+            PatKind::Range(_, _, Spanned { node: RangeEnd::Excluded, .. }) => {
+                gate_feature_post!(&self, exclusive_range_pattern, pattern.span,
+                                   "exclusive range pattern syntax is experimental");
+            }
+            _ => {}
+        }
+        visit::walk_pat(self, pattern)
+    }
+
+    fn visit_fn(&mut self,
+                fn_kind: FnKind<'a>,
+                fn_decl: &'a ast::FnDecl,
+                span: Span,
+                _node_id: NodeId) {
+        if let Some(header) = fn_kind.header() {
+            // Stability of const fn methods are covered in
+            // `visit_trait_item` and `visit_impl_item` below; this is
+            // because default methods don't pass through this point.
+            self.check_abi(header.abi, span);
+        }
+
+        if fn_decl.c_variadic {
+            gate_feature_post!(&self, c_variadic, span, "C-variadic functions are unstable");
+        }
+
+        visit::walk_fn(self, fn_kind, fn_decl, span)
+    }
+
+    fn visit_generic_param(&mut self, param: &'a GenericParam) {
+        match param.kind {
+            GenericParamKind::Const { .. } =>
+                gate_feature_post!(&self, const_generics, param.ident.span,
+                    "const generics are unstable"),
+            _ => {}
+        }
+        visit::walk_generic_param(self, param)
+    }
+
+    fn visit_assoc_ty_constraint(&mut self, constraint: &'a AssocTyConstraint) {
+        match constraint.kind {
+            AssocTyConstraintKind::Bound { .. } =>
+                gate_feature_post!(&self, associated_type_bounds, constraint.span,
+                    "associated type bounds are unstable"),
+            _ => {}
+        }
+        visit::walk_assoc_ty_constraint(self, constraint)
+    }
+
+    fn visit_trait_item(&mut self, ti: &'a ast::TraitItem) {
+        match ti.node {
+            ast::TraitItemKind::Method(ref sig, ref block) => {
+                if block.is_none() {
+                    self.check_abi(sig.header.abi, ti.span);
+                }
+                if sig.decl.c_variadic {
+                    gate_feature_post!(&self, c_variadic, ti.span,
+                                       "C-variadic functions are unstable");
+                }
+                if sig.header.constness.node == ast::Constness::Const {
+                    gate_feature_post!(&self, const_fn, ti.span, "const fn is unstable");
+                }
+            }
+            ast::TraitItemKind::Type(_, ref default) => {
+                // We use three if statements instead of something like match guards so that all
+                // of these errors can be emitted if all cases apply.
+                if default.is_some() {
+                    gate_feature_post!(&self, associated_type_defaults, ti.span,
+                                       "associated type defaults are unstable");
+                }
+                if !ti.generics.params.is_empty() {
+                    gate_feature_post!(&self, generic_associated_types, ti.span,
+                                       "generic associated types are unstable");
+                }
+                if !ti.generics.where_clause.predicates.is_empty() {
+                    gate_feature_post!(&self, generic_associated_types, ti.span,
+                                       "where clauses on associated types are unstable");
+                }
+            }
+            _ => {}
+        }
+        visit::walk_trait_item(self, ti)
+    }
+
+    fn visit_impl_item(&mut self, ii: &'a ast::ImplItem) {
+        if ii.defaultness == ast::Defaultness::Default {
+            gate_feature_post!(&self, specialization,
+                              ii.span,
+                              "specialization is unstable");
+        }
+
+        match ii.node {
+            ast::ImplItemKind::Method(..) => {}
+            ast::ImplItemKind::OpaqueTy(..) => {
+                gate_feature_post!(
+                    &self,
+                    type_alias_impl_trait,
+                    ii.span,
+                    "`impl Trait` in type aliases is unstable"
+                );
+            }
+            ast::ImplItemKind::TyAlias(_) => {
+                if !ii.generics.params.is_empty() {
+                    gate_feature_post!(&self, generic_associated_types, ii.span,
+                                       "generic associated types are unstable");
+                }
+                if !ii.generics.where_clause.predicates.is_empty() {
+                    gate_feature_post!(&self, generic_associated_types, ii.span,
+                                       "where clauses on associated types are unstable");
+                }
+            }
+            _ => {}
+        }
+        visit::walk_impl_item(self, ii)
+    }
+
+    fn visit_vis(&mut self, vis: &'a ast::Visibility) {
+        if let ast::VisibilityKind::Crate(ast::CrateSugar::JustCrate) = vis.node {
+            gate_feature_post!(&self, crate_visibility_modifier, vis.span,
+                               "`crate` visibility modifier is experimental");
+        }
+        visit::walk_vis(self, vis)
+    }
+}
+
+pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute],
+                    crate_edition: Edition, allow_features: &Option<Vec<String>>) -> Features {
+    fn feature_removed(span_handler: &Handler, span: Span, reason: Option<&str>) {
+        let mut err = struct_span_err!(span_handler, span, E0557, "feature has been removed");
+        if let Some(reason) = reason {
+            err.span_note(span, reason);
+        } else {
+            err.span_label(span, "feature has been removed");
+        }
+        err.emit();
+    }
+
+    let mut features = Features::new();
+    let mut edition_enabled_features = FxHashMap::default();
+
+    for &edition in ALL_EDITIONS {
+        if edition <= crate_edition {
+            // The `crate_edition` implies its respective umbrella feature-gate
+            // (i.e., `#![feature(rust_20XX_preview)]` isn't needed on edition 20XX).
+            edition_enabled_features.insert(edition.feature_name(), edition);
+        }
+    }
+
+    for feature in active_features_up_to(crate_edition) {
+        feature.set(&mut features, DUMMY_SP);
+        edition_enabled_features.insert(feature.name, crate_edition);
+    }
+
+    // Process the edition umbrella feature-gates first, to ensure
+    // `edition_enabled_features` is completed before it's queried.
+    for attr in krate_attrs {
+        if !attr.check_name(sym::feature) {
+            continue
+        }
+
+        let list = match attr.meta_item_list() {
+            Some(list) => list,
+            None => continue,
+        };
+
+        for mi in list {
+            if !mi.is_word() {
+                continue;
+            }
+
+            let name = mi.name_or_empty();
+
+            let edition = ALL_EDITIONS.iter().find(|e| name == e.feature_name()).copied();
+            if let Some(edition) = edition {
+                if edition <= crate_edition {
+                    continue;
+                }
+
+                for feature in active_features_up_to(edition) {
+                    // FIXME(Manishearth) there is currently no way to set
+                    // lib features by edition
+                    feature.set(&mut features, DUMMY_SP);
+                    edition_enabled_features.insert(feature.name, edition);
+                }
+            }
+        }
+    }
+
+    for attr in krate_attrs {
+        if !attr.check_name(sym::feature) {
+            continue
+        }
+
+        let list = match attr.meta_item_list() {
+            Some(list) => list,
+            None => continue,
+        };
+
+        let bad_input = |span| {
+            struct_span_err!(span_handler, span, E0556, "malformed `feature` attribute input")
+        };
+
+        for mi in list {
+            let name = match mi.ident() {
+                Some(ident) if mi.is_word() => ident.name,
+                Some(ident) => {
+                    bad_input(mi.span()).span_suggestion(
+                        mi.span(),
+                        "expected just one word",
+                        format!("{}", ident.name),
+                        Applicability::MaybeIncorrect,
+                    ).emit();
+                    continue
+                }
+                None => {
+                    bad_input(mi.span()).span_label(mi.span(), "expected just one word").emit();
+                    continue
+                }
+            };
+
+            if let Some(edition) = edition_enabled_features.get(&name) {
+                struct_span_warn!(
+                    span_handler,
+                    mi.span(),
+                    E0705,
+                    "the feature `{}` is included in the Rust {} edition",
+                    name,
+                    edition,
+                ).emit();
+                continue;
+            }
+
+            if ALL_EDITIONS.iter().any(|e| name == e.feature_name()) {
+                // Handled in the separate loop above.
+                continue;
+            }
+
+            let removed = REMOVED_FEATURES.iter().find(|f| name == f.name);
+            let stable_removed = STABLE_REMOVED_FEATURES.iter().find(|f| name == f.name);
+            if let Some(Feature { state, .. }) = removed.or(stable_removed) {
+                if let FeatureState::Removed { reason }
+                | FeatureState::Stabilized { reason } = state
+                {
+                    feature_removed(span_handler, mi.span(), *reason);
+                    continue;
+                }
+            }
+
+            if let Some(Feature { since, .. }) = ACCEPTED_FEATURES.iter().find(|f| name == f.name) {
+                let since = Some(Symbol::intern(since));
+                features.declared_lang_features.push((name, mi.span(), since));
+                continue;
+            }
+
+            if let Some(allowed) = allow_features.as_ref() {
+                if allowed.iter().find(|f| *f == name.as_str()).is_none() {
+                    span_err!(span_handler, mi.span(), E0725,
+                              "the feature `{}` is not in the list of allowed features",
+                              name);
+                    continue;
+                }
+            }
+
+            if let Some(f) = ACTIVE_FEATURES.iter().find(|f| name == f.name) {
+                f.set(&mut features, mi.span());
+                features.declared_lang_features.push((name, mi.span(), None));
+                continue;
+            }
+
+            features.declared_lib_features.push((name, mi.span()));
+        }
+    }
+
+    features
+}
+
+fn active_features_up_to(edition: Edition) -> impl Iterator<Item=&'static Feature> {
+    ACTIVE_FEATURES.iter()
+    .filter(move |feature| {
+        if let Some(feature_edition) = feature.edition {
+            feature_edition <= edition
+        } else {
+            false
+        }
+    })
+}
+
+pub fn check_crate(krate: &ast::Crate,
+                   sess: &ParseSess,
+                   features: &Features,
+                   plugin_attributes: &[(Symbol, AttributeType)],
+                   unstable: UnstableFeatures) {
+    maybe_stage_features(&sess.span_diagnostic, krate, unstable);
+    let ctx = Context {
+        features,
+        parse_sess: sess,
+        plugin_attributes,
+    };
+
+    macro_rules! gate_all {
+        ($gate:ident, $msg:literal) => { gate_all!($gate, $gate, $msg); };
+        ($spans:ident, $gate:ident, $msg:literal) => {
+            for span in &*sess.gated_spans.$spans.borrow() {
+                gate_feature!(&ctx, $gate, *span, $msg);
+            }
+        }
+    }
+
+    gate_all!(param_attrs, "attributes on function parameters are unstable");
+    gate_all!(let_chains, "`let` expressions in this position are experimental");
+    gate_all!(async_closure, "async closures are unstable");
+    gate_all!(yields, generators, "yield syntax is experimental");
+    gate_all!(or_patterns, "or-patterns syntax is experimental");
+
+    let visitor = &mut PostExpansionVisitor {
+        context: &ctx,
+        builtin_attributes: &*BUILTIN_ATTRIBUTE_MAP,
+    };
+    visit::walk_crate(visitor, krate);
+}
+
+#[derive(Clone, Copy, Hash)]
+pub enum UnstableFeatures {
+    /// Hard errors for unstable features are active, as on beta/stable channels.
+    Disallow,
+    /// Allow features to be activated, as on nightly.
+    Allow,
+    /// Errors are bypassed for bootstrapping. This is required any time
+    /// during the build that feature-related lints are set to warn or above
+    /// because the build turns on warnings-as-errors and uses lots of unstable
+    /// features. As a result, this is always required for building Rust itself.
+    Cheat
+}
+
+impl UnstableFeatures {
+    pub fn from_environment() -> UnstableFeatures {
+        // Whether this is a feature-staged build, i.e., on the beta or stable channel
+        let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
+        // Whether we should enable unstable features for bootstrapping
+        let bootstrap = env::var("RUSTC_BOOTSTRAP").is_ok();
+        match (disable_unstable_features, bootstrap) {
+            (_, true) => UnstableFeatures::Cheat,
+            (true, _) => UnstableFeatures::Disallow,
+            (false, _) => UnstableFeatures::Allow
+        }
+    }
+
+    pub fn is_nightly_build(&self) -> bool {
+        match *self {
+            UnstableFeatures::Allow | UnstableFeatures::Cheat => true,
+            _ => false,
+        }
+    }
+}
+
+fn maybe_stage_features(span_handler: &Handler, krate: &ast::Crate, unstable: UnstableFeatures) {
+    let allow_features = match unstable {
+        UnstableFeatures::Allow => true,
+        UnstableFeatures::Disallow => false,
+        UnstableFeatures::Cheat => true
+    };
+    if !allow_features {
+        for attr in &krate.attrs {
+            if attr.check_name(sym::feature) {
+                let release_channel = option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)");
+                span_err!(span_handler, attr.span, E0554,
+                          "`#![feature]` may not be used on the {} release channel",
+                          release_channel);
+            }
+        }
+    }
+}
diff --git a/src/libsyntax/feature_gate/mod.rs b/src/libsyntax/feature_gate/mod.rs
new file mode 100644
index 00000000000..1e41667ea41
--- /dev/null
+++ b/src/libsyntax/feature_gate/mod.rs
@@ -0,0 +1,64 @@
+//! # Feature gating
+//!
+//! This module implements the gating necessary for preventing certain compiler
+//! features from being used by default. This module will crawl a pre-expanded
+//! AST to ensure that there are no features which are used that are not
+//! enabled.
+//!
+//! Features are enabled in programs via the crate-level attributes of
+//! `#![feature(...)]` with a comma-separated list of features.
+//!
+//! For the purpose of future feature-tracking, once code for detection of feature
+//! gate usage is added, *do not remove it again* even once the feature
+//! becomes stable.
+
+mod accepted;
+mod removed;
+mod active;
+mod builtin_attrs;
+mod check;
+
+use std::fmt;
+use crate::{edition::Edition, symbol::Symbol};
+use syntax_pos::Span;
+
+#[derive(Clone, Copy)]
+pub enum State {
+    Accepted,
+    Active { set: fn(&mut Features, Span) },
+    Removed { reason: Option<&'static str> },
+    Stabilized { reason: Option<&'static str> },
+}
+
+impl fmt::Debug for State {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            State::Accepted { .. } => write!(f, "accepted"),
+            State::Active { .. } => write!(f, "active"),
+            State::Removed { .. } => write!(f, "removed"),
+            State::Stabilized { .. } => write!(f, "stabilized"),
+        }
+    }
+}
+
+#[derive(Debug, Clone)]
+pub struct Feature {
+    state: State,
+    name: Symbol,
+    since: &'static str,
+    issue: Option<u32>,
+    edition: Option<Edition>,
+    description: &'static str,
+}
+
+pub use active::{Features, INCOMPLETE_FEATURES};
+pub use builtin_attrs::{
+    AttributeGate, AttributeType, GatedCfg,
+    BuiltinAttribute, BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP,
+    deprecated_attributes, is_builtin_attr,  is_builtin_attr_name,
+};
+pub use check::{
+    check_attribute, check_crate, get_features, feature_err, emit_feature_err,
+    Stability, GateIssue, UnstableFeatures,
+    EXPLAIN_STMT_ATTR_SYNTAX, EXPLAIN_UNSIZED_TUPLE_COERCION,
+};
diff --git a/src/libsyntax/feature_gate/removed.rs b/src/libsyntax/feature_gate/removed.rs
new file mode 100644
index 00000000000..ad7d69b3e73
--- /dev/null
+++ b/src/libsyntax/feature_gate/removed.rs
@@ -0,0 +1,105 @@
+//! List of the removed feature gates.
+
+use crate::symbol::sym;
+use super::{State, Feature};
+
+macro_rules! declare_features {
+    ($(
+        $(#[doc = $doc:tt])* (removed, $feature:ident, $ver:expr, $issue:expr, None, $reason:expr),
+    )+) => {
+        /// Represents unstable features which have since been removed (it was once Active)
+        pub const REMOVED_FEATURES: &[Feature] = &[
+            $(
+                Feature {
+                    state: State::Removed { reason: $reason },
+                    name: sym::$feature,
+                    since: $ver,
+                    issue: $issue,
+                    edition: None,
+                    description: concat!($($doc,)*),
+                }
+            ),+
+        ];
+    };
+
+    ($(
+        $(#[doc = $doc:tt])* (stable_removed, $feature:ident, $ver:expr, $issue:expr, None),
+    )+) => {
+        /// Represents stable features which have since been removed (it was once Accepted)
+        pub const STABLE_REMOVED_FEATURES: &[Feature] = &[
+            $(
+                Feature {
+                    state: State::Stabilized { reason: None },
+                    name: sym::$feature,
+                    since: $ver,
+                    issue: $issue,
+                    edition: None,
+                    description: concat!($($doc,)*),
+                }
+            ),+
+        ];
+    };
+}
+
+declare_features! (
+    // -------------------------------------------------------------------------
+    // feature-group-start: removed features
+    // -------------------------------------------------------------------------
+
+    (removed, import_shadowing, "1.0.0", None, None, None),
+    (removed, managed_boxes, "1.0.0", None, None, None),
+    /// Allows use of unary negate on unsigned integers, e.g., -e for e: u8
+    (removed, negate_unsigned, "1.0.0", Some(29645), None, None),
+    (removed, reflect, "1.0.0", Some(27749), None, None),
+    /// A way to temporarily opt out of opt in copy. This will *never* be accepted.
+    (removed, opt_out_copy, "1.0.0", None, None, None),
+    (removed, quad_precision_float, "1.0.0", None, None, None),
+    (removed, struct_inherit, "1.0.0", None, None, None),
+    (removed, test_removed_feature, "1.0.0", None, None, None),
+    (removed, visible_private_types, "1.0.0", None, None, None),
+    (removed, unsafe_no_drop_flag, "1.0.0", None, None, None),
+    /// Allows using items which are missing stability attributes
+    (removed, unmarked_api, "1.0.0", None, None, None),
+    (removed, allocator, "1.0.0", None, None, None),
+    (removed, simd, "1.0.0", Some(27731), None,
+     Some("removed in favor of `#[repr(simd)]`")),
+    (removed, advanced_slice_patterns, "1.0.0", Some(62254), None,
+     Some("merged into `#![feature(slice_patterns)]`")),
+    (removed, macro_reexport, "1.0.0", Some(29638), None,
+     Some("subsumed by `pub use`")),
+    (removed, pushpop_unsafe, "1.2.0", None, None, None),
+    (removed, needs_allocator, "1.4.0", Some(27389), None,
+     Some("subsumed by `#![feature(allocator_internals)]`")),
+    (removed, proc_macro_mod, "1.27.0", Some(54727), None,
+     Some("subsumed by `#![feature(proc_macro_hygiene)]`")),
+    (removed, proc_macro_expr, "1.27.0", Some(54727), None,
+     Some("subsumed by `#![feature(proc_macro_hygiene)]`")),
+    (removed, proc_macro_non_items, "1.27.0", Some(54727), None,
+     Some("subsumed by `#![feature(proc_macro_hygiene)]`")),
+    (removed, proc_macro_gen, "1.27.0", Some(54727), None,
+     Some("subsumed by `#![feature(proc_macro_hygiene)]`")),
+    (removed, panic_implementation, "1.28.0", Some(44489), None,
+     Some("subsumed by `#[panic_handler]`")),
+    /// Allows the use of `#[derive(Anything)]` as sugar for `#[derive_Anything]`.
+    (removed, custom_derive, "1.32.0", Some(29644), None,
+     Some("subsumed by `#[proc_macro_derive]`")),
+    /// Paths of the form: `extern::foo::bar`
+    (removed, extern_in_paths, "1.33.0", Some(55600), None,
+     Some("subsumed by `::foo::bar` paths")),
+    (removed, quote, "1.33.0", Some(29601), None, None),
+    /// Allows using `#[unsafe_destructor_blind_to_params]` (RFC 1238).
+    (removed, dropck_parametricity, "1.38.0", Some(28498), None, None),
+    (removed, await_macro, "1.38.0", Some(50547), None,
+     Some("subsumed by `.await` syntax")),
+    /// Allows defining `existential type`s.
+    (removed, existential_type, "1.38.0", Some(63063), None,
+     Some("removed in favor of `#![feature(type_alias_impl_trait)]`")),
+
+    // -------------------------------------------------------------------------
+    // feature-group-end: removed features
+    // -------------------------------------------------------------------------
+);
+
+declare_features! (
+    (stable_removed, no_stack_check, "1.0.0", None, None),
+);
diff --git a/src/libsyntax/json.rs b/src/libsyntax/json.rs
index 83c9c692bd3..ada46f7bc5a 100644
--- a/src/libsyntax/json.rs
+++ b/src/libsyntax/json.rs
@@ -219,7 +219,7 @@ impl Diagnostic {
         }
         let buf = BufWriter::default();
         let output = buf.clone();
-        je.json_rendered.new_emitter(Box::new(buf), Some(je.sm.clone()), false)
+        je.json_rendered.new_emitter(Box::new(buf), Some(je.sm.clone()), false, None)
             .ui_testing(je.ui_testing).emit_diagnostic(db);
         let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap();
         let output = String::from_utf8(output).unwrap();
diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs
index 8ac48d8d74a..1741932c1b8 100644
--- a/src/libsyntax/lib.rs
+++ b/src/libsyntax/lib.rs
@@ -124,7 +124,6 @@ pub mod diagnostics {
     #[macro_use]
     pub mod macros;
     pub mod plugin;
-    pub mod metadata;
 }
 
 // N.B., this module needs to be declared first so diagnostics are
diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs
index 9785f8e2de0..e14ca4b06a0 100644
--- a/src/libsyntax/mut_visit.rs
+++ b/src/libsyntax/mut_visit.rs
@@ -98,8 +98,8 @@ pub trait MutVisitor: Sized {
         noop_visit_fn_header(header, self);
     }
 
-    fn visit_struct_field(&mut self, sf: &mut StructField) {
-        noop_visit_struct_field(sf, self);
+    fn flat_map_struct_field(&mut self, sf: StructField) -> SmallVec<[StructField; 1]> {
+        noop_flat_map_struct_field(sf, self)
     }
 
     fn visit_item_kind(&mut self, i: &mut ItemKind) {
@@ -130,8 +130,8 @@ pub trait MutVisitor: Sized {
         noop_flat_map_stmt(s, self)
     }
 
-    fn visit_arm(&mut self, a: &mut Arm) {
-        noop_visit_arm(a, self);
+    fn flat_map_arm(&mut self, arm: Arm) -> SmallVec<[Arm; 1]> {
+        noop_flat_map_arm(arm, self)
     }
 
     fn visit_pat(&mut self, p: &mut P<Pat>) {
@@ -174,8 +174,8 @@ pub trait MutVisitor: Sized {
         noop_visit_foreign_mod(nm, self);
     }
 
-    fn visit_variant(&mut self, v: &mut Variant) {
-        noop_visit_variant(v, self);
+    fn flat_map_variant(&mut self, v: Variant) -> SmallVec<[Variant; 1]>  {
+        noop_flat_map_variant(v, self)
     }
 
     fn visit_ident(&mut self, i: &mut Ident) {
@@ -225,8 +225,8 @@ pub trait MutVisitor: Sized {
         noop_visit_attribute(at, self);
     }
 
-    fn visit_arg(&mut self, a: &mut Arg) {
-        noop_visit_arg(a, self);
+    fn flat_map_param(&mut self, param: Param) -> SmallVec<[Param; 1]> {
+        noop_flat_map_param(param, self)
     }
 
     fn visit_generics(&mut self, generics: &mut Generics) {
@@ -245,12 +245,8 @@ pub trait MutVisitor: Sized {
         noop_visit_variant_data(vdata, self);
     }
 
-    fn visit_generic_param(&mut self, param: &mut GenericParam) {
-        noop_visit_generic_param(param, self);
-    }
-
-    fn visit_generic_params(&mut self, params: &mut Vec<GenericParam>) {
-        noop_visit_generic_params(params, self);
+    fn flat_map_generic_param(&mut self, param: GenericParam) -> SmallVec<[GenericParam; 1]> {
+        noop_flat_map_generic_param(param, self)
     }
 
     fn visit_tt(&mut self, tt: &mut TokenTree) {
@@ -277,8 +273,8 @@ pub trait MutVisitor: Sized {
         noop_visit_mt(mt, self);
     }
 
-    fn visit_field(&mut self, field: &mut Field) {
-        noop_visit_field(field, self);
+    fn flat_map_field(&mut self, f: Field) -> SmallVec<[Field; 1]> {
+        noop_flat_map_field(f, self)
     }
 
     fn visit_where_clause(&mut self, where_clause: &mut WhereClause) {
@@ -300,6 +296,10 @@ pub trait MutVisitor: Sized {
     fn visit_span(&mut self, _sp: &mut Span) {
         // Do nothing.
     }
+
+    fn flat_map_field_pattern(&mut self, fp: FieldPat) -> SmallVec<[FieldPat; 1]> {
+        noop_flat_map_field_pattern(fp, self)
+    }
 }
 
 /// Use a map-style function (`FnOnce(T) -> T`) to overwrite a `&mut T`. Useful
@@ -362,6 +362,26 @@ pub fn visit_method_sig<T: MutVisitor>(MethodSig { header, decl }: &mut MethodSi
     vis.visit_fn_decl(decl);
 }
 
+pub fn noop_flat_map_field_pattern<T: MutVisitor>(
+    mut fp: FieldPat,
+    vis: &mut T,
+) -> SmallVec<[FieldPat; 1]> {
+    let FieldPat {
+        attrs,
+        id,
+        ident,
+        is_shorthand: _,
+        pat,
+        span,
+    } = &mut fp;
+    vis.visit_id(id);
+    vis.visit_ident(ident);
+    vis.visit_pat(pat);
+    vis.visit_span(span);
+    visit_thin_attrs(attrs, vis);
+    smallvec![fp]
+}
+
 pub fn noop_visit_use_tree<T: MutVisitor>(use_tree: &mut UseTree, vis: &mut T) {
     let UseTree { prefix, kind, span } = use_tree;
     vis.visit_path(prefix);
@@ -382,16 +402,18 @@ pub fn noop_visit_use_tree<T: MutVisitor>(use_tree: &mut UseTree, vis: &mut T) {
     vis.visit_span(span);
 }
 
-pub fn noop_visit_arm<T: MutVisitor>(
-    Arm { attrs, pats, guard, body, span, id }: &mut Arm,
+pub fn noop_flat_map_arm<T: MutVisitor>(
+    mut arm: Arm,
     vis: &mut T,
-) {
+) -> SmallVec<[Arm; 1]> {
+    let Arm { attrs, pats, guard, body, span, id } = &mut arm;
     visit_attrs(attrs, vis);
     vis.visit_id(id);
     visit_vec(pats, |pat| vis.visit_pat(pat));
     visit_opt(guard, |guard| vis.visit_expr(guard));
     vis.visit_expr(body);
     vis.visit_span(span);
+    smallvec![arm]
 }
 
 pub fn noop_visit_ty_constraint<T: MutVisitor>(
@@ -425,7 +447,7 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
         }
         TyKind::BareFn(bft) => {
             let BareFnTy { unsafety: _, abi: _, generic_params, decl } = bft.deref_mut();
-            vis.visit_generic_params(generic_params);
+            generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param));
             vis.visit_fn_decl(decl);
         }
         TyKind::Tup(tys) => visit_vec(tys, |ty| vis.visit_ty(ty)),
@@ -455,14 +477,17 @@ pub fn noop_visit_foreign_mod<T: MutVisitor>(foreign_mod: &mut ForeignMod, vis:
     items.flat_map_in_place(|item| vis.flat_map_foreign_item(item));
 }
 
-pub fn noop_visit_variant<T: MutVisitor>(variant: &mut Variant, vis: &mut T) {
-    let Variant { ident, attrs, id, data, disr_expr, span } = variant;
+pub fn noop_flat_map_variant<T: MutVisitor>(mut variant: Variant, vis: &mut T)
+    -> SmallVec<[Variant; 1]>
+{
+    let Variant { ident, attrs, id, data, disr_expr, span } = &mut variant;
     vis.visit_ident(ident);
     visit_attrs(attrs, vis);
     vis.visit_id(id);
     vis.visit_variant_data(data);
     visit_opt(disr_expr, |disr_expr| vis.visit_anon_const(disr_expr));
     vis.visit_span(span);
+    smallvec![variant]
 }
 
 pub fn noop_visit_ident<T: MutVisitor>(Ident { name: _, span }: &mut Ident, vis: &mut T) {
@@ -562,12 +587,14 @@ pub fn noop_visit_meta_item<T: MutVisitor>(mi: &mut MetaItem, vis: &mut T) {
     vis.visit_span(span);
 }
 
-pub fn noop_visit_arg<T: MutVisitor>(Arg { attrs, id, pat, span, ty }: &mut Arg, vis: &mut T) {
+pub fn noop_flat_map_param<T: MutVisitor>(mut param: Param, vis: &mut T) -> SmallVec<[Param; 1]> {
+    let Param { attrs, id, pat, span, ty } = &mut param;
     vis.visit_id(id);
     visit_thin_attrs(attrs, vis);
     vis.visit_pat(pat);
     vis.visit_span(span);
     vis.visit_ty(ty);
+    smallvec![param]
 }
 
 pub fn noop_visit_tt<T: MutVisitor>(tt: &mut TokenTree, vis: &mut T) {
@@ -693,7 +720,7 @@ pub fn noop_visit_asyncness<T: MutVisitor>(asyncness: &mut IsAsync, vis: &mut T)
 
 pub fn noop_visit_fn_decl<T: MutVisitor>(decl: &mut P<FnDecl>, vis: &mut T) {
     let FnDecl { inputs, output, c_variadic: _ } = decl.deref_mut();
-    visit_vec(inputs, |input| vis.visit_arg(input));
+    inputs.flat_map_in_place(|param| vis.flat_map_param(param));
     match output {
         FunctionRetTy::Default(span) => vis.visit_span(span),
         FunctionRetTy::Ty(ty) => vis.visit_ty(ty),
@@ -707,8 +734,12 @@ pub fn noop_visit_param_bound<T: MutVisitor>(pb: &mut GenericBound, vis: &mut T)
     }
 }
 
-pub fn noop_visit_generic_param<T: MutVisitor>(param: &mut GenericParam, vis: &mut T) {
-    let GenericParam { id, ident, attrs, bounds, kind } = param;
+pub fn noop_flat_map_generic_param<T: MutVisitor>(
+    mut param: GenericParam,
+    vis: &mut T
+) -> SmallVec<[GenericParam; 1]>
+{
+    let GenericParam { id, ident, attrs, bounds, kind } = &mut param;
     vis.visit_id(id);
     vis.visit_ident(ident);
     visit_thin_attrs(attrs, vis);
@@ -722,10 +753,7 @@ pub fn noop_visit_generic_param<T: MutVisitor>(param: &mut GenericParam, vis: &m
             vis.visit_ty(ty);
         }
     }
-}
-
-pub fn noop_visit_generic_params<T: MutVisitor>(params: &mut Vec<GenericParam>, vis: &mut T){
-    visit_vec(params, |param| vis.visit_generic_param(param));
+    smallvec![param]
 }
 
 pub fn noop_visit_label<T: MutVisitor>(Label { ident }: &mut Label, vis: &mut T) {
@@ -739,7 +767,7 @@ fn noop_visit_lifetime<T: MutVisitor>(Lifetime { id, ident }: &mut Lifetime, vis
 
 pub fn noop_visit_generics<T: MutVisitor>(generics: &mut Generics, vis: &mut T) {
     let Generics { params, where_clause, span } = generics;
-    vis.visit_generic_params(params);
+    params.flat_map_in_place(|param| vis.flat_map_generic_param(param));
     vis.visit_where_clause(where_clause);
     vis.visit_span(span);
 }
@@ -755,7 +783,7 @@ pub fn noop_visit_where_predicate<T: MutVisitor>(pred: &mut WherePredicate, vis:
         WherePredicate::BoundPredicate(bp) => {
             let WhereBoundPredicate { span, bound_generic_params, bounded_ty, bounds } = bp;
             vis.visit_span(span);
-            vis.visit_generic_params(bound_generic_params);
+            bound_generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param));
             vis.visit_ty(bounded_ty);
             visit_vec(bounds, |bound| vis.visit_param_bound(bound));
         }
@@ -777,9 +805,11 @@ pub fn noop_visit_where_predicate<T: MutVisitor>(pred: &mut WherePredicate, vis:
 
 pub fn noop_visit_variant_data<T: MutVisitor>(vdata: &mut VariantData, vis: &mut T) {
     match vdata {
-        VariantData::Struct(fields, ..) => visit_vec(fields, |field| vis.visit_struct_field(field)),
+        VariantData::Struct(fields, ..) => {
+            fields.flat_map_in_place(|field| vis.flat_map_struct_field(field));
+        },
         VariantData::Tuple(fields, id) => {
-            visit_vec(fields, |field| vis.visit_struct_field(field));
+            fields.flat_map_in_place(|field| vis.flat_map_struct_field(field));
             vis.visit_id(id);
         },
         VariantData::Unit(id) => vis.visit_id(id),
@@ -793,28 +823,32 @@ pub fn noop_visit_trait_ref<T: MutVisitor>(TraitRef { path, ref_id }: &mut Trait
 
 pub fn noop_visit_poly_trait_ref<T: MutVisitor>(p: &mut PolyTraitRef, vis: &mut T) {
     let PolyTraitRef { bound_generic_params, trait_ref, span } = p;
-    vis.visit_generic_params(bound_generic_params);
+    bound_generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param));
     vis.visit_trait_ref(trait_ref);
     vis.visit_span(span);
 }
 
-pub fn noop_visit_struct_field<T: MutVisitor>(f: &mut StructField, visitor: &mut T) {
-    let StructField { span, ident, vis, id, ty, attrs } = f;
+pub fn noop_flat_map_struct_field<T: MutVisitor>(mut sf: StructField, visitor: &mut T)
+    -> SmallVec<[StructField; 1]>
+{
+    let StructField { span, ident, vis, id, ty, attrs } = &mut sf;
     visitor.visit_span(span);
     visit_opt(ident, |ident| visitor.visit_ident(ident));
     visitor.visit_vis(vis);
     visitor.visit_id(id);
     visitor.visit_ty(ty);
     visit_attrs(attrs, visitor);
+    smallvec![sf]
 }
 
-pub fn noop_visit_field<T: MutVisitor>(f: &mut Field, vis: &mut T) {
-    let Field { ident, expr, span, is_shorthand: _, attrs, id } = f;
+pub fn noop_flat_map_field<T: MutVisitor>(mut f: Field, vis: &mut T) -> SmallVec<[Field; 1]> {
+    let Field { ident, expr, span, is_shorthand: _, attrs, id } = &mut f;
     vis.visit_ident(ident);
     vis.visit_expr(expr);
     vis.visit_id(id);
     vis.visit_span(span);
     visit_thin_attrs(attrs, vis);
+    smallvec![f]
 }
 
 pub fn noop_visit_mt<T: MutVisitor>(MutTy { ty, mutbl: _ }: &mut MutTy, vis: &mut T) {
@@ -858,7 +892,7 @@ pub fn noop_visit_item_kind<T: MutVisitor>(kind: &mut ItemKind, vis: &mut T) {
             vis.visit_generics(generics);
         }
         ItemKind::Enum(EnumDef { variants }, generics) => {
-            visit_vec(variants, |variant| vis.visit_variant(variant));
+            variants.flat_map_in_place(|variant| vis.flat_map_variant(variant));
             vis.visit_generics(generics);
         }
         ItemKind::Struct(variant_data, generics) |
@@ -1042,13 +1076,7 @@ pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, vis: &mut T) {
         }
         PatKind::Struct(path, fields, _etc) => {
             vis.visit_path(path);
-            for FieldPat { ident, pat, is_shorthand: _, attrs, id, span } in fields {
-                vis.visit_ident(ident);
-                vis.visit_id(id);
-                vis.visit_pat(pat);
-                visit_thin_attrs(attrs, vis);
-                vis.visit_span(span);
-            };
+            fields.flat_map_in_place(|field| vis.flat_map_field_pattern(field));
         }
         PatKind::Box(inner) => vis.visit_pat(inner),
         PatKind::Ref(inner, _mutbl) => vis.visit_pat(inner),
@@ -1130,7 +1158,7 @@ pub fn noop_visit_expr<T: MutVisitor>(Expr { node, id, span, attrs }: &mut Expr,
         }
         ExprKind::Match(expr, arms) => {
             vis.visit_expr(expr);
-            visit_vec(arms, |arm| vis.visit_arm(arm));
+            arms.flat_map_in_place(|arm| vis.flat_map_arm(arm));
         }
         ExprKind::Closure(_capture_by, asyncness, _movability, decl, body, span) => {
             vis.visit_asyncness(asyncness);
@@ -1193,7 +1221,7 @@ pub fn noop_visit_expr<T: MutVisitor>(Expr { node, id, span, attrs }: &mut Expr,
         ExprKind::Mac(mac) => vis.visit_mac(mac),
         ExprKind::Struct(path, fields, expr) => {
             vis.visit_path(path);
-            visit_vec(fields, |field| vis.visit_field(field));
+            fields.flat_map_in_place(|field| vis.flat_map_field(field));
             visit_opt(expr, |expr| vis.visit_expr(expr));
         },
         ExprKind::Paren(expr) => {
diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs
index a42da112360..671178223f5 100644
--- a/src/libsyntax/parse/attr.rs
+++ b/src/libsyntax/parse/attr.rs
@@ -19,11 +19,10 @@ const DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG: &str = "an inner attribute is not \
                                                      permitted in this context";
 
 impl<'a> Parser<'a> {
-    crate fn parse_arg_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
+    crate fn parse_param_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
         let attrs = self.parse_outer_attributes()?;
-        attrs.iter().for_each(|a|
-            self.sess.param_attr_spans.borrow_mut().push(a.span)
-        );
+        self.sess.gated_spans.param_attrs.borrow_mut()
+            .extend(attrs.iter().map(|a| a.span));
         Ok(attrs)
     }
 
diff --git a/src/libsyntax/parse/diagnostics.rs b/src/libsyntax/parse/diagnostics.rs
index 1fbf28fb830..d4e661d1a38 100644
--- a/src/libsyntax/parse/diagnostics.rs
+++ b/src/libsyntax/parse/diagnostics.rs
@@ -1,5 +1,5 @@
 use crate::ast::{
-    self, Arg, BinOpKind, BindingMode, BlockCheckMode, Expr, ExprKind, Ident, Item, ItemKind,
+    self, Param, BinOpKind, BindingMode, BlockCheckMode, Expr, ExprKind, Ident, Item, ItemKind,
     Mutability, Pat, PatKind, PathSegment, QSelf, Ty, TyKind, VariantData,
 };
 use crate::feature_gate::{feature_err, UnstableFeatures};
@@ -18,7 +18,7 @@ use log::{debug, trace};
 use std::mem;
 
 /// Creates a placeholder argument.
-crate fn dummy_arg(ident: Ident) -> Arg {
+crate fn dummy_arg(ident: Ident) -> Param {
     let pat = P(Pat {
         id: ast::DUMMY_NODE_ID,
         node: PatKind::Ident(BindingMode::ByValue(Mutability::Immutable), ident, None),
@@ -29,7 +29,7 @@ crate fn dummy_arg(ident: Ident) -> Arg {
         span: ident.span,
         id: ast::DUMMY_NODE_ID
     };
-    Arg { attrs: ThinVec::default(), id: ast::DUMMY_NODE_ID, pat, span: ident.span, ty: P(ty) }
+    Param { attrs: ThinVec::default(), id: ast::DUMMY_NODE_ID, pat, span: ident.span, ty: P(ty) }
 }
 
 pub enum Error {
@@ -1183,7 +1183,7 @@ impl<'a> Parser<'a> {
         Err(err)
     }
 
-    crate fn eat_incorrect_doc_comment_for_arg_type(&mut self) {
+    crate fn eat_incorrect_doc_comment_for_param_type(&mut self) {
         if let token::DocComment(_) = self.token.kind {
             self.struct_span_err(
                 self.token.span,
@@ -1211,7 +1211,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    crate fn argument_without_type(
+    crate fn parameter_without_type(
         &mut self,
         err: &mut DiagnosticBuilder<'_>,
         pat: P<ast::Pat>,
@@ -1286,13 +1286,13 @@ impl<'a> Parser<'a> {
         Ok((pat, ty))
     }
 
-    crate fn recover_bad_self_arg(
+    crate fn recover_bad_self_param(
         &mut self,
-        mut arg: ast::Arg,
+        mut param: ast::Param,
         is_trait_item: bool,
-    ) -> PResult<'a, ast::Arg> {
-        let sp = arg.pat.span;
-        arg.ty.node = TyKind::Err;
+    ) -> PResult<'a, ast::Param> {
+        let sp = param.pat.span;
+        param.ty.node = TyKind::Err;
         let mut err = self.struct_span_err(sp, "unexpected `self` parameter in function");
         if is_trait_item {
             err.span_label(sp, "must be the first associated function parameter");
@@ -1301,7 +1301,7 @@ impl<'a> Parser<'a> {
             err.note("`self` is only valid as the first parameter of an associated function");
         }
         err.emit();
-        Ok(arg)
+        Ok(param)
     }
 
     crate fn consume_block(&mut self, delim: token::DelimToken) {
@@ -1344,15 +1344,15 @@ impl<'a> Parser<'a> {
         err
     }
 
-    /// Replace duplicated recovered arguments with `_` pattern to avoid unecessary errors.
+    /// Replace duplicated recovered parameters with `_` pattern to avoid unecessary errors.
     ///
     /// This is necessary because at this point we don't know whether we parsed a function with
-    /// anonymous arguments or a function with names but no types. In order to minimize
-    /// unecessary errors, we assume the arguments are in the shape of `fn foo(a, b, c)` where
-    /// the arguments are *names* (so we don't emit errors about not being able to find `b` in
+    /// anonymous parameters or a function with names but no types. In order to minimize
+    /// unecessary errors, we assume the parameters are in the shape of `fn foo(a, b, c)` where
+    /// the parameters are *names* (so we don't emit errors about not being able to find `b` in
     /// the local scope), but if we find the same name multiple times, like in `fn foo(i8, i8)`,
-    /// we deduplicate them to not complain about duplicated argument names.
-    crate fn deduplicate_recovered_arg_names(&self, fn_inputs: &mut Vec<Arg>) {
+    /// we deduplicate them to not complain about duplicated parameter names.
+    crate fn deduplicate_recovered_params_names(&self, fn_inputs: &mut Vec<Param>) {
         let mut seen_inputs = FxHashSet::default();
         for input in fn_inputs.iter_mut() {
             let opt_ident = if let (PatKind::Ident(_, ident, _), TyKind::Err) = (
diff --git a/src/libsyntax/parse/lexer/tests.rs b/src/libsyntax/parse/lexer/tests.rs
index a915aa42fd1..652ae95c853 100644
--- a/src/libsyntax/parse/lexer/tests.rs
+++ b/src/libsyntax/parse/lexer/tests.rs
@@ -10,7 +10,14 @@ use errors::{Handler, emitter::EmitterWriter};
 use syntax_pos::{BytePos, Span};
 
 fn mk_sess(sm: Lrc<SourceMap>) -> ParseSess {
-    let emitter = EmitterWriter::new(Box::new(io::sink()), Some(sm.clone()), false, false, false);
+    let emitter = EmitterWriter::new(
+        Box::new(io::sink()),
+        Some(sm.clone()),
+        false,
+        false,
+        false,
+        None,
+    );
     ParseSess::with_span_handler(Handler::with_emitter(true, None, Box::new(emitter)), sm)
 }
 
@@ -28,10 +35,11 @@ fn t1() {
     with_default_globals(|| {
         let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
         let sh = mk_sess(sm.clone());
-        let mut string_reader = setup(&sm,
-                                    &sh,
-                                    "/* my source file */ fn main() { println!(\"zebra\"); }\n"
-                                        .to_string());
+        let mut string_reader = setup(
+            &sm,
+            &sh,
+            "/* my source file */ fn main() { println!(\"zebra\"); }\n".to_string(),
+        );
         assert_eq!(string_reader.next_token(), token::Comment);
         assert_eq!(string_reader.next_token(), token::Whitespace);
         let tok1 = string_reader.next_token();
@@ -127,8 +135,10 @@ fn character_a() {
     with_default_globals(|| {
         let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
         let sh = mk_sess(sm.clone());
-        assert_eq!(setup(&sm, &sh, "'a'".to_string()).next_token(),
-                    mk_lit(token::Char, "a", None));
+        assert_eq!(
+            setup(&sm, &sh, "'a'".to_string()).next_token(),
+            mk_lit(token::Char, "a", None),
+        );
     })
 }
 
@@ -137,8 +147,10 @@ fn character_space() {
     with_default_globals(|| {
         let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
         let sh = mk_sess(sm.clone());
-        assert_eq!(setup(&sm, &sh, "' '".to_string()).next_token(),
-                    mk_lit(token::Char, " ", None));
+        assert_eq!(
+            setup(&sm, &sh, "' '".to_string()).next_token(),
+            mk_lit(token::Char, " ", None),
+        );
     })
 }
 
@@ -147,8 +159,10 @@ fn character_escaped() {
     with_default_globals(|| {
         let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
         let sh = mk_sess(sm.clone());
-        assert_eq!(setup(&sm, &sh, "'\\n'".to_string()).next_token(),
-                    mk_lit(token::Char, "\\n", None));
+        assert_eq!(
+            setup(&sm, &sh, "'\\n'".to_string()).next_token(),
+            mk_lit(token::Char, "\\n", None),
+        );
     })
 }
 
@@ -157,8 +171,10 @@ fn lifetime_name() {
     with_default_globals(|| {
         let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
         let sh = mk_sess(sm.clone());
-        assert_eq!(setup(&sm, &sh, "'abc".to_string()).next_token(),
-                    token::Lifetime(Symbol::intern("'abc")));
+        assert_eq!(
+            setup(&sm, &sh, "'abc".to_string()).next_token(),
+            token::Lifetime(Symbol::intern("'abc")),
+        );
     })
 }
 
@@ -167,8 +183,10 @@ fn raw_string() {
     with_default_globals(|| {
         let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
         let sh = mk_sess(sm.clone());
-        assert_eq!(setup(&sm, &sh, "r###\"\"#a\\b\x00c\"\"###".to_string()).next_token(),
-                    mk_lit(token::StrRaw(3), "\"#a\\b\x00c\"", None));
+        assert_eq!(
+            setup(&sm, &sh, "r###\"\"#a\\b\x00c\"\"###".to_string()).next_token(),
+            mk_lit(token::StrRaw(3), "\"#a\\b\x00c\"", None),
+        );
     })
 }
 
@@ -179,11 +197,15 @@ fn literal_suffixes() {
         let sh = mk_sess(sm.clone());
         macro_rules! test {
             ($input: expr, $tok_type: ident, $tok_contents: expr) => {{
-                assert_eq!(setup(&sm, &sh, format!("{}suffix", $input)).next_token(),
-                            mk_lit(token::$tok_type, $tok_contents, Some("suffix")));
+                assert_eq!(
+                    setup(&sm, &sh, format!("{}suffix", $input)).next_token(),
+                    mk_lit(token::$tok_type, $tok_contents, Some("suffix")),
+                );
                 // with a whitespace separator:
-                assert_eq!(setup(&sm, &sh, format!("{} suffix", $input)).next_token(),
-                            mk_lit(token::$tok_type, $tok_contents, None));
+                assert_eq!(
+                    setup(&sm, &sh, format!("{} suffix", $input)).next_token(),
+                    mk_lit(token::$tok_type, $tok_contents, None),
+                );
             }}
         }
 
@@ -197,12 +219,18 @@ fn literal_suffixes() {
         test!("1.0", Float, "1.0");
         test!("1.0e10", Float, "1.0e10");
 
-        assert_eq!(setup(&sm, &sh, "2us".to_string()).next_token(),
-                    mk_lit(token::Integer, "2", Some("us")));
-        assert_eq!(setup(&sm, &sh, "r###\"raw\"###suffix".to_string()).next_token(),
-                    mk_lit(token::StrRaw(3), "raw", Some("suffix")));
-        assert_eq!(setup(&sm, &sh, "br###\"raw\"###suffix".to_string()).next_token(),
-                    mk_lit(token::ByteStrRaw(3), "raw", Some("suffix")));
+        assert_eq!(
+            setup(&sm, &sh, "2us".to_string()).next_token(),
+            mk_lit(token::Integer, "2", Some("us")),
+        );
+        assert_eq!(
+            setup(&sm, &sh, "r###\"raw\"###suffix".to_string()).next_token(),
+            mk_lit(token::StrRaw(3), "raw", Some("suffix")),
+        );
+        assert_eq!(
+            setup(&sm, &sh, "br###\"raw\"###suffix".to_string()).next_token(),
+            mk_lit(token::ByteStrRaw(3), "raw", Some("suffix")),
+        );
     })
 }
 
diff --git a/src/libsyntax/parse/literal.rs b/src/libsyntax/parse/literal.rs
index 6409acba573..36233de3cfb 100644
--- a/src/libsyntax/parse/literal.rs
+++ b/src/libsyntax/parse/literal.rs
@@ -104,7 +104,7 @@ impl LitKind {
 
         Ok(match kind {
             token::Bool => {
-                assert!(symbol == kw::True || symbol == kw::False);
+                assert!(symbol.is_bool_lit());
                 LitKind::Bool(symbol == kw::True)
             }
             token::Byte => return unescape_byte(&symbol.as_str())
@@ -261,7 +261,7 @@ impl Lit {
     /// Converts arbitrary token into an AST literal.
     crate fn from_token(token: &Token) -> Result<Lit, LitError> {
         let lit = match token.kind {
-            token::Ident(name, false) if name == kw::True || name == kw::False =>
+            token::Ident(name, false) if name.is_bool_lit() =>
                 token::Lit::new(token::Bool, name, None),
             token::Literal(lit) =>
                 lit,
diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs
index b1f3612a839..b1af4806e2d 100644
--- a/src/libsyntax/parse/mod.rs
+++ b/src/libsyntax/parse/mod.rs
@@ -39,6 +39,22 @@ crate mod unescape_error_reporting;
 
 pub type PResult<'a, T> = Result<T, DiagnosticBuilder<'a>>;
 
+/// Collected spans during parsing for places where a certain feature was
+/// used and should be feature gated accordingly in `check_crate`.
+#[derive(Default)]
+pub struct GatedSpans {
+    /// Spans collected for gating `param_attrs`, e.g. `fn foo(#[attr] x: u8) {}`.
+    pub param_attrs: Lock<Vec<Span>>,
+    /// Spans collected for gating `let_chains`, e.g. `if a && let b = c {}`.
+    pub let_chains: Lock<Vec<Span>>,
+    /// Spans collected for gating `async_closure`, e.g. `async || ..`.
+    pub async_closure: Lock<Vec<Span>>,
+    /// Spans collected for gating `yield e?` expressions (`generators` gate).
+    pub yields: Lock<Vec<Span>>,
+    /// Spans collected for gating `or_patterns`, e.g. `Some(Foo | Bar)`.
+    pub or_patterns: Lock<Vec<Span>>,
+}
+
 /// Info about a parsing session.
 pub struct ParseSess {
     pub span_diagnostic: Handler,
@@ -58,16 +74,8 @@ pub struct ParseSess {
     /// operation token that followed it, but that the parser cannot identify without further
     /// analysis.
     pub ambiguous_block_expr_parse: Lock<FxHashMap<Span, Span>>,
-    pub param_attr_spans: Lock<Vec<Span>>,
-    // Places where `let` exprs were used and should be feature gated according to `let_chains`.
-    pub let_chains_spans: Lock<Vec<Span>>,
-    // Places where `async || ..` exprs were used and should be feature gated.
-    pub async_closure_spans: Lock<Vec<Span>>,
-    // Places where `yield e?` exprs were used and should be feature gated.
-    pub yield_spans: Lock<Vec<Span>>,
     pub injected_crate_name: Once<Symbol>,
-    // Places where or-patterns e.g. `Some(Foo | Bar)` were used and should be feature gated.
-    pub or_pattern_spans: Lock<Vec<Span>>,
+    pub gated_spans: GatedSpans,
 }
 
 impl ParseSess {
@@ -93,12 +101,8 @@ impl ParseSess {
             buffered_lints: Lock::new(vec![]),
             edition: ExpnId::root().expn_data().edition,
             ambiguous_block_expr_parse: Lock::new(FxHashMap::default()),
-            param_attr_spans: Lock::new(Vec::new()),
-            let_chains_spans: Lock::new(Vec::new()),
-            async_closure_spans: Lock::new(Vec::new()),
-            yield_spans: Lock::new(Vec::new()),
             injected_crate_name: Once::new(),
-            or_pattern_spans: Lock::new(Vec::new()),
+            gated_spans: GatedSpans::default(),
         }
     }
 
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 89725d8b339..49b05551bae 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -10,7 +10,7 @@ pub use path::PathStyle;
 mod stmt;
 mod generics;
 
-use crate::ast::{self, AttrStyle, Attribute, Arg, BindingMode, StrStyle, SelfKind};
+use crate::ast::{self, AttrStyle, Attribute, Param, BindingMode, StrStyle, SelfKind};
 use crate::ast::{FnDecl, Ident, IsAsync, MacDelimiter, Mutability, TyKind};
 use crate::ast::{Visibility, VisibilityKind, Unsafety, CrateSugar};
 use crate::source_map::{self, respan};
@@ -375,10 +375,11 @@ impl<'a> Parser<'a> {
         if let Some(directory) = directory {
             parser.directory = directory;
         } else if !parser.token.span.is_dummy() {
-            if let FileName::Real(mut path) =
-                    sess.source_map().span_to_unmapped_path(parser.token.span) {
-                path.pop();
-                parser.directory.path = Cow::from(path);
+            if let Some(FileName::Real(path)) =
+                    &sess.source_map().lookup_char_pos(parser.token.span.lo()).file.unmapped_path {
+                if let Some(directory_path) = path.parent() {
+                    parser.directory.path = Cow::from(directory_path.to_path_buf());
+                }
             }
         }
 
@@ -970,30 +971,27 @@ impl<'a> Parser<'a> {
 
     /// Skips unexpected attributes and doc comments in this position and emits an appropriate
     /// error.
-    /// This version of parse arg doesn't necessarily require identifier names.
-    fn parse_arg_general<F>(
+    /// This version of parse param doesn't necessarily require identifier names.
+    fn parse_param_general(
         &mut self,
         is_trait_item: bool,
         allow_c_variadic: bool,
-        is_name_required: F,
-    ) -> PResult<'a, Arg>
-    where
-        F: Fn(&token::Token) -> bool
-    {
+        is_name_required: impl Fn(&token::Token) -> bool,
+    ) -> PResult<'a, Param> {
         let lo = self.token.span;
-        let attrs = self.parse_arg_attributes()?;
-        if let Some(mut arg) = self.parse_self_arg()? {
-            arg.attrs = attrs.into();
-            return self.recover_bad_self_arg(arg, is_trait_item);
+        let attrs = self.parse_param_attributes()?;
+        if let Some(mut param) = self.parse_self_param()? {
+            param.attrs = attrs.into();
+            return self.recover_bad_self_param(param, is_trait_item);
         }
 
         let is_name_required = is_name_required(&self.token);
         let (pat, ty) = if is_name_required || self.is_named_argument() {
-            debug!("parse_arg_general parse_pat (is_name_required:{})", is_name_required);
+            debug!("parse_param_general parse_pat (is_name_required:{})", is_name_required);
 
-            let pat = self.parse_pat(Some("argument name"))?;
+            let pat = self.parse_fn_param_pat()?;
             if let Err(mut err) = self.expect(&token::Colon) {
-                if let Some(ident) = self.argument_without_type(
+                if let Some(ident) = self.parameter_without_type(
                     &mut err,
                     pat,
                     is_name_required,
@@ -1006,12 +1004,12 @@ impl<'a> Parser<'a> {
                 }
             }
 
-            self.eat_incorrect_doc_comment_for_arg_type();
+            self.eat_incorrect_doc_comment_for_param_type();
             (pat, self.parse_ty_common(true, true, allow_c_variadic)?)
         } else {
-            debug!("parse_arg_general ident_to_pat");
+            debug!("parse_param_general ident_to_pat");
             let parser_snapshot_before_ty = self.clone();
-            self.eat_incorrect_doc_comment_for_arg_type();
+            self.eat_incorrect_doc_comment_for_param_type();
             let mut ty = self.parse_ty_common(true, true, allow_c_variadic);
             if ty.is_ok() && self.token != token::Comma &&
                self.token != token::CloseDelim(token::Paren) {
@@ -1042,7 +1040,7 @@ impl<'a> Parser<'a> {
 
         let span = lo.to(self.token.span);
 
-        Ok(Arg { attrs: attrs.into(), id: ast::DUMMY_NODE_ID, pat, span, ty })
+        Ok(Param { attrs: attrs.into(), id: ast::DUMMY_NODE_ID, pat, span, ty })
     }
 
     /// Parses mutability (`mut` or nothing).
@@ -1188,26 +1186,26 @@ impl<'a> Parser<'a> {
 
     }
 
-    fn parse_fn_args(&mut self, named_args: bool, allow_c_variadic: bool)
-                     -> PResult<'a, (Vec<Arg> , bool)> {
+    fn parse_fn_params(&mut self, named_params: bool, allow_c_variadic: bool)
+                     -> PResult<'a, (Vec<Param> , bool)> {
         let sp = self.token.span;
         let mut c_variadic = false;
-        let (args, _): (Vec<Option<Arg>>, _) = self.parse_paren_comma_seq(|p| {
+        let (params, _): (Vec<Option<Param>>, _) = self.parse_paren_comma_seq(|p| {
             let do_not_enforce_named_arguments_for_c_variadic =
                 |token: &token::Token| -> bool {
                     if token == &token::DotDotDot {
                         false
                     } else {
-                        named_args
+                        named_params
                     }
                 };
-            match p.parse_arg_general(
+            match p.parse_param_general(
                 false,
                 allow_c_variadic,
                 do_not_enforce_named_arguments_for_c_variadic
             ) {
-                Ok(arg) => {
-                    if let TyKind::CVarArgs = arg.ty.node {
+                Ok(param) => {
+                    if let TyKind::CVarArgs = param.ty.node {
                         c_variadic = true;
                         if p.token != token::CloseDelim(token::Paren) {
                             let span = p.token.span;
@@ -1215,10 +1213,10 @@ impl<'a> Parser<'a> {
                                 "`...` must be the last argument of a C-variadic function");
                             Ok(None)
                         } else {
-                            Ok(Some(arg))
+                            Ok(Some(param))
                         }
                     } else {
-                        Ok(Some(arg))
+                        Ok(Some(param))
                     }
                 },
                 Err(mut e) => {
@@ -1233,20 +1231,20 @@ impl<'a> Parser<'a> {
             }
         })?;
 
-        let args: Vec<_> = args.into_iter().filter_map(|x| x).collect();
+        let params: Vec<_> = params.into_iter().filter_map(|x| x).collect();
 
-        if c_variadic && args.len() <= 1 {
+        if c_variadic && params.len() <= 1 {
             self.span_err(sp,
                           "C-variadic function must be declared with at least one named argument");
         }
 
-        Ok((args, c_variadic))
+        Ok((params, c_variadic))
     }
 
-    /// Returns the parsed optional self argument and whether a self shortcut was used.
+    /// Returns the parsed optional self parameter and whether a self shortcut was used.
     ///
-    /// See `parse_self_arg_with_attrs` to collect attributes.
-    fn parse_self_arg(&mut self) -> PResult<'a, Option<Arg>> {
+    /// See `parse_self_param_with_attrs` to collect attributes.
+    fn parse_self_param(&mut self) -> PResult<'a, Option<Param>> {
         let expect_ident = |this: &mut Self| match this.token.kind {
             // Preserve hygienic context.
             token::Ident(name, _) =>
@@ -1351,49 +1349,51 @@ impl<'a> Parser<'a> {
         };
 
         let eself = source_map::respan(eself_lo.to(eself_hi), eself);
-        Ok(Some(Arg::from_self(ThinVec::default(), eself, eself_ident)))
+        Ok(Some(Param::from_self(ThinVec::default(), eself, eself_ident)))
     }
 
-    /// Returns the parsed optional self argument with attributes and whether a self
+    /// Returns the parsed optional self parameter with attributes and whether a self
     /// shortcut was used.
-    fn parse_self_arg_with_attrs(&mut self) -> PResult<'a, Option<Arg>> {
-        let attrs = self.parse_arg_attributes()?;
-        let arg_opt = self.parse_self_arg()?;
-        Ok(arg_opt.map(|mut arg| {
-            arg.attrs = attrs.into();
-            arg
+    fn parse_self_parameter_with_attrs(&mut self) -> PResult<'a, Option<Param>> {
+        let attrs = self.parse_param_attributes()?;
+        let param_opt = self.parse_self_param()?;
+        Ok(param_opt.map(|mut param| {
+            param.attrs = attrs.into();
+            param
         }))
     }
 
     /// Parses the parameter list and result type of a function that may have a `self` parameter.
-    fn parse_fn_decl_with_self<F>(&mut self, parse_arg_fn: F) -> PResult<'a, P<FnDecl>>
-        where F: FnMut(&mut Parser<'a>) -> PResult<'a,  Arg>,
+    fn parse_fn_decl_with_self<F>(&mut self, parse_param_fn: F) -> PResult<'a, P<FnDecl>>
+        where F: FnMut(&mut Parser<'a>) -> PResult<'a,  Param>,
     {
         self.expect(&token::OpenDelim(token::Paren))?;
 
         // Parse optional self argument.
-        let self_arg = self.parse_self_arg_with_attrs()?;
+        let self_param = self.parse_self_parameter_with_attrs()?;
 
         // Parse the rest of the function parameter list.
         let sep = SeqSep::trailing_allowed(token::Comma);
-        let (mut fn_inputs, recovered) = if let Some(self_arg) = self_arg {
+        let (mut fn_inputs, recovered) = if let Some(self_param) = self_param {
             if self.check(&token::CloseDelim(token::Paren)) {
-                (vec![self_arg], false)
+                (vec![self_param], false)
             } else if self.eat(&token::Comma) {
-                let mut fn_inputs = vec![self_arg];
+                let mut fn_inputs = vec![self_param];
                 let (mut input, _, recovered) = self.parse_seq_to_before_end(
-                    &token::CloseDelim(token::Paren), sep, parse_arg_fn)?;
+                    &token::CloseDelim(token::Paren), sep, parse_param_fn)?;
                 fn_inputs.append(&mut input);
                 (fn_inputs, recovered)
             } else {
                 match self.expect_one_of(&[], &[]) {
                     Err(err) => return Err(err),
-                    Ok(recovered) => (vec![self_arg], recovered),
+                    Ok(recovered) => (vec![self_param], recovered),
                 }
             }
         } else {
             let (input, _, recovered) =
-                self.parse_seq_to_before_end(&token::CloseDelim(token::Paren), sep, parse_arg_fn)?;
+                self.parse_seq_to_before_end(&token::CloseDelim(token::Paren),
+                                             sep,
+                                             parse_param_fn)?;
             (input, recovered)
         };
 
@@ -1401,8 +1401,8 @@ impl<'a> Parser<'a> {
             // Parse closing paren and return type.
             self.expect(&token::CloseDelim(token::Paren))?;
         }
-        // Replace duplicated recovered arguments with `_` pattern to avoid unecessary errors.
-        self.deduplicate_recovered_arg_names(&mut fn_inputs);
+        // Replace duplicated recovered params with `_` pattern to avoid unecessary errors.
+        self.deduplicate_recovered_params_names(&mut fn_inputs);
 
         Ok(P(FnDecl {
             inputs: fn_inputs,
diff --git a/src/libsyntax/parse/parser/expr.rs b/src/libsyntax/parse/parser/expr.rs
index ccc6bd15067..5b9f0f1df67 100644
--- a/src/libsyntax/parse/parser/expr.rs
+++ b/src/libsyntax/parse/parser/expr.rs
@@ -1,12 +1,13 @@
 use super::{Parser, PResult, Restrictions, PrevTokenKind, TokenType, PathStyle};
 use super::{BlockMode, SemiColonMode};
 use super::{SeqSep, TokenExpectType};
+use super::pat::{GateOr, PARAM_EXPECTED};
 
 use crate::maybe_recover_from_interpolated_ty_qpath;
 use crate::ptr::P;
 use crate::ast::{self, Attribute, AttrStyle, Ident, CaptureBy, BlockCheckMode};
 use crate::ast::{Expr, ExprKind, RangeLimits, Label, Movability, IsAsync, Arm};
-use crate::ast::{Ty, TyKind, FunctionRetTy, Arg, FnDecl};
+use crate::ast::{Ty, TyKind, FunctionRetTy, Param, FnDecl};
 use crate::ast::{BinOpKind, BinOp, UnOp};
 use crate::ast::{Mac, AnonConst, Field};
 
@@ -999,7 +1000,7 @@ impl<'a> Parser<'a> {
                     }
 
                     let span = lo.to(hi);
-                    self.sess.yield_spans.borrow_mut().push(span);
+                    self.sess.gated_spans.yields.borrow_mut().push(span);
                 } else if self.eat_keyword(kw::Let) {
                     return self.parse_let_expr(attrs);
                 } else if is_span_rust_2018 && self.eat_keyword(kw::Await) {
@@ -1111,7 +1112,7 @@ impl<'a> Parser<'a> {
         };
         if asyncness.is_async() {
             // Feature gate `async ||` closures.
-            self.sess.async_closure_spans.borrow_mut().push(self.prev_span);
+            self.sess.gated_spans.async_closure.borrow_mut().push(self.prev_span);
         }
 
         let capture_clause = self.parse_capture_clause();
@@ -1156,7 +1157,7 @@ impl<'a> Parser<'a> {
                     &[&token::BinOp(token::Or), &token::OrOr],
                     SeqSep::trailing_allowed(token::Comma),
                     TokenExpectType::NoExpect,
-                    |p| p.parse_fn_block_arg()
+                    |p| p.parse_fn_block_param()
                 )?.0;
                 self.expect_or()?;
                 args
@@ -1171,11 +1172,11 @@ impl<'a> Parser<'a> {
         }))
     }
 
-    /// Parses an argument in a lambda header (e.g., `|arg, arg|`).
-    fn parse_fn_block_arg(&mut self) -> PResult<'a, Arg> {
+    /// Parses a parameter in a lambda header (e.g., `|arg, arg|`).
+    fn parse_fn_block_param(&mut self) -> PResult<'a, Param> {
         let lo = self.token.span;
-        let attrs = self.parse_arg_attributes()?;
-        let pat = self.parse_pat(Some("argument name"))?;
+        let attrs = self.parse_param_attributes()?;
+        let pat = self.parse_pat(PARAM_EXPECTED)?;
         let t = if self.eat(&token::Colon) {
             self.parse_ty()?
         } else {
@@ -1186,7 +1187,7 @@ impl<'a> Parser<'a> {
             })
         };
         let span = lo.to(self.token.span);
-        Ok(Arg {
+        Ok(Param {
             attrs: attrs.into(),
             ty: t,
             pat,
@@ -1234,26 +1235,27 @@ impl<'a> Parser<'a> {
 
         if let ExprKind::Let(..) = cond.node {
             // Remove the last feature gating of a `let` expression since it's stable.
-            let last = self.sess.let_chains_spans.borrow_mut().pop();
+            let last = self.sess.gated_spans.let_chains.borrow_mut().pop();
             debug_assert_eq!(cond.span, last.unwrap());
         }
 
         Ok(cond)
     }
 
-    /// Parses a `let $pats = $expr` pseudo-expression.
+    /// Parses a `let $pat = $expr` pseudo-expression.
     /// The `let` token has already been eaten.
     fn parse_let_expr(&mut self, attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> {
         let lo = self.prev_span;
-        let pats = self.parse_pats()?;
+        // FIXME(or_patterns, Centril | dlrobertson): use `parse_top_pat` instead.
+        let pat = self.parse_top_pat_unpack(GateOr::No)?;
         self.expect(&token::Eq)?;
         let expr = self.with_res(
             Restrictions::NO_STRUCT_LITERAL,
             |this| this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into())
         )?;
         let span = lo.to(expr.span);
-        self.sess.let_chains_spans.borrow_mut().push(span);
-        Ok(self.mk_expr(span, ExprKind::Let(pats, expr), attrs))
+        self.sess.gated_spans.let_chains.borrow_mut().push(span);
+        Ok(self.mk_expr(span, ExprKind::Let(pat, expr), attrs))
     }
 
     /// `else` token already eaten
@@ -1283,7 +1285,7 @@ impl<'a> Parser<'a> {
             _ => None,
         };
 
-        let pat = self.parse_top_level_pat()?;
+        let pat = self.parse_top_pat(GateOr::Yes)?;
         if !self.eat_keyword(kw::In) {
             let in_span = self.prev_span.between(self.token.span);
             self.struct_span_err(in_span, "missing `in` in `for` loop")
@@ -1387,7 +1389,8 @@ impl<'a> Parser<'a> {
     crate fn parse_arm(&mut self) -> PResult<'a, Arm> {
         let attrs = self.parse_outer_attributes()?;
         let lo = self.token.span;
-        let pats = self.parse_pats()?;
+        // FIXME(or_patterns, Centril | dlrobertson): use `parse_top_pat` instead.
+        let pat = self.parse_top_pat_unpack(GateOr::No)?;
         let guard = if self.eat_keyword(kw::If) {
             Some(self.parse_expr()?)
         } else {
@@ -1448,7 +1451,7 @@ impl<'a> Parser<'a> {
 
         Ok(ast::Arm {
             attrs,
-            pats,
+            pats: pat, // FIXME(or_patterns, Centril | dlrobertson): this should just be `pat,`.
             guard,
             body: expr,
             span: lo.to(hi),
diff --git a/src/libsyntax/parse/parser/item.rs b/src/libsyntax/parse/parser/item.rs
index 72819c99660..59a3ade9c30 100644
--- a/src/libsyntax/parse/parser/item.rs
+++ b/src/libsyntax/parse/parser/item.rs
@@ -422,7 +422,7 @@ impl<'a> Parser<'a> {
             } else if self.look_ahead(1, |t| *t == token::OpenDelim(token::Paren)) {
                 let ident = self.parse_ident().unwrap();
                 self.bump();  // `(`
-                let kw_name = if let Ok(Some(_)) = self.parse_self_arg_with_attrs()
+                let kw_name = if let Ok(Some(_)) = self.parse_self_parameter_with_attrs()
                     .map_err(|mut e| e.cancel())
                 {
                     "method"
@@ -475,7 +475,7 @@ impl<'a> Parser<'a> {
                 self.eat_to_tokens(&[&token::Gt]);
                 self.bump();  // `>`
                 let (kw, kw_name, ambiguous) = if self.eat(&token::OpenDelim(token::Paren)) {
-                    if let Ok(Some(_)) = self.parse_self_arg_with_attrs()
+                    if let Ok(Some(_)) = self.parse_self_parameter_with_attrs()
                         .map_err(|mut e| e.cancel())
                     {
                         ("fn", "method", false)
@@ -825,6 +825,7 @@ impl<'a> Parser<'a> {
             self.is_keyword_ahead(1, &[
                 kw::Impl,
                 kw::Const,
+                kw::Async,
                 kw::Fn,
                 kw::Unsafe,
                 kw::Extern,
@@ -860,7 +861,7 @@ impl<'a> Parser<'a> {
             let ident = self.parse_ident()?;
             let mut generics = self.parse_generics()?;
             let decl = self.parse_fn_decl_with_self(|p| {
-                p.parse_arg_general(true, false, |_| true)
+                p.parse_param_general(true, false, |_| true)
             })?;
             generics.where_clause = self.parse_where_clause()?;
             *at_end = true;
@@ -1039,7 +1040,7 @@ impl<'a> Parser<'a> {
 
                 // We don't allow argument names to be left off in edition 2018.
                 let is_name_required = p.token.span.rust_2018();
-                p.parse_arg_general(true, false, |_| is_name_required)
+                p.parse_param_general(true, false, |_| is_name_required)
             })?;
             generics.where_clause = self.parse_where_clause()?;
 
@@ -1290,7 +1291,7 @@ impl<'a> Parser<'a> {
 
     /// Parses the argument list and result type of a function declaration.
     fn parse_fn_decl(&mut self, allow_c_variadic: bool) -> PResult<'a, P<FnDecl>> {
-        let (args, c_variadic) = self.parse_fn_args(true, allow_c_variadic)?;
+        let (args, c_variadic) = self.parse_fn_params(true, allow_c_variadic)?;
         let ret_ty = self.parse_ret_ty(true)?;
 
         Ok(P(FnDecl {
diff --git a/src/libsyntax/parse/parser/pat.rs b/src/libsyntax/parse/parser/pat.rs
index fd458aec743..823f880337d 100644
--- a/src/libsyntax/parse/parser/pat.rs
+++ b/src/libsyntax/parse/parser/pat.rs
@@ -4,6 +4,7 @@ use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
 use crate::ptr::P;
 use crate::ast::{self, Attribute, Pat, PatKind, FieldPat, RangeEnd, RangeSyntax, Mac};
 use crate::ast::{BindingMode, Ident, Mutability, Path, QSelf, Expr, ExprKind};
+use crate::mut_visit::{noop_visit_pat, MutVisitor};
 use crate::parse::token::{self};
 use crate::print::pprust;
 use crate::source_map::{respan, Span, Spanned};
@@ -12,80 +13,184 @@ use crate::ThinVec;
 
 use errors::{Applicability, DiagnosticBuilder};
 
+type Expected = Option<&'static str>;
+
+/// `Expected` for function and lambda parameter patterns.
+pub(super) const PARAM_EXPECTED: Expected = Some("parameter name");
+
+/// Whether or not an or-pattern should be gated when occurring in the current context.
+#[derive(PartialEq)]
+pub enum GateOr { Yes, No }
+
+/// Whether or not to recover a `,` when parsing or-patterns.
+#[derive(PartialEq, Copy, Clone)]
+enum RecoverComma { Yes, No }
+
 impl<'a> Parser<'a> {
     /// Parses a pattern.
-    pub fn parse_pat(
+    ///
+    /// Corresponds to `pat<no_top_alt>` in RFC 2535 and does not admit or-patterns
+    /// at the top level. Used when parsing the parameters of lambda expressions,
+    /// functions, function pointers, and `pat` macro fragments.
+    pub fn parse_pat(&mut self, expected: Expected) -> PResult<'a, P<Pat>> {
+        self.parse_pat_with_range_pat(true, expected)
+    }
+
+    // FIXME(or_patterns, Centril | dlrobertson):
+    // remove this and use `parse_top_pat` everywhere it is used instead.
+    pub(super) fn parse_top_pat_unpack(&mut self, gate_or: GateOr) -> PResult<'a, Vec<P<Pat>>> {
+        self.parse_top_pat(gate_or)
+            .map(|pat| pat.and_then(|pat| match pat.node {
+                PatKind::Or(pats) => pats,
+                node => vec![self.mk_pat(pat.span, node)],
+            }))
+    }
+
+    /// Entry point to the main pattern parser.
+    /// Corresponds to `top_pat` in RFC 2535 and allows or-pattern at the top level.
+    pub(super) fn parse_top_pat(&mut self, gate_or: GateOr) -> PResult<'a, P<Pat>> {
+        // Allow a '|' before the pats (RFCs 1925, 2530, and 2535).
+        let gated_leading_vert = self.eat_or_separator() && gate_or == GateOr::Yes;
+        let leading_vert_span = self.prev_span;
+
+        // Parse the possibly-or-pattern.
+        let pat = self.parse_pat_with_or(None, gate_or, RecoverComma::Yes)?;
+
+        // If we parsed a leading `|` which should be gated,
+        // and no other gated or-pattern has been parsed thus far,
+        // then we should really gate the leading `|`.
+        // This complicated procedure is done purely for diagnostics UX.
+        if gated_leading_vert {
+            let mut or_pattern_spans = self.sess.gated_spans.or_patterns.borrow_mut();
+            if or_pattern_spans.is_empty() {
+                or_pattern_spans.push(leading_vert_span);
+            }
+        }
+
+        Ok(pat)
+    }
+
+    /// Parse the pattern for a function or function pointer parameter.
+    /// Special recovery is provided for or-patterns and leading `|`.
+    pub(super) fn parse_fn_param_pat(&mut self) -> PResult<'a, P<Pat>> {
+        self.recover_leading_vert("not allowed in a parameter pattern");
+        let pat = self.parse_pat_with_or(PARAM_EXPECTED, GateOr::No, RecoverComma::No)?;
+
+        if let PatKind::Or(..) = &pat.node {
+            self.ban_illegal_fn_param_or_pat(&pat);
+        }
+
+        Ok(pat)
+    }
+
+    /// Ban `A | B` immediately in a parameter pattern and suggest wrapping in parens.
+    fn ban_illegal_fn_param_or_pat(&self, pat: &Pat) {
+        let msg = "wrap the pattern in parenthesis";
+        let fix = format!("({})", pprust::pat_to_string(pat));
+        self.struct_span_err(pat.span, "an or-pattern parameter must be wrapped in parenthesis")
+            .span_suggestion(pat.span, msg, fix, Applicability::MachineApplicable)
+            .emit();
+    }
+
+    /// Parses a pattern, that may be a or-pattern (e.g. `Foo | Bar` in `Some(Foo | Bar)`).
+    /// Corresponds to `pat<allow_top_alt>` in RFC 2535.
+    fn parse_pat_with_or(
         &mut self,
-        expected: Option<&'static str>
+        expected: Expected,
+        gate_or: GateOr,
+        rc: RecoverComma,
     ) -> PResult<'a, P<Pat>> {
-        self.parse_pat_with_range_pat(true, expected)
+        // Parse the first pattern.
+        let first_pat = self.parse_pat(expected)?;
+        self.maybe_recover_unexpected_comma(first_pat.span, rc)?;
+
+        // If the next token is not a `|`,
+        // this is not an or-pattern and we should exit here.
+        if !self.check(&token::BinOp(token::Or)) && self.token != token::OrOr {
+            return Ok(first_pat)
+        }
+
+        let lo = first_pat.span;
+        let mut pats = vec![first_pat];
+        while self.eat_or_separator() {
+            let pat = self.parse_pat(expected).map_err(|mut err| {
+                err.span_label(lo, "while parsing this or-pattern starting here");
+                err
+            })?;
+            self.maybe_recover_unexpected_comma(pat.span, rc)?;
+            pats.push(pat);
+        }
+        let or_pattern_span = lo.to(self.prev_span);
+
+        // Feature gate the or-pattern if instructed:
+        if gate_or == GateOr::Yes {
+            self.sess.gated_spans.or_patterns.borrow_mut().push(or_pattern_span);
+        }
+
+        Ok(self.mk_pat(or_pattern_span, PatKind::Or(pats)))
     }
 
-    /// Parses patterns, separated by '|' s.
-    pub(super) fn parse_pats(&mut self) -> PResult<'a, Vec<P<Pat>>> {
-        // Allow a '|' before the pats (RFC 1925 + RFC 2530)
-        self.eat(&token::BinOp(token::Or));
-
-        let mut pats = Vec::new();
-        loop {
-            pats.push(self.parse_top_level_pat()?);
-
-            if self.token == token::OrOr {
-                self.struct_span_err(self.token.span, "unexpected token `||` after pattern")
-                    .span_suggestion(
-                        self.token.span,
-                        "use a single `|` to specify multiple patterns",
-                        "|".to_owned(),
-                        Applicability::MachineApplicable
-                    )
-                    .emit();
+    /// Eat the or-pattern `|` separator.
+    /// If instead a `||` token is encountered, recover and pretend we parsed `|`.
+    fn eat_or_separator(&mut self) -> bool {
+        match self.token.kind {
+            token::OrOr => {
+                // Found `||`; Recover and pretend we parsed `|`.
+                self.ban_unexpected_or_or();
                 self.bump();
-            } else if self.eat(&token::BinOp(token::Or)) {
-                // This is a No-op. Continue the loop to parse the next
-                // pattern.
-            } else {
-                return Ok(pats);
+                true
             }
-        };
+            _ => self.eat(&token::BinOp(token::Or)),
+        }
     }
 
-    /// A wrapper around `parse_pat` with some special error handling for the
-    /// "top-level" patterns in a match arm, `for` loop, `let`, &c. (in contrast
-    /// to subpatterns within such).
-    pub(super) fn parse_top_level_pat(&mut self) -> PResult<'a, P<Pat>> {
-        let pat = self.parse_pat(None)?;
-        if self.token == token::Comma {
-            // An unexpected comma after a top-level pattern is a clue that the
-            // user (perhaps more accustomed to some other language) forgot the
-            // parentheses in what should have been a tuple pattern; return a
-            // suggestion-enhanced error here rather than choking on the comma
-            // later.
-            let comma_span = self.token.span;
-            self.bump();
-            if let Err(mut err) = self.skip_pat_list() {
-                // We didn't expect this to work anyway; we just wanted
-                // to advance to the end of the comma-sequence so we know
-                // the span to suggest parenthesizing
-                err.cancel();
-            }
-            let seq_span = pat.span.to(self.prev_span);
-            let mut err = self.struct_span_err(comma_span, "unexpected `,` in pattern");
-            if let Ok(seq_snippet) = self.span_to_snippet(seq_span) {
-                err.span_suggestion(
-                    seq_span,
-                    "try adding parentheses to match on a tuple..",
-                    format!("({})", seq_snippet),
-                    Applicability::MachineApplicable
-                ).span_suggestion(
-                    seq_span,
-                    "..or a vertical bar to match on multiple alternatives",
-                    format!("{}", seq_snippet.replace(",", " |")),
-                    Applicability::MachineApplicable
-                );
-            }
-            return Err(err);
+    /// We have parsed `||` instead of `|`. Error and suggest `|` instead.
+    fn ban_unexpected_or_or(&mut self) {
+        self.struct_span_err(self.token.span, "unexpected token `||` after pattern")
+            .span_suggestion(
+                self.token.span,
+                "use a single `|` to separate multiple alternative patterns",
+                "|".to_owned(),
+                Applicability::MachineApplicable
+            )
+            .emit();
+    }
+
+    /// Some special error handling for the "top-level" patterns in a match arm,
+    /// `for` loop, `let`, &c. (in contrast to subpatterns within such).
+    fn maybe_recover_unexpected_comma(&mut self, lo: Span, rc: RecoverComma) -> PResult<'a, ()> {
+        if rc == RecoverComma::No || self.token != token::Comma {
+            return Ok(());
         }
-        Ok(pat)
+
+        // An unexpected comma after a top-level pattern is a clue that the
+        // user (perhaps more accustomed to some other language) forgot the
+        // parentheses in what should have been a tuple pattern; return a
+        // suggestion-enhanced error here rather than choking on the comma later.
+        let comma_span = self.token.span;
+        self.bump();
+        if let Err(mut err) = self.skip_pat_list() {
+            // We didn't expect this to work anyway; we just wanted to advance to the
+            // end of the comma-sequence so we know the span to suggest parenthesizing.
+            err.cancel();
+        }
+        let seq_span = lo.to(self.prev_span);
+        let mut err = self.struct_span_err(comma_span, "unexpected `,` in pattern");
+        if let Ok(seq_snippet) = self.span_to_snippet(seq_span) {
+            err.span_suggestion(
+                seq_span,
+                "try adding parentheses to match on a tuple..",
+                format!("({})", seq_snippet),
+                Applicability::MachineApplicable
+            )
+            .span_suggestion(
+                seq_span,
+                "..or a vertical bar to match on multiple alternatives",
+                format!("{}", seq_snippet.replace(",", " |")),
+                Applicability::MachineApplicable
+            );
+        }
+        Err(err)
     }
 
     /// Parse and throw away a parentesized comma separated
@@ -100,32 +205,26 @@ impl<'a> Parser<'a> {
         Ok(())
     }
 
-    /// Parses a pattern, that may be a or-pattern (e.g. `Some(Foo | Bar)`).
-    fn parse_pat_with_or(&mut self, expected: Option<&'static str>) -> PResult<'a, P<Pat>> {
-        // Parse the first pattern.
-        let first_pat = self.parse_pat(expected)?;
-
-        // If the next token is not a `|`, this is not an or-pattern and
-        // we should exit here.
-        if !self.check(&token::BinOp(token::Or)) {
-            return Ok(first_pat)
-        }
+    /// Recursive possibly-or-pattern parser with recovery for an erroneous leading `|`.
+    /// See `parse_pat_with_or` for details on parsing or-patterns.
+    fn parse_pat_with_or_inner(&mut self) -> PResult<'a, P<Pat>> {
+        self.recover_leading_vert("only allowed in a top-level pattern");
+        self.parse_pat_with_or(None, GateOr::Yes, RecoverComma::No)
+    }
 
-        let lo = first_pat.span;
+    /// Recover if `|` or `||` is here.
+    /// The user is thinking that a leading `|` is allowed in this position.
+    fn recover_leading_vert(&mut self, ctx: &str) {
+        if let token::BinOp(token::Or) | token::OrOr = self.token.kind {
+            let span = self.token.span;
+            let rm_msg = format!("remove the `{}`", pprust::token_to_string(&self.token));
 
-        let mut pats = vec![first_pat];
+            self.struct_span_err(span, &format!("a leading `|` is {}", ctx))
+                .span_suggestion(span, &rm_msg, String::new(), Applicability::MachineApplicable)
+                .emit();
 
-        while self.eat(&token::BinOp(token::Or)) {
-            pats.push(self.parse_pat_with_range_pat(
-                true, expected
-            )?);
+            self.bump();
         }
-
-        let or_pattern_span = lo.to(self.prev_span);
-
-        self.sess.or_pattern_spans.borrow_mut().push(or_pattern_span);
-
-        Ok(self.mk_pat(or_pattern_span, PatKind::Or(pats)))
     }
 
     /// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are
@@ -133,7 +232,7 @@ impl<'a> Parser<'a> {
     fn parse_pat_with_range_pat(
         &mut self,
         allow_range_pat: bool,
-        expected: Option<&'static str>,
+        expected: Expected,
     ) -> PResult<'a, P<Pat>> {
         maybe_recover_from_interpolated_ty_qpath!(self, true);
         maybe_whole!(self, NtPat, |x| x);
@@ -144,7 +243,11 @@ impl<'a> Parser<'a> {
             token::OpenDelim(token::Paren) => self.parse_pat_tuple_or_parens()?,
             token::OpenDelim(token::Bracket) => {
                 // Parse `[pat, pat,...]` as a slice pattern.
-                PatKind::Slice(self.parse_delim_comma_seq(token::Bracket, |p| p.parse_pat(None))?.0)
+                let (pats, _) = self.parse_delim_comma_seq(
+                    token::Bracket,
+                    |p| p.parse_pat_with_or_inner(),
+                )?;
+                PatKind::Slice(pats)
             }
             token::DotDot => {
                 self.bump();
@@ -171,7 +274,7 @@ impl<'a> Parser<'a> {
                 // Parse _
                 PatKind::Wild
             } else if self.eat_keyword(kw::Mut) {
-                self.recover_pat_ident_mut_first()?
+                self.parse_pat_ident_mut()?
             } else if self.eat_keyword(kw::Ref) {
                 // Parse ref ident @ pat / ref mut ident @ pat
                 let mutbl = self.parse_mutability();
@@ -179,13 +282,12 @@ impl<'a> Parser<'a> {
             } else if self.eat_keyword(kw::Box) {
                 // Parse `box pat`
                 PatKind::Box(self.parse_pat_with_range_pat(false, None)?)
-            } else if self.token.is_ident() && !self.token.is_reserved_ident() &&
-                      self.parse_as_ident() {
+            } else if self.can_be_ident_pat() {
                 // Parse `ident @ pat`
                 // This can give false positives and parse nullary enums,
                 // they are dealt with later in resolve.
                 self.parse_pat_ident(BindingMode::ByValue(Mutability::Immutable))?
-            } else if self.token.is_path_start() {
+            } else if self.is_start_of_pat_with_path() {
                 // Parse pattern starting with a path
                 let (qself, path) = if self.eat_lt() {
                     // Parse a qualified path
@@ -255,7 +357,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse `&pat` / `&mut pat`.
-    fn parse_pat_deref(&mut self, expected: Option<&'static str>) -> PResult<'a, PatKind> {
+    fn parse_pat_deref(&mut self, expected: Expected) -> PResult<'a, PatKind> {
         self.expect_and()?;
         let mutbl = self.parse_mutability();
 
@@ -271,9 +373,7 @@ impl<'a> Parser<'a> {
 
     /// Parse a tuple or parenthesis pattern.
     fn parse_pat_tuple_or_parens(&mut self) -> PResult<'a, PatKind> {
-        let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| {
-            p.parse_pat_with_or(None)
-        })?;
+        let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| p.parse_pat_with_or_inner())?;
 
         // Here, `(pat,)` is a tuple pattern.
         // For backward compatibility, `(..)` is a tuple pattern as well.
@@ -284,24 +384,108 @@ impl<'a> Parser<'a> {
         })
     }
 
+    /// Parse a mutable binding with the `mut` token already eaten.
+    fn parse_pat_ident_mut(&mut self) -> PResult<'a, PatKind> {
+        let mut_span = self.prev_span;
+
+        if self.eat_keyword(kw::Ref) {
+            return self.recover_mut_ref_ident(mut_span)
+        }
+
+        self.recover_additional_muts();
+
+        // Make sure we don't allow e.g. `let mut $p;` where `$p:pat`.
+        if let token::Interpolated(ref nt) = self.token.kind {
+             if let token::NtPat(_) = **nt {
+                 self.expected_ident_found().emit();
+             }
+        }
+
+        // Parse the pattern we hope to be an identifier.
+        let mut pat = self.parse_pat(Some("identifier"))?;
+
+        // Add `mut` to any binding in the parsed pattern.
+        let changed_any_binding = Self::make_all_value_bindings_mutable(&mut pat);
+
+        // Unwrap; If we don't have `mut $ident`, error.
+        let pat = pat.into_inner();
+        match &pat.node {
+            PatKind::Ident(..) => {}
+            _ => self.ban_mut_general_pat(mut_span, &pat, changed_any_binding),
+        }
+
+        Ok(pat.node)
+    }
+
     /// Recover on `mut ref? ident @ pat` and suggest
     /// that the order of `mut` and `ref` is incorrect.
-    fn recover_pat_ident_mut_first(&mut self) -> PResult<'a, PatKind> {
-        let mutref_span = self.prev_span.to(self.token.span);
-        let binding_mode = if self.eat_keyword(kw::Ref) {
-            self.struct_span_err(mutref_span, "the order of `mut` and `ref` is incorrect")
-                .span_suggestion(
-                    mutref_span,
-                    "try switching the order",
-                    "ref mut".into(),
-                    Applicability::MachineApplicable
-                )
-                .emit();
-            BindingMode::ByRef(Mutability::Mutable)
+    fn recover_mut_ref_ident(&mut self, lo: Span) -> PResult<'a, PatKind> {
+        let mutref_span = lo.to(self.prev_span);
+        self.struct_span_err(mutref_span, "the order of `mut` and `ref` is incorrect")
+            .span_suggestion(
+                mutref_span,
+                "try switching the order",
+                "ref mut".into(),
+                Applicability::MachineApplicable
+            )
+            .emit();
+
+        self.parse_pat_ident(BindingMode::ByRef(Mutability::Mutable))
+    }
+
+    /// Turn all by-value immutable bindings in a pattern into mutable bindings.
+    /// Returns `true` if any change was made.
+    fn make_all_value_bindings_mutable(pat: &mut P<Pat>) -> bool {
+        struct AddMut(bool);
+        impl MutVisitor for AddMut {
+            fn visit_pat(&mut self, pat: &mut P<Pat>) {
+                if let PatKind::Ident(BindingMode::ByValue(ref mut m @ Mutability::Immutable), ..)
+                    = pat.node
+                {
+                    *m = Mutability::Mutable;
+                    self.0 = true;
+                }
+                noop_visit_pat(pat, self);
+            }
+        }
+
+        let mut add_mut = AddMut(false);
+        add_mut.visit_pat(pat);
+        add_mut.0
+    }
+
+    /// Error on `mut $pat` where `$pat` is not an ident.
+    fn ban_mut_general_pat(&self, lo: Span, pat: &Pat, changed_any_binding: bool) {
+        let span = lo.to(pat.span);
+        let fix = pprust::pat_to_string(&pat);
+        let (problem, suggestion) = if changed_any_binding {
+            ("`mut` must be attached to each individual binding", "add `mut` to each binding")
         } else {
-            BindingMode::ByValue(Mutability::Mutable)
+            ("`mut` must be followed by a named binding", "remove the `mut` prefix")
         };
-        self.parse_pat_ident(binding_mode)
+        self.struct_span_err(span, problem)
+            .span_suggestion(span, suggestion, fix, Applicability::MachineApplicable)
+            .note("`mut` may be followed by `variable` and `variable @ pattern`")
+            .emit()
+    }
+
+    /// Eat any extraneous `mut`s and error + recover if we ate any.
+    fn recover_additional_muts(&mut self) {
+        let lo = self.token.span;
+        while self.eat_keyword(kw::Mut) {}
+        if lo == self.token.span {
+            return;
+        }
+
+        let span = lo.to(self.prev_span);
+        self.struct_span_err(span, "`mut` on a binding may not be repeated")
+            .span_suggestion(
+                span,
+                "remove the additional `mut`s",
+                String::new(),
+                Applicability::MachineApplicable,
+            )
+            .emit();
     }
 
     /// Parse macro invocation
@@ -361,7 +545,7 @@ impl<'a> Parser<'a> {
     fn fatal_unexpected_non_pat(
         &mut self,
         mut err: DiagnosticBuilder<'a>,
-        expected: Option<&'static str>,
+        expected: Expected,
     ) -> PResult<'a, P<Pat>> {
         self.cancel(&mut err);
 
@@ -379,17 +563,6 @@ impl<'a> Parser<'a> {
         Err(err)
     }
 
-    // Helper function to decide whether to parse as ident binding
-    // or to try to do something more complex like range patterns.
-    fn parse_as_ident(&mut self) -> bool {
-        self.look_ahead(1, |t| match t.kind {
-            token::OpenDelim(token::Paren) | token::OpenDelim(token::Brace) |
-            token::DotDotDot | token::DotDotEq | token::DotDot |
-            token::ModSep | token::Not => false,
-            _ => true,
-        })
-    }
-
     /// Is the current token suitable as the start of a range patterns end?
     fn is_pat_range_end_start(&self) -> bool {
         self.token.is_path_start() // e.g. `MY_CONST`;
@@ -463,6 +636,30 @@ impl<'a> Parser<'a> {
         }
     }
 
+    /// Is this the start of a pattern beginning with a path?
+    fn is_start_of_pat_with_path(&mut self) -> bool {
+        self.check_path()
+        // Just for recovery (see `can_be_ident`).
+        || self.token.is_ident() && !self.token.is_bool_lit() && !self.token.is_keyword(kw::In)
+    }
+
+    /// Would `parse_pat_ident` be appropriate here?
+    fn can_be_ident_pat(&mut self) -> bool {
+        self.check_ident()
+        && !self.token.is_bool_lit() // Avoid `true` or `false` as a binding as it is a literal.
+        && !self.token.is_path_segment_keyword() // Avoid e.g. `Self` as it is a path.
+        // Avoid `in`. Due to recovery in the list parser this messes with `for ( $pat in $expr )`.
+        && !self.token.is_keyword(kw::In)
+        && self.look_ahead(1, |t| match t.kind { // Try to do something more complex?
+            token::OpenDelim(token::Paren) // A tuple struct pattern.
+            | token::OpenDelim(token::Brace) // A struct pattern.
+            | token::DotDotDot | token::DotDotEq | token::DotDot // A range pattern.
+            | token::ModSep // A tuple / struct variant pattern.
+            | token::Not => false, // A macro expanding to a pattern.
+            _ => true,
+        })
+    }
+
     /// Parses `ident` or `ident @ pat`.
     /// Used by the copy foo and ref foo patterns to give a good
     /// error message when parsing mistakes like `ref foo(a, b)`.
@@ -516,7 +713,7 @@ impl<'a> Parser<'a> {
             err.span_label(self.token.span, msg);
             return Err(err);
         }
-        let (fields, _) = self.parse_paren_comma_seq(|p| p.parse_pat_with_or(None))?;
+        let (fields, _) = self.parse_paren_comma_seq(|p| p.parse_pat_with_or_inner())?;
         Ok(PatKind::TupleStruct(path, fields))
     }
 
@@ -660,7 +857,7 @@ impl<'a> Parser<'a> {
             // Parsing a pattern of the form "fieldname: pat"
             let fieldname = self.parse_field_name()?;
             self.bump();
-            let pat = self.parse_pat_with_or(None)?;
+            let pat = self.parse_pat_with_or_inner()?;
             hi = pat.span;
             (pat, fieldname, false)
         } else {
diff --git a/src/libsyntax/parse/parser/path.rs b/src/libsyntax/parse/parser/path.rs
index 3eb4d45045a..d4b13cc2e01 100644
--- a/src/libsyntax/parse/parser/path.rs
+++ b/src/libsyntax/parse/parser/path.rs
@@ -423,7 +423,7 @@ impl<'a> Parser<'a> {
                     // FIXME(const_generics): to distinguish between idents for types and consts,
                     // we should introduce a GenericArg::Ident in the AST and distinguish when
                     // lowering to the HIR. For now, idents for const args are not permitted.
-                    if self.token.is_keyword(kw::True) || self.token.is_keyword(kw::False) {
+                    if self.token.is_bool_lit() {
                         self.parse_literal_maybe_minus()?
                     } else {
                         return Err(
diff --git a/src/libsyntax/parse/parser/stmt.rs b/src/libsyntax/parse/parser/stmt.rs
index c911caba4cd..651ebf6342e 100644
--- a/src/libsyntax/parse/parser/stmt.rs
+++ b/src/libsyntax/parse/parser/stmt.rs
@@ -1,6 +1,7 @@
 use super::{Parser, PResult, Restrictions, PrevTokenKind, SemiColonMode, BlockMode};
 use super::expr::LhsExpr;
 use super::path::PathStyle;
+use super::pat::GateOr;
 
 use crate::ptr::P;
 use crate::{maybe_whole, ThinVec};
@@ -207,7 +208,7 @@ impl<'a> Parser<'a> {
     /// Parses a local variable declaration.
     fn parse_local(&mut self, attrs: ThinVec<Attribute>) -> PResult<'a, P<Local>> {
         let lo = self.prev_span;
-        let pat = self.parse_top_level_pat()?;
+        let pat = self.parse_top_pat(GateOr::Yes)?;
 
         let (err, ty) = if self.eat(&token::Colon) {
             // Save the state of the parser before parsing type normally, in case there is a `:`
diff --git a/src/libsyntax/parse/parser/ty.rs b/src/libsyntax/parse/parser/ty.rs
index 337702b8d30..465e31ac57e 100644
--- a/src/libsyntax/parse/parser/ty.rs
+++ b/src/libsyntax/parse/parser/ty.rs
@@ -292,7 +292,7 @@ impl<'a> Parser<'a> {
         };
 
         self.expect_keyword(kw::Fn)?;
-        let (inputs, c_variadic) = self.parse_fn_args(false, true)?;
+        let (inputs, c_variadic) = self.parse_fn_params(false, true)?;
         let ret_ty = self.parse_ret_ty(false)?;
         let decl = P(FnDecl {
             inputs,
diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index 1865f925165..fe3b51aa246 100644
--- a/src/libsyntax/parse/token.rs
+++ b/src/libsyntax/parse/token.rs
@@ -409,7 +409,7 @@ impl Token {
     crate fn expect_lit(&self) -> Lit {
         match self.kind {
             Literal(lit) => lit,
-            _=> panic!("`expect_lit` called on non-literal"),
+            _ => panic!("`expect_lit` called on non-literal"),
         }
     }
 
@@ -417,10 +417,8 @@ impl Token {
     /// for example a '-42', or one of the boolean idents).
     crate fn can_begin_literal_or_bool(&self) -> bool {
         match self.kind {
-            Literal(..)  => true,
-            BinOp(Minus) => true,
-            Ident(name, false) if name == kw::True => true,
-            Ident(name, false) if name == kw::False => true,
+            Literal(..) | BinOp(Minus) => true,
+            Ident(name, false) if name.is_bool_lit() => true,
             Interpolated(ref nt) => match **nt {
                 NtLiteral(..) => true,
                 _             => false,
@@ -457,6 +455,7 @@ impl Token {
     pub fn is_ident(&self) -> bool {
         self.ident().is_some()
     }
+
     /// Returns `true` if the token is a lifetime.
     crate fn is_lifetime(&self) -> bool {
         self.lifetime().is_some()
@@ -508,45 +507,43 @@ impl Token {
 
     /// Returns `true` if the token is a given keyword, `kw`.
     pub fn is_keyword(&self, kw: Symbol) -> bool {
-        self.ident().map(|(id, is_raw)| id.name == kw && !is_raw).unwrap_or(false)
+        self.is_non_raw_ident_where(|id| id.name == kw)
     }
 
     crate fn is_path_segment_keyword(&self) -> bool {
-        match self.ident() {
-            Some((id, false)) => id.is_path_segment_keyword(),
-            _ => false,
-        }
+        self.is_non_raw_ident_where(ast::Ident::is_path_segment_keyword)
     }
 
     // Returns true for reserved identifiers used internally for elided lifetimes,
     // unnamed method parameters, crate root module, error recovery etc.
     crate fn is_special_ident(&self) -> bool {
-        match self.ident() {
-            Some((id, false)) => id.is_special(),
-            _ => false,
-        }
+        self.is_non_raw_ident_where(ast::Ident::is_special)
     }
 
     /// Returns `true` if the token is a keyword used in the language.
     crate fn is_used_keyword(&self) -> bool {
-        match self.ident() {
-            Some((id, false)) => id.is_used_keyword(),
-            _ => false,
-        }
+        self.is_non_raw_ident_where(ast::Ident::is_used_keyword)
     }
 
     /// Returns `true` if the token is a keyword reserved for possible future use.
     crate fn is_unused_keyword(&self) -> bool {
-        match self.ident() {
-            Some((id, false)) => id.is_unused_keyword(),
-            _ => false,
-        }
+        self.is_non_raw_ident_where(ast::Ident::is_unused_keyword)
     }
 
     /// Returns `true` if the token is either a special identifier or a keyword.
     pub fn is_reserved_ident(&self) -> bool {
+        self.is_non_raw_ident_where(ast::Ident::is_reserved)
+    }
+
+    /// Returns `true` if the token is the identifier `true` or `false`.
+    crate fn is_bool_lit(&self) -> bool {
+        self.is_non_raw_ident_where(|id| id.name.is_bool_lit())
+    }
+
+    /// Returns `true` if the token is a non-raw identifier for which `pred` holds.
+    fn is_non_raw_ident_where(&self, pred: impl FnOnce(ast::Ident) -> bool) -> bool {
         match self.ident() {
-            Some((id, false)) => id.is_reserved(),
+            Some((id, false)) => pred(id),
             _ => false,
         }
     }
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 4dc00af4860..bead941b20d 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -152,6 +152,18 @@ pub fn to_string<F>(f: F) -> String where
     printer.s.eof()
 }
 
+// This makes comma-separated lists look slightly nicer,
+// and also addresses a specific regression described in issue #63896.
+fn tt_prepend_space(tt: &TokenTree) -> bool {
+    match tt {
+        TokenTree::Token(token) => match token.kind {
+            token::Comma => false,
+            _ => true,
+        }
+        _ => true,
+    }
+}
+
 fn binop_to_string(op: BinOpToken) -> &'static str {
     match op {
         token::Plus     => "+",
@@ -406,8 +418,8 @@ pub fn attribute_to_string(attr: &ast::Attribute) -> String {
     to_string(|s| s.print_attribute(attr))
 }
 
-pub fn arg_to_string(arg: &ast::Arg) -> String {
-    to_string(|s| s.print_arg(arg, false))
+pub fn param_to_string(arg: &ast::Param) -> String {
+    to_string(|s| s.print_param(arg, false))
 }
 
 fn foreign_item_to_string(arg: &ast::ForeignItem) -> String {
@@ -696,7 +708,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target=pp::Printer> + std::ops::DerefM
 
     fn print_tts(&mut self, tts: tokenstream::TokenStream, convert_dollar_crate: bool) {
         for (i, tt) in tts.into_trees().enumerate() {
-            if i != 0 {
+            if i != 0 && tt_prepend_space(&tt) {
                 self.space();
             }
             self.print_tt(tt, convert_dollar_crate);
@@ -2089,7 +2101,7 @@ impl<'a> State<'a> {
                 self.print_asyncness(asyncness);
                 self.print_capture_clause(capture_clause);
 
-                self.print_fn_block_args(decl);
+                self.print_fn_block_params(decl);
                 self.s.space();
                 self.print_expr(body);
                 self.end(); // need to close a box
@@ -2524,21 +2536,21 @@ impl<'a> State<'a> {
             self.print_ident(name);
         }
         self.print_generic_params(&generics.params);
-        self.print_fn_args_and_ret(decl);
+        self.print_fn_params_and_ret(decl);
         self.print_where_clause(&generics.where_clause)
     }
 
-    crate fn print_fn_args_and_ret(&mut self, decl: &ast::FnDecl) {
+    crate fn print_fn_params_and_ret(&mut self, decl: &ast::FnDecl) {
         self.popen();
-        self.commasep(Inconsistent, &decl.inputs, |s, arg| s.print_arg(arg, false));
+        self.commasep(Inconsistent, &decl.inputs, |s, param| s.print_param(param, false));
         self.pclose();
 
         self.print_fn_output(decl)
     }
 
-    crate fn print_fn_block_args(&mut self, decl: &ast::FnDecl) {
+    crate fn print_fn_block_params(&mut self, decl: &ast::FnDecl) {
         self.s.word("|");
-        self.commasep(Inconsistent, &decl.inputs, |s, arg| s.print_arg(arg, true));
+        self.commasep(Inconsistent, &decl.inputs, |s, param| s.print_param(param, true));
         self.s.word("|");
 
         if let ast::FunctionRetTy::Default(..) = decl.output {
@@ -2747,7 +2759,7 @@ impl<'a> State<'a> {
         self.print_type(&mt.ty)
     }
 
-    crate fn print_arg(&mut self, input: &ast::Arg, is_closure: bool) {
+    crate fn print_param(&mut self, input: &ast::Param, is_closure: bool) {
         self.ibox(INDENT_UNIT);
 
         self.print_outer_attributes_inline(&input.attrs);
diff --git a/src/libsyntax/tests.rs b/src/libsyntax/tests.rs
index 4c0e1e3704d..c472212bc20 100644
--- a/src/libsyntax/tests.rs
+++ b/src/libsyntax/tests.rs
@@ -144,11 +144,14 @@ fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &
             println!("text: {:?}", source_map.span_to_snippet(span));
         }
 
-        let emitter = EmitterWriter::new(Box::new(Shared { data: output.clone() }),
-                                        Some(source_map.clone()),
-                                        false,
-                                        false,
-                                        false);
+        let emitter = EmitterWriter::new(
+            Box::new(Shared { data: output.clone() }),
+            Some(source_map.clone()),
+            false,
+            false,
+            false,
+            None,
+        );
         let handler = Handler::with_emitter(true, None, Box::new(emitter));
         handler.span_err(msp, "foo");
 
diff --git a/src/libsyntax/tokenstream.rs b/src/libsyntax/tokenstream.rs
index 09a1b93c7bb..0d9f3769ce9 100644
--- a/src/libsyntax/tokenstream.rs
+++ b/src/libsyntax/tokenstream.rs
@@ -19,7 +19,7 @@ use crate::parse::Directory;
 use crate::parse::token::{self, DelimToken, Token, TokenKind};
 use crate::print::pprust;
 
-use syntax_pos::{BytePos, ExpnId, Span, DUMMY_SP};
+use syntax_pos::{BytePos, Span, DUMMY_SP};
 #[cfg(target_arch = "x86_64")]
 use rustc_data_structures::static_assert_size;
 use rustc_data_structures::sync::Lrc;
@@ -547,11 +547,4 @@ impl DelimSpan {
     pub fn entire(self) -> Span {
         self.open.with_hi(self.close.hi())
     }
-
-    pub fn apply_mark(self, expn_id: ExpnId) -> Self {
-        DelimSpan {
-            open: self.open.apply_mark(expn_id),
-            close: self.close.apply_mark(expn_id),
-        }
-    }
 }
diff --git a/src/libsyntax/util/node_count.rs b/src/libsyntax/util/node_count.rs
index f17eb3b3943..a64fec70961 100644
--- a/src/libsyntax/util/node_count.rs
+++ b/src/libsyntax/util/node_count.rs
@@ -93,8 +93,7 @@ impl<'ast> Visitor<'ast> for NodeCounter {
         self.count += 1;
         walk_poly_trait_ref(self, t, m)
     }
-    fn visit_variant_data(&mut self, s: &VariantData, _: Ident,
-                          _: &Generics, _: NodeId, _: Span) {
+    fn visit_variant_data(&mut self, s: &VariantData) {
         self.count += 1;
         walk_struct_def(self, s)
     }
@@ -107,9 +106,9 @@ impl<'ast> Visitor<'ast> for NodeCounter {
         self.count += 1;
         walk_enum_def(self, enum_definition, generics, item_id)
     }
-    fn visit_variant(&mut self, v: &Variant, g: &Generics, item_id: NodeId) {
+    fn visit_variant(&mut self, v: &Variant) {
         self.count += 1;
-        walk_variant(self, v, g, item_id)
+        walk_variant(self, v)
     }
     fn visit_lifetime(&mut self, lifetime: &Lifetime) {
         self.count += 1;
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 91b92d84a81..ce1568316f8 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -66,7 +66,7 @@ pub trait Visitor<'ast>: Sized {
     fn visit_local(&mut self, l: &'ast Local) { walk_local(self, l) }
     fn visit_block(&mut self, b: &'ast Block) { walk_block(self, b) }
     fn visit_stmt(&mut self, s: &'ast Stmt) { walk_stmt(self, s) }
-    fn visit_arg(&mut self, arg: &'ast Arg) { walk_arg(self, arg) }
+    fn visit_param(&mut self, param: &'ast Param) { walk_param(self, param) }
     fn visit_arm(&mut self, a: &'ast Arm) { walk_arm(self, a) }
     fn visit_pat(&mut self, p: &'ast Pat) { walk_pat(self, p) }
     fn visit_anon_const(&mut self, c: &'ast AnonConst) { walk_anon_const(self, c) }
@@ -92,8 +92,7 @@ pub trait Visitor<'ast>: Sized {
     fn visit_poly_trait_ref(&mut self, t: &'ast PolyTraitRef, m: &'ast TraitBoundModifier) {
         walk_poly_trait_ref(self, t, m)
     }
-    fn visit_variant_data(&mut self, s: &'ast VariantData, _: Ident,
-                          _: &'ast Generics, _: NodeId, _: Span) {
+    fn visit_variant_data(&mut self, s: &'ast VariantData) {
         walk_struct_def(self, s)
     }
     fn visit_struct_field(&mut self, s: &'ast StructField) { walk_struct_field(self, s) }
@@ -101,8 +100,8 @@ pub trait Visitor<'ast>: Sized {
                       generics: &'ast Generics, item_id: NodeId, _: Span) {
         walk_enum_def(self, enum_definition, generics, item_id)
     }
-    fn visit_variant(&mut self, v: &'ast Variant, g: &'ast Generics, item_id: NodeId) {
-        walk_variant(self, v, g, item_id)
+    fn visit_variant(&mut self, v: &'ast Variant) {
+        walk_variant(self, v)
     }
     fn visit_label(&mut self, label: &'ast Label) {
         walk_label(self, label)
@@ -163,6 +162,12 @@ pub trait Visitor<'ast>: Sized {
     fn visit_fn_header(&mut self, _header: &'ast FnHeader) {
         // Nothing to do
     }
+    fn visit_field(&mut self, f: &'ast Field) {
+        walk_field(self, f)
+    }
+    fn visit_field_pattern(&mut self, fp: &'ast FieldPat) {
+        walk_field_pattern(self, fp)
+    }
 }
 
 #[macro_export]
@@ -280,8 +285,7 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) {
         ItemKind::Struct(ref struct_definition, ref generics) |
         ItemKind::Union(ref struct_definition, ref generics) => {
             visitor.visit_generics(generics);
-            visitor.visit_variant_data(struct_definition, item.ident,
-                                     generics, item.id, item.span);
+            visitor.visit_variant_data(struct_definition);
         }
         ItemKind::Trait(.., ref generics, ref bounds, ref methods) => {
             visitor.visit_generics(generics);
@@ -300,24 +304,32 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) {
 
 pub fn walk_enum_def<'a, V: Visitor<'a>>(visitor: &mut V,
                                  enum_definition: &'a EnumDef,
-                                 generics: &'a Generics,
-                                 item_id: NodeId) {
-    walk_list!(visitor, visit_variant, &enum_definition.variants, generics, item_id);
+                                 _: &'a Generics,
+                                 _: NodeId) {
+    walk_list!(visitor, visit_variant, &enum_definition.variants);
 }
 
-pub fn walk_variant<'a, V>(visitor: &mut V,
-                           variant: &'a Variant,
-                           generics: &'a Generics,
-                           item_id: NodeId)
+pub fn walk_variant<'a, V: Visitor<'a>>(visitor: &mut V, variant: &'a Variant)
     where V: Visitor<'a>,
 {
     visitor.visit_ident(variant.ident);
-    visitor.visit_variant_data(&variant.data, variant.ident,
-                             generics, item_id, variant.span);
+    visitor.visit_variant_data(&variant.data);
     walk_list!(visitor, visit_anon_const, &variant.disr_expr);
     walk_list!(visitor, visit_attribute, &variant.attrs);
 }
 
+pub fn walk_field<'a, V: Visitor<'a>>(visitor: &mut V, f: &'a Field) {
+    visitor.visit_expr(&f.expr);
+    visitor.visit_ident(f.ident);
+    walk_list!(visitor, visit_attribute, f.attrs.iter());
+}
+
+pub fn walk_field_pattern<'a, V: Visitor<'a>>(visitor: &mut V, fp: &'a FieldPat) {
+    visitor.visit_ident(fp.ident);
+    visitor.visit_pat(&fp.pat);
+    walk_list!(visitor, visit_attribute, fp.attrs.iter());
+}
+
 pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) {
     match typ.node {
         TyKind::Slice(ref ty) | TyKind::Paren(ref ty) => {
@@ -441,11 +453,7 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
         }
         PatKind::Struct(ref path, ref fields, _) => {
             visitor.visit_path(path, pattern.id);
-            for field in fields {
-                walk_list!(visitor, visit_attribute, field.attrs.iter());
-                visitor.visit_ident(field.ident);
-                visitor.visit_pat(&field.pat)
-            }
+            walk_list!(visitor, visit_field_pattern, fields);
         }
         PatKind::Box(ref subpattern) |
         PatKind::Ref(ref subpattern, _) |
@@ -547,8 +555,8 @@ pub fn walk_fn_ret_ty<'a, V: Visitor<'a>>(visitor: &mut V, ret_ty: &'a FunctionR
 }
 
 pub fn walk_fn_decl<'a, V: Visitor<'a>>(visitor: &mut V, function_declaration: &'a FnDecl) {
-    for arg in &function_declaration.inputs {
-        visitor.visit_arg(arg);
+    for param in &function_declaration.inputs {
+        visitor.visit_param(param);
     }
     visitor.visit_fn_ret_ty(&function_declaration.output);
 }
@@ -686,11 +694,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
         }
         ExprKind::Struct(ref path, ref fields, ref optional_base) => {
             visitor.visit_path(path, expression.id);
-            for field in fields {
-                walk_list!(visitor, visit_attribute, field.attrs.iter());
-                visitor.visit_ident(field.ident);
-                visitor.visit_expr(&field.expr)
-            }
+            walk_list!(visitor, visit_field, fields);
             walk_list!(visitor, visit_expr, optional_base);
         }
         ExprKind::Tup(ref subexpressions) => {
@@ -820,10 +824,10 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
     visitor.visit_expr_post(expression)
 }
 
-pub fn walk_arg<'a, V: Visitor<'a>>(visitor: &mut V, arg: &'a Arg) {
-    walk_list!(visitor, visit_attribute, arg.attrs.iter());
-    visitor.visit_pat(&arg.pat);
-    visitor.visit_ty(&arg.ty);
+pub fn walk_param<'a, V: Visitor<'a>>(visitor: &mut V, param: &'a Param) {
+    walk_list!(visitor, visit_attribute, param.attrs.iter());
+    visitor.visit_pat(&param.pat);
+    visitor.visit_ty(&param.ty);
 }
 
 pub fn walk_arm<'a, V: Visitor<'a>>(visitor: &mut V, arm: &'a Arm) {