about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-05-18 10:57:05 +0000
committerbors <bors@rust-lang.org>2018-05-18 10:57:05 +0000
commitdf40e61382a2cba0be621fdabb9971ce3475e9a7 (patch)
tree7bdf66599659eccd8b03f5921c2e5087efe94008
parentfd18d2537ddcffb24b3739393b085ed28dfc340e (diff)
parentd8bbc1ee1ad44e9c7bd93c8d59103eacd0ed36e8 (diff)
downloadrust-df40e61382a2cba0be621fdabb9971ce3475e9a7.tar.gz
rust-df40e61382a2cba0be621fdabb9971ce3475e9a7.zip
Auto merge of #50307 - petrochenkov:keyhyg2, r=nikomatsakis
Implement edition hygiene for keywords

Determine "keywordness" of an identifier in its hygienic context.
cc https://github.com/rust-lang/rust/pull/49611

I've resurrected `proc` as an Edition-2015-only keyword for testing purposes, but it should probably be buried again. EDIT: `proc` is removed again.
-rw-r--r--src/libproc_macro/lib.rs2
-rw-r--r--src/librustc/hir/lowering.rs1
-rw-r--r--src/librustc/hir/print.rs4
-rw-r--r--src/librustc/ich/impls_syntax.rs10
-rw-r--r--src/librustc/middle/cstore.rs3
-rw-r--r--src/librustc_allocator/expand.rs3
-rw-r--r--src/librustc_driver/lib.rs3
-rw-r--r--src/librustc_metadata/creader.rs24
-rw-r--r--src/librustc_metadata/cstore.rs5
-rw-r--r--src/librustc_metadata/cstore_impl.rs9
-rw-r--r--src/librustc_metadata/encoder.rs3
-rw-r--r--src/librustc_metadata/schema.rs2
-rw-r--r--src/librustc_passes/ast_validation.rs6
-rw-r--r--src/librustc_plugin/registry.rs8
-rw-r--r--src/librustc_resolve/build_reduced_graph.rs3
-rw-r--r--src/librustc_resolve/lib.rs3
-rw-r--r--src/librustc_resolve/macros.rs17
-rw-r--r--src/librustc_resolve/resolve_imports.rs7
-rw-r--r--src/libsyntax/ast.rs3
-rw-r--r--src/libsyntax/ext/base.rs31
-rw-r--r--src/libsyntax/ext/derive.rs3
-rw-r--r--src/libsyntax/ext/expand.rs25
-rw-r--r--src/libsyntax/ext/tt/macro_rules.rs9
-rw-r--r--src/libsyntax/lib.rs3
-rw-r--r--src/libsyntax/parse/lexer/mod.rs2
-rw-r--r--src/libsyntax/parse/token.rs50
-rw-r--r--src/libsyntax/print/pprust.rs2
-rw-r--r--src/libsyntax/std_inject.rs3
-rw-r--r--src/libsyntax/test.rs3
-rw-r--r--src/libsyntax_ext/lib.rs5
-rw-r--r--src/libsyntax_ext/proc_macro_registrar.rs3
-rw-r--r--src/libsyntax_pos/edition.rs (renamed from src/libsyntax/edition.rs)2
-rw-r--r--src/libsyntax_pos/hygiene.rs13
-rw-r--r--src/libsyntax_pos/lib.rs8
-rw-r--r--src/libsyntax_pos/symbol.rs69
-rw-r--r--src/test/run-pass-fulldeps/auxiliary/plugin_args.rs2
-rw-r--r--src/test/run-pass/auxiliary/edition-kw-macro-2015.rs38
-rw-r--r--src/test/run-pass/auxiliary/edition-kw-macro-2018.rs38
-rw-r--r--src/test/run-pass/edition-keywords-2015-2015.rs43
-rw-r--r--src/test/run-pass/edition-keywords-2015-2018.rs43
-rw-r--r--src/test/run-pass/edition-keywords-2018-2015.rs43
-rw-r--r--src/test/run-pass/edition-keywords-2018-2018.rs43
-rw-r--r--src/test/ui/auxiliary/edition-kw-macro-2015.rs38
-rw-r--r--src/test/ui/auxiliary/edition-kw-macro-2018.rs38
-rw-r--r--src/test/ui/edition-keywords-2015-2015-expansion.rs27
-rw-r--r--src/test/ui/edition-keywords-2015-2015-parsing.rs32
-rw-r--r--src/test/ui/edition-keywords-2015-2015-parsing.stderr14
-rw-r--r--src/test/ui/edition-keywords-2015-2018-expansion.rs24
-rw-r--r--src/test/ui/edition-keywords-2015-2018-expansion.stderr10
-rw-r--r--src/test/ui/edition-keywords-2015-2018-parsing.rs32
-rw-r--r--src/test/ui/edition-keywords-2015-2018-parsing.stderr14
-rw-r--r--src/test/ui/edition-keywords-2018-2015-expansion.rs27
-rw-r--r--src/test/ui/edition-keywords-2018-2015-parsing.rs32
-rw-r--r--src/test/ui/edition-keywords-2018-2015-parsing.stderr32
-rw-r--r--src/test/ui/edition-keywords-2018-2018-expansion.rs24
-rw-r--r--src/test/ui/edition-keywords-2018-2018-expansion.stderr10
-rw-r--r--src/test/ui/edition-keywords-2018-2018-parsing.rs32
-rw-r--r--src/test/ui/edition-keywords-2018-2018-parsing.stderr32
58 files changed, 896 insertions, 119 deletions
diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs
index 610a9a2a394..bcab6168096 100644
--- a/src/libproc_macro/lib.rs
+++ b/src/libproc_macro/lib.rs
@@ -818,7 +818,7 @@ impl Ident {
     pub fn new_raw(string: &str, span: Span) -> Ident {
         let mut ident = Ident::new(string, span);
         if ident.sym == keywords::Underscore.name() ||
-           token::is_path_segment_keyword(ast::Ident::with_empty_ctxt(ident.sym)) {
+           ast::Ident::with_empty_ctxt(ident.sym).is_path_segment_keyword() {
             panic!("`{:?}` is not a valid raw identifier", string)
         }
         ident.is_raw = true;
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 0f4871954d6..45d429612a1 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -593,6 +593,7 @@ impl<'a> LoweringContext<'a> {
                 span: Some(span),
                 allow_internal_unstable: true,
                 allow_internal_unsafe: false,
+                edition: codemap::hygiene::default_edition(),
             },
         });
         span.with_ctxt(SyntaxContext::empty().apply_mark(mark))
diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs
index 9cd9e0dce54..940a68e8ce5 100644
--- a/src/librustc/hir/print.rs
+++ b/src/librustc/hir/print.rs
@@ -13,7 +13,7 @@ pub use self::AnnNode::*;
 use rustc_target::spec::abi::Abi;
 use syntax::ast;
 use syntax::codemap::{CodeMap, Spanned};
-use syntax::parse::{token, ParseSess};
+use syntax::parse::ParseSess;
 use syntax::parse::lexer::comments;
 use syntax::print::pp::{self, Breaks};
 use syntax::print::pp::Breaks::{Consistent, Inconsistent};
@@ -1559,7 +1559,7 @@ impl<'a> State<'a> {
     }
 
     pub fn print_name(&mut self, name: ast::Name) -> io::Result<()> {
-        if token::is_raw_guess(ast::Ident::with_empty_ctxt(name)) {
+        if name.to_ident().is_raw_guess() {
             self.s.word(&format!("r#{}", name))?;
         } else {
             self.s.word(&name.as_str())?;
diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs
index f56d701b028..3a37c1c18c8 100644
--- a/src/librustc/ich/impls_syntax.rs
+++ b/src/librustc/ich/impls_syntax.rs
@@ -132,6 +132,15 @@ impl_stable_hash_for!(struct ::syntax::attr::Stability {
 });
 
 impl<'a> HashStable<StableHashingContext<'a>>
+for ::syntax::edition::Edition {
+    fn hash_stable<W: StableHasherResult>(&self,
+                                          hcx: &mut StableHashingContext<'a>,
+                                          hasher: &mut StableHasher<W>) {
+        mem::discriminant(self).hash_stable(hcx, hasher);
+    }
+}
+
+impl<'a> HashStable<StableHashingContext<'a>>
 for ::syntax::attr::StabilityLevel {
     fn hash_stable<W: StableHasherResult>(&self,
                                           hcx: &mut StableHashingContext<'a>,
@@ -389,6 +398,7 @@ impl_stable_hash_for!(struct ::syntax_pos::hygiene::NameAndSpan {
     format,
     allow_internal_unstable,
     allow_internal_unsafe,
+    edition,
     span
 });
 
diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs
index 61c8470b616..518021f4125 100644
--- a/src/librustc/middle/cstore.rs
+++ b/src/librustc/middle/cstore.rs
@@ -34,6 +34,7 @@ use session::search_paths::PathKind;
 use std::any::Any;
 use std::path::{Path, PathBuf};
 use syntax::ast;
+use syntax::edition::Edition;
 use syntax::ext::base::SyntaxExtension;
 use syntax::symbol::Symbol;
 use syntax_pos::Span;
@@ -235,6 +236,7 @@ pub trait CrateStore {
     fn crate_name_untracked(&self, cnum: CrateNum) -> Symbol;
     fn crate_disambiguator_untracked(&self, cnum: CrateNum) -> CrateDisambiguator;
     fn crate_hash_untracked(&self, cnum: CrateNum) -> Svh;
+    fn crate_edition_untracked(&self, cnum: CrateNum) -> Edition;
     fn struct_field_names_untracked(&self, def: DefId) -> Vec<ast::Name>;
     fn item_children_untracked(&self, did: DefId, sess: &Session) -> Vec<def::Export>;
     fn load_macro_untracked(&self, did: DefId, sess: &Session) -> LoadedMacro;
@@ -309,6 +311,7 @@ impl CrateStore for DummyCrateStore {
         bug!("crate_disambiguator")
     }
     fn crate_hash_untracked(&self, cnum: CrateNum) -> Svh { bug!("crate_hash") }
+    fn crate_edition_untracked(&self, cnum: CrateNum) -> Edition { bug!("crate_edition_untracked") }
 
     // resolve
     fn def_key(&self, def: DefId) -> DefKey { bug!("def_key") }
diff --git a/src/librustc_allocator/expand.rs b/src/librustc_allocator/expand.rs
index de8814d3d6a..497d5fdcac7 100644
--- a/src/librustc_allocator/expand.rs
+++ b/src/librustc_allocator/expand.rs
@@ -21,7 +21,7 @@ use syntax::ext::base::ExtCtxt;
 use syntax::ext::base::Resolver;
 use syntax::ext::build::AstBuilder;
 use syntax::ext::expand::ExpansionConfig;
-use syntax::ext::hygiene::{Mark, SyntaxContext};
+use syntax::ext::hygiene::{self, Mark, SyntaxContext};
 use syntax::fold::{self, Folder};
 use syntax::parse::ParseSess;
 use syntax::ptr::P;
@@ -86,6 +86,7 @@ impl<'a> Folder for ExpandAllocatorDirectives<'a> {
                 span: None,
                 allow_internal_unstable: true,
                 allow_internal_unsafe: false,
+                edition: hygiene::default_edition(),
             },
         });
         let span = item.span.with_ctxt(SyntaxContext::empty().apply_mark(mark));
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index 0b4b090f1f0..2f89814032e 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -108,7 +108,7 @@ use syntax::ast;
 use syntax::codemap::{CodeMap, FileLoader, RealFileLoader};
 use syntax::feature_gate::{GatedCfg, UnstableFeatures};
 use syntax::parse::{self, PResult};
-use syntax_pos::{DUMMY_SP, MultiSpan, FileName};
+use syntax_pos::{hygiene, DUMMY_SP, MultiSpan, FileName};
 
 #[cfg(test)]
 mod test;
@@ -466,6 +466,7 @@ pub fn run_compiler<'a>(args: &[String],
         };
 
         let (sopts, cfg) = config::build_session_options_and_crate_config(&matches);
+        hygiene::set_default_edition(sopts.edition);
 
         driver::spawn_thread_pool(sopts, |sopts| {
             run_compiler_with_pool(matches, sopts, cfg, callbacks, file_loader, emitter_dest)
diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs
index 87b3a2dc69f..6c1f72f5f9c 100644
--- a/src/librustc_metadata/creader.rs
+++ b/src/librustc_metadata/creader.rs
@@ -35,6 +35,7 @@ use std::{cmp, fs};
 
 use syntax::ast;
 use syntax::attr;
+use syntax::edition::Edition;
 use syntax::ext::base::SyntaxExtension;
 use syntax::symbol::Symbol;
 use syntax::visit;
@@ -535,7 +536,10 @@ impl<'a> CrateLoader<'a> {
             mem::transmute::<*mut u8, fn(&mut Registry)>(sym)
         };
 
-        struct MyRegistrar(Vec<(ast::Name, Lrc<SyntaxExtension>)>);
+        struct MyRegistrar {
+            extensions: Vec<(ast::Name, Lrc<SyntaxExtension>)>,
+            edition: Edition,
+        }
 
         impl Registry for MyRegistrar {
             fn register_custom_derive(&mut self,
@@ -544,36 +548,38 @@ impl<'a> CrateLoader<'a> {
                                       attributes: &[&'static str]) {
                 let attrs = attributes.iter().cloned().map(Symbol::intern).collect::<Vec<_>>();
                 let derive = ProcMacroDerive::new(expand, attrs.clone());
-                let derive = SyntaxExtension::ProcMacroDerive(Box::new(derive), attrs);
-                self.0.push((Symbol::intern(trait_name), Lrc::new(derive)));
+                let derive = SyntaxExtension::ProcMacroDerive(
+                    Box::new(derive), attrs, self.edition
+                );
+                self.extensions.push((Symbol::intern(trait_name), Lrc::new(derive)));
             }
 
             fn register_attr_proc_macro(&mut self,
                                         name: &str,
                                         expand: fn(TokenStream, TokenStream) -> TokenStream) {
                 let expand = SyntaxExtension::AttrProcMacro(
-                    Box::new(AttrProcMacro { inner: expand })
+                    Box::new(AttrProcMacro { inner: expand }), self.edition
                 );
-                self.0.push((Symbol::intern(name), Lrc::new(expand)));
+                self.extensions.push((Symbol::intern(name), Lrc::new(expand)));
             }
 
             fn register_bang_proc_macro(&mut self,
                                         name: &str,
                                         expand: fn(TokenStream) -> TokenStream) {
                 let expand = SyntaxExtension::ProcMacro(
-                    Box::new(BangProcMacro { inner: expand })
+                    Box::new(BangProcMacro { inner: expand }), self.edition
                 );
-                self.0.push((Symbol::intern(name), Lrc::new(expand)));
+                self.extensions.push((Symbol::intern(name), Lrc::new(expand)));
             }
         }
 
-        let mut my_registrar = MyRegistrar(Vec::new());
+        let mut my_registrar = MyRegistrar { extensions: Vec::new(), edition: root.edition };
         registrar(&mut my_registrar);
 
         // Intentionally leak the dynamic library. We can't ever unload it
         // since the library can make things that will live arbitrarily long.
         mem::forget(lib);
-        my_registrar.0
+        my_registrar.extensions
     }
 
     /// Look for a plugin registrar. Returns library path, crate
diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs
index 9bbce563b61..f2d2d090e0a 100644
--- a/src/librustc_metadata/cstore.rs
+++ b/src/librustc_metadata/cstore.rs
@@ -24,6 +24,7 @@ use rustc::util::nodemap::{FxHashMap, NodeMap};
 
 use rustc_data_structures::sync::{Lrc, RwLock, Lock};
 use syntax::{ast, attr};
+use syntax::edition::Edition;
 use syntax::ext::base::SyntaxExtension;
 use syntax::symbol::Symbol;
 use syntax_pos;
@@ -234,4 +235,8 @@ impl CrateMetadata {
     pub fn panic_strategy(&self) -> PanicStrategy {
         self.root.panic_strategy.clone()
     }
+
+    pub fn edition(&self) -> Edition {
+        self.root.edition
+    }
 }
diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs
index fdfe2a98bf9..c8f25f935e9 100644
--- a/src/librustc_metadata/cstore_impl.rs
+++ b/src/librustc_metadata/cstore_impl.rs
@@ -38,6 +38,7 @@ use std::sync::Arc;
 use syntax::ast;
 use syntax::attr;
 use syntax::codemap;
+use syntax::edition::Edition;
 use syntax::ext::base::SyntaxExtension;
 use syntax::parse::filemap_to_stream;
 use syntax::symbol::Symbol;
@@ -464,6 +465,11 @@ impl CrateStore for cstore::CStore {
         self.get_crate_data(cnum).hash()
     }
 
+    fn crate_edition_untracked(&self, cnum: CrateNum) -> Edition
+    {
+        self.get_crate_data(cnum).edition()
+    }
+
     /// Returns the `DefKey` for a given `DefId`. This indicates the
     /// parent `DefId` as well as some idea of what kind of data the
     /// `DefId` refers to.
@@ -512,7 +518,8 @@ impl CrateStore for cstore::CStore {
             return LoadedMacro::ProcMacro(proc_macros[id.index.to_proc_macro_index()].1.clone());
         } else if data.name == "proc_macro" &&
                   self.get_crate_data(id.krate).item_name(id.index) == "quote" {
-            let ext = SyntaxExtension::ProcMacro(Box::new(::proc_macro::__internal::Quoter));
+            let ext = SyntaxExtension::ProcMacro(Box::new(::proc_macro::__internal::Quoter),
+                                                 data.edition());
             return LoadedMacro::ProcMacro(Lrc::new(ext));
         }
 
diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs
index bbc4120f060..d00f4f32c10 100644
--- a/src/librustc_metadata/encoder.rs
+++ b/src/librustc_metadata/encoder.rs
@@ -44,7 +44,7 @@ use syntax::ast::{self, CRATE_NODE_ID};
 use syntax::codemap::Spanned;
 use syntax::attr;
 use syntax::symbol::Symbol;
-use syntax_pos::{self, FileName, FileMap, Span, DUMMY_SP};
+use syntax_pos::{self, hygiene, FileName, FileMap, Span, DUMMY_SP};
 
 use rustc::hir::{self, PatKind};
 use rustc::hir::itemlikevisit::ItemLikeVisitor;
@@ -496,6 +496,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             hash: link_meta.crate_hash,
             disambiguator: tcx.sess.local_crate_disambiguator(),
             panic_strategy: tcx.sess.panic_strategy(),
+            edition: hygiene::default_edition(),
             has_global_allocator: has_global_allocator,
             has_default_lib_allocator: has_default_lib_allocator,
             plugin_registrar_fn: tcx.sess
diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs
index c9949389ace..8e17b7f8d69 100644
--- a/src/librustc_metadata/schema.rs
+++ b/src/librustc_metadata/schema.rs
@@ -23,6 +23,7 @@ use rustc_target::spec::{PanicStrategy, TargetTriple};
 
 use rustc_serialize as serialize;
 use syntax::{ast, attr};
+use syntax::edition::Edition;
 use syntax::symbol::Symbol;
 use syntax_pos::{self, Span};
 
@@ -189,6 +190,7 @@ pub struct CrateRoot {
     pub hash: hir::svh::Svh,
     pub disambiguator: CrateDisambiguator,
     pub panic_strategy: PanicStrategy,
+    pub edition: Edition,
     pub has_global_allocator: bool,
     pub has_default_lib_allocator: bool,
     pub plugin_registrar_fn: Option<DefIndex>,
diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs
index 4789e2e50ca..4f239a0868e 100644
--- a/src/librustc_passes/ast_validation.rs
+++ b/src/librustc_passes/ast_validation.rs
@@ -21,7 +21,6 @@ use rustc::session::Session;
 use syntax::ast::*;
 use syntax::attr;
 use syntax::codemap::Spanned;
-use syntax::parse::token;
 use syntax::symbol::keywords;
 use syntax::visit::{self, Visitor};
 use syntax_pos::Span;
@@ -40,14 +39,13 @@ impl<'a> AstValidator<'a> {
         let valid_names = [keywords::UnderscoreLifetime.name(),
                            keywords::StaticLifetime.name(),
                            keywords::Invalid.name()];
-        if !valid_names.contains(&ident.name) &&
-            token::is_reserved_ident(ident.without_first_quote()) {
+        if !valid_names.contains(&ident.name) && ident.without_first_quote().is_reserved() {
             self.err_handler().span_err(ident.span, "lifetimes cannot use keyword names");
         }
     }
 
     fn check_label(&self, ident: Ident) {
-        if token::is_reserved_ident(ident.without_first_quote()) {
+        if ident.without_first_quote().is_reserved() {
             self.err_handler()
                 .span_err(ident.span, &format!("invalid label name `{}`", ident.name));
         }
diff --git a/src/librustc_plugin/registry.rs b/src/librustc_plugin/registry.rs
index 7e3c411c1d2..ea15f4c75b9 100644
--- a/src/librustc_plugin/registry.rs
+++ b/src/librustc_plugin/registry.rs
@@ -15,6 +15,7 @@ use rustc::session::Session;
 
 use syntax::ext::base::{SyntaxExtension, NamedSyntaxExtension, NormalTT, IdentTT};
 use syntax::ext::base::MacroExpanderFn;
+use syntax::ext::hygiene;
 use syntax::symbol::Symbol;
 use syntax::ast;
 use syntax::feature_gate::AttributeType;
@@ -107,7 +108,8 @@ impl<'a> Registry<'a> {
                 def_info: _,
                 allow_internal_unstable,
                 allow_internal_unsafe,
-                unstable_feature
+                unstable_feature,
+                edition,
             } => {
                 let nid = ast::CRATE_NODE_ID;
                 NormalTT {
@@ -115,7 +117,8 @@ impl<'a> Registry<'a> {
                     def_info: Some((nid, self.krate_span)),
                     allow_internal_unstable,
                     allow_internal_unsafe,
-                    unstable_feature
+                    unstable_feature,
+                    edition,
                 }
             }
             IdentTT(ext, _, allow_internal_unstable) => {
@@ -150,6 +153,7 @@ impl<'a> Registry<'a> {
             allow_internal_unstable: false,
             allow_internal_unsafe: false,
             unstable_feature: None,
+            edition: hygiene::default_edition(),
         });
     }
 
diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs
index ef5cc958283..d3cc533cd36 100644
--- a/src/librustc_resolve/build_reduced_graph.rs
+++ b/src/librustc_resolve/build_reduced_graph.rs
@@ -588,7 +588,8 @@ impl<'a> Resolver<'a> {
 
         let ext = Lrc::new(macro_rules::compile(&self.session.parse_sess,
                                                &self.session.features_untracked(),
-                                               &macro_def));
+                                               &macro_def,
+                                               self.cstore.crate_edition_untracked(def_id.krate)));
         self.macro_map.insert(def_id, ext.clone());
         ext
     }
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 5a5f5ce2e38..e13e6bc6b74 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -58,7 +58,6 @@ use syntax::ast::{Item, ItemKind, ImplItem, ImplItemKind};
 use syntax::ast::{Label, Local, Mutability, Pat, PatKind, Path};
 use syntax::ast::{QSelf, TraitItemKind, TraitRef, Ty, TyKind};
 use syntax::feature_gate::{feature_err, GateIssue};
-use syntax::parse::token;
 use syntax::ptr::P;
 
 use syntax_pos::{Span, DUMMY_SP, MultiSpan};
@@ -3274,7 +3273,7 @@ impl<'a> Resolver<'a> {
                     // `$crate::a::b`
                     module = Some(self.resolve_crate_root(ident.span.ctxt(), true));
                     continue
-                } else if i == 1 && !token::is_path_segment_keyword(ident) {
+                } else if i == 1 && !ident.is_path_segment_keyword() {
                     let prev_name = path[0].name;
                     if prev_name == keywords::Extern.name() ||
                        prev_name == keywords::CrateRoot.name() &&
diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs
index 4afc621ad8b..0fc963a1367 100644
--- a/src/librustc_resolve/macros.rs
+++ b/src/librustc_resolve/macros.rs
@@ -24,7 +24,7 @@ use syntax::errors::DiagnosticBuilder;
 use syntax::ext::base::{self, Annotatable, Determinacy, MultiModifier, MultiDecorator};
 use syntax::ext::base::{MacroKind, SyntaxExtension, Resolver as SyntaxResolver};
 use syntax::ext::expand::{Expansion, ExpansionKind, Invocation, InvocationKind, find_attr_invoc};
-use syntax::ext::hygiene::{Mark, MarkKind};
+use syntax::ext::hygiene::{self, Mark, MarkKind};
 use syntax::ext::placeholders::placeholder;
 use syntax::ext::tt::macro_rules;
 use syntax::feature_gate::{self, emit_feature_err, GateIssue};
@@ -328,7 +328,7 @@ impl<'a> base::Resolver for Resolver<'a> {
         for did in self.unused_macros.iter() {
             let id_span = match *self.macro_map[did] {
                 SyntaxExtension::NormalTT { def_info, .. } => def_info,
-                SyntaxExtension::DeclMacro(.., osp) => osp,
+                SyntaxExtension::DeclMacro(.., osp, _) => osp,
                 _ => None,
             };
             if let Some((id, span)) = id_span {
@@ -371,7 +371,7 @@ impl<'a> Resolver<'a> {
         };
         for path in traits {
             match self.resolve_macro(scope, path, MacroKind::Derive, force) {
-                Ok(ext) => if let SyntaxExtension::ProcMacroDerive(_, ref inert_attrs) = *ext {
+                Ok(ext) => if let SyntaxExtension::ProcMacroDerive(_, ref inert_attrs, _) = *ext {
                     if inert_attrs.contains(&attr_name) {
                         // FIXME(jseyfried) Avoid `mem::replace` here.
                         let dummy_item = placeholder(ExpansionKind::Items, ast::DUMMY_NODE_ID)
@@ -755,7 +755,7 @@ impl<'a> Resolver<'a> {
         let def_id = self.definitions.local_def_id(item.id);
         let ext = Lrc::new(macro_rules::compile(&self.session.parse_sess,
                                                &self.session.features_untracked(),
-                                               item));
+                                               item, hygiene::default_edition()));
         self.macro_map.insert(def_id, ext);
 
         let def = match item.node { ast::ItemKind::MacroDef(ref def) => def, _ => unreachable!() };
@@ -803,14 +803,15 @@ impl<'a> Resolver<'a> {
 
         match *ext {
             // If `ext` is a procedural macro, check if we've already warned about it
-            AttrProcMacro(_) | ProcMacro(_) => if !self.warned_proc_macros.insert(name) { return; },
+            AttrProcMacro(..) | ProcMacro(..) =>
+                if !self.warned_proc_macros.insert(name) { return; },
             _ => return,
         }
 
         let warn_msg = match *ext {
-            AttrProcMacro(_) => "attribute procedural macros cannot be \
-                                 imported with `#[macro_use]`",
-            ProcMacro(_) => "procedural macros cannot be imported with `#[macro_use]`",
+            AttrProcMacro(..) => "attribute procedural macros cannot be \
+                                  imported with `#[macro_use]`",
+            ProcMacro(..) => "procedural macros cannot be imported with `#[macro_use]`",
             _ => return,
         };
 
diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs
index 09c421fba47..16d5d3fa043 100644
--- a/src/librustc_resolve/resolve_imports.rs
+++ b/src/librustc_resolve/resolve_imports.rs
@@ -27,7 +27,6 @@ use rustc::util::nodemap::{FxHashMap, FxHashSet};
 use syntax::ast::{Ident, Name, NodeId};
 use syntax::ext::base::Determinacy::{self, Determined, Undetermined};
 use syntax::ext::hygiene::Mark;
-use syntax::parse::token;
 use syntax::symbol::keywords;
 use syntax::util::lev_distance::find_best_match_for_name;
 use syntax_pos::Span;
@@ -667,7 +666,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
                         } else {
                             Some(self.resolve_crate_root(source.span.ctxt().modern(), false))
                         }
-                    } else if is_extern && !token::is_path_segment_keyword(source) {
+                    } else if is_extern && !source.is_path_segment_keyword() {
                         let crate_id =
                             self.resolver.crate_loader.process_use_extern(
                                 source.name,
@@ -715,8 +714,8 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
             }
             PathResult::Failed(span, msg, true) => {
                 let (mut self_path, mut self_result) = (module_path.clone(), None);
-                let is_special = |ident| token::is_path_segment_keyword(ident) &&
-                                         ident.name != keywords::CrateRoot.name();
+                let is_special = |ident: Ident| ident.is_path_segment_keyword() &&
+                                                ident.name != keywords::CrateRoot.name();
                 if !self_path.is_empty() && !is_special(self_path[0]) &&
                    !(self_path.len() > 1 && is_special(self_path[1])) {
                     self_path[0].name = keywords::SelfValue.name();
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 2b6635ec783..1817726d6a1 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -107,8 +107,7 @@ impl Path {
     // or starts with something like `self`/`super`/`$crate`/etc.
     pub fn make_root(&self) -> Option<PathSegment> {
         if let Some(ident) = self.segments.get(0).map(|seg| seg.ident) {
-            if ::parse::token::is_path_segment_keyword(ident) &&
-               ident.name != keywords::Crate.name() {
+            if ident.is_path_segment_keyword() && ident.name != keywords::Crate.name() {
                 return None;
             }
         }
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index 3b76084f2fb..f7d4227977c 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -14,9 +14,10 @@ use ast::{self, Attribute, Name, PatKind, MetaItem};
 use attr::HasAttrs;
 use codemap::{self, CodeMap, Spanned, respan};
 use syntax_pos::{Span, MultiSpan, DUMMY_SP};
+use edition::Edition;
 use errors::{DiagnosticBuilder, DiagnosticId};
 use ext::expand::{self, Expansion, Invocation};
-use ext::hygiene::{Mark, SyntaxContext};
+use ext::hygiene::{self, Mark, SyntaxContext};
 use fold::{self, Folder};
 use parse::{self, parser, DirectoryOwnership};
 use parse::token;
@@ -586,13 +587,13 @@ pub enum SyntaxExtension {
     MultiModifier(Box<MultiItemModifier + sync::Sync + sync::Send>),
 
     /// A function-like procedural macro. TokenStream -> TokenStream.
-    ProcMacro(Box<ProcMacro + sync::Sync + sync::Send>),
+    ProcMacro(Box<ProcMacro + sync::Sync + sync::Send>, Edition),
 
     /// An attribute-like procedural macro. TokenStream, TokenStream -> TokenStream.
     /// The first TokenSteam is the attribute, the second is the annotated item.
     /// Allows modification of the input items and adding new items, similar to
     /// MultiModifier, but uses TokenStreams, rather than AST nodes.
-    AttrProcMacro(Box<AttrProcMacro + sync::Sync + sync::Send>),
+    AttrProcMacro(Box<AttrProcMacro + sync::Sync + sync::Send>, Edition),
 
     /// A normal, function-like syntax extension.
     ///
@@ -608,6 +609,8 @@ pub enum SyntaxExtension {
         allow_internal_unsafe: bool,
         /// The macro's feature name if it is unstable, and the stability feature
         unstable_feature: Option<(Symbol, u32)>,
+        /// Edition of the crate in which the macro is defined
+        edition: Edition,
     },
 
     /// A function-like syntax extension that has an extra ident before
@@ -619,9 +622,8 @@ pub enum SyntaxExtension {
     /// The input is the annotated item.
     /// Allows generating code to implement a Trait for a given struct
     /// or enum item.
-    ProcMacroDerive(Box<MultiItemModifier +
-                        sync::Sync +
-                        sync::Send>, Vec<Symbol> /* inert attribute names */),
+    ProcMacroDerive(Box<MultiItemModifier + sync::Sync + sync::Send>,
+                    Vec<Symbol> /* inert attribute names */, Edition),
 
     /// An attribute-like procedural macro that derives a builtin trait.
     BuiltinDerive(BuiltinDeriveFn),
@@ -629,7 +631,7 @@ pub enum SyntaxExtension {
     /// A declarative macro, e.g. `macro m() {}`.
     ///
     /// The second element is the definition site span.
-    DeclMacro(Box<TTMacroExpander + sync::Sync + sync::Send>, Option<(ast::NodeId, Span)>),
+    DeclMacro(Box<TTMacroExpander + sync::Sync + sync::Send>, Option<(ast::NodeId, Span)>, Edition),
 }
 
 impl SyntaxExtension {
@@ -660,6 +662,21 @@ impl SyntaxExtension {
             _ => false,
         }
     }
+
+    pub fn edition(&self) -> Edition {
+        match *self {
+            SyntaxExtension::NormalTT { edition, .. } |
+            SyntaxExtension::DeclMacro(.., edition) |
+            SyntaxExtension::ProcMacro(.., edition) |
+            SyntaxExtension::AttrProcMacro(.., edition) |
+            SyntaxExtension::ProcMacroDerive(.., edition) => edition,
+            // Unstable legacy stuff
+            SyntaxExtension::IdentTT(..) |
+            SyntaxExtension::MultiDecorator(..) |
+            SyntaxExtension::MultiModifier(..) |
+            SyntaxExtension::BuiltinDerive(..) => hygiene::default_edition(),
+        }
+    }
 }
 
 pub type NamedSyntaxExtension = (Name, SyntaxExtension);
diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs
index 6bf166dfe95..0b6a7e1c4f4 100644
--- a/src/libsyntax/ext/derive.rs
+++ b/src/libsyntax/ext/derive.rs
@@ -10,7 +10,7 @@
 
 use attr::HasAttrs;
 use ast;
-use codemap::{ExpnInfo, NameAndSpan, ExpnFormat};
+use codemap::{hygiene, ExpnInfo, NameAndSpan, ExpnFormat};
 use ext::base::ExtCtxt;
 use ext::build::AstBuilder;
 use parse::parser::PathStyle;
@@ -65,6 +65,7 @@ pub fn add_derived_markers<T>(cx: &mut ExtCtxt, span: Span, traits: &[ast::Path]
             span: None,
             allow_internal_unstable: true,
             allow_internal_unsafe: false,
+            edition: hygiene::default_edition(),
         },
     });
 
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 584b9455a93..ee96963362b 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -16,7 +16,7 @@ use config::{is_test_or_bench, StripUnconfigured};
 use errors::FatalError;
 use ext::base::*;
 use ext::derive::{add_derived_markers, collect_derives};
-use ext::hygiene::{Mark, SyntaxContext};
+use ext::hygiene::{self, Mark, SyntaxContext};
 use ext::placeholders::{placeholder, PlaceholderExpander};
 use feature_gate::{self, Features, GateIssue, is_builtin_attr, emit_feature_err};
 use fold;
@@ -502,6 +502,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 span: None,
                 allow_internal_unstable: false,
                 allow_internal_unsafe: false,
+                edition: ext.edition(),
             }
         });
 
@@ -520,7 +521,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 items.push(item);
                 Some(kind.expect_from_annotatables(items))
             }
-            AttrProcMacro(ref mac) => {
+            AttrProcMacro(ref mac, ..) => {
                 self.gate_proc_macro_attr_item(attr.span, &item);
                 let item_tok = TokenTree::Token(DUMMY_SP, Token::interpolated(match item {
                     Annotatable::Item(item) => token::NtItem(item),
@@ -609,7 +610,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                                           allow_internal_unstable,
                                           allow_internal_unsafe,
                                           // can't infer this type
-                                          unstable_feature: Option<(Symbol, u32)>| {
+                                          unstable_feature: Option<(Symbol, u32)>,
+                                          edition| {
 
             // feature-gate the macro invocation
             if let Some((feature, issue)) = unstable_feature {
@@ -642,15 +644,17 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                     span: def_site_span,
                     allow_internal_unstable,
                     allow_internal_unsafe,
+                    edition,
                 },
             });
             Ok(())
         };
 
         let opt_expanded = match *ext {
-            DeclMacro(ref expand, def_span) => {
+            DeclMacro(ref expand, def_span, edition) => {
                 if let Err(dummy_span) = validate_and_set_expn_info(self, def_span.map(|(_, s)| s),
-                                                                    false, false, None) {
+                                                                    false, false, None,
+                                                                    edition) {
                     dummy_span
                 } else {
                     kind.make_from(expand.expand(self.cx, span, mac.node.stream()))
@@ -663,11 +667,13 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 allow_internal_unstable,
                 allow_internal_unsafe,
                 unstable_feature,
+                edition,
             } => {
                 if let Err(dummy_span) = validate_and_set_expn_info(self, def_info.map(|(_, s)| s),
                                                                     allow_internal_unstable,
                                                                     allow_internal_unsafe,
-                                                                    unstable_feature) {
+                                                                    unstable_feature,
+                                                                    edition) {
                     dummy_span
                 } else {
                     kind.make_from(expander.expand(self.cx, span, mac.node.stream()))
@@ -688,6 +694,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                             span: tt_span,
                             allow_internal_unstable,
                             allow_internal_unsafe: false,
+                            edition: hygiene::default_edition(),
                         }
                     });
 
@@ -709,7 +716,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 kind.dummy(span)
             }
 
-            ProcMacro(ref expandfun) => {
+            ProcMacro(ref expandfun, edition) => {
                 if ident.name != keywords::Invalid.name() {
                     let msg =
                         format!("macro {}! expects no ident argument, given '{}'", path, ident);
@@ -728,6 +735,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                             // FIXME probably want to follow macro_rules macros here.
                             allow_internal_unstable: false,
                             allow_internal_unsafe: false,
+                            edition,
                         },
                     });
 
@@ -802,11 +810,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 span: None,
                 allow_internal_unstable: false,
                 allow_internal_unsafe: false,
+                edition: ext.edition(),
             }
         };
 
         match *ext {
-            ProcMacroDerive(ref ext, _) => {
+            ProcMacroDerive(ref ext, ..) => {
                 invoc.expansion_data.mark.set_expn_info(expn_info);
                 let span = span.with_ctxt(self.cx.backtrace());
                 let dummy = ast::MetaItem { // FIXME(jseyfried) avoid this
diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs
index e96a0e838cf..d1a7e7aac26 100644
--- a/src/libsyntax/ext/tt/macro_rules.rs
+++ b/src/libsyntax/ext/tt/macro_rules.rs
@@ -10,6 +10,7 @@
 
 use {ast, attr};
 use syntax_pos::{Span, DUMMY_SP};
+use edition::Edition;
 use ext::base::{DummyResult, ExtCtxt, MacResult, SyntaxExtension};
 use ext::base::{NormalTT, TTMacroExpander};
 use ext::expand::{Expansion, ExpansionKind};
@@ -183,7 +184,8 @@ fn generic_extension<'cx>(cx: &'cx mut ExtCtxt,
 // Holy self-referential!
 
 /// Converts a `macro_rules!` invocation into a syntax extension.
-pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item) -> SyntaxExtension {
+pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item, edition: Edition)
+               -> SyntaxExtension {
     let lhs_nm = ast::Ident::with_empty_ctxt(Symbol::gensym("lhs"));
     let rhs_nm = ast::Ident::with_empty_ctxt(Symbol::gensym("rhs"));
 
@@ -298,10 +300,11 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item) -> Syntax
             def_info: Some((def.id, def.span)),
             allow_internal_unstable,
             allow_internal_unsafe,
-            unstable_feature
+            unstable_feature,
+            edition,
         }
     } else {
-        SyntaxExtension::DeclMacro(expander, Some((def.id, def.span)))
+        SyntaxExtension::DeclMacro(expander, Some((def.id, def.span)), edition)
     }
 }
 
diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs
index 90af3ba51ec..e9817034569 100644
--- a/src/libsyntax/lib.rs
+++ b/src/libsyntax/lib.rs
@@ -22,7 +22,6 @@
 #![feature(unicode_internals)]
 #![feature(rustc_diagnostic_macros)]
 #![feature(slice_sort_by_cached_key)]
-#![feature(non_exhaustive)]
 #![feature(const_atomic_usize_new)]
 #![feature(rustc_attrs)]
 #![feature(str_escape)]
@@ -142,7 +141,6 @@ pub mod codemap;
 #[macro_use]
 pub mod config;
 pub mod entry;
-pub mod edition;
 pub mod feature_gate;
 pub mod fold;
 pub mod parse;
@@ -150,6 +148,7 @@ pub mod ptr;
 pub mod show_span;
 pub mod std_inject;
 pub mod str;
+pub use syntax_pos::edition;
 pub use syntax_pos::symbol;
 pub mod test;
 pub mod tokenstream;
diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs
index a0434fe6616..bbece1ee5e3 100644
--- a/src/libsyntax/parse/lexer/mod.rs
+++ b/src/libsyntax/parse/lexer/mod.rs
@@ -1128,7 +1128,7 @@ impl<'a> StringReader<'a> {
                 return Ok(self.with_str_from(start, |string| {
                     // FIXME: perform NFKC normalization here. (Issue #2253)
                     let ident = self.mk_ident(string);
-                    if is_raw_ident && (token::is_path_segment_keyword(ident) ||
+                    if is_raw_ident && (ident.is_path_segment_keyword() ||
                                         ident.name == keywords::Underscore.name()) {
                         self.fatal_span_(raw_start, self.pos,
                             &format!("`r#{}` is not currently supported.", ident.name)
diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index a1c056cbb2c..5575614a4d4 100644
--- a/src/libsyntax/parse/token.rs
+++ b/src/libsyntax/parse/token.rs
@@ -138,44 +138,6 @@ fn ident_can_begin_type(ident: ast::Ident, is_raw: bool) -> bool {
     ].contains(&ident.name)
 }
 
-pub fn is_path_segment_keyword(id: ast::Ident) -> bool {
-    id.name == keywords::Super.name() ||
-    id.name == keywords::SelfValue.name() ||
-    id.name == keywords::SelfType.name() ||
-    id.name == keywords::Extern.name() ||
-    id.name == keywords::Crate.name() ||
-    id.name == keywords::CrateRoot.name() ||
-    id.name == keywords::DollarCrate.name()
-}
-
-// We see this identifier in a normal identifier position, like variable name or a type.
-// How was it written originally? Did it use the raw form? Let's try to guess.
-pub fn is_raw_guess(ident: ast::Ident) -> bool {
-    ident.name != keywords::Invalid.name() &&
-    is_reserved_ident(ident) && !is_path_segment_keyword(ident)
-}
-
-// Returns true for reserved identifiers used internally for elided lifetimes,
-// unnamed method parameters, crate root module, error recovery etc.
-pub fn is_special_ident(id: ast::Ident) -> bool {
-    id.name <= keywords::Underscore.name()
-}
-
-/// Returns `true` if the token is a keyword used in the language.
-pub fn is_used_keyword(id: ast::Ident) -> bool {
-    id.name >= keywords::As.name() && id.name <= keywords::While.name()
-}
-
-/// Returns `true` if the token is a keyword reserved for possible future use.
-pub fn is_unused_keyword(id: ast::Ident) -> bool {
-    id.name >= keywords::Abstract.name() && id.name <= keywords::Yield.name()
-}
-
-/// Returns `true` if the token is either a special identifier or a keyword.
-pub fn is_reserved_ident(id: ast::Ident) -> bool {
-    is_special_ident(id) || is_used_keyword(id) || is_unused_keyword(id)
-}
-
 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug)]
 pub enum Token {
     /* Expression-operator symbols. */
@@ -251,7 +213,7 @@ impl Token {
 
     /// Recovers a `Token` from an `ast::Ident`. This creates a raw identifier if necessary.
     pub fn from_ast_ident(ident: ast::Ident) -> Token {
-        Ident(ident, is_raw_guess(ident))
+        Ident(ident, ident.is_raw_guess())
     }
 
     /// Returns `true` if the token starts with '>'.
@@ -431,7 +393,7 @@ impl Token {
 
     pub fn is_path_segment_keyword(&self) -> bool {
         match self.ident() {
-            Some((id, false)) => is_path_segment_keyword(id),
+            Some((id, false)) => id.is_path_segment_keyword(),
             _ => false,
         }
     }
@@ -440,7 +402,7 @@ impl Token {
     // unnamed method parameters, crate root module, error recovery etc.
     pub fn is_special_ident(&self) -> bool {
         match self.ident() {
-            Some((id, false)) => is_special_ident(id),
+            Some((id, false)) => id.is_special(),
             _ => false,
         }
     }
@@ -448,7 +410,7 @@ impl Token {
     /// Returns `true` if the token is a keyword used in the language.
     pub fn is_used_keyword(&self) -> bool {
         match self.ident() {
-            Some((id, false)) => is_used_keyword(id),
+            Some((id, false)) => id.is_used_keyword(),
             _ => false,
         }
     }
@@ -456,7 +418,7 @@ impl Token {
     /// Returns `true` if the token is a keyword reserved for possible future use.
     pub fn is_unused_keyword(&self) -> bool {
         match self.ident() {
-            Some((id, false)) => is_unused_keyword(id),
+            Some((id, false)) => id.is_unused_keyword(),
             _ => false,
         }
     }
@@ -464,7 +426,7 @@ impl Token {
     /// Returns `true` if the token is either a special identifier or a keyword.
     pub fn is_reserved_ident(&self) -> bool {
         match self.ident() {
-            Some((id, false)) => is_reserved_ident(id),
+            Some((id, false)) => id.is_reserved(),
             _ => false,
         }
     }
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index a700799cde5..17f83a09c77 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -2374,7 +2374,7 @@ impl<'a> State<'a> {
     }
 
     pub fn print_ident(&mut self, ident: ast::Ident) -> io::Result<()> {
-        if token::is_raw_guess(ident) {
+        if ident.is_raw_guess() {
             self.s.word(&format!("r#{}", ident))?;
         } else {
             self.s.word(&ident.name.as_str())?;
diff --git a/src/libsyntax/std_inject.rs b/src/libsyntax/std_inject.rs
index 53dc19ba37d..e9cd7adb9c1 100644
--- a/src/libsyntax/std_inject.rs
+++ b/src/libsyntax/std_inject.rs
@@ -14,7 +14,7 @@ use std::cell::Cell;
 use ext::hygiene::{Mark, SyntaxContext};
 use symbol::{Symbol, keywords};
 use syntax_pos::{DUMMY_SP, Span};
-use codemap::{ExpnInfo, NameAndSpan, MacroAttribute, dummy_spanned, respan};
+use codemap::{ExpnInfo, NameAndSpan, MacroAttribute, dummy_spanned, hygiene, respan};
 use ptr::P;
 use tokenstream::TokenStream;
 
@@ -30,6 +30,7 @@ fn ignored_span(sp: Span) -> Span {
             span: None,
             allow_internal_unstable: true,
             allow_internal_unsafe: false,
+            edition: hygiene::default_edition(),
         }
     });
     sp.with_ctxt(SyntaxContext::empty().apply_mark(mark))
diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs
index 1734692f9e7..1dfd48a24c3 100644
--- a/src/libsyntax/test.rs
+++ b/src/libsyntax/test.rs
@@ -29,7 +29,7 @@ use entry::{self, EntryPointType};
 use ext::base::{ExtCtxt, Resolver};
 use ext::build::AstBuilder;
 use ext::expand::ExpansionConfig;
-use ext::hygiene::{Mark, SyntaxContext};
+use ext::hygiene::{self, Mark, SyntaxContext};
 use fold::Folder;
 use feature_gate::Features;
 use util::move_map::MoveMap;
@@ -300,6 +300,7 @@ fn generate_test_harness(sess: &ParseSess,
             span: None,
             allow_internal_unstable: true,
             allow_internal_unsafe: false,
+            edition: hygiene::default_edition(),
         }
     });
 
diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs
index e100ef29225..15fcfac13ad 100644
--- a/src/libsyntax_ext/lib.rs
+++ b/src/libsyntax_ext/lib.rs
@@ -54,6 +54,7 @@ pub mod proc_macro_impl;
 use rustc_data_structures::sync::Lrc;
 use syntax::ast;
 use syntax::ext::base::{MacroExpanderFn, NormalTT, NamedSyntaxExtension};
+use syntax::ext::hygiene;
 use syntax::symbol::Symbol;
 
 pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver,
@@ -74,6 +75,7 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver,
                         allow_internal_unstable: false,
                         allow_internal_unsafe: false,
                         unstable_feature: None,
+                        edition: hygiene::default_edition(),
                     });
         )* }
     }
@@ -128,7 +130,8 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver,
                 def_info: None,
                 allow_internal_unstable: true,
                 allow_internal_unsafe: false,
-                unstable_feature: None
+                unstable_feature: None,
+                edition: hygiene::default_edition(),
             });
 
     for (name, ext) in user_exts {
diff --git a/src/libsyntax_ext/proc_macro_registrar.rs b/src/libsyntax_ext/proc_macro_registrar.rs
index d684e8b4ffe..3593165023a 100644
--- a/src/libsyntax_ext/proc_macro_registrar.rs
+++ b/src/libsyntax_ext/proc_macro_registrar.rs
@@ -14,7 +14,7 @@ use errors;
 
 use syntax::ast::{self, Ident, NodeId};
 use syntax::attr;
-use syntax::codemap::{ExpnInfo, NameAndSpan, MacroAttribute, respan};
+use syntax::codemap::{ExpnInfo, NameAndSpan, MacroAttribute, hygiene, respan};
 use syntax::ext::base::ExtCtxt;
 use syntax::ext::build::AstBuilder;
 use syntax::ext::expand::ExpansionConfig;
@@ -369,6 +369,7 @@ fn mk_registrar(cx: &mut ExtCtxt,
             span: None,
             allow_internal_unstable: true,
             allow_internal_unsafe: false,
+            edition: hygiene::default_edition(),
         }
     });
     let span = DUMMY_SP.apply_mark(mark);
diff --git a/src/libsyntax/edition.rs b/src/libsyntax_pos/edition.rs
index c98b54581f3..18446c10996 100644
--- a/src/libsyntax/edition.rs
+++ b/src/libsyntax_pos/edition.rs
@@ -12,7 +12,7 @@ use std::fmt;
 use std::str::FromStr;
 
 /// The edition of the compiler (RFC 2052)
-#[derive(Clone, Copy, Hash, PartialOrd, Ord, Eq, PartialEq, Debug)]
+#[derive(Clone, Copy, Hash, PartialOrd, Ord, Eq, PartialEq, Debug, RustcEncodable, RustcDecodable)]
 #[non_exhaustive]
 pub enum Edition {
     // editions must be kept in order, newest to oldest
diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs
index be031ea98c9..1365ac396ff 100644
--- a/src/libsyntax_pos/hygiene.rs
+++ b/src/libsyntax_pos/hygiene.rs
@@ -17,6 +17,7 @@
 
 use GLOBALS;
 use Span;
+use edition::Edition;
 use symbol::{Ident, Symbol};
 
 use serialize::{Encodable, Decodable, Encoder, Decoder};
@@ -151,6 +152,7 @@ pub struct HygieneData {
     syntax_contexts: Vec<SyntaxContextData>,
     markings: HashMap<(SyntaxContext, Mark), SyntaxContext>,
     gensym_to_ctxt: HashMap<Symbol, Span>,
+    default_edition: Edition,
 }
 
 impl HygieneData {
@@ -168,6 +170,7 @@ impl HygieneData {
             }],
             markings: HashMap::new(),
             gensym_to_ctxt: HashMap::new(),
+            default_edition: Edition::Edition2015,
         }
     }
 
@@ -176,6 +179,14 @@ impl HygieneData {
     }
 }
 
+pub fn default_edition() -> Edition {
+    HygieneData::with(|data| data.default_edition)
+}
+
+pub fn set_default_edition(edition: Edition) {
+    HygieneData::with(|data| data.default_edition = edition);
+}
+
 pub fn clear_markings() {
     HygieneData::with(|data| data.markings = HashMap::new());
 }
@@ -443,6 +454,8 @@ pub struct NameAndSpan {
     /// Whether the macro is allowed to use `unsafe` internally
     /// even if the user crate has `#![forbid(unsafe_code)]`.
     pub allow_internal_unsafe: bool,
+    /// Edition of the crate in which the macro is defined.
+    pub edition: Edition,
     /// The span of the macro definition itself. The macro may not
     /// have a sensible definition span (e.g. something defined
     /// completely inside libsyntax) in which case this is None.
diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs
index 73f0e6a6018..17163576901 100644
--- a/src/libsyntax_pos/lib.rs
+++ b/src/libsyntax_pos/lib.rs
@@ -20,6 +20,7 @@
 
 #![feature(const_fn)]
 #![feature(custom_attribute)]
+#![feature(non_exhaustive)]
 #![feature(optin_builtin_traits)]
 #![allow(unused_attributes)]
 #![feature(specialization)]
@@ -48,6 +49,7 @@ extern crate serialize as rustc_serialize; // used by deriving
 
 extern crate unicode_width;
 
+pub mod edition;
 pub mod hygiene;
 pub use hygiene::{Mark, SyntaxContext, ExpnInfo, ExpnFormat, NameAndSpan, CompilerDesugaringKind};
 
@@ -298,6 +300,12 @@ impl Span {
         self.ctxt().outer().expn_info().map(|i| i.call_site)
     }
 
+    /// Edition of the crate from which this span came.
+    pub fn edition(self) -> edition::Edition {
+        self.ctxt().outer().expn_info().map_or_else(|| hygiene::default_edition(),
+                                                    |einfo| einfo.callee.edition)
+    }
+
     /// Return the source callee.
     ///
     /// Returns None if the supplied span has no expansion trace,
diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs
index 2258ed12779..a08f9b2e54a 100644
--- a/src/libsyntax_pos/symbol.rs
+++ b/src/libsyntax_pos/symbol.rs
@@ -12,6 +12,7 @@
 //! allows bidirectional lookup; i.e. given a value, one can easily find the
 //! type, and vice versa.
 
+use edition::Edition;
 use hygiene::SyntaxContext;
 use {Span, DUMMY_SP, GLOBALS};
 
@@ -318,7 +319,7 @@ macro_rules! declare_keywords {(
 // NB: leaving holes in the ident table is bad! a different ident will get
 // interned with the id from the hole, but it will be between the min and max
 // of the reserved words, and thus tagged as "reserved".
-// After modifying this list adjust `is_special_ident`, `is_used_keyword`/`is_unused_keyword`,
+// After modifying this list adjust `is_special`, `is_used_keyword`/`is_unused_keyword`,
 // this should be rarely necessary though if the keywords are kept in alphabetic order.
 declare_keywords! {
     // Special reserved identifiers used internally for elided lifetimes,
@@ -383,16 +384,68 @@ declare_keywords! {
     (53, Virtual,            "virtual")
     (54, Yield,              "yield")
 
+    // Edition-specific keywords reserved for future use.
+    (55, Async,              "async") // >= 2018 Edition Only
+
     // Special lifetime names
-    (55, UnderscoreLifetime, "'_")
-    (56, StaticLifetime,     "'static")
+    (56, UnderscoreLifetime, "'_")
+    (57, StaticLifetime,     "'static")
 
     // Weak keywords, have special meaning only in specific contexts.
-    (57, Auto,               "auto")
-    (58, Catch,              "catch")
-    (59, Default,            "default")
-    (60, Dyn,                "dyn")
-    (61, Union,              "union")
+    (58, Auto,               "auto")
+    (59, Catch,              "catch")
+    (60, Default,            "default")
+    (61, Dyn,                "dyn")
+    (62, Union,              "union")
+}
+
+impl Symbol {
+    fn is_unused_keyword_2018(self) -> bool {
+        self == keywords::Async.name()
+    }
+}
+
+impl Ident {
+    // Returns true for reserved identifiers used internally for elided lifetimes,
+    // unnamed method parameters, crate root module, error recovery etc.
+    pub fn is_special(self) -> bool {
+        self.name <= keywords::Underscore.name()
+    }
+
+    /// Returns `true` if the token is a keyword used in the language.
+    pub fn is_used_keyword(self) -> bool {
+        self.name >= keywords::As.name() && self.name <= keywords::While.name()
+    }
+
+    /// Returns `true` if the token is a keyword reserved for possible future use.
+    pub fn is_unused_keyword(self) -> bool {
+        // Note: `span.edition()` is relatively expensive, don't call it unless necessary.
+        self.name >= keywords::Abstract.name() && self.name <= keywords::Yield.name() ||
+        self.name.is_unused_keyword_2018() && self.span.edition() == Edition::Edition2018
+    }
+
+    /// Returns `true` if the token is either a special identifier or a keyword.
+    pub fn is_reserved(self) -> bool {
+        self.is_special() || self.is_used_keyword() || self.is_unused_keyword()
+    }
+
+    /// A keyword or reserved identifier that can be used as a path segment.
+    pub fn is_path_segment_keyword(self) -> bool {
+        self.name == keywords::Super.name() ||
+        self.name == keywords::SelfValue.name() ||
+        self.name == keywords::SelfType.name() ||
+        self.name == keywords::Extern.name() ||
+        self.name == keywords::Crate.name() ||
+        self.name == keywords::CrateRoot.name() ||
+        self.name == keywords::DollarCrate.name()
+    }
+
+    // We see this identifier in a normal identifier position, like variable name or a type.
+    // How was it written originally? Did it use the raw form? Let's try to guess.
+    pub fn is_raw_guess(self) -> bool {
+        self.name != keywords::Invalid.name() &&
+        self.is_reserved() && !self.is_path_segment_keyword()
+    }
 }
 
 // If an interner exists, return it. Otherwise, prepare a fresh one.
diff --git a/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs b/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs
index 231ed2898f1..d7ede763838 100644
--- a/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs
+++ b/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs
@@ -20,6 +20,7 @@ extern crate rustc_plugin;
 
 use std::borrow::ToOwned;
 use syntax::ast;
+use syntax::ext::hygiene;
 use syntax::ext::build::AstBuilder;
 use syntax::ext::base::{TTMacroExpander, ExtCtxt, MacResult, MacEager, NormalTT};
 use syntax::print::pprust;
@@ -54,5 +55,6 @@ pub fn plugin_registrar(reg: &mut Registry) {
             allow_internal_unstable: false,
             allow_internal_unsafe: false,
             unstable_feature: None,
+            edition: hygiene::default_edition(),
         });
 }
diff --git a/src/test/run-pass/auxiliary/edition-kw-macro-2015.rs b/src/test/run-pass/auxiliary/edition-kw-macro-2015.rs
new file mode 100644
index 00000000000..9127c8e350a
--- /dev/null
+++ b/src/test/run-pass/auxiliary/edition-kw-macro-2015.rs
@@ -0,0 +1,38 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: --edition=2015
+
+#![feature(raw_identifiers)]
+
+#[macro_export]
+macro_rules! produces_async {
+    () => (pub fn async() {})
+}
+
+#[macro_export]
+macro_rules! produces_async_raw {
+    () => (pub fn r#async() {})
+}
+
+#[macro_export]
+macro_rules! consumes_async {
+    (async) => (1)
+}
+
+#[macro_export]
+macro_rules! consumes_async_raw {
+    (r#async) => (1)
+}
+
+#[macro_export]
+macro_rules! passes_ident {
+    ($i: ident) => ($i)
+}
diff --git a/src/test/run-pass/auxiliary/edition-kw-macro-2018.rs b/src/test/run-pass/auxiliary/edition-kw-macro-2018.rs
new file mode 100644
index 00000000000..4fef77d67ea
--- /dev/null
+++ b/src/test/run-pass/auxiliary/edition-kw-macro-2018.rs
@@ -0,0 +1,38 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: --edition=2018
+
+#![feature(raw_identifiers)]
+
+#[macro_export]
+macro_rules! produces_async {
+    () => (pub fn async() {})
+}
+
+#[macro_export]
+macro_rules! produces_async_raw {
+    () => (pub fn r#async() {})
+}
+
+#[macro_export]
+macro_rules! consumes_async {
+    (async) => (1)
+}
+
+#[macro_export]
+macro_rules! consumes_async_raw {
+    (r#async) => (1)
+}
+
+#[macro_export]
+macro_rules! passes_ident {
+    ($i: ident) => ($i)
+}
diff --git a/src/test/run-pass/edition-keywords-2015-2015.rs b/src/test/run-pass/edition-keywords-2015-2015.rs
new file mode 100644
index 00000000000..41480bb978e
--- /dev/null
+++ b/src/test/run-pass/edition-keywords-2015-2015.rs
@@ -0,0 +1,43 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: --edition=2015
+// aux-build:edition-kw-macro-2015.rs
+
+#![feature(raw_identifiers)]
+
+#[macro_use]
+extern crate edition_kw_macro_2015;
+
+pub fn check_async() {
+    let mut async = 1; // OK
+    let mut r#async = 1; // OK
+
+    r#async = consumes_async!(async); // OK
+    // r#async = consumes_async!(r#async); // ERROR, not a match
+    // r#async = consumes_async_raw!(async); // ERROR, not a match
+    r#async = consumes_async_raw!(r#async); // OK
+
+    if passes_ident!(async) == 1 {} // OK
+    if passes_ident!(r#async) == 1 {} // OK
+    one_async::async(); // OK
+    one_async::r#async(); // OK
+    two_async::async(); // OK
+    two_async::r#async(); // OK
+}
+
+mod one_async {
+    produces_async! {} // OK
+}
+mod two_async {
+    produces_async_raw! {} // OK
+}
+
+fn main() {}
diff --git a/src/test/run-pass/edition-keywords-2015-2018.rs b/src/test/run-pass/edition-keywords-2015-2018.rs
new file mode 100644
index 00000000000..78835d51063
--- /dev/null
+++ b/src/test/run-pass/edition-keywords-2015-2018.rs
@@ -0,0 +1,43 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: --edition=2015
+// aux-build:edition-kw-macro-2018.rs
+
+#![feature(raw_identifiers)]
+
+#[macro_use]
+extern crate edition_kw_macro_2018;
+
+pub fn check_async() {
+    let mut async = 1; // OK
+    let mut r#async = 1; // OK
+
+    r#async = consumes_async!(async); // OK
+    // r#async = consumes_async!(r#async); // ERROR, not a match
+    // r#async = consumes_async_raw!(async); // ERROR, not a match
+    r#async = consumes_async_raw!(r#async); // OK
+
+    if passes_ident!(async) == 1 {} // OK
+    if passes_ident!(r#async) == 1 {} // OK
+    // one_async::async(); // ERROR, unresolved name
+    // one_async::r#async(); // ERROR, unresolved name
+    two_async::async(); // OK
+    two_async::r#async(); // OK
+}
+
+mod one_async {
+    // produces_async! {} // ERROR, reserved
+}
+mod two_async {
+    produces_async_raw! {} // OK
+}
+
+fn main() {}
diff --git a/src/test/run-pass/edition-keywords-2018-2015.rs b/src/test/run-pass/edition-keywords-2018-2015.rs
new file mode 100644
index 00000000000..46d5f222cbb
--- /dev/null
+++ b/src/test/run-pass/edition-keywords-2018-2015.rs
@@ -0,0 +1,43 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: --edition=2018
+// aux-build:edition-kw-macro-2015.rs
+
+#![feature(raw_identifiers)]
+
+#[macro_use]
+extern crate edition_kw_macro_2015;
+
+pub fn check_async() {
+    // let mut async = 1; // ERROR, reserved
+    let mut r#async = 1; // OK
+
+    r#async = consumes_async!(async); // OK
+    // r#async = consumes_async!(r#async); // ERROR, not a match
+    // r#async = consumes_async_raw!(async); // ERROR, not a match
+    r#async = consumes_async_raw!(r#async); // OK
+
+    // if passes_ident!(async) == 1 {} // ERROR, reserved
+    if passes_ident!(r#async) == 1 {} // OK
+    // one_async::async(); // ERROR, reserved
+    one_async::r#async(); // OK
+    // two_async::async(); // ERROR, reserved
+    two_async::r#async(); // OK
+}
+
+mod one_async {
+    produces_async! {} // OK
+}
+mod two_async {
+    produces_async_raw! {} // OK
+}
+
+fn main() {}
diff --git a/src/test/run-pass/edition-keywords-2018-2018.rs b/src/test/run-pass/edition-keywords-2018-2018.rs
new file mode 100644
index 00000000000..06482988937
--- /dev/null
+++ b/src/test/run-pass/edition-keywords-2018-2018.rs
@@ -0,0 +1,43 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: --edition=2018
+// aux-build:edition-kw-macro-2018.rs
+
+#![feature(raw_identifiers)]
+
+#[macro_use]
+extern crate edition_kw_macro_2018;
+
+pub fn check_async() {
+    // let mut async = 1; // ERROR, reserved
+    let mut r#async = 1; // OK
+
+    r#async = consumes_async!(async); // OK
+    // r#async = consumes_async!(r#async); // ERROR, not a match
+    // r#async = consumes_async_raw!(async); // ERROR, not a match
+    r#async = consumes_async_raw!(r#async); // OK
+
+    // if passes_ident!(async) == 1 {} // ERROR, reserved
+    if passes_ident!(r#async) == 1 {} // OK
+    // one_async::async(); // ERROR, reserved
+    // one_async::r#async(); // ERROR, unresolved name
+    // two_async::async(); // ERROR, reserved
+    two_async::r#async(); // OK
+}
+
+mod one_async {
+    // produces_async! {} // ERROR, reserved
+}
+mod two_async {
+    produces_async_raw! {} // OK
+}
+
+fn main() {}
diff --git a/src/test/ui/auxiliary/edition-kw-macro-2015.rs b/src/test/ui/auxiliary/edition-kw-macro-2015.rs
new file mode 100644
index 00000000000..9127c8e350a
--- /dev/null
+++ b/src/test/ui/auxiliary/edition-kw-macro-2015.rs
@@ -0,0 +1,38 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: --edition=2015
+
+#![feature(raw_identifiers)]
+
+#[macro_export]
+macro_rules! produces_async {
+    () => (pub fn async() {})
+}
+
+#[macro_export]
+macro_rules! produces_async_raw {
+    () => (pub fn r#async() {})
+}
+
+#[macro_export]
+macro_rules! consumes_async {
+    (async) => (1)
+}
+
+#[macro_export]
+macro_rules! consumes_async_raw {
+    (r#async) => (1)
+}
+
+#[macro_export]
+macro_rules! passes_ident {
+    ($i: ident) => ($i)
+}
diff --git a/src/test/ui/auxiliary/edition-kw-macro-2018.rs b/src/test/ui/auxiliary/edition-kw-macro-2018.rs
new file mode 100644
index 00000000000..4fef77d67ea
--- /dev/null
+++ b/src/test/ui/auxiliary/edition-kw-macro-2018.rs
@@ -0,0 +1,38 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: --edition=2018
+
+#![feature(raw_identifiers)]
+
+#[macro_export]
+macro_rules! produces_async {
+    () => (pub fn async() {})
+}
+
+#[macro_export]
+macro_rules! produces_async_raw {
+    () => (pub fn r#async() {})
+}
+
+#[macro_export]
+macro_rules! consumes_async {
+    (async) => (1)
+}
+
+#[macro_export]
+macro_rules! consumes_async_raw {
+    (r#async) => (1)
+}
+
+#[macro_export]
+macro_rules! passes_ident {
+    ($i: ident) => ($i)
+}
diff --git a/src/test/ui/edition-keywords-2015-2015-expansion.rs b/src/test/ui/edition-keywords-2015-2015-expansion.rs
new file mode 100644
index 00000000000..b8a1994a105
--- /dev/null
+++ b/src/test/ui/edition-keywords-2015-2015-expansion.rs
@@ -0,0 +1,27 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: --edition=2015
+// aux-build:edition-kw-macro-2015.rs
+// compile-pass
+
+#![feature(raw_identifiers)]
+
+#[macro_use]
+extern crate edition_kw_macro_2015;
+
+mod one_async {
+    produces_async! {} // OK
+}
+mod two_async {
+    produces_async_raw! {} // OK
+}
+
+fn main() {}
diff --git a/src/test/ui/edition-keywords-2015-2015-parsing.rs b/src/test/ui/edition-keywords-2015-2015-parsing.rs
new file mode 100644
index 00000000000..1fb91ca006c
--- /dev/null
+++ b/src/test/ui/edition-keywords-2015-2015-parsing.rs
@@ -0,0 +1,32 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: --edition=2015
+// aux-build:edition-kw-macro-2015.rs
+
+#![feature(raw_identifiers)]
+
+#[macro_use]
+extern crate edition_kw_macro_2015;
+
+pub fn check_async() {
+    let mut async = 1; // OK
+    let mut r#async = 1; // OK
+
+    r#async = consumes_async!(async); // OK
+    r#async = consumes_async!(r#async); //~ ERROR no rules expected the token `r#async`
+    r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async`
+    r#async = consumes_async_raw!(r#async); // OK
+
+    if passes_ident!(async) == 1 {} // OK
+    if passes_ident!(r#async) == 1 {} // OK
+    module::async(); // OK
+    module::r#async(); // OK
+}
diff --git a/src/test/ui/edition-keywords-2015-2015-parsing.stderr b/src/test/ui/edition-keywords-2015-2015-parsing.stderr
new file mode 100644
index 00000000000..5b6fd3e1c9c
--- /dev/null
+++ b/src/test/ui/edition-keywords-2015-2015-parsing.stderr
@@ -0,0 +1,14 @@
+error: no rules expected the token `r#async`
+  --> $DIR/edition-keywords-2015-2015-parsing.rs:24:31
+   |
+LL |     r#async = consumes_async!(r#async); //~ ERROR no rules expected the token `r#async`
+   |                               ^^^^^^^
+
+error: no rules expected the token `async`
+  --> $DIR/edition-keywords-2015-2015-parsing.rs:25:35
+   |
+LL |     r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async`
+   |                                   ^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/edition-keywords-2015-2018-expansion.rs b/src/test/ui/edition-keywords-2015-2018-expansion.rs
new file mode 100644
index 00000000000..bc14c104c49
--- /dev/null
+++ b/src/test/ui/edition-keywords-2015-2018-expansion.rs
@@ -0,0 +1,24 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: --edition=2015
+// aux-build:edition-kw-macro-2018.rs
+
+#![feature(raw_identifiers)]
+
+#[macro_use]
+extern crate edition_kw_macro_2018;
+
+mod one_async {
+    produces_async! {} // ERROR expected identifier, found reserved keyword
+}
+mod two_async {
+    produces_async_raw! {} // OK
+}
diff --git a/src/test/ui/edition-keywords-2015-2018-expansion.stderr b/src/test/ui/edition-keywords-2015-2018-expansion.stderr
new file mode 100644
index 00000000000..13c4ee82537
--- /dev/null
+++ b/src/test/ui/edition-keywords-2015-2018-expansion.stderr
@@ -0,0 +1,10 @@
+error: expected identifier, found reserved keyword `async`
+  --> $DIR/edition-keywords-2015-2018-expansion.rs:20:5
+   |
+LL |     produces_async! {} // ERROR expected identifier, found reserved keyword
+   |     ^^^^^^^^^^^^^^^^^^ expected identifier, found reserved keyword
+   |
+   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/edition-keywords-2015-2018-parsing.rs b/src/test/ui/edition-keywords-2015-2018-parsing.rs
new file mode 100644
index 00000000000..0b680eb16c7
--- /dev/null
+++ b/src/test/ui/edition-keywords-2015-2018-parsing.rs
@@ -0,0 +1,32 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: --edition=2015
+// aux-build:edition-kw-macro-2018.rs
+
+#![feature(raw_identifiers)]
+
+#[macro_use]
+extern crate edition_kw_macro_2018;
+
+pub fn check_async() {
+    let mut async = 1; // OK
+    let mut r#async = 1; // OK
+
+    r#async = consumes_async!(async); // OK
+    r#async = consumes_async!(r#async); //~ ERROR no rules expected the token `r#async`
+    r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async`
+    r#async = consumes_async_raw!(r#async); // OK
+
+    if passes_ident!(async) == 1 {} // OK
+    if passes_ident!(r#async) == 1 {} // OK
+    module::async(); // OK
+    module::r#async(); // OK
+}
diff --git a/src/test/ui/edition-keywords-2015-2018-parsing.stderr b/src/test/ui/edition-keywords-2015-2018-parsing.stderr
new file mode 100644
index 00000000000..60cfbce3ff0
--- /dev/null
+++ b/src/test/ui/edition-keywords-2015-2018-parsing.stderr
@@ -0,0 +1,14 @@
+error: no rules expected the token `r#async`
+  --> $DIR/edition-keywords-2015-2018-parsing.rs:24:31
+   |
+LL |     r#async = consumes_async!(r#async); //~ ERROR no rules expected the token `r#async`
+   |                               ^^^^^^^
+
+error: no rules expected the token `async`
+  --> $DIR/edition-keywords-2015-2018-parsing.rs:25:35
+   |
+LL |     r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async`
+   |                                   ^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/edition-keywords-2018-2015-expansion.rs b/src/test/ui/edition-keywords-2018-2015-expansion.rs
new file mode 100644
index 00000000000..6f85f427eb0
--- /dev/null
+++ b/src/test/ui/edition-keywords-2018-2015-expansion.rs
@@ -0,0 +1,27 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: --edition=2018
+// aux-build:edition-kw-macro-2015.rs
+// compile-pass
+
+#![feature(raw_identifiers)]
+
+#[macro_use]
+extern crate edition_kw_macro_2015;
+
+mod one_async {
+    produces_async! {} // OK
+}
+mod two_async {
+    produces_async_raw! {} // OK
+}
+
+fn main() {}
diff --git a/src/test/ui/edition-keywords-2018-2015-parsing.rs b/src/test/ui/edition-keywords-2018-2015-parsing.rs
new file mode 100644
index 00000000000..29c5ea41f1f
--- /dev/null
+++ b/src/test/ui/edition-keywords-2018-2015-parsing.rs
@@ -0,0 +1,32 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: --edition=2018
+// aux-build:edition-kw-macro-2015.rs
+
+#![feature(raw_identifiers)]
+
+#[macro_use]
+extern crate edition_kw_macro_2015;
+
+pub fn check_async() {
+    let mut async = 1; //~ ERROR expected identifier, found reserved keyword `async`
+    let mut r#async = 1; // OK
+
+    r#async = consumes_async!(async); // OK
+    r#async = consumes_async!(r#async); //~ ERROR no rules expected the token `r#async`
+    r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async`
+    r#async = consumes_async_raw!(r#async); // OK
+
+    if passes_ident!(async) == 1 {} //~ ERROR expected expression, found reserved keyword `async`
+    if passes_ident!(r#async) == 1 {} // OK
+    module::async(); //~ ERROR expected identifier, found reserved keyword `async`
+    module::r#async(); // OK
+}
diff --git a/src/test/ui/edition-keywords-2018-2015-parsing.stderr b/src/test/ui/edition-keywords-2018-2015-parsing.stderr
new file mode 100644
index 00000000000..0b3ca57bfab
--- /dev/null
+++ b/src/test/ui/edition-keywords-2018-2015-parsing.stderr
@@ -0,0 +1,32 @@
+error: expected identifier, found reserved keyword `async`
+  --> $DIR/edition-keywords-2018-2015-parsing.rs:20:13
+   |
+LL |     let mut async = 1; //~ ERROR expected identifier, found reserved keyword `async`
+   |             ^^^^^ expected identifier, found reserved keyword
+
+error: expected identifier, found reserved keyword `async`
+  --> $DIR/edition-keywords-2018-2015-parsing.rs:30:13
+   |
+LL |     module::async(); //~ ERROR expected identifier, found reserved keyword `async`
+   |             ^^^^^ expected identifier, found reserved keyword
+
+error: no rules expected the token `r#async`
+  --> $DIR/edition-keywords-2018-2015-parsing.rs:24:31
+   |
+LL |     r#async = consumes_async!(r#async); //~ ERROR no rules expected the token `r#async`
+   |                               ^^^^^^^
+
+error: no rules expected the token `async`
+  --> $DIR/edition-keywords-2018-2015-parsing.rs:25:35
+   |
+LL |     r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async`
+   |                                   ^^^^^
+
+error: expected expression, found reserved keyword `async`
+  --> $DIR/edition-keywords-2018-2015-parsing.rs:28:22
+   |
+LL |     if passes_ident!(async) == 1 {} //~ ERROR expected expression, found reserved keyword `async`
+   |                      ^^^^^ expected expression
+
+error: aborting due to 5 previous errors
+
diff --git a/src/test/ui/edition-keywords-2018-2018-expansion.rs b/src/test/ui/edition-keywords-2018-2018-expansion.rs
new file mode 100644
index 00000000000..ef7f63e225c
--- /dev/null
+++ b/src/test/ui/edition-keywords-2018-2018-expansion.rs
@@ -0,0 +1,24 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: --edition=2018
+// aux-build:edition-kw-macro-2018.rs
+
+#![feature(raw_identifiers)]
+
+#[macro_use]
+extern crate edition_kw_macro_2018;
+
+mod one_async {
+    produces_async! {} // ERROR expected identifier, found reserved keyword `async`
+}
+mod two_async {
+    produces_async_raw! {} // OK
+}
diff --git a/src/test/ui/edition-keywords-2018-2018-expansion.stderr b/src/test/ui/edition-keywords-2018-2018-expansion.stderr
new file mode 100644
index 00000000000..cd51030fd28
--- /dev/null
+++ b/src/test/ui/edition-keywords-2018-2018-expansion.stderr
@@ -0,0 +1,10 @@
+error: expected identifier, found reserved keyword `async`
+  --> $DIR/edition-keywords-2018-2018-expansion.rs:20:5
+   |
+LL |     produces_async! {} // ERROR expected identifier, found reserved keyword `async`
+   |     ^^^^^^^^^^^^^^^^^^ expected identifier, found reserved keyword
+   |
+   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/edition-keywords-2018-2018-parsing.rs b/src/test/ui/edition-keywords-2018-2018-parsing.rs
new file mode 100644
index 00000000000..a94808eb224
--- /dev/null
+++ b/src/test/ui/edition-keywords-2018-2018-parsing.rs
@@ -0,0 +1,32 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: --edition=2018
+// aux-build:edition-kw-macro-2018.rs
+
+#![feature(raw_identifiers)]
+
+#[macro_use]
+extern crate edition_kw_macro_2018;
+
+pub fn check_async() {
+    let mut async = 1; //~ ERROR expected identifier, found reserved keyword `async`
+    let mut r#async = 1; // OK
+
+    r#async = consumes_async!(async); // OK
+    r#async = consumes_async!(r#async); //~ ERROR no rules expected the token `r#async`
+    r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async`
+    r#async = consumes_async_raw!(r#async); // OK
+
+    if passes_ident!(async) == 1 {} //~ ERROR expected expression, found reserved keyword `async`
+    if passes_ident!(r#async) == 1 {} // OK
+    module::async(); //~ ERROR expected identifier, found reserved keyword `async`
+    module::r#async(); // OK
+}
diff --git a/src/test/ui/edition-keywords-2018-2018-parsing.stderr b/src/test/ui/edition-keywords-2018-2018-parsing.stderr
new file mode 100644
index 00000000000..1b18d8a39be
--- /dev/null
+++ b/src/test/ui/edition-keywords-2018-2018-parsing.stderr
@@ -0,0 +1,32 @@
+error: expected identifier, found reserved keyword `async`
+  --> $DIR/edition-keywords-2018-2018-parsing.rs:20:13
+   |
+LL |     let mut async = 1; //~ ERROR expected identifier, found reserved keyword `async`
+   |             ^^^^^ expected identifier, found reserved keyword
+
+error: expected identifier, found reserved keyword `async`
+  --> $DIR/edition-keywords-2018-2018-parsing.rs:30:13
+   |
+LL |     module::async(); //~ ERROR expected identifier, found reserved keyword `async`
+   |             ^^^^^ expected identifier, found reserved keyword
+
+error: no rules expected the token `r#async`
+  --> $DIR/edition-keywords-2018-2018-parsing.rs:24:31
+   |
+LL |     r#async = consumes_async!(r#async); //~ ERROR no rules expected the token `r#async`
+   |                               ^^^^^^^
+
+error: no rules expected the token `async`
+  --> $DIR/edition-keywords-2018-2018-parsing.rs:25:35
+   |
+LL |     r#async = consumes_async_raw!(async); //~ ERROR no rules expected the token `async`
+   |                                   ^^^^^
+
+error: expected expression, found reserved keyword `async`
+  --> $DIR/edition-keywords-2018-2018-parsing.rs:28:22
+   |
+LL |     if passes_ident!(async) == 1 {} //~ ERROR expected expression, found reserved keyword `async`
+   |                      ^^^^^ expected expression
+
+error: aborting due to 5 previous errors
+