diff --git a/README.md b/README.md
index 724bc36ecc6fb..4c2e4eef6552f 100644
--- a/README.md
+++ b/README.md
@@ -32,6 +32,7 @@ or reading the [rustc guide][rustcguidebuild].
    * `cmake` 3.4.3 or later
    * `curl`
    * `git`
+   * `ssl` which comes in `libssl-dev` or `openssl-devel`
 
 2. Clone the [source] with `git`:
 
@@ -56,6 +57,8 @@ or reading the [rustc guide][rustcguidebuild].
     an installation (using `./x.py install`) that you set the `prefix` value
     in the `[install]` section to a directory that you have write permissions.
 
+    Create install directory if you are not installing in default directory
+
 4. Build and install:
 
     ```sh
diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs
index 0f4ac63651ca9..500d5766a899e 100644
--- a/src/bootstrap/dist.rs
+++ b/src/bootstrap/dist.rs
@@ -808,6 +808,7 @@ fn copy_src_dirs(builder: &Builder<'_>, src_dirs: &[&str], exclude_dirs: &[&str]
             "llvm-project/lld", "llvm-project\\lld",
             "llvm-project/lldb", "llvm-project\\lldb",
             "llvm-project/llvm", "llvm-project\\llvm",
+            "llvm-project/compiler-rt", "llvm-project\\compiler-rt",
         ];
         if spath.contains("llvm-project") && !spath.ends_with("llvm-project")
             && !LLVM_PROJECTS.iter().any(|path| spath.contains(path))
diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs
index ddfca3a4cfb72..646060bc8be77 100644
--- a/src/librustc/ich/impls_syntax.rs
+++ b/src/librustc/ich/impls_syntax.rs
@@ -390,9 +390,17 @@ impl_stable_hash_for!(struct ::syntax_pos::hygiene::ExpnData {
 impl_stable_hash_for!(enum ::syntax_pos::hygiene::ExpnKind {
     Root,
     Macro(kind, descr),
+    AstPass(kind),
     Desugaring(kind)
 });
 
+impl_stable_hash_for!(enum ::syntax_pos::hygiene::AstPass {
+    StdImports,
+    TestHarness,
+    ProcMacroHarness,
+    PluginMacroDefs,
+});
+
 impl_stable_hash_for!(enum ::syntax_pos::hygiene::DesugaringKind {
     CondTemporary,
     Async,
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index 368f5bb64fe6c..bf4330a29a7df 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -97,7 +97,6 @@ pub mod query;
 
 #[macro_use]
 pub mod arena;
-pub mod cfg;
 pub mod dep_graph;
 pub mod hir;
 pub mod ich;
diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs
index a3518b2b478ad..e33f8e27871b2 100644
--- a/src/librustc/lint/mod.rs
+++ b/src/librustc/lint/mod.rs
@@ -646,6 +646,30 @@ pub fn struct_lint_level<'a>(sess: &'a Session,
         (Level::Forbid, None) => sess.struct_err(msg),
     };
 
+    // Check for future incompatibility lints and issue a stronger warning.
+    let lints = sess.lint_store.borrow();
+    let lint_id = LintId::of(lint);
+    let future_incompatible = lints.future_incompatible(lint_id);
+
+    // If this code originates in a foreign macro, aka something that this crate
+    // did not itself author, then it's likely that there's nothing this crate
+    // can do about it. We probably want to skip the lint entirely.
+    if err.span.primary_spans().iter().any(|s| in_external_macro(sess, *s)) {
+        // Any suggestions made here are likely to be incorrect, so anything we
+        // emit shouldn't be automatically fixed by rustfix.
+        err.allow_suggestions(false);
+
+        // If this is a future incompatible lint it'll become a hard error, so
+        // we have to emit *something*. Also allow lints to whitelist themselves
+        // on a case-by-case basis for emission in a foreign macro.
+        if future_incompatible.is_none() && !lint.report_in_external_macro {
+            err.cancel();
+            // Don't continue further, since we don't want to have
+            // `diag_span_note_once` called for a diagnostic that isn't emitted.
+            return err;
+        }
+    }
+
     let name = lint.name_lower();
     match src {
         LintSource::Default => {
@@ -695,10 +719,6 @@ pub fn struct_lint_level<'a>(sess: &'a Session,
 
     err.code(DiagnosticId::Lint(name));
 
-    // Check for future incompatibility lints and issue a stronger warning.
-    let lints = sess.lint_store.borrow();
-    let lint_id = LintId::of(lint);
-    let future_incompatible = lints.future_incompatible(lint_id);
     if let Some(future_incompatible) = future_incompatible {
         const STANDARD_MESSAGE: &str =
             "this was previously accepted by the compiler but is being phased out; \
@@ -723,22 +743,6 @@ pub fn struct_lint_level<'a>(sess: &'a Session,
         err.note(&citation);
     }
 
-    // If this code originates in a foreign macro, aka something that this crate
-    // did not itself author, then it's likely that there's nothing this crate
-    // can do about it. We probably want to skip the lint entirely.
-    if err.span.primary_spans().iter().any(|s| in_external_macro(sess, *s)) {
-        // Any suggestions made here are likely to be incorrect, so anything we
-        // emit shouldn't be automatically fixed by rustfix.
-        err.allow_suggestions(false);
-
-        // If this is a future incompatible lint it'll become a hard error, so
-        // we have to emit *something*. Also allow lints to whitelist themselves
-        // on a case-by-case basis for emission in a foreign macro.
-        if future_incompatible.is_none() && !lint.report_in_external_macro {
-            err.cancel()
-        }
-    }
-
     return err
 }
 
@@ -868,7 +872,7 @@ pub fn in_external_macro(sess: &Session, span: Span) -> bool {
     let expn_data = span.ctxt().outer_expn_data();
     match expn_data.kind {
         ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop) => false,
-        ExpnKind::Desugaring(_) => true, // well, it's "external"
+        ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => true, // well, it's "external"
         ExpnKind::Macro(MacroKind::Bang, _) => {
             if expn_data.def_site.is_dummy() {
                 // dummy span for the def_site means it's an external macro
diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs
index fe8f94ab1d314..f67526ea4a1d9 100644
--- a/src/librustc/ty/error.rs
+++ b/src/librustc/ty/error.rs
@@ -200,7 +200,9 @@ impl<'tcx> ty::TyS<'tcx> {
             ty::Array(_, n) => {
                 let n = tcx.lift_to_global(&n).unwrap();
                 match n.try_eval_usize(tcx, ty::ParamEnv::empty()) {
-                    Some(n) => format!("array of {} elements", n).into(),
+                    Some(n) => {
+                        format!("array of {} element{}", n, if n != 1 { "s" } else { "" }).into()
+                    }
                     None => "array".into(),
                 }
             }
diff --git a/src/librustc_ast_borrowck/borrowck/mod.rs b/src/librustc_ast_borrowck/borrowck/mod.rs
index 3bbd7ae5c352f..23d5480c60562 100644
--- a/src/librustc_ast_borrowck/borrowck/mod.rs
+++ b/src/librustc_ast_borrowck/borrowck/mod.rs
@@ -9,7 +9,6 @@ use InteriorKind::*;
 
 use rustc::hir::HirId;
 use rustc::hir::Node;
-use rustc::cfg;
 use rustc::middle::borrowck::{BorrowCheckResult, SignalledError};
 use rustc::hir::def_id::{DefId, LocalDefId};
 use rustc::middle::mem_categorization as mc;
@@ -28,6 +27,7 @@ use log::debug;
 
 use rustc::hir;
 
+use crate::cfg;
 use crate::dataflow::{DataFlowContext, BitwiseOperator, DataFlowOperator, KillFrom};
 
 pub mod check_loans;
diff --git a/src/librustc_ast_borrowck/borrowck/move_data.rs b/src/librustc_ast_borrowck/borrowck/move_data.rs
index 887a0e2f20e16..67d818161b1b5 100644
--- a/src/librustc_ast_borrowck/borrowck/move_data.rs
+++ b/src/librustc_ast_borrowck/borrowck/move_data.rs
@@ -4,7 +4,7 @@
 use crate::dataflow::{DataFlowContext, BitwiseOperator, DataFlowOperator, KillFrom};
 
 use crate::borrowck::*;
-use rustc::cfg;
+use crate::cfg;
 use rustc::ty::{self, TyCtxt};
 use rustc::util::nodemap::FxHashMap;
 
diff --git a/src/librustc/cfg/construct.rs b/src/librustc_ast_borrowck/cfg/construct.rs
similarity index 98%
rename from src/librustc/cfg/construct.rs
rename to src/librustc_ast_borrowck/cfg/construct.rs
index 0dad2dda837b5..339d92145c64f 100644
--- a/src/librustc/cfg/construct.rs
+++ b/src/librustc_ast_borrowck/cfg/construct.rs
@@ -1,11 +1,11 @@
 use crate::cfg::*;
-use crate::middle::region;
 use rustc_data_structures::graph::implementation as graph;
-use crate::ty::{self, TyCtxt};
+use rustc::middle::region;
+use rustc::ty::{self, TyCtxt};
 
-use crate::hir::{self, PatKind};
-use crate::hir::def_id::DefId;
-use crate::hir::ptr::P;
+use rustc::hir::{self, PatKind};
+use rustc::hir::def_id::DefId;
+use rustc::hir::ptr::P;
 
 struct CFGBuilder<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
@@ -30,7 +30,7 @@ struct LoopScope {
     break_index: CFGIndex,    // where to go on a `break`
 }
 
-pub fn construct(tcx: TyCtxt<'_>, body: &hir::Body) -> CFG {
+pub(super) fn construct(tcx: TyCtxt<'_>, body: &hir::Body) -> CFG {
     let mut graph = graph::Graph::new();
     let entry = graph.add_node(CFGNodeData::Entry);
 
diff --git a/src/librustc/cfg/graphviz.rs b/src/librustc_ast_borrowck/cfg/graphviz.rs
similarity index 94%
rename from src/librustc/cfg/graphviz.rs
rename to src/librustc_ast_borrowck/cfg/graphviz.rs
index 918120057d4d3..46409f1a1cebf 100644
--- a/src/librustc/cfg/graphviz.rs
+++ b/src/librustc_ast_borrowck/cfg/graphviz.rs
@@ -1,15 +1,12 @@
 /// This module provides linkage between rustc::middle::graph and
 /// libgraphviz traits.
 
-// For clarity, rename the graphviz crate locally to dot.
-use graphviz as dot;
-
 use crate::cfg;
-use crate::hir;
-use crate::ty::TyCtxt;
+use rustc::hir;
+use rustc::ty::TyCtxt;
 
-pub type Node<'a> = (cfg::CFGIndex, &'a cfg::CFGNode);
-pub type Edge<'a> = &'a cfg::CFGEdge;
+pub(crate) type Node<'a> = (cfg::CFGIndex, &'a cfg::CFGNode);
+pub(crate) type Edge<'a> = &'a cfg::CFGEdge;
 
 pub struct LabelledCFG<'a, 'tcx> {
     pub tcx: TyCtxt<'tcx>,
diff --git a/src/librustc/cfg/mod.rs b/src/librustc_ast_borrowck/cfg/mod.rs
similarity index 51%
rename from src/librustc/cfg/mod.rs
rename to src/librustc_ast_borrowck/cfg/mod.rs
index 88fc7fbfad51f..981199c91d513 100644
--- a/src/librustc/cfg/mod.rs
+++ b/src/librustc_ast_borrowck/cfg/mod.rs
@@ -2,18 +2,18 @@
 //! Uses `Graph` as the underlying representation.
 
 use rustc_data_structures::graph::implementation as graph;
-use crate::ty::TyCtxt;
-use crate::hir;
-use crate::hir::def_id::DefId;
+use rustc::ty::TyCtxt;
+use rustc::hir;
+use rustc::hir::def_id::DefId;
 
 mod construct;
 pub mod graphviz;
 
 pub struct CFG {
-    pub owner_def_id: DefId,
-    pub graph: CFGGraph,
-    pub entry: CFGIndex,
-    pub exit: CFGIndex,
+    owner_def_id: DefId,
+    pub(crate) graph: CFGGraph,
+    pub(crate) entry: CFGIndex,
+    exit: CFGIndex,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
@@ -26,7 +26,7 @@ pub enum CFGNodeData {
 }
 
 impl CFGNodeData {
-    pub fn id(&self) -> hir::ItemLocalId {
+    pub(crate) fn id(&self) -> hir::ItemLocalId {
         if let CFGNodeData::AST(id) = *self {
             id
         } else {
@@ -37,24 +37,19 @@ impl CFGNodeData {
 
 #[derive(Debug)]
 pub struct CFGEdgeData {
-    pub exiting_scopes: Vec<hir::ItemLocalId>
+    pub(crate) exiting_scopes: Vec<hir::ItemLocalId>
 }
 
-pub type CFGIndex = graph::NodeIndex;
+pub(crate) type CFGIndex = graph::NodeIndex;
 
-pub type CFGGraph = graph::Graph<CFGNodeData, CFGEdgeData>;
+pub(crate) type CFGGraph = graph::Graph<CFGNodeData, CFGEdgeData>;
 
-pub type CFGNode = graph::Node<CFGNodeData>;
+pub(crate) type CFGNode = graph::Node<CFGNodeData>;
 
-pub type CFGEdge = graph::Edge<CFGEdgeData>;
+pub(crate) type CFGEdge = graph::Edge<CFGEdgeData>;
 
 impl CFG {
     pub fn new(tcx: TyCtxt<'_>, body: &hir::Body) -> CFG {
         construct::construct(tcx, body)
     }
-
-    pub fn node_is_reachable(&self, id: hir::ItemLocalId) -> bool {
-        self.graph.depth_traverse(self.entry, graph::OUTGOING)
-                  .any(|idx| self.graph.node_data(idx).id() == id)
-    }
 }
diff --git a/src/librustc_ast_borrowck/dataflow.rs b/src/librustc_ast_borrowck/dataflow.rs
index 3a4c8c924764e..a8562901d99c5 100644
--- a/src/librustc_ast_borrowck/dataflow.rs
+++ b/src/librustc_ast_borrowck/dataflow.rs
@@ -3,9 +3,7 @@
 //! and thus uses bitvectors. Your job is simply to specify the so-called
 //! GEN and KILL bits for each expression.
 
-use rustc::cfg;
-use rustc::cfg::CFGIndex;
-use rustc::ty::TyCtxt;
+use crate::cfg::{self, CFGIndex};
 use std::mem;
 use std::usize;
 use log::debug;
@@ -16,6 +14,7 @@ use rustc::util::nodemap::FxHashMap;
 use rustc::hir;
 use rustc::hir::intravisit;
 use rustc::hir::print as pprust;
+use rustc::ty::TyCtxt;
 
 #[derive(Copy, Clone, Debug)]
 pub enum EntryOrExit {
diff --git a/src/librustc_ast_borrowck/graphviz.rs b/src/librustc_ast_borrowck/graphviz.rs
index 7a8a23ca76afc..c077dc828aba2 100644
--- a/src/librustc_ast_borrowck/graphviz.rs
+++ b/src/librustc_ast_borrowck/graphviz.rs
@@ -4,13 +4,12 @@
 
 pub use Variant::*;
 
-pub use rustc::cfg::graphviz::{Node, Edge};
-use rustc::cfg::graphviz as cfg_dot;
-
+pub(crate) use crate::cfg::graphviz::{Node, Edge};
+use crate::cfg::graphviz as cfg_dot;
+use crate::cfg::CFGIndex;
 use crate::borrowck::{self, BorrowckCtxt, LoanPath};
 use crate::dataflow::{DataFlowOperator, DataFlowContext, EntryOrExit};
 use log::debug;
-use rustc::cfg::CFGIndex;
 use std::rc::Rc;
 
 #[derive(Debug, Copy, Clone)]
diff --git a/src/librustc_ast_borrowck/lib.rs b/src/librustc_ast_borrowck/lib.rs
index dc818278a4b74..aea97fea1a9fd 100644
--- a/src/librustc_ast_borrowck/lib.rs
+++ b/src/librustc_ast_borrowck/lib.rs
@@ -18,5 +18,6 @@ mod borrowck;
 pub mod graphviz;
 
 mod dataflow;
+pub mod cfg;
 
 pub use borrowck::provide;
diff --git a/src/librustc_codegen_llvm/debuginfo/mod.rs b/src/librustc_codegen_llvm/debuginfo/mod.rs
index cad2bcdc05fc9..6dedf10f0ab83 100644
--- a/src/librustc_codegen_llvm/debuginfo/mod.rs
+++ b/src/librustc_codegen_llvm/debuginfo/mod.rs
@@ -32,7 +32,7 @@ use rustc_codegen_ssa::debuginfo::{FunctionDebugContext, MirDebugScope, Variable
 
 use libc::c_uint;
 use std::cell::RefCell;
-use std::ffi::CString;
+use std::ffi::{CStr, CString};
 
 use syntax_pos::{self, Span, Pos};
 use syntax::ast;
@@ -224,8 +224,37 @@ impl DebugInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
         gdb::insert_reference_to_gdb_debug_scripts_section_global(self)
     }
 
-    fn set_value_name(&mut self, value: &'ll Value, name: &str) {
-        let cname = SmallCStr::new(name);
+    fn set_var_name(&mut self, value: &'ll Value, name: impl ToString) {
+        // Avoid wasting time if LLVM value names aren't even enabled.
+        if self.sess().fewer_names() {
+            return;
+        }
+
+        // Only function parameters and instructions are local to a function,
+        // don't change the name of anything else (e.g. globals).
+        let param_or_inst = unsafe {
+            llvm::LLVMIsAArgument(value).is_some() ||
+            llvm::LLVMIsAInstruction(value).is_some()
+        };
+        if !param_or_inst {
+            return;
+        }
+
+        let old_name = unsafe {
+            CStr::from_ptr(llvm::LLVMGetValueName(value))
+        };
+        match old_name.to_str() {
+            Ok("") => {}
+            Ok(_) => {
+                // Avoid replacing the name if it already exists.
+                // While we could combine the names somehow, it'd
+                // get noisy quick, and the usefulness is dubious.
+                return;
+            }
+            Err(_) => return,
+        }
+
+        let cname = CString::new(name.to_string()).unwrap();
         unsafe {
             llvm::LLVMSetValueName(value, cname.as_ptr());
         }
diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs
index 9f9410560e373..b07214fdc03f3 100644
--- a/src/librustc_codegen_llvm/llvm/ffi.rs
+++ b/src/librustc_codegen_llvm/llvm/ffi.rs
@@ -806,6 +806,7 @@ extern "C" {
     pub fn LLVMRustRemoveFunctionAttributes(Fn: &Value, index: c_uint, attr: Attribute);
 
     // Operations on parameters
+    pub fn LLVMIsAArgument(Val: &Value) -> Option<&Value>;
     pub fn LLVMCountParams(Fn: &Value) -> c_uint;
     pub fn LLVMGetParam(Fn: &Value, Index: c_uint) -> &Value;
 
@@ -818,6 +819,7 @@ extern "C" {
     pub fn LLVMDeleteBasicBlock(BB: &BasicBlock);
 
     // Operations on instructions
+    pub fn LLVMIsAInstruction(Val: &Value) -> Option<&Value>;
     pub fn LLVMGetFirstBasicBlock(Fn: &Value) -> &BasicBlock;
 
     // Operations on call sites
diff --git a/src/librustc_codegen_ssa/mir/mod.rs b/src/librustc_codegen_ssa/mir/mod.rs
index 8acb3ba06267e..00e9ca01f4dd2 100644
--- a/src/librustc_codegen_ssa/mir/mod.rs
+++ b/src/librustc_codegen_ssa/mir/mod.rs
@@ -518,19 +518,19 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
                 PassMode::Ignore(IgnoreMode::CVarArgs) => {}
                 PassMode::Direct(_) => {
                     let llarg = bx.get_param(llarg_idx);
-                    bx.set_value_name(llarg, &name);
+                    bx.set_var_name(llarg, &name);
                     llarg_idx += 1;
                     return local(
                         OperandRef::from_immediate_or_packed_pair(bx, llarg, arg.layout));
                 }
                 PassMode::Pair(..) => {
-                    let a = bx.get_param(llarg_idx);
-                    bx.set_value_name(a, &(name.clone() + ".0"));
-                    llarg_idx += 1;
+                    let (a, b) = (bx.get_param(llarg_idx), bx.get_param(llarg_idx + 1));
+                    llarg_idx += 2;
 
-                    let b = bx.get_param(llarg_idx);
-                    bx.set_value_name(b, &(name + ".1"));
-                    llarg_idx += 1;
+                    // FIXME(eddyb) these are scalar components,
+                    // maybe extract the high-level fields?
+                    bx.set_var_name(a, format_args!("{}.0", name));
+                    bx.set_var_name(b, format_args!("{}.1", name));
 
                     return local(OperandRef {
                         val: OperandValue::Pair(a, b),
@@ -546,7 +546,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
             // already put it in a temporary alloca and gave it up.
             // FIXME: lifetimes
             let llarg = bx.get_param(llarg_idx);
-            bx.set_value_name(llarg, &name);
+            bx.set_var_name(llarg, &name);
             llarg_idx += 1;
             PlaceRef::new_sized(llarg, arg.layout)
         } else if arg.is_unsized_indirect() {
diff --git a/src/librustc_codegen_ssa/mir/statement.rs b/src/librustc_codegen_ssa/mir/statement.rs
index 3617f3afaae41..594f45c833758 100644
--- a/src/librustc_codegen_ssa/mir/statement.rs
+++ b/src/librustc_codegen_ssa/mir/statement.rs
@@ -29,7 +29,21 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                             self.codegen_rvalue_unsized(bx, cg_indirect_dest, rvalue)
                         }
                         LocalRef::Operand(None) => {
-                            let (bx, operand) = self.codegen_rvalue_operand(bx, rvalue);
+                            let (mut bx, operand) = self.codegen_rvalue_operand(bx, rvalue);
+                            if let Some(name) = self.mir.local_decls[index].name {
+                                match operand.val {
+                                    OperandValue::Ref(x, ..) |
+                                    OperandValue::Immediate(x) => {
+                                        bx.set_var_name(x, name);
+                                    }
+                                    OperandValue::Pair(a, b) => {
+                                        // FIXME(eddyb) these are scalar components,
+                                        // maybe extract the high-level fields?
+                                        bx.set_var_name(a, format_args!("{}.0", name));
+                                        bx.set_var_name(b, format_args!("{}.1", name));
+                                    }
+                                }
+                            }
                             self.locals[index] = LocalRef::Operand(Some(operand));
                             bx
                         }
diff --git a/src/librustc_codegen_ssa/traits/debuginfo.rs b/src/librustc_codegen_ssa/traits/debuginfo.rs
index be2fa7279aa7c..9c16b864ef21d 100644
--- a/src/librustc_codegen_ssa/traits/debuginfo.rs
+++ b/src/librustc_codegen_ssa/traits/debuginfo.rs
@@ -57,5 +57,5 @@ pub trait DebugInfoBuilderMethods<'tcx>: BackendTypes {
         span: Span,
     );
     fn insert_reference_to_gdb_debug_scripts_section_global(&mut self);
-    fn set_value_name(&mut self, value: Self::Value, name: &str);
+    fn set_var_name(&mut self, value: Self::Value, name: impl ToString);
 }
diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs
index cb17401f6247b..c4d3ad946f9f6 100644
--- a/src/librustc_driver/pretty.rs
+++ b/src/librustc_driver/pretty.rs
@@ -1,7 +1,5 @@
 //! The various pretty-printing routines.
 
-use rustc::cfg;
-use rustc::cfg::graphviz::LabelledCFG;
 use rustc::hir;
 use rustc::hir::map as hir_map;
 use rustc::hir::map::blocks;
@@ -14,6 +12,7 @@ use rustc::util::common::ErrorReported;
 use rustc_interface::util::ReplaceBodyWithLoop;
 use rustc_ast_borrowck as borrowck;
 use rustc_ast_borrowck::graphviz as borrowck_dot;
+use rustc_ast_borrowck::cfg::{self, graphviz::LabelledCFG};
 use rustc_mir::util::{write_mir_pretty, write_mir_graphviz};
 
 use syntax::ast;
diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs
index 24b44964e4fd2..7fcad4d79c204 100644
--- a/src/librustc_interface/passes.rs
+++ b/src/librustc_interface/passes.rs
@@ -230,10 +230,12 @@ pub fn register_plugins<'a>(
     crate_name: &str,
 ) -> Result<(ast::Crate, PluginInfo)> {
     krate = time(sess, "attributes injection", || {
-        syntax::attr::inject(krate, &sess.parse_sess, &sess.opts.debugging_opts.crate_attr)
+        syntax_ext::cmdline_attrs::inject(
+            krate, &sess.parse_sess, &sess.opts.debugging_opts.crate_attr
+        )
     });
 
-    let (mut krate, features) = syntax::config::features(
+    let (krate, features) = syntax::config::features(
         krate,
         &sess.parse_sess,
         sess.edition(),
@@ -268,16 +270,6 @@ pub fn register_plugins<'a>(
         middle::recursion_limit::update_limits(sess, &krate);
     });
 
-    krate = time(sess, "crate injection", || {
-        let alt_std_name = sess.opts.alt_std_name.as_ref().map(|s| &**s);
-        let (krate, name) =
-            syntax_ext::standard_library_imports::inject(krate, alt_std_name, sess.edition());
-        if let Some(name) = name {
-            sess.parse_sess.injected_crate_name.set(name);
-        }
-        krate
-    });
-
     let registrars = time(sess, "plugin loading", || {
         plugin::load::load_plugins(
             sess,
@@ -370,6 +362,21 @@ fn configure_and_expand_inner<'a>(
         &resolver_arenas,
     );
     syntax_ext::register_builtin_macros(&mut resolver, sess.edition());
+
+    krate = time(sess, "crate injection", || {
+        let alt_std_name = sess.opts.alt_std_name.as_ref().map(|s| Symbol::intern(s));
+        let (krate, name) = syntax_ext::standard_library_imports::inject(
+            krate,
+            &mut resolver,
+            &sess.parse_sess,
+            alt_std_name,
+        );
+        if let Some(name) = name {
+            sess.parse_sess.injected_crate_name.set(name);
+        }
+        krate
+    });
+
     syntax_ext::plugin_macro_defs::inject(
         &mut krate, &mut resolver, plugin_info.syntax_exts, sess.edition()
     );
diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs
index 165a4c707bb6d..81bd687e26321 100644
--- a/src/librustc_resolve/build_reduced_graph.rs
+++ b/src/librustc_resolve/build_reduced_graph.rs
@@ -126,7 +126,8 @@ impl<'a> Resolver<'a> {
     crate fn macro_def_scope(&mut self, expn_id: ExpnId) -> Module<'a> {
         let def_id = match self.macro_defs.get(&expn_id) {
             Some(def_id) => *def_id,
-            None => return self.graph_root,
+            None => return self.ast_transform_scopes.get(&expn_id)
+                .unwrap_or(&self.graph_root),
         };
         if let Some(id) = self.definitions.as_local_node_id(def_id) {
             self.local_macro_def_scopes[&id]
diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs
index b79e0c2bd3b26..c479912b4ef81 100644
--- a/src/librustc_resolve/diagnostics.rs
+++ b/src/librustc_resolve/diagnostics.rs
@@ -604,6 +604,14 @@ impl<'a> Resolver<'a> {
         if lookup_ident.span.rust_2018() {
             let extern_prelude_names = self.extern_prelude.clone();
             for (ident, _) in extern_prelude_names.into_iter() {
+                if ident.span.from_expansion() {
+                    // Idents are adjusted to the root context before being
+                    // resolved in the extern prelude, so reporting this to the
+                    // user is no help. This skips the injected
+                    // `extern crate std` in the 2018 edition, which would
+                    // otherwise cause duplicate suggestions.
+                    continue;
+                }
                 if let Some(crate_id) = self.crate_loader.maybe_process_path_extern(ident.name,
                                                                                     ident.span) {
                     let crate_root = self.get_module(DefId {
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 9b92bb7698aaa..7f65e5dfaa2bf 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -880,6 +880,10 @@ pub struct Resolver<'a> {
     /// There will be an anonymous module created around `g` with the ID of the
     /// entry block for `f`.
     block_map: NodeMap<Module<'a>>,
+    /// A fake module that contains no definition and no prelude. Used so that
+    /// some AST passes can generate identifiers that only resolve to local or
+    /// language items.
+    empty_module: Module<'a>,
     module_map: FxHashMap<DefId, Module<'a>>,
     extern_module_map: FxHashMap<(DefId, bool /* MacrosOnly? */), Module<'a>>,
     binding_parent_modules: FxHashMap<PtrKey<'a, NameBinding<'a>>, Module<'a>>,
@@ -914,6 +918,7 @@ pub struct Resolver<'a> {
     non_macro_attrs: [Lrc<SyntaxExtension>; 2],
     macro_defs: FxHashMap<ExpnId, DefId>,
     local_macro_def_scopes: FxHashMap<NodeId, Module<'a>>,
+    ast_transform_scopes: FxHashMap<ExpnId, Module<'a>>,
     unused_macros: NodeMap<Span>,
     proc_macro_stubs: NodeSet,
     /// Traces collected during macro resolution and validated when it's complete.
@@ -1081,6 +1086,21 @@ impl<'a> Resolver<'a> {
             no_implicit_prelude: attr::contains_name(&krate.attrs, sym::no_implicit_prelude),
             ..ModuleData::new(None, root_module_kind, root_def_id, ExpnId::root(), krate.span)
         });
+        let empty_module_kind = ModuleKind::Def(
+            DefKind::Mod,
+            root_def_id,
+            kw::Invalid,
+        );
+        let empty_module = arenas.alloc_module(ModuleData {
+            no_implicit_prelude: true,
+            ..ModuleData::new(
+                Some(graph_root),
+                empty_module_kind,
+                root_def_id,
+                ExpnId::root(),
+                DUMMY_SP,
+            )
+        });
         let mut module_map = FxHashMap::default();
         module_map.insert(DefId::local(CRATE_DEF_INDEX), graph_root);
 
@@ -1140,10 +1160,12 @@ impl<'a> Resolver<'a> {
             label_res_map: Default::default(),
             export_map: FxHashMap::default(),
             trait_map: Default::default(),
+            empty_module,
             module_map,
             block_map: Default::default(),
             extern_module_map: FxHashMap::default(),
             binding_parent_modules: FxHashMap::default(),
+            ast_transform_scopes: FxHashMap::default(),
 
             glob_map: Default::default(),
 
diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs
index 7224bd74230b3..054b17fec7849 100644
--- a/src/librustc_resolve/macros.rs
+++ b/src/librustc_resolve/macros.rs
@@ -8,6 +8,7 @@ use crate::{ModuleOrUniformRoot, KNOWN_TOOLS};
 use crate::Namespace::*;
 use crate::resolve_imports::ImportResolver;
 use rustc::hir::def::{self, DefKind, NonMacroAttrKind};
+use rustc::hir::def_id;
 use rustc::middle::stability;
 use rustc::{ty, lint, span_bug};
 use syntax::ast::{self, NodeId, Ident};
@@ -25,6 +26,7 @@ use syntax_pos::{Span, DUMMY_SP};
 
 use std::{mem, ptr};
 use rustc_data_structures::sync::Lrc;
+use syntax_pos::hygiene::AstPass;
 
 type Res = def::Res<NodeId>;
 
@@ -95,16 +97,6 @@ impl<'a> base::Resolver for Resolver<'a> {
         self.session.next_node_id()
     }
 
-    fn get_module_scope(&mut self, id: NodeId) -> ExpnId {
-        let expn_id = ExpnId::fresh(Some(ExpnData::default(
-            ExpnKind::Macro(MacroKind::Attr, sym::test_case), DUMMY_SP, self.session.edition()
-        )));
-        let module = self.module_map[&self.definitions.local_def_id(id)];
-        self.invocation_parent_scopes.insert(expn_id, ParentScope::module(module));
-        self.definitions.set_invocation_parent(expn_id, module.def_id().unwrap().index);
-        expn_id
-    }
-
     fn resolve_dollar_crates(&mut self) {
         hygiene::update_dollar_crate_names(|ctxt| {
             let ident = Ident::new(kw::DollarCrate, DUMMY_SP.with_ctxt(ctxt));
@@ -136,6 +128,37 @@ impl<'a> base::Resolver for Resolver<'a> {
         }
     }
 
+    // Create a new Expansion with a definition site of the provided module, or
+    // a fake empty `#[no_implicit_prelude]` module if no module is provided.
+    fn expansion_for_ast_pass(
+        &mut self,
+        call_site: Span,
+        pass: AstPass,
+        features: &[Symbol],
+        parent_module_id: Option<NodeId>,
+    ) -> ExpnId {
+        let expn_id = ExpnId::fresh(Some(ExpnData::allow_unstable(
+            ExpnKind::AstPass(pass),
+            call_site,
+            self.session.edition(),
+            features.into(),
+        )));
+
+        let parent_scope = if let Some(module_id) = parent_module_id {
+            let parent_def_id = self.definitions.local_def_id(module_id);
+            self.definitions.add_parent_module_of_macro_def(expn_id, parent_def_id);
+            self.module_map[&parent_def_id]
+        } else {
+            self.definitions.add_parent_module_of_macro_def(
+                expn_id,
+                def_id::DefId::local(def_id::CRATE_DEF_INDEX),
+            );
+            self.empty_module
+        };
+        self.ast_transform_scopes.insert(expn_id, parent_scope);
+        expn_id
+    }
+
     fn resolve_imports(&mut self) {
         ImportResolver { r: self }.resolve_imports()
     }
diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs
index fd222a132a3f8..ca189e71800b3 100644
--- a/src/librustc_resolve/resolve_imports.rs
+++ b/src/librustc_resolve/resolve_imports.rs
@@ -1307,12 +1307,11 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                 None => continue,
             };
 
-            // Filter away ambiguous and gensymed imports. Gensymed imports
-            // (e.g. implicitly injected `std`) cannot be properly encoded in metadata,
-            // so they can cause name conflict errors downstream.
-            let is_good_import = binding.is_import() && !binding.is_ambiguity() &&
-                                 // Note that as_str() de-gensyms the Symbol
-                                 !(ident.is_gensymed() && ident.name.as_str() != "_");
+            // Filter away ambiguous imports and anything that has def-site
+            // hygiene.
+            // FIXME: Implement actual cross-crate hygiene.
+            let is_good_import = binding.is_import() && !binding.is_ambiguity()
+                && !ident.span.modern().from_expansion();
             if is_good_import || binding.is_macro_def() {
                 let res = binding.res();
                 if res != Res::Err {
diff --git a/src/librustc_typeck/check/pat.rs b/src/librustc_typeck/check/pat.rs
index 24d0659391b04..8502b89de1469 100644
--- a/src/librustc_typeck/check/pat.rs
+++ b/src/librustc_typeck/check/pat.rs
@@ -1098,22 +1098,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn error_scrutinee_inconsistent_length(&self, span: Span, min_len: u64, size: u64) {
         struct_span_err!(
-            self.tcx.sess, span, E0527,
-            "pattern requires {} elements but array has {}",
-            min_len, size
+            self.tcx.sess,
+            span,
+            E0527,
+            "pattern requires {} element{} but array has {}",
+            min_len,
+            if min_len != 1 { "s" } else { "" },
+            size,
         )
-        .span_label(span, format!("expected {} elements", size))
+        .span_label(span, format!("expected {} element{}", size, if size != 1 { "s" } else { "" }))
         .emit();
     }
 
     fn error_scrutinee_with_rest_inconsistent_length(&self, span: Span, min_len: u64, size: u64) {
         struct_span_err!(
-            self.tcx.sess, span, E0528,
-            "pattern requires at least {} elements but array has {}",
-            min_len, size
-        )
-        .span_label(span, format!("pattern cannot match array of {} elements", size))
-        .emit();
+            self.tcx.sess,
+            span,
+            E0528,
+            "pattern requires at least {} element{} but array has {}",
+            min_len,
+            if min_len != 1 { "s" } else { "" },
+            size,
+        ).span_label(
+            span,
+            format!(
+                "pattern cannot match array of {} element{}",
+                size,
+                if size != 1 { "s" } else { "" },
+            ),
+        ).emit();
     }
 
     fn error_scrutinee_unfixed_length(&self, span: Span) {
diff --git a/src/libsyntax/attr/mod.rs b/src/libsyntax/attr/mod.rs
index 0e5cfa73a9e3a..69de3150354e7 100644
--- a/src/libsyntax/attr/mod.rs
+++ b/src/libsyntax/attr/mod.rs
@@ -16,7 +16,7 @@ use crate::mut_visit::visit_clobber;
 use crate::source_map::{BytePos, Spanned, DUMMY_SP};
 use crate::parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration};
 use crate::parse::parser::Parser;
-use crate::parse::{self, ParseSess, PResult};
+use crate::parse::{ParseSess, PResult};
 use crate::parse::token::{self, Token};
 use crate::ptr::P;
 use crate::symbol::{sym, Symbol};
@@ -25,7 +25,7 @@ use crate::tokenstream::{TokenStream, TokenTree, DelimSpan};
 use crate::GLOBALS;
 
 use log::debug;
-use syntax_pos::{FileName, Span};
+use syntax_pos::Span;
 
 use std::iter;
 use std::ops::DerefMut;
@@ -381,28 +381,25 @@ crate fn mk_attr_id() -> AttrId {
     AttrId(id)
 }
 
-/// Returns an inner attribute with the given value and span.
-pub fn mk_attr_inner(item: MetaItem) -> Attribute {
+pub fn mk_attr(style: AttrStyle, path: Path, tokens: TokenStream, span: Span) -> Attribute {
     Attribute {
         id: mk_attr_id(),
-        style: ast::AttrStyle::Inner,
-        path: item.path,
-        tokens: item.node.tokens(item.span),
+        style,
+        path,
+        tokens,
         is_sugared_doc: false,
-        span: item.span,
+        span,
     }
 }
 
+/// Returns an inner attribute with the given value and span.
+pub fn mk_attr_inner(item: MetaItem) -> Attribute {
+    mk_attr(AttrStyle::Inner, item.path, item.node.tokens(item.span), item.span)
+}
+
 /// Returns an outer attribute with the given value and span.
 pub fn mk_attr_outer(item: MetaItem) -> Attribute {
-    Attribute {
-        id: mk_attr_id(),
-        style: ast::AttrStyle::Outer,
-        path: item.path,
-        tokens: item.node.tokens(item.span),
-        is_sugared_doc: false,
-        span: item.span,
-    }
+    mk_attr(AttrStyle::Outer, item.path, item.node.tokens(item.span), item.span)
 }
 
 pub fn mk_sugared_doc_attr(text: Symbol, span: Span) -> Attribute {
@@ -716,33 +713,3 @@ derive_has_attrs! {
     Item, Expr, Local, ast::ForeignItem, ast::StructField, ast::ImplItem, ast::TraitItem, ast::Arm,
     ast::Field, ast::FieldPat, ast::Variant, ast::Param
 }
-
-pub fn inject(mut krate: ast::Crate, parse_sess: &ParseSess, attrs: &[String]) -> ast::Crate {
-    for raw_attr in attrs {
-        let mut parser = parse::new_parser_from_source_str(
-            parse_sess,
-            FileName::cli_crate_attr_source_code(&raw_attr),
-            raw_attr.clone(),
-        );
-
-        let start_span = parser.token.span;
-        let (path, tokens) = panictry!(parser.parse_meta_item_unrestricted());
-        let end_span = parser.token.span;
-        if parser.token != token::Eof {
-            parse_sess.span_diagnostic
-                .span_err(start_span.to(end_span), "invalid crate attribute");
-            continue;
-        }
-
-        krate.attrs.push(Attribute {
-            id: mk_attr_id(),
-            style: AttrStyle::Inner,
-            path,
-            tokens,
-            is_sugared_doc: false,
-            span: start_span.to(end_span),
-        });
-    }
-
-    krate
-}
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index 109ba041016c6..5b2515d20cbab 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -3,7 +3,7 @@ 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, Transparency};
+use crate::ext::hygiene::ExpnId;
 use crate::mut_visit::{self, MutVisitor};
 use crate::parse::{self, parser, ParseSess, DirectoryOwnership};
 use crate::parse::token;
@@ -16,7 +16,7 @@ use crate::visit::Visitor;
 use errors::{DiagnosticBuilder, DiagnosticId};
 use smallvec::{smallvec, SmallVec};
 use syntax_pos::{FileName, Span, MultiSpan, DUMMY_SP};
-use syntax_pos::hygiene::{ExpnData, ExpnKind};
+use syntax_pos::hygiene::{AstPass, ExpnData, ExpnKind};
 
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::{self, Lrc};
@@ -732,13 +732,19 @@ bitflags::bitflags! {
 pub trait Resolver {
     fn next_node_id(&mut self) -> NodeId;
 
-    fn get_module_scope(&mut self, id: NodeId) -> ExpnId;
-
     fn resolve_dollar_crates(&mut self);
     fn visit_ast_fragment_with_placeholders(&mut self, expn_id: ExpnId, fragment: &AstFragment,
                                             extra_placeholders: &[NodeId]);
     fn register_builtin_macro(&mut self, ident: ast::Ident, ext: SyntaxExtension);
 
+    fn expansion_for_ast_pass(
+        &mut self,
+        call_site: Span,
+        pass: AstPass,
+        features: &[Symbol],
+        parent_module_id: Option<NodeId>,
+    ) -> ExpnId;
+
     fn resolve_imports(&mut self);
 
     fn resolve_macro_invocation(
@@ -822,20 +828,20 @@ impl<'a> ExtCtxt<'a> {
     /// 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)
+        span.with_def_site_ctxt(self.current_expansion.id)
     }
 
     /// 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_call_site_ctxt(self.current_expansion.id)
     }
 
     /// 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)
+        span.with_legacy_ctxt(self.current_expansion.id)
     }
 
     /// Returns span for the macro which originally caused the current expansion to happen.
diff --git a/src/libsyntax/ext/tt/transcribe.rs b/src/libsyntax/ext/tt/transcribe.rs
index 30d5df13dcedb..23735727fe8cf 100644
--- a/src/libsyntax/ext/tt/transcribe.rs
+++ b/src/libsyntax/ext/tt/transcribe.rs
@@ -345,8 +345,13 @@ impl LockstepIterSize {
                 LockstepIterSize::Constraint(r_len, _) if l_len == r_len => self,
                 LockstepIterSize::Constraint(r_len, r_id) => {
                     let msg = format!(
-                        "meta-variable `{}` repeats {} times, but `{}` repeats {} times",
-                        l_id, l_len, r_id, r_len
+                        "meta-variable `{}` repeats {} time{}, but `{}` repeats {} time{}",
+                        l_id,
+                        l_len,
+                        if l_len != 1 { "s" } else { "" },
+                        r_id,
+                        r_len,
+                        if r_len != 1 { "s" } else { "" },
                     );
                     LockstepIterSize::Contradiction(msg)
                 }
diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs
index 671178223f503..d9c4baad49ded 100644
--- a/src/libsyntax/parse/attr.rs
+++ b/src/libsyntax/parse/attr.rs
@@ -176,7 +176,7 @@ impl<'a> Parser<'a> {
     /// PATH
     /// PATH `=` TOKEN_TREE
     /// The delimiters or `=` are still put into the resulting token stream.
-    crate fn parse_meta_item_unrestricted(&mut self) -> PResult<'a, (ast::Path, TokenStream)> {
+    pub fn parse_meta_item_unrestricted(&mut self) -> PResult<'a, (ast::Path, TokenStream)> {
         let meta = match self.token.kind {
             token::Interpolated(ref nt) => match **nt {
                 Nonterminal::NtMeta(ref meta) => Some(meta.clone()),
diff --git a/src/libsyntax/parse/diagnostics.rs b/src/libsyntax/parse/diagnostics.rs
index d4e661d1a38b7..d050d4f4ce705 100644
--- a/src/libsyntax/parse/diagnostics.rs
+++ b/src/libsyntax/parse/diagnostics.rs
@@ -544,7 +544,7 @@ impl<'a> Parser<'a> {
     /// Produce an error if comparison operators are chained (RFC #558).
     /// We only need to check lhs, not rhs, because all comparison ops
     /// have same precedence and are left-associative
-    crate fn check_no_chained_comparison(&self, lhs: &Expr, outer_op: &AssocOp) {
+    crate fn check_no_chained_comparison(&self, lhs: &Expr, outer_op: &AssocOp) -> PResult<'a, ()> {
         debug_assert!(outer_op.is_comparison(),
                       "check_no_chained_comparison: {:?} is not comparison",
                       outer_op);
@@ -563,11 +563,14 @@ impl<'a> Parser<'a> {
                     err.help(
                         "use `::<...>` instead of `<...>` if you meant to specify type arguments");
                     err.help("or use `(...)` if you meant to specify fn arguments");
+                    // These cases cause too many knock-down errors, bail out (#61329).
+                    return Err(err);
                 }
                 err.emit();
             }
             _ => {}
         }
+        Ok(())
     }
 
     crate fn maybe_report_ambiguous_plus(
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 49b05551bae86..ab5462baaf721 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -132,7 +132,7 @@ pub struct Parser<'a> {
     /// into modules, and sub-parsers have new values for this name.
     pub root_module_name: Option<String>,
     crate expected_tokens: Vec<TokenType>,
-    crate token_cursor: TokenCursor,
+    token_cursor: TokenCursor,
     desugar_doc_comments: bool,
     /// `true` we should configure out of line modules as we parse.
     pub cfg_mods: bool,
@@ -161,19 +161,19 @@ impl<'a> Drop for Parser<'a> {
 }
 
 #[derive(Clone)]
-crate struct TokenCursor {
-    crate frame: TokenCursorFrame,
-    crate stack: Vec<TokenCursorFrame>,
+struct TokenCursor {
+    frame: TokenCursorFrame,
+    stack: Vec<TokenCursorFrame>,
 }
 
 #[derive(Clone)]
-crate struct TokenCursorFrame {
-    crate delim: token::DelimToken,
-    crate span: DelimSpan,
-    crate open_delim: bool,
-    crate tree_cursor: tokenstream::Cursor,
-    crate close_delim: bool,
-    crate last_token: LastToken,
+struct TokenCursorFrame {
+    delim: token::DelimToken,
+    span: DelimSpan,
+    open_delim: bool,
+    tree_cursor: tokenstream::Cursor,
+    close_delim: bool,
+    last_token: LastToken,
 }
 
 /// This is used in `TokenCursorFrame` above to track tokens that are consumed
diff --git a/src/libsyntax/parse/parser/expr.rs b/src/libsyntax/parse/parser/expr.rs
index 59b936a7eb404..e8c8e199fd06b 100644
--- a/src/libsyntax/parse/parser/expr.rs
+++ b/src/libsyntax/parse/parser/expr.rs
@@ -231,7 +231,7 @@ impl<'a> Parser<'a> {
 
             self.bump();
             if op.is_comparison() {
-                self.check_no_chained_comparison(&lhs, &op);
+                self.check_no_chained_comparison(&lhs, &op)?;
             }
             // Special cases:
             if op == AssocOp::As {
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 6772bbce21b50..8c1632dc7808d 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -128,10 +128,14 @@ pub fn print_crate<'a>(cm: &'a SourceMap,
         let fake_attr = attr::mk_attr_inner(list);
         s.print_attribute(&fake_attr);
 
-        // #![no_std]
-        let no_std_meta = attr::mk_word_item(ast::Ident::with_dummy_span(sym::no_std));
-        let fake_attr = attr::mk_attr_inner(no_std_meta);
-        s.print_attribute(&fake_attr);
+        // Currently on Rust 2018 we don't have `extern crate std;` at the crate
+        // root, so this is not needed, and actually breaks things.
+        if sess.edition == syntax_pos::edition::Edition::Edition2015 {
+            // #![no_std]
+            let no_std_meta = attr::mk_word_item(ast::Ident::with_dummy_span(sym::no_std));
+            let fake_attr = attr::mk_attr_inner(no_std_meta);
+            s.print_attribute(&fake_attr);
+        }
     }
 
     s.print_mod(&krate.module, &krate.attrs);
diff --git a/src/libsyntax/util/parser.rs b/src/libsyntax/util/parser.rs
index a501541c95909..fceaed360cdb4 100644
--- a/src/libsyntax/util/parser.rs
+++ b/src/libsyntax/util/parser.rs
@@ -69,7 +69,7 @@ pub enum Fixity {
 
 impl AssocOp {
     /// Creates a new AssocOP from a token
-    pub fn from_token(t: &Token) -> Option<AssocOp> {
+    crate fn from_token(t: &Token) -> Option<AssocOp> {
         use AssocOp::*;
         match t.kind {
             token::BinOpEq(k) => Some(AssignOp(k)),
diff --git a/src/libsyntax_ext/cmdline_attrs.rs b/src/libsyntax_ext/cmdline_attrs.rs
new file mode 100644
index 0000000000000..bb8e3df3db921
--- /dev/null
+++ b/src/libsyntax_ext/cmdline_attrs.rs
@@ -0,0 +1,30 @@
+//! Attributes injected into the crate root from command line using `-Z crate-attr`.
+
+use syntax::ast::{self, AttrStyle};
+use syntax::attr::mk_attr;
+use syntax::panictry;
+use syntax::parse::{self, token, ParseSess};
+use syntax_pos::FileName;
+
+pub fn inject(mut krate: ast::Crate, parse_sess: &ParseSess, attrs: &[String]) -> ast::Crate {
+    for raw_attr in attrs {
+        let mut parser = parse::new_parser_from_source_str(
+            parse_sess,
+            FileName::cli_crate_attr_source_code(&raw_attr),
+            raw_attr.clone(),
+        );
+
+        let start_span = parser.token.span;
+        let (path, tokens) = panictry!(parser.parse_meta_item_unrestricted());
+        let end_span = parser.token.span;
+        if parser.token != token::Eof {
+            parse_sess.span_diagnostic
+                .span_err(start_span.to(end_span), "invalid crate attribute");
+            continue;
+        }
+
+        krate.attrs.push(mk_attr(AttrStyle::Inner, path, tokens, start_span.to(end_span)));
+    }
+
+    krate
+}
diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs
index e29f12c50c526..dec84c8286292 100644
--- a/src/libsyntax_ext/format.rs
+++ b/src/libsyntax_ext/format.rs
@@ -291,7 +291,7 @@ impl<'a, 'b> Context<'a, 'b> {
                 &format!(
                     "{} positional argument{} in format string, but {}",
                     count,
-                    if count > 1 { "s" } else { "" },
+                    if count != 1 { "s" } else { "" },
                     self.describe_num_args(),
                 ),
             );
diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs
index 26ef80b2b06df..5c0a63ebbe7cb 100644
--- a/src/libsyntax_ext/lib.rs
+++ b/src/libsyntax_ext/lib.rs
@@ -40,6 +40,7 @@ mod source_util;
 mod test;
 mod trace_macros;
 
+pub mod cmdline_attrs;
 pub mod plugin_macro_defs;
 pub mod proc_macro_harness;
 pub mod standard_library_imports;
diff --git a/src/libsyntax_ext/plugin_macro_defs.rs b/src/libsyntax_ext/plugin_macro_defs.rs
index dbfd8fe98f389..ccdc5bd81a04b 100644
--- a/src/libsyntax_ext/plugin_macro_defs.rs
+++ b/src/libsyntax_ext/plugin_macro_defs.rs
@@ -11,7 +11,7 @@ use syntax::source_map::respan;
 use syntax::symbol::sym;
 use syntax::tokenstream::*;
 use syntax_pos::{Span, DUMMY_SP};
-use syntax_pos::hygiene::{ExpnData, ExpnKind, MacroKind};
+use syntax_pos::hygiene::{ExpnData, ExpnKind, AstPass};
 
 use std::mem;
 
@@ -44,7 +44,7 @@ pub fn inject(
     if !named_exts.is_empty() {
         let mut extra_items = Vec::new();
         let span = DUMMY_SP.fresh_expansion(ExpnData::allow_unstable(
-            ExpnKind::Macro(MacroKind::Attr, sym::plugin), DUMMY_SP, edition,
+            ExpnKind::AstPass(AstPass::PluginMacroDefs), DUMMY_SP, edition,
             [sym::rustc_attrs][..].into(),
         ));
         for (name, ext) in named_exts {
diff --git a/src/libsyntax_ext/proc_macro_harness.rs b/src/libsyntax_ext/proc_macro_harness.rs
index e772eaf834964..31d32d23a645e 100644
--- a/src/libsyntax_ext/proc_macro_harness.rs
+++ b/src/libsyntax_ext/proc_macro_harness.rs
@@ -3,8 +3,7 @@ use std::mem;
 use smallvec::smallvec;
 use syntax::ast::{self, Ident};
 use syntax::attr;
-use syntax::source_map::{ExpnData, ExpnKind, respan};
-use syntax::ext::base::{ExtCtxt, MacroKind};
+use syntax::ext::base::ExtCtxt;
 use syntax::ext::expand::{AstFragment, ExpansionConfig};
 use syntax::ext::proc_macro::is_proc_macro_attr;
 use syntax::parse::ParseSess;
@@ -12,6 +11,7 @@ use syntax::ptr::P;
 use syntax::symbol::{kw, sym};
 use syntax::visit::{self, Visitor};
 use syntax_pos::{Span, DUMMY_SP};
+use syntax_pos::hygiene::AstPass;
 
 struct ProcMacroDerive {
     trait_name: ast::Name,
@@ -308,8 +308,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
 
 // Creates a new module which looks like:
 //
-//      #[doc(hidden)]
-//      mod $gensym {
+//      const _: () = {
 //          extern crate proc_macro;
 //
 //          use proc_macro::bridge::client::ProcMacro;
@@ -327,32 +326,30 @@ fn mk_decls(
     custom_attrs: &[ProcMacroDef],
     custom_macros: &[ProcMacroDef],
 ) -> P<ast::Item> {
-    let span = DUMMY_SP.fresh_expansion(ExpnData::allow_unstable(
-        ExpnKind::Macro(MacroKind::Attr, sym::proc_macro), DUMMY_SP, cx.parse_sess.edition,
-        [sym::rustc_attrs, sym::proc_macro_internals][..].into(),
-    ));
-
-    let hidden = cx.meta_list_item_word(span, sym::hidden);
-    let doc = cx.meta_list(span, sym::doc, vec![hidden]);
-    let doc_hidden = cx.attribute(doc);
-
-    let proc_macro = Ident::with_dummy_span(sym::proc_macro);
+    let expn_id = cx.resolver.expansion_for_ast_pass(
+        DUMMY_SP,
+        AstPass::ProcMacroHarness,
+        &[sym::rustc_attrs, sym::proc_macro_internals],
+        None,
+    );
+    let span = DUMMY_SP.with_def_site_ctxt(expn_id);
+
+    let proc_macro = Ident::new(sym::proc_macro, span);
     let krate = cx.item(span,
                         proc_macro,
                         Vec::new(),
                         ast::ItemKind::ExternCrate(None));
 
-    let bridge = Ident::from_str("bridge");
-    let client = Ident::from_str("client");
-    let proc_macro_ty = Ident::from_str("ProcMacro");
-    let custom_derive = Ident::from_str("custom_derive");
-    let attr = Ident::from_str("attr");
-    let bang = Ident::from_str("bang");
-    let crate_kw = Ident::with_dummy_span(kw::Crate);
+    let bridge = Ident::from_str_and_span("bridge", span);
+    let client = Ident::from_str_and_span("client", span);
+    let proc_macro_ty = Ident::from_str_and_span("ProcMacro", span);
+    let custom_derive = Ident::from_str_and_span("custom_derive", span);
+    let attr = Ident::from_str_and_span("attr", span);
+    let bang = Ident::from_str_and_span("bang", span);
 
     let decls = {
         let local_path = |sp: Span, name| {
-            cx.expr_path(cx.path(sp.with_ctxt(span.ctxt()), vec![crate_kw, name]))
+            cx.expr_path(cx.path(sp.with_ctxt(span.ctxt()), vec![name]))
         };
         let proc_macro_ty_method_path = |method| cx.expr_path(cx.path(span, vec![
             proc_macro, bridge, client, proc_macro_ty, method,
@@ -381,7 +378,7 @@ fn mk_decls(
 
     let decls_static = cx.item_static(
         span,
-        Ident::from_str("_DECLS"),
+        Ident::from_str_and_span("_DECLS", span),
         cx.ty_rptr(span,
             cx.ty(span, ast::TyKind::Slice(
                 cx.ty_path(cx.path(span,
@@ -392,22 +389,22 @@ fn mk_decls(
     ).map(|mut i| {
         let attr = cx.meta_word(span, sym::rustc_proc_macro_decls);
         i.attrs.push(cx.attribute(attr));
-        i.vis = respan(span, ast::VisibilityKind::Public);
         i
     });
 
-    let module = cx.item_mod(
+    let block = cx.expr_block(cx.block(
         span,
-        span,
-        ast::Ident::from_str("decls").gensym(),
-        vec![doc_hidden],
-        vec![krate, decls_static],
-    ).map(|mut i| {
-        i.vis = respan(span, ast::VisibilityKind::Public);
-        i
-    });
+        vec![cx.stmt_item(span, krate), cx.stmt_item(span, decls_static)],
+    ));
 
-    // Integrate the new module into existing module structures.
-    let module = AstFragment::Items(smallvec![module]);
-    cx.monotonic_expander().fully_expand_fragment(module).make_items().pop().unwrap()
+    let anon_constant = cx.item_const(
+        span,
+        ast::Ident::new(kw::Underscore, span),
+        cx.ty(span, ast::TyKind::Tup(Vec::new())),
+        block,
+    );
+
+    // Integrate the new item into existing module structures.
+    let items = AstFragment::Items(smallvec![anon_constant]);
+    cx.monotonic_expander().fully_expand_fragment(items).make_items().pop().unwrap()
 }
diff --git a/src/libsyntax_ext/standard_library_imports.rs b/src/libsyntax_ext/standard_library_imports.rs
index 8ca376341fcdb..c577b1e33cfeb 100644
--- a/src/libsyntax_ext/standard_library_imports.rs
+++ b/src/libsyntax_ext/standard_library_imports.rs
@@ -1,86 +1,86 @@
 use syntax::{ast, attr};
 use syntax::edition::Edition;
-use syntax::ext::hygiene::MacroKind;
+use syntax::ext::expand::ExpansionConfig;
+use syntax::ext::hygiene::AstPass;
+use syntax::ext::base::{ExtCtxt, Resolver};
+use syntax::parse::ParseSess;
 use syntax::ptr::P;
-use syntax::source_map::{ExpnData, ExpnKind, dummy_spanned, respan};
 use syntax::symbol::{Ident, Symbol, kw, sym};
 use syntax_pos::DUMMY_SP;
 
-use std::iter;
-
 pub fn inject(
-    mut krate: ast::Crate, alt_std_name: Option<&str>, edition: Edition
+    mut krate: ast::Crate,
+    resolver: &mut dyn Resolver,
+    sess: &ParseSess,
+    alt_std_name: Option<Symbol>,
 ) -> (ast::Crate, Option<Symbol>) {
-    let rust_2018 = edition >= Edition::Edition2018;
+    let rust_2018 = sess.edition >= Edition::Edition2018;
 
     // the first name in this list is the crate name of the crate with the prelude
-    let names: &[&str] = if attr::contains_name(&krate.attrs, sym::no_core) {
+    let names: &[Symbol] = if attr::contains_name(&krate.attrs, sym::no_core) {
         return (krate, None);
     } else if attr::contains_name(&krate.attrs, sym::no_std) {
         if attr::contains_name(&krate.attrs, sym::compiler_builtins) {
-            &["core"]
+            &[sym::core]
         } else {
-            &["core", "compiler_builtins"]
+            &[sym::core, sym::compiler_builtins]
         }
     } else {
-        &["std"]
+        &[sym::std]
     };
 
+    let expn_id = resolver.expansion_for_ast_pass(
+        DUMMY_SP,
+        AstPass::StdImports,
+        &[sym::prelude_import],
+        None,
+    );
+    let span = DUMMY_SP.with_def_site_ctxt(expn_id);
+    let call_site = DUMMY_SP.with_call_site_ctxt(expn_id);
+
+    let ecfg = ExpansionConfig::default("std_lib_injection".to_string());
+    let cx = ExtCtxt::new(sess, ecfg, resolver);
+
+
     // .rev() to preserve ordering above in combination with insert(0, ...)
-    let alt_std_name = alt_std_name.map(Symbol::intern);
-    for orig_name_str in names.iter().rev() {
-        // HACK(eddyb) gensym the injected crates on the Rust 2018 edition,
-        // so they don't accidentally interfere with the new import paths.
-        let orig_name_sym = Symbol::intern(orig_name_str);
-        let orig_name_ident = Ident::with_dummy_span(orig_name_sym);
-        let (rename, orig_name) = if rust_2018 {
-            (orig_name_ident.gensym(), Some(orig_name_sym))
+    for &name in names.iter().rev() {
+        let ident = if rust_2018 {
+            Ident::new(name, span)
         } else {
-            (orig_name_ident, None)
+            Ident::new(name, call_site)
         };
-        krate.module.items.insert(0, P(ast::Item {
-            attrs: vec![attr::mk_attr_outer(
-                attr::mk_word_item(ast::Ident::with_dummy_span(sym::macro_use))
-            )],
-            vis: dummy_spanned(ast::VisibilityKind::Inherited),
-            node: ast::ItemKind::ExternCrate(alt_std_name.or(orig_name)),
-            ident: rename,
-            id: ast::DUMMY_NODE_ID,
-            span: DUMMY_SP,
-            tokens: None,
-        }));
+        krate.module.items.insert(0, cx.item(
+            span,
+            ident,
+            vec![cx.attribute(cx.meta_word(span, sym::macro_use))],
+            ast::ItemKind::ExternCrate(alt_std_name),
+        ));
     }
 
-    // the crates have been injected, the assumption is that the first one is the one with
-    // the prelude.
+    // The crates have been injected, the assumption is that the first one is
+    // the one with the prelude.
     let name = names[0];
 
-    let span = DUMMY_SP.fresh_expansion(ExpnData::allow_unstable(
-        ExpnKind::Macro(MacroKind::Attr, sym::std_inject), DUMMY_SP, edition,
-        [sym::prelude_import][..].into(),
-    ));
+    let import_path = if rust_2018 {
+        [name, sym::prelude, sym::v1].iter()
+            .map(|symbol| ast::Ident::new(*symbol, span)).collect()
+    } else {
+        [kw::PathRoot, name, sym::prelude, sym::v1].iter()
+            .map(|symbol| ast::Ident::new(*symbol, span)).collect()
+    };
 
-    krate.module.items.insert(0, P(ast::Item {
-        attrs: vec![attr::mk_attr_outer(
-            attr::mk_word_item(ast::Ident::new(sym::prelude_import, span)))],
-        vis: respan(span.shrink_to_lo(), ast::VisibilityKind::Inherited),
-        node: ast::ItemKind::Use(P(ast::UseTree {
-            prefix: ast::Path {
-                segments: iter::once(ast::Ident::with_dummy_span(kw::PathRoot))
-                    .chain(
-                        [name, "prelude", "v1"].iter().cloned()
-                            .map(ast::Ident::from_str)
-                    ).map(ast::PathSegment::from_ident).collect(),
-                span,
-            },
+    let use_item = cx.item(
+        span,
+        ast::Ident::invalid(),
+        vec![cx.attribute(cx.meta_word(span, sym::prelude_import))],
+        ast::ItemKind::Use(P(ast::UseTree {
+            prefix: cx.path(span, import_path),
             kind: ast::UseTreeKind::Glob,
             span,
         })),
-        id: ast::DUMMY_NODE_ID,
-        ident: ast::Ident::invalid(),
-        span,
-        tokens: None,
-    }));
+    );
+
+    krate.module.items.insert(0, use_item);
 
-    (krate, Some(Symbol::intern(name)))
+    (krate, Some(name))
 }
diff --git a/src/libsyntax_ext/test.rs b/src/libsyntax_ext/test.rs
index 5fd87d3a0e5c6..be5aca73f5cb1 100644
--- a/src/libsyntax_ext/test.rs
+++ b/src/libsyntax_ext/test.rs
@@ -28,11 +28,11 @@ pub fn expand_test_case(
 
     if !ecx.ecfg.should_test { return vec![]; }
 
-    let sp = ecx.with_legacy_ctxt(attr_sp);
+    let sp = ecx.with_def_site_ctxt(attr_sp);
     let mut item = anno_item.expect_item();
     item = item.map(|mut item| {
         item.vis = respan(item.vis.span, ast::VisibilityKind::Public);
-        item.ident = item.ident.gensym();
+        item.ident.span = item.ident.span.with_ctxt(sp.ctxt());
         item.attrs.push(
             ecx.attribute(ecx.meta_word(sp, sym::rustc_test_marker))
         );
@@ -92,10 +92,9 @@ pub fn expand_test_or_bench(
         return vec![Annotatable::Item(item)];
     }
 
-    let (sp, attr_sp) = (cx.with_legacy_ctxt(item.span), cx.with_legacy_ctxt(attr_sp));
+    let (sp, attr_sp) = (cx.with_def_site_ctxt(item.span), cx.with_def_site_ctxt(attr_sp));
 
-    // Gensym "test" so we can extern crate without conflicting with any local names
-    let test_id = cx.ident_of("test").gensym();
+    let test_id = ast::Ident::new(sym::test, attr_sp);
 
     // creates test::$name
     let test_path = |name| {
@@ -112,7 +111,7 @@ pub fn expand_test_or_bench(
 
     let test_fn = if is_bench {
         // A simple ident for a lambda
-        let b = cx.ident_of("b");
+        let b = ast::Ident::from_str_and_span("b", attr_sp);
 
         cx.expr_call(sp, cx.expr_path(test_path("StaticBenchFn")), vec![
             // |b| self::test::assert_test_result(
@@ -143,7 +142,7 @@ pub fn expand_test_or_bench(
         ])
     };
 
-    let mut test_const = cx.item(sp, ast::Ident::new(item.ident.name, sp).gensym(),
+    let mut test_const = cx.item(sp, ast::Ident::new(item.ident.name, sp),
         vec![
             // #[cfg(test)]
             cx.attribute(cx.meta_list(attr_sp, sym::cfg, vec![
@@ -192,17 +191,17 @@ pub fn expand_test_or_bench(
         ));
     test_const = test_const.map(|mut tc| { tc.vis.node = ast::VisibilityKind::Public; tc});
 
-    // extern crate test as test_gensym
+    // extern crate test
     let test_extern = cx.item(sp,
         test_id,
         vec![],
-        ast::ItemKind::ExternCrate(Some(sym::test))
+        ast::ItemKind::ExternCrate(None)
     );
 
     log::debug!("synthetic test item:\n{}\n", pprust::item_to_string(&test_const));
 
     vec![
-        // Access to libtest under a gensymed name
+        // Access to libtest under a hygienic name
         Annotatable::Item(test_extern),
         // The generated test case
         Annotatable::Item(test_const),
diff --git a/src/libsyntax_ext/test_harness.rs b/src/libsyntax_ext/test_harness.rs
index 4a6ea0ebf85e5..b93c11fad3823 100644
--- a/src/libsyntax_ext/test_harness.rs
+++ b/src/libsyntax_ext/test_harness.rs
@@ -5,32 +5,30 @@ use smallvec::{smallvec, SmallVec};
 use syntax::ast::{self, Ident};
 use syntax::attr;
 use syntax::entry::{self, EntryPointType};
-use syntax::ext::base::{ExtCtxt, MacroKind, Resolver};
+use syntax::ext::base::{ExtCtxt, Resolver};
 use syntax::ext::expand::{AstFragment, ExpansionConfig};
 use syntax::feature_gate::Features;
 use syntax::mut_visit::{*, ExpectOne};
 use syntax::parse::ParseSess;
 use syntax::ptr::P;
-use syntax::source_map::{ExpnData, ExpnKind, dummy_spanned};
-use syntax::symbol::{kw, sym, Symbol};
+use syntax::source_map::respan;
+use syntax::symbol::{sym, Symbol};
 use syntax_pos::{Span, DUMMY_SP};
+use syntax_pos::hygiene::{AstPass, SyntaxContext, Transparency};
 
 use std::{iter, mem};
 
 struct Test {
     span: Span,
-    path: Vec<Ident>,
+    ident: Ident,
 }
 
 struct TestCtxt<'a> {
-    span_diagnostic: &'a errors::Handler,
-    path: Vec<Ident>,
     ext_cx: ExtCtxt<'a>,
+    def_site: Span,
     test_cases: Vec<Test>,
     reexport_test_harness_main: Option<Symbol>,
     test_runner: Option<ast::Path>,
-    // top-level re-export submodule, filled out after folding is finished
-    toplevel_reexport: Option<Ident>,
 }
 
 // Traverse the crate, collecting all the test functions, eliding any
@@ -43,8 +41,8 @@ pub fn inject(
     span_diagnostic: &errors::Handler,
     features: &Features,
 ) {
-    // Check for #[reexport_test_harness_main = "some_name"] which
-    // creates a `use __test::main as some_name;`. This needs to be
+    // Check for #![reexport_test_harness_main = "some_name"] which gives the
+    // main test function the name `some_name` without hygiene. This needs to be
     // unconditional, so that the attribute is still marked as used in
     // non-test builds.
     let reexport_test_harness_main =
@@ -56,16 +54,13 @@ pub fn inject(
 
     if should_test {
         generate_test_harness(sess, resolver, reexport_test_harness_main,
-                              krate, span_diagnostic, features, test_runner)
+                              krate, features, test_runner)
     }
 }
 
 struct TestHarnessGenerator<'a> {
     cx: TestCtxt<'a>,
-    tests: Vec<Ident>,
-
-    // submodule name, gensym'd identifier for re-exports
-    tested_submods: Vec<(Ident, Ident)>,
+    tests: Vec<Test>,
 }
 
 impl<'a> MutVisitor for TestHarnessGenerator<'a> {
@@ -77,49 +72,47 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
     }
 
     fn flat_map_item(&mut self, i: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
-        let ident = i.ident;
-        if ident.name != kw::Invalid {
-            self.cx.path.push(ident);
-        }
-        debug!("current path: {}", path_name_i(&self.cx.path));
-
         let mut item = i.into_inner();
         if is_test_case(&item) {
             debug!("this is a test item");
 
             let test = Test {
                 span: item.span,
-                path: self.cx.path.clone(),
+                ident: item.ident,
             };
-            self.cx.test_cases.push(test);
-            self.tests.push(item.ident);
+            self.tests.push(test);
         }
 
         // We don't want to recurse into anything other than mods, since
         // mods or tests inside of functions will break things
         if let ast::ItemKind::Mod(mut module) = item.node {
             let tests = mem::take(&mut self.tests);
-            let tested_submods = mem::take(&mut self.tested_submods);
             noop_visit_mod(&mut module, self);
-            let tests = mem::replace(&mut self.tests, tests);
-            let tested_submods = mem::replace(&mut self.tested_submods, tested_submods);
+            let mut tests = mem::replace(&mut self.tests, tests);
 
-            if !tests.is_empty() || !tested_submods.is_empty() {
-                let (it, sym) = mk_reexport_mod(&mut self.cx, item.id, tests, tested_submods);
-                module.items.push(it);
-
-                if !self.cx.path.is_empty() {
-                    self.tested_submods.push((self.cx.path[self.cx.path.len()-1], sym));
+            if !tests.is_empty() {
+                let parent = if item.id == ast::DUMMY_NODE_ID {
+                    ast::CRATE_NODE_ID
                 } else {
-                    debug!("pushing nothing, sym: {:?}", sym);
-                    self.cx.toplevel_reexport = Some(sym);
+                    item.id
+                };
+                // Create an identifier that will hygienically resolve the test
+                // case name, even in another module.
+                let expn_id = self.cx.ext_cx.resolver.expansion_for_ast_pass(
+                    module.inner,
+                    AstPass::TestHarness,
+                    &[],
+                    Some(parent),
+                );
+                for test in &mut tests {
+                    // See the comment on `mk_main` for why we're using
+                    // `apply_mark` directly.
+                    test.ident.span = test.ident.span.apply_mark(expn_id, Transparency::Opaque);
                 }
+                self.cx.test_cases.extend(tests);
             }
             item.node = ast::ItemKind::Mod(module);
         }
-        if ident.name != kw::Invalid {
-            self.cx.path.pop();
-        }
         smallvec![P(item)]
     }
 
@@ -133,6 +126,7 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
 struct EntryPointCleaner {
     // Current depth in the ast
     depth: usize,
+    def_site: Span,
 }
 
 impl MutVisitor for EntryPointCleaner {
@@ -149,8 +143,10 @@ impl MutVisitor for EntryPointCleaner {
             EntryPointType::MainAttr |
             EntryPointType::Start =>
                 item.map(|ast::Item {id, ident, attrs, node, vis, span, tokens}| {
-                    let allow_ident = Ident::with_dummy_span(sym::allow);
-                    let dc_nested = attr::mk_nested_word_item(Ident::from_str("dead_code"));
+                    let allow_ident = Ident::new(sym::allow, self.def_site);
+                    let dc_nested = attr::mk_nested_word_item(
+                        Ident::from_str_and_span("dead_code", self.def_site),
+                    );
                     let allow_dead_code_item = attr::mk_list_item(allow_ident, vec![dc_nested]);
                     let allow_dead_code = attr::mk_attr_outer(allow_dead_code_item);
 
@@ -181,124 +177,99 @@ impl MutVisitor for EntryPointCleaner {
     }
 }
 
-/// Creates an item (specifically a module) that "pub use"s the tests passed in.
-/// Each tested submodule will contain a similar reexport module that we will export
-/// under the name of the original module. That is, `submod::__test_reexports` is
-/// reexported like so `pub use submod::__test_reexports as submod`.
-fn mk_reexport_mod(cx: &mut TestCtxt<'_>,
-                   parent: ast::NodeId,
-                   tests: Vec<Ident>,
-                   tested_submods: Vec<(Ident, Ident)>)
-                   -> (P<ast::Item>, Ident) {
-    let super_ = Ident::with_dummy_span(kw::Super);
-
-    let items = tests.into_iter().map(|r| {
-        cx.ext_cx.item_use_simple(DUMMY_SP, dummy_spanned(ast::VisibilityKind::Public),
-                                  cx.ext_cx.path(DUMMY_SP, vec![super_, r]))
-    }).chain(tested_submods.into_iter().map(|(r, sym)| {
-        let path = cx.ext_cx.path(DUMMY_SP, vec![super_, r, sym]);
-        cx.ext_cx.item_use_simple_(DUMMY_SP, dummy_spanned(ast::VisibilityKind::Public),
-                                   Some(r), path)
-    })).collect();
-
-    let reexport_mod = ast::Mod {
-        inline: true,
-        inner: DUMMY_SP,
-        items,
-    };
-
-    let name = Ident::from_str("__test_reexports").gensym();
-    let parent = if parent == ast::DUMMY_NODE_ID { ast::CRATE_NODE_ID } else { parent };
-    cx.ext_cx.current_expansion.id = cx.ext_cx.resolver.get_module_scope(parent);
-    let module = P(ast::Item {
-        ident: name,
-        attrs: Vec::new(),
-        id: ast::DUMMY_NODE_ID,
-        node: ast::ItemKind::Mod(reexport_mod),
-        vis: dummy_spanned(ast::VisibilityKind::Public),
-        span: DUMMY_SP,
-        tokens: None,
-    });
-
-    // Integrate the new module into existing module structures.
-    let module = AstFragment::Items(smallvec![module]);
-    let module =
-        cx.ext_cx.monotonic_expander().fully_expand_fragment(module).make_items().pop().unwrap();
-
-    (module, name)
-}
-
 /// Crawl over the crate, inserting test reexports and the test main function
 fn generate_test_harness(sess: &ParseSess,
                          resolver: &mut dyn Resolver,
                          reexport_test_harness_main: Option<Symbol>,
                          krate: &mut ast::Crate,
-                         sd: &errors::Handler,
                          features: &Features,
                          test_runner: Option<ast::Path>) {
-    // Remove the entry points
-    let mut cleaner = EntryPointCleaner { depth: 0 };
-    cleaner.visit_crate(krate);
-
     let mut econfig = ExpansionConfig::default("test".to_string());
     econfig.features = Some(features);
 
+    let ext_cx = ExtCtxt::new(sess, econfig, resolver);
+
+    let expn_id = ext_cx.resolver.expansion_for_ast_pass(
+        DUMMY_SP,
+        AstPass::TestHarness,
+        &[sym::main, sym::test, sym::rustc_attrs],
+        None,
+    );
+    let def_site = DUMMY_SP.with_def_site_ctxt(expn_id);
+
+    // Remove the entry points
+    let mut cleaner = EntryPointCleaner { depth: 0, def_site };
+    cleaner.visit_crate(krate);
+
     let cx = TestCtxt {
-        span_diagnostic: sd,
-        ext_cx: ExtCtxt::new(sess, econfig, resolver),
-        path: Vec::new(),
+        ext_cx,
+        def_site,
         test_cases: Vec::new(),
         reexport_test_harness_main,
-        toplevel_reexport: None,
         test_runner
     };
 
     TestHarnessGenerator {
         cx,
         tests: Vec::new(),
-        tested_submods: Vec::new(),
     }.visit_crate(krate);
 }
 
 /// Creates a function item for use as the main function of a test build.
 /// This function will call the `test_runner` as specified by the crate attribute
+///
+/// By default this expands to
+///
+/// #[main]
+/// pub fn main() {
+///     extern crate test;
+///     test::test_main_static(&[
+///         &test_const1,
+///         &test_const2,
+///         &test_const3,
+///     ]);
+/// }
+///
+/// Most of the Ident have the usual def-site hygiene for the AST pass. The
+/// exception is the `test_const`s. These have a syntax context that has two
+/// opaque marks: one from the expansion of `test` or `test_case`, and one
+/// generated  in `TestHarnessGenerator::flat_map_item`. When resolving this
+/// identifier after failing to find a matching identifier in the root module
+/// we remove the outer mark, and try resolving at its def-site, which will
+/// then resolve to `test_const`.
+///
+/// The expansion here can be controlled by two attributes:
+///
+/// `reexport_test_harness_main` provides a different name for the `main`
+/// function and `test_runner` provides a path that replaces
+/// `test::test_main_static`.
 fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
-    // Writing this out by hand:
-    //        pub fn main() {
-    //            #![main]
-    //            test::test_main_static(&[..tests]);
-    //        }
-    let sp = DUMMY_SP.fresh_expansion(ExpnData::allow_unstable(
-        ExpnKind::Macro(MacroKind::Attr, sym::test_case), DUMMY_SP, cx.ext_cx.parse_sess.edition,
-        [sym::main, sym::test, sym::rustc_attrs][..].into(),
-    ));
+    let sp = cx.def_site;
     let ecx = &cx.ext_cx;
-    let test_id = Ident::with_dummy_span(sym::test);
+    let test_id = Ident::new(sym::test, sp);
 
     // test::test_main_static(...)
     let mut test_runner = cx.test_runner.clone().unwrap_or(
-        ecx.path(sp, vec![
-            test_id, ecx.ident_of("test_main_static")
-        ]));
+        ecx.path(sp, vec![test_id, Ident::from_str_and_span("test_main_static", sp)]));
 
     test_runner.span = sp;
 
     let test_main_path_expr = ecx.expr_path(test_runner);
     let call_test_main = ecx.expr_call(sp, test_main_path_expr,
-                                       vec![mk_tests_slice(cx)]);
+                                       vec![mk_tests_slice(cx, sp)]);
     let call_test_main = ecx.stmt_expr(call_test_main);
 
-    // #![main]
-    let main_meta = ecx.meta_word(sp, sym::main);
-    let main_attr = ecx.attribute(main_meta);
-
-    // extern crate test as test_gensym
+    // extern crate test
     let test_extern_stmt = ecx.stmt_item(sp, ecx.item(sp,
         test_id,
         vec![],
         ast::ItemKind::ExternCrate(None)
     ));
 
+    // #[main]
+    let main_meta = ecx.meta_word(sp, sym::main);
+    let main_attr = ecx.attribute(main_meta);
+
     // pub fn main() { ... }
     let main_ret_ty = ecx.ty(sp, ast::TyKind::Tup(vec![]));
 
@@ -316,8 +287,8 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
 
     // Honor the reexport_test_harness_main attribute
     let main_id = match cx.reexport_test_harness_main {
-        Some(sym) => Ident::new(sym, sp),
-        None => Ident::from_str_and_span("main", sp).gensym(),
+        Some(sym) => Ident::new(sym, sp.with_ctxt(SyntaxContext::root())),
+        None => Ident::from_str_and_span("main", sp),
     };
 
     let main = P(ast::Item {
@@ -325,7 +296,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
         attrs: vec![main_attr],
         id: ast::DUMMY_NODE_ID,
         node: main,
-        vis: dummy_spanned(ast::VisibilityKind::Public),
+        vis: respan(sp, ast::VisibilityKind::Public),
         span: sp,
         tokens: None,
     });
@@ -335,44 +306,20 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
     cx.ext_cx.monotonic_expander().fully_expand_fragment(main).make_items().pop().unwrap()
 }
 
-fn path_name_i(idents: &[Ident]) -> String {
-    let mut path_name = "".to_string();
-    let mut idents_iter = idents.iter().peekable();
-    while let Some(ident) = idents_iter.next() {
-        path_name.push_str(&ident.as_str());
-        if idents_iter.peek().is_some() {
-            path_name.push_str("::")
-        }
-    }
-    path_name
-}
-
 /// Creates a slice containing every test like so:
-/// &[path::to::test1, path::to::test2]
-fn mk_tests_slice(cx: &TestCtxt<'_>) -> P<ast::Expr> {
+/// &[&test1, &test2]
+fn mk_tests_slice(cx: &TestCtxt<'_>, sp: Span) -> P<ast::Expr> {
     debug!("building test vector from {} tests", cx.test_cases.len());
     let ref ecx = cx.ext_cx;
 
-    ecx.expr_vec_slice(DUMMY_SP,
+
+    ecx.expr_vec_slice(sp,
         cx.test_cases.iter().map(|test| {
             ecx.expr_addr_of(test.span,
-                ecx.expr_path(ecx.path(test.span, visible_path(cx, &test.path))))
+                ecx.expr_path(ecx.path(test.span, vec![test.ident])))
         }).collect())
 }
 
-/// Creates a path from the top-level __test module to the test via __test_reexports
-fn visible_path(cx: &TestCtxt<'_>, path: &[Ident]) -> Vec<Ident>{
-    let mut visible_path = vec![];
-    match cx.toplevel_reexport {
-        Some(id) => visible_path.push(id),
-        None => {
-            cx.span_diagnostic.bug("expected to find top-level re-export name, but found None");
-        }
-    }
-    visible_path.extend_from_slice(path);
-    visible_path
-}
-
 fn is_test_case(i: &ast::Item) -> bool {
     attr::contains_name(&i.attrs, sym::rustc_test_marker)
 }
diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs
index 733f6f0449065..f0e7344c1b986 100644
--- a/src/libsyntax_pos/hygiene.rs
+++ b/src/libsyntax_pos/hygiene.rs
@@ -360,7 +360,7 @@ impl SyntaxContext {
     }
 
     /// Extend a syntax context with a given expansion and transparency.
-    pub fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> SyntaxContext {
+    crate fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> SyntaxContext {
         HygieneData::with(|data| data.apply_mark(self, expn_id, transparency))
     }
 
@@ -550,7 +550,7 @@ impl Span {
     /// The returned span belongs to the created expansion and has the new properties,
     /// but its location is inherited from the current span.
     pub fn fresh_expansion(self, expn_data: ExpnData) -> Span {
-        self.fresh_expansion_with_transparency(expn_data, Transparency::SemiTransparent)
+        self.fresh_expansion_with_transparency(expn_data, Transparency::Transparent)
     }
 
     pub fn fresh_expansion_with_transparency(
@@ -639,8 +639,9 @@ pub enum ExpnKind {
     /// No expansion, aka root expansion. Only `ExpnId::root()` has this kind.
     Root,
     /// Expansion produced by a macro.
-    /// FIXME: Some code injected by the compiler before HIR lowering also gets this kind.
     Macro(MacroKind, Symbol),
+    /// Transform done by the compiler on the AST.
+    AstPass(AstPass),
     /// Desugaring done by the compiler during HIR lowering.
     Desugaring(DesugaringKind)
 }
@@ -650,6 +651,7 @@ impl ExpnKind {
         match *self {
             ExpnKind::Root => kw::PathRoot,
             ExpnKind::Macro(_, descr) => descr,
+            ExpnKind::AstPass(kind) => Symbol::intern(kind.descr()),
             ExpnKind::Desugaring(kind) => Symbol::intern(kind.descr()),
         }
     }
@@ -683,6 +685,26 @@ impl MacroKind {
     }
 }
 
+/// The kind of AST transform.
+#[derive(Clone, Copy, PartialEq, Debug, RustcEncodable, RustcDecodable)]
+pub enum AstPass {
+    StdImports,
+    TestHarness,
+    ProcMacroHarness,
+    PluginMacroDefs,
+}
+
+impl AstPass {
+    fn descr(self) -> &'static str {
+        match self {
+            AstPass::StdImports => "standard library imports",
+            AstPass::TestHarness => "test harness",
+            AstPass::ProcMacroHarness => "proc macro harness",
+            AstPass::PluginMacroDefs => "plugin macro definitions",
+        }
+    }
+}
+
 /// The kind of compiler desugaring.
 #[derive(Clone, Copy, PartialEq, Debug, RustcEncodable, RustcDecodable)]
 pub enum DesugaringKind {
diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs
index 3d8bfc77a8950..9a296f17aaf4a 100644
--- a/src/libsyntax_pos/lib.rs
+++ b/src/libsyntax_pos/lib.rs
@@ -442,6 +442,7 @@ impl Span {
                 let (pre, post) = match expn_data.kind {
                     ExpnKind::Root => break,
                     ExpnKind::Desugaring(..) => ("desugaring of ", ""),
+                    ExpnKind::AstPass(..) => ("", ""),
                     ExpnKind::Macro(macro_kind, _) => match macro_kind {
                         MacroKind::Bang => ("", "!"),
                         MacroKind::Attr => ("#[", "]"),
@@ -513,6 +514,25 @@ impl Span {
                   span.ctxt)
     }
 
+    /// Equivalent of `Span::def_site` from the proc macro API,
+    /// except that the location is taken from the `self` span.
+    pub fn with_def_site_ctxt(self, expn_id: ExpnId) -> Span {
+        self.with_ctxt_from_mark(expn_id, Transparency::Opaque)
+    }
+
+    /// Equivalent of `Span::call_site` from the proc macro API,
+    /// except that the location is taken from the `self` span.
+    pub fn with_call_site_ctxt(&self, expn_id: ExpnId) -> Span {
+        self.with_ctxt_from_mark(expn_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, expn_id: ExpnId) -> Span {
+        self.with_ctxt_from_mark(expn_id, Transparency::SemiTransparent)
+    }
+
     /// Produces a span with the same location as `self` and context produced by a macro with the
     /// given ID and transparency, assuming that macro was defined directly and not produced by
     /// some other macro (which is the case for built-in and procedural macros).
diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs
index 3a4dc1f5a096b..ecc31cfeb3d1f 100644
--- a/src/libsyntax_pos/symbol.rs
+++ b/src/libsyntax_pos/symbol.rs
@@ -801,21 +801,15 @@ impl Ident {
         Ident::new(self.name, self.span.modern_and_legacy())
     }
 
-    /// Transforms an identifier into one with the same name, but gensymed.
-    pub fn gensym(self) -> Ident {
-        let name = with_interner(|interner| interner.gensymed(self.name));
-        Ident::new(name, self.span)
-    }
-
     /// Transforms an underscore identifier into one with the same name, but
     /// gensymed. Leaves non-underscore identifiers unchanged.
     pub fn gensym_if_underscore(self) -> Ident {
-        if self.name == kw::Underscore { self.gensym() } else { self }
-    }
-
-    // WARNING: this function is deprecated and will be removed in the future.
-    pub fn is_gensymed(self) -> bool {
-        with_interner(|interner| interner.is_gensymed(self.name))
+        if self.name == kw::Underscore {
+            let name = with_interner(|interner| interner.gensymed(self.name));
+            Ident::new(name, self.span)
+        } else {
+            self
+        }
     }
 
     /// Convert the name to a `LocalInternedString`. This is a slowish
@@ -892,9 +886,12 @@ impl UseSpecializedDecodable for Ident {
 ///
 /// Examples:
 /// ```
-/// assert_eq!(Ident::from_str("x"), Ident::from_str("x"))
-/// assert_ne!(Ident::from_str("x").gensym(), Ident::from_str("x"))
-/// assert_ne!(Ident::from_str("x").gensym(), Ident::from_str("x").gensym())
+/// assert_eq!(Ident::from_str("_"), Ident::from_str("_"))
+/// assert_ne!(Ident::from_str("_").gensym_if_underscore(), Ident::from_str("_"))
+/// assert_ne!(
+///     Ident::from_str("_").gensym_if_underscore(),
+///     Ident::from_str("_").gensym_if_underscore(),
+/// )
 /// ```
 /// Internally, a symbol is implemented as an index, and all operations
 /// (including hashing, equality, and ordering) operate on that index. The use
diff --git a/src/test/codegen/var-names.rs b/src/test/codegen/var-names.rs
new file mode 100644
index 0000000000000..3140a7c6b6cc2
--- /dev/null
+++ b/src/test/codegen/var-names.rs
@@ -0,0 +1,15 @@
+// compile-flags: -O -C no-prepopulate-passes
+
+#![crate_type = "lib"]
+
+// CHECK-LABEL: define i32 @test(i32 %a, i32 %b)
+#[no_mangle]
+pub fn test(a: u32, b: u32) -> u32 {
+    let c = a + b;
+    // CHECK: %c = add i32 %a, %b
+    let d = c;
+    let e = d * a;
+    // CHECK-NEXT: %e = mul i32 %c, %a
+    e
+    // CHECK-NEXT: ret i32 %e
+}
diff --git a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs
index b4c154e5d95f7..596f515da2f7f 100644
--- a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs
+++ b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs
@@ -32,13 +32,15 @@ use syntax::print::pprust;
 use syntax::ptr::P;
 
 
-fn parse_expr(ps: &ParseSess, src: &str) -> P<Expr> {
+fn parse_expr(ps: &ParseSess, src: &str) -> Option<P<Expr>> {
     let src_as_string = src.to_string();
 
-    let mut p = parse::new_parser_from_source_str(ps,
-                                                  FileName::Custom(src_as_string.clone()),
-                                                  src_as_string);
-    p.parse_expr().unwrap()
+    let mut p = parse::new_parser_from_source_str(
+        ps,
+        FileName::Custom(src_as_string.clone()),
+        src_as_string,
+    );
+    p.parse_expr().map_err(|mut e| e.cancel()).ok()
 }
 
 
@@ -209,22 +211,23 @@ fn run() {
         let printed = pprust::expr_to_string(&e);
         println!("printed: {}", printed);
 
-        let mut parsed = parse_expr(&ps, &printed);
-
-        // We want to know if `parsed` is structurally identical to `e`, ignoring trivial
-        // differences like placement of `Paren`s or the exact ranges of node spans.
-        // Unfortunately, there is no easy way to make this comparison. Instead, we add `Paren`s
-        // everywhere we can, then pretty-print. This should give an unambiguous representation of
-        // each `Expr`, and it bypasses nearly all of the parenthesization logic, so we aren't
-        // relying on the correctness of the very thing we're testing.
-        RemoveParens.visit_expr(&mut e);
-        AddParens.visit_expr(&mut e);
-        let text1 = pprust::expr_to_string(&e);
-        RemoveParens.visit_expr(&mut parsed);
-        AddParens.visit_expr(&mut parsed);
-        let text2 = pprust::expr_to_string(&parsed);
-        assert!(text1 == text2,
-                "exprs are not equal:\n  e =      {:?}\n  parsed = {:?}",
-                text1, text2);
+        // Ignore expressions with chained comparisons that fail to parse
+        if let Some(mut parsed) = parse_expr(&ps, &printed) {
+            // We want to know if `parsed` is structurally identical to `e`, ignoring trivial
+            // differences like placement of `Paren`s or the exact ranges of node spans.
+            // Unfortunately, there is no easy way to make this comparison. Instead, we add `Paren`s
+            // everywhere we can, then pretty-print. This should give an unambiguous representation
+            // of each `Expr`, and it bypasses nearly all of the parenthesization logic, so we
+            // aren't relying on the correctness of the very thing we're testing.
+            RemoveParens.visit_expr(&mut e);
+            AddParens.visit_expr(&mut e);
+            let text1 = pprust::expr_to_string(&e);
+            RemoveParens.visit_expr(&mut parsed);
+            AddParens.visit_expr(&mut parsed);
+            let text2 = pprust::expr_to_string(&parsed);
+            assert!(text1 == text2,
+                    "exprs are not equal:\n  e =      {:?}\n  parsed = {:?}",
+                    text1, text2);
+        }
     });
 }
diff --git a/src/test/ui/coercion/coercion-slice.rs b/src/test/ui/coercion/coercion-slice.rs
index 312b634c9fd0b..b69edcf260637 100644
--- a/src/test/ui/coercion/coercion-slice.rs
+++ b/src/test/ui/coercion/coercion-slice.rs
@@ -4,5 +4,5 @@ fn main() {
     let _: &[i32] = [0];
     //~^ ERROR mismatched types
     //~| expected type `&[i32]`
-    //~| expected &[i32], found array of 1 elements
+    //~| expected &[i32], found array of 1 element
 }
diff --git a/src/test/ui/coercion/coercion-slice.stderr b/src/test/ui/coercion/coercion-slice.stderr
index 6fa712371178b..ccd776e987938 100644
--- a/src/test/ui/coercion/coercion-slice.stderr
+++ b/src/test/ui/coercion/coercion-slice.stderr
@@ -4,7 +4,7 @@ error[E0308]: mismatched types
 LL |     let _: &[i32] = [0];
    |                     ^^^
    |                     |
-   |                     expected &[i32], found array of 1 elements
+   |                     expected &[i32], found array of 1 element
    |                     help: consider borrowing here: `&[0]`
    |
    = note: expected type `&[i32]`
diff --git a/src/test/ui/did_you_mean/issue-40396.rs b/src/test/ui/did_you_mean/issue-40396.rs
index 63eec50c2d29f..6902779f33d23 100644
--- a/src/test/ui/did_you_mean/issue-40396.rs
+++ b/src/test/ui/did_you_mean/issue-40396.rs
@@ -1,27 +1,16 @@
 fn foo() {
     (0..13).collect<Vec<i32>>();
     //~^ ERROR chained comparison
-    //~| ERROR expected value, found struct `Vec`
-    //~| ERROR expected value, found builtin type `i32`
-    //~| ERROR attempted to take value of method `collect`
 }
 
 fn bar() {
     Vec<i32>::new();
     //~^ ERROR chained comparison
-    //~| ERROR expected value, found struct `Vec`
-    //~| ERROR expected value, found builtin type `i32`
-    //~| ERROR cannot find function `new` in the crate root
 }
 
 fn qux() {
     (0..13).collect<Vec<i32>();
     //~^ ERROR chained comparison
-    //~| ERROR chained comparison
-    //~| ERROR expected value, found struct `Vec`
-    //~| ERROR expected value, found builtin type `i32`
-    //~| ERROR attempted to take value of method `collect`
-    //~| ERROR mismatched types
 }
 
 fn main() {}
diff --git a/src/test/ui/did_you_mean/issue-40396.stderr b/src/test/ui/did_you_mean/issue-40396.stderr
index fe517ee34949d..7a08fda27e355 100644
--- a/src/test/ui/did_you_mean/issue-40396.stderr
+++ b/src/test/ui/did_you_mean/issue-40396.stderr
@@ -8,7 +8,7 @@ LL |     (0..13).collect<Vec<i32>>();
    = help: or use `(...)` if you meant to specify fn arguments
 
 error: chained comparison operators require parentheses
-  --> $DIR/issue-40396.rs:10:8
+  --> $DIR/issue-40396.rs:7:8
    |
 LL |     Vec<i32>::new();
    |        ^^^^^^^
@@ -17,7 +17,7 @@ LL |     Vec<i32>::new();
    = help: or use `(...)` if you meant to specify fn arguments
 
 error: chained comparison operators require parentheses
-  --> $DIR/issue-40396.rs:18:20
+  --> $DIR/issue-40396.rs:12:20
    |
 LL |     (0..13).collect<Vec<i32>();
    |                    ^^^^^^^^
@@ -25,79 +25,5 @@ LL |     (0..13).collect<Vec<i32>();
    = help: use `::<...>` instead of `<...>` if you meant to specify type arguments
    = help: or use `(...)` if you meant to specify fn arguments
 
-error: chained comparison operators require parentheses
-  --> $DIR/issue-40396.rs:18:24
-   |
-LL |     (0..13).collect<Vec<i32>();
-   |                        ^^^^^^
-   |
-   = help: use `::<...>` instead of `<...>` if you meant to specify type arguments
-   = help: or use `(...)` if you meant to specify fn arguments
-
-error[E0423]: expected value, found struct `Vec`
-  --> $DIR/issue-40396.rs:2:21
-   |
-LL |     (0..13).collect<Vec<i32>>();
-   |                     ^^^ did you mean `Vec { /* fields */ }`?
-
-error[E0423]: expected value, found builtin type `i32`
-  --> $DIR/issue-40396.rs:2:25
-   |
-LL |     (0..13).collect<Vec<i32>>();
-   |                         ^^^ not a value
-
-error[E0423]: expected value, found struct `Vec`
-  --> $DIR/issue-40396.rs:10:5
-   |
-LL |     Vec<i32>::new();
-   |     ^^^ did you mean `Vec { /* fields */ }`?
-
-error[E0423]: expected value, found builtin type `i32`
-  --> $DIR/issue-40396.rs:10:9
-   |
-LL |     Vec<i32>::new();
-   |         ^^^ not a value
-
-error[E0425]: cannot find function `new` in the crate root
-  --> $DIR/issue-40396.rs:10:15
-   |
-LL |     Vec<i32>::new();
-   |               ^^^ not found in the crate root
-
-error[E0423]: expected value, found struct `Vec`
-  --> $DIR/issue-40396.rs:18:21
-   |
-LL |     (0..13).collect<Vec<i32>();
-   |                     ^^^ did you mean `Vec { /* fields */ }`?
-
-error[E0423]: expected value, found builtin type `i32`
-  --> $DIR/issue-40396.rs:18:25
-   |
-LL |     (0..13).collect<Vec<i32>();
-   |                         ^^^ not a value
-
-error[E0615]: attempted to take value of method `collect` on type `std::ops::Range<{integer}>`
-  --> $DIR/issue-40396.rs:2:13
-   |
-LL |     (0..13).collect<Vec<i32>>();
-   |             ^^^^^^^ help: use parentheses to call the method: `collect()`
-
-error[E0615]: attempted to take value of method `collect` on type `std::ops::Range<{integer}>`
-  --> $DIR/issue-40396.rs:18:13
-   |
-LL |     (0..13).collect<Vec<i32>();
-   |             ^^^^^^^ help: use parentheses to call the method: `collect()`
-
-error[E0308]: mismatched types
-  --> $DIR/issue-40396.rs:18:29
-   |
-LL |     (0..13).collect<Vec<i32>();
-   |                             ^^ expected bool, found ()
-   |
-   = note: expected type `bool`
-              found type `()`
-
-error: aborting due to 14 previous errors
+error: aborting due to 3 previous errors
 
-Some errors have detailed explanations: E0308, E0423, E0425, E0615.
-For more information about an error, try `rustc --explain E0308`.
diff --git a/src/test/ui/hygiene/auxiliary/not-libstd.rs b/src/test/ui/hygiene/auxiliary/not-libstd.rs
new file mode 100644
index 0000000000000..babba293d03bd
--- /dev/null
+++ b/src/test/ui/hygiene/auxiliary/not-libstd.rs
@@ -0,0 +1 @@
+pub fn not_in_lib_std() {}
diff --git a/src/test/ui/hygiene/prelude-import-hygiene.rs b/src/test/ui/hygiene/prelude-import-hygiene.rs
new file mode 100644
index 0000000000000..51e7bed6580b3
--- /dev/null
+++ b/src/test/ui/hygiene/prelude-import-hygiene.rs
@@ -0,0 +1,29 @@
+// Make sure that attribute used when injecting the prelude are resolved
+// hygienically.
+
+// check-pass
+// aux-build:not-libstd.rs
+
+//revisions: rust2015 rust2018
+//[rust2018] edition:2018
+
+// The prelude import shouldn't see these as candidates for when it's trying to
+// use the built-in macros.
+extern crate core;
+use core::prelude::v1::test as prelude_import;
+use core::prelude::v1::test as macro_use;
+
+// Should not be used for the prelude import - not a concern in the 2015 edition
+// because `std` is already declared in the crate root.
+#[cfg(rust2018)]
+extern crate not_libstd as std;
+
+#[cfg(rust2018)]
+mod x {
+    // The extern crate item should override `std` in the extern prelude.
+    fn f() {
+        std::not_in_lib_std();
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/imports/gensymed.rs b/src/test/ui/imports/gensymed.rs
index 613ccc0b24234..7b53f0c536ad9 100644
--- a/src/test/ui/imports/gensymed.rs
+++ b/src/test/ui/imports/gensymed.rs
@@ -1,7 +1,9 @@
-// build-pass (FIXME(62277): could be check-pass?)
+// check-pass
 // edition:2018
 // aux-build:gensymed.rs
 
 extern crate gensymed;
 
+use gensymed::*;
+
 fn main() {}
diff --git a/src/test/ui/inaccessible-test-modules.stderr b/src/test/ui/inaccessible-test-modules.stderr
deleted file mode 100644
index b6a817e6b1d30..0000000000000
--- a/src/test/ui/inaccessible-test-modules.stderr
+++ /dev/null
@@ -1,21 +0,0 @@
-error[E0432]: unresolved import `__test`
-  --> $DIR/inaccessible-test-modules.rs:5:5
-   |
-LL | use __test as x;
-   |     ------^^^^^
-   |     |
-   |     no `__test` in the root
-   |     help: a similar name exists in the module: `test`
-
-error[E0432]: unresolved import `__test_reexports`
-  --> $DIR/inaccessible-test-modules.rs:6:5
-   |
-LL | use __test_reexports as y;
-   |     ----------------^^^^^
-   |     |
-   |     no `__test_reexports` in the root
-   |     help: a similar name exists in the module: `__test_reexports`
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0432`.
diff --git a/src/test/ui/issues/issue-15783.rs b/src/test/ui/issues/issue-15783.rs
index 77eae914fa1ca..5189f550cfbd6 100644
--- a/src/test/ui/issues/issue-15783.rs
+++ b/src/test/ui/issues/issue-15783.rs
@@ -6,9 +6,9 @@ fn main() {
     let name = "Foo";
     let x = Some(&[name]);
     let msg = foo(x);
-//~^ ERROR mismatched types
-//~| expected type `std::option::Option<&[&str]>`
-//~| found type `std::option::Option<&[&str; 1]>`
-//~| expected slice, found array of 1 elements
+    //~^ ERROR mismatched types
+    //~| expected type `std::option::Option<&[&str]>`
+    //~| found type `std::option::Option<&[&str; 1]>`
+    //~| expected slice, found array of 1 element
     assert_eq!(msg, 3);
 }
diff --git a/src/test/ui/issues/issue-15783.stderr b/src/test/ui/issues/issue-15783.stderr
index 595fe4025ad48..1d54b2830d6c5 100644
--- a/src/test/ui/issues/issue-15783.stderr
+++ b/src/test/ui/issues/issue-15783.stderr
@@ -2,7 +2,7 @@ error[E0308]: mismatched types
   --> $DIR/issue-15783.rs:8:19
    |
 LL |     let msg = foo(x);
-   |                   ^ expected slice, found array of 1 elements
+   |                   ^ expected slice, found array of 1 element
    |
    = note: expected type `std::option::Option<&[&str]>`
               found type `std::option::Option<&[&str; 1]>`
diff --git a/src/test/ui/match/match-vec-mismatch.stderr b/src/test/ui/match/match-vec-mismatch.stderr
index 2f1bbb7621659..a3523bb689e6b 100644
--- a/src/test/ui/match/match-vec-mismatch.stderr
+++ b/src/test/ui/match/match-vec-mismatch.stderr
@@ -10,7 +10,7 @@ error[E0529]: expected an array or slice, found `std::string::String`
 LL |         ['f', 'o', ..] => {}
    |         ^^^^^^^^^^^^^^ pattern cannot match with input type `std::string::String`
 
-error[E0527]: pattern requires 1 elements but array has 3
+error[E0527]: pattern requires 1 element but array has 3
   --> $DIR/match-vec-mismatch.rs:20:9
    |
 LL |         [0] => {},
diff --git a/src/test/ui/parser/require-parens-for-chained-comparison.rs b/src/test/ui/parser/require-parens-for-chained-comparison.rs
index 525be5d20e27f..3dcc0c8f3d496 100644
--- a/src/test/ui/parser/require-parens-for-chained-comparison.rs
+++ b/src/test/ui/parser/require-parens-for-chained-comparison.rs
@@ -11,8 +11,7 @@ fn main() {
     //~| ERROR: mismatched types
 
     f<X>();
-    //~^ ERROR: chained comparison operators require parentheses
-    //~| ERROR: binary operation `<` cannot be applied to type `fn() {f::<_>}`
+    //~^ ERROR chained comparison operators require parentheses
     //~| HELP: use `::<...>` instead of `<...>`
     //~| HELP: or use `(...)`
 }
diff --git a/src/test/ui/parser/require-parens-for-chained-comparison.stderr b/src/test/ui/parser/require-parens-for-chained-comparison.stderr
index 76e548de045aa..e927f4c32484e 100644
--- a/src/test/ui/parser/require-parens-for-chained-comparison.stderr
+++ b/src/test/ui/parser/require-parens-for-chained-comparison.stderr
@@ -37,17 +37,6 @@ LL |     false == 0 < 2;
    = note: expected type `bool`
               found type `{integer}`
 
-error[E0369]: binary operation `<` cannot be applied to type `fn() {f::<_>}`
-  --> $DIR/require-parens-for-chained-comparison.rs:13:6
-   |
-LL |     f<X>();
-   |     -^- X
-   |     |
-   |     fn() {f::<_>}
-   |
-   = note: an implementation of `std::cmp::PartialOrd` might be missing for `fn() {f::<_>}`
-
-error: aborting due to 6 previous errors
+error: aborting due to 5 previous errors
 
-Some errors have detailed explanations: E0308, E0369.
-For more information about an error, try `rustc --explain E0308`.
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/parser/trait-object-lifetime-parens.rs b/src/test/ui/parser/trait-object-lifetime-parens.rs
index 5bbda4296ca7e..c8b0eb684f33d 100644
--- a/src/test/ui/parser/trait-object-lifetime-parens.rs
+++ b/src/test/ui/parser/trait-object-lifetime-parens.rs
@@ -9,7 +9,6 @@ fn check<'a>() {
     let _: Box<('a) + Trait>;
     //~^ ERROR expected type, found `'a`
     //~| ERROR expected `:`, found `)`
-    //~| ERROR chained comparison operators require parentheses
 }
 
 fn main() {}
diff --git a/src/test/ui/parser/trait-object-lifetime-parens.stderr b/src/test/ui/parser/trait-object-lifetime-parens.stderr
index 7ffc26e9edead..319a308c0137c 100644
--- a/src/test/ui/parser/trait-object-lifetime-parens.stderr
+++ b/src/test/ui/parser/trait-object-lifetime-parens.stderr
@@ -16,15 +16,6 @@ error: expected `:`, found `)`
 LL |     let _: Box<('a) + Trait>;
    |                   ^ expected `:`
 
-error: chained comparison operators require parentheses
-  --> $DIR/trait-object-lifetime-parens.rs:9:15
-   |
-LL |     let _: Box<('a) + Trait>;
-   |               ^^^^^^^^^^^^^^^
-   |
-   = help: use `::<...>` instead of `<...>` if you meant to specify type arguments
-   = help: or use `(...)` if you meant to specify fn arguments
-
 error: expected type, found `'a`
   --> $DIR/trait-object-lifetime-parens.rs:9:17
    |
@@ -33,5 +24,5 @@ LL |     let _: Box<('a) + Trait>;
    |         |
    |         while parsing the type for `_`
 
-error: aborting due to 5 previous errors
+error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout b/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout
index 0fe02a9a34d18..ea06f6c1acaf9 100644
--- a/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout
+++ b/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout
@@ -2,40 +2,40 @@ PRINT-BANG INPUT (DISPLAY): struct M ($crate :: S) ;
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Ident {
         ident: "M",
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "$crate",
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Joint,
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Ident {
                 ident: "S",
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
         ],
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
 ]
 PRINT-ATTR INPUT (DISPLAY): struct A(crate::S);
@@ -43,39 +43,39 @@ PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ($crate :: S) ;
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Ident {
         ident: "A",
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "$crate",
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Joint,
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Ident {
                 ident: "S",
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
         ],
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
 ]
diff --git a/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout b/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout
index a499e1362ec0b..7ee8078b2c5d2 100644
--- a/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout
+++ b/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout
@@ -3,55 +3,55 @@ PRINT-ATTR RE-COLLECTED (DISPLAY): struct A (identity ! ($crate :: S)) ;
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Ident {
         ident: "A",
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "identity",
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Punct {
                 ch: '!',
                 spacing: Alone,
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Group {
                 delimiter: Parenthesis,
                 stream: TokenStream [
                     Ident {
                         ident: "$crate",
-                        span: #2 bytes(LO..HI),
+                        span: #3 bytes(LO..HI),
                     },
                     Punct {
                         ch: ':',
                         spacing: Joint,
-                        span: #2 bytes(LO..HI),
+                        span: #3 bytes(LO..HI),
                     },
                     Punct {
                         ch: ':',
                         spacing: Alone,
-                        span: #2 bytes(LO..HI),
+                        span: #3 bytes(LO..HI),
                     },
                     Ident {
                         ident: "S",
-                        span: #2 bytes(LO..HI),
+                        span: #3 bytes(LO..HI),
                     },
                 ],
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
         ],
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
 ]
 PRINT-ATTR INPUT (DISPLAY): struct B(identity!(::dollar_crate_external :: S));
@@ -59,54 +59,54 @@ PRINT-ATTR RE-COLLECTED (DISPLAY): struct B (identity ! ($crate :: S)) ;
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #7 bytes(LO..HI),
+        span: #8 bytes(LO..HI),
     },
     Ident {
         ident: "B",
-        span: #7 bytes(LO..HI),
+        span: #8 bytes(LO..HI),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "identity",
-                span: #7 bytes(LO..HI),
+                span: #8 bytes(LO..HI),
             },
             Punct {
                 ch: '!',
                 spacing: Alone,
-                span: #7 bytes(LO..HI),
+                span: #8 bytes(LO..HI),
             },
             Group {
                 delimiter: Parenthesis,
                 stream: TokenStream [
                     Ident {
                         ident: "$crate",
-                        span: #7 bytes(LO..HI),
+                        span: #8 bytes(LO..HI),
                     },
                     Punct {
                         ch: ':',
                         spacing: Joint,
-                        span: #7 bytes(LO..HI),
+                        span: #8 bytes(LO..HI),
                     },
                     Punct {
                         ch: ':',
                         spacing: Alone,
-                        span: #7 bytes(LO..HI),
+                        span: #8 bytes(LO..HI),
                     },
                     Ident {
                         ident: "S",
-                        span: #7 bytes(LO..HI),
+                        span: #8 bytes(LO..HI),
                     },
                 ],
-                span: #7 bytes(LO..HI),
+                span: #8 bytes(LO..HI),
             },
         ],
-        span: #7 bytes(LO..HI),
+        span: #8 bytes(LO..HI),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #7 bytes(LO..HI),
+        span: #8 bytes(LO..HI),
     },
 ]
diff --git a/src/test/ui/proc-macro/dollar-crate.stdout b/src/test/ui/proc-macro/dollar-crate.stdout
index da1d7549d0750..4f7e000265eb0 100644
--- a/src/test/ui/proc-macro/dollar-crate.stdout
+++ b/src/test/ui/proc-macro/dollar-crate.stdout
@@ -2,40 +2,40 @@ PRINT-BANG INPUT (DISPLAY): struct M ($crate :: S) ;
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Ident {
         ident: "M",
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "$crate",
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Joint,
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Ident {
                 ident: "S",
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
         ],
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
 ]
 PRINT-ATTR INPUT (DISPLAY): struct A(crate::S);
@@ -43,40 +43,40 @@ PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ($crate :: S) ;
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Ident {
         ident: "A",
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "$crate",
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Joint,
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Ident {
                 ident: "S",
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
         ],
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
 ]
 PRINT-DERIVE INPUT (DISPLAY): struct D(crate::S);
@@ -84,80 +84,80 @@ PRINT-DERIVE RE-COLLECTED (DISPLAY): struct D ($crate :: S) ;
 PRINT-DERIVE INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Ident {
         ident: "D",
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "$crate",
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Joint,
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Ident {
                 ident: "S",
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
         ],
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
 ]
 PRINT-BANG INPUT (DISPLAY): struct M ($crate :: S) ;
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #9 bytes(LO..HI),
+        span: #10 bytes(LO..HI),
     },
     Ident {
         ident: "M",
-        span: #9 bytes(LO..HI),
+        span: #10 bytes(LO..HI),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "$crate",
-                span: #9 bytes(LO..HI),
+                span: #10 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Joint,
-                span: #9 bytes(LO..HI),
+                span: #10 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: #9 bytes(LO..HI),
+                span: #10 bytes(LO..HI),
             },
             Ident {
                 ident: "S",
-                span: #9 bytes(LO..HI),
+                span: #10 bytes(LO..HI),
             },
         ],
-        span: #9 bytes(LO..HI),
+        span: #10 bytes(LO..HI),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #9 bytes(LO..HI),
+        span: #10 bytes(LO..HI),
     },
 ]
 PRINT-ATTR INPUT (DISPLAY): struct A(::dollar_crate_external::S);
@@ -165,40 +165,40 @@ PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ($crate :: S) ;
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #9 bytes(LO..HI),
+        span: #10 bytes(LO..HI),
     },
     Ident {
         ident: "A",
-        span: #9 bytes(LO..HI),
+        span: #10 bytes(LO..HI),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "$crate",
-                span: #9 bytes(LO..HI),
+                span: #10 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Joint,
-                span: #9 bytes(LO..HI),
+                span: #10 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: #9 bytes(LO..HI),
+                span: #10 bytes(LO..HI),
             },
             Ident {
                 ident: "S",
-                span: #9 bytes(LO..HI),
+                span: #10 bytes(LO..HI),
             },
         ],
-        span: #9 bytes(LO..HI),
+        span: #10 bytes(LO..HI),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #9 bytes(LO..HI),
+        span: #10 bytes(LO..HI),
     },
 ]
 PRINT-DERIVE INPUT (DISPLAY): struct D(::dollar_crate_external::S);
@@ -206,39 +206,39 @@ PRINT-DERIVE RE-COLLECTED (DISPLAY): struct D ($crate :: S) ;
 PRINT-DERIVE INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #9 bytes(LO..HI),
+        span: #10 bytes(LO..HI),
     },
     Ident {
         ident: "D",
-        span: #9 bytes(LO..HI),
+        span: #10 bytes(LO..HI),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "$crate",
-                span: #9 bytes(LO..HI),
+                span: #10 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Joint,
-                span: #9 bytes(LO..HI),
+                span: #10 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: #9 bytes(LO..HI),
+                span: #10 bytes(LO..HI),
             },
             Ident {
                 ident: "S",
-                span: #9 bytes(LO..HI),
+                span: #10 bytes(LO..HI),
             },
         ],
-        span: #9 bytes(LO..HI),
+        span: #10 bytes(LO..HI),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #9 bytes(LO..HI),
+        span: #10 bytes(LO..HI),
     },
 ]
diff --git a/src/test/ui/test-shadowing/auxiliary/test_macro.rs b/src/test/ui/test-attrs/auxiliary/test_macro.rs
similarity index 100%
rename from src/test/ui/test-shadowing/auxiliary/test_macro.rs
rename to src/test/ui/test-attrs/auxiliary/test_macro.rs
diff --git a/src/test/ui/test-attrs/decl-macro-test.rs b/src/test/ui/test-attrs/decl-macro-test.rs
new file mode 100644
index 0000000000000..fcbe9f49e5564
--- /dev/null
+++ b/src/test/ui/test-attrs/decl-macro-test.rs
@@ -0,0 +1,22 @@
+// Check that declarative macros can declare tests
+
+// check-pass
+// compile-flags: --test
+
+#![feature(decl_macro)]
+
+macro create_test() {
+    #[test]
+    fn test() {}
+}
+
+macro create_module_test() {
+    mod x {
+        #[test]
+        fn test() {}
+    }
+}
+
+create_test!();
+create_test!();
+create_module_test!();
diff --git a/src/test/ui/inaccessible-test-modules.rs b/src/test/ui/test-attrs/inaccessible-test-modules.rs
similarity index 56%
rename from src/test/ui/inaccessible-test-modules.rs
rename to src/test/ui/test-attrs/inaccessible-test-modules.rs
index 7095ec290f8f2..f5b3479379480 100644
--- a/src/test/ui/inaccessible-test-modules.rs
+++ b/src/test/ui/test-attrs/inaccessible-test-modules.rs
@@ -2,8 +2,8 @@
 
 // the `--test` harness creates modules with these textual names, but
 // they should be inaccessible from normal code.
-use __test as x; //~ ERROR unresolved import `__test`
-use __test_reexports as y; //~ ERROR unresolved import `__test_reexports`
+use main as x; //~ ERROR unresolved import `main`
+use test as y; //~ ERROR unresolved import `test`
 
 #[test]
 fn baz() {}
diff --git a/src/test/ui/test-attrs/inaccessible-test-modules.stderr b/src/test/ui/test-attrs/inaccessible-test-modules.stderr
new file mode 100644
index 0000000000000..a94ea1e79bc51
--- /dev/null
+++ b/src/test/ui/test-attrs/inaccessible-test-modules.stderr
@@ -0,0 +1,21 @@
+error[E0432]: unresolved import `main`
+  --> $DIR/inaccessible-test-modules.rs:5:5
+   |
+LL | use main as x;
+   |     ----^^^^^
+   |     |
+   |     no `main` in the root
+   |     help: a similar name exists in the module: `main`
+
+error[E0432]: unresolved import `test`
+  --> $DIR/inaccessible-test-modules.rs:6:5
+   |
+LL | use test as y;
+   |     ----^^^^^
+   |     |
+   |     no `test` in the root
+   |     help: a similar name exists in the module: `test`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0432`.
diff --git a/src/test/ui/test-allow-fail-attr.rs b/src/test/ui/test-attrs/test-allow-fail-attr.rs
similarity index 100%
rename from src/test/ui/test-allow-fail-attr.rs
rename to src/test/ui/test-attrs/test-allow-fail-attr.rs
diff --git a/src/test/ui/test-attr-non-associated-functions.rs b/src/test/ui/test-attrs/test-attr-non-associated-functions.rs
similarity index 100%
rename from src/test/ui/test-attr-non-associated-functions.rs
rename to src/test/ui/test-attrs/test-attr-non-associated-functions.rs
diff --git a/src/test/ui/test-attr-non-associated-functions.stderr b/src/test/ui/test-attrs/test-attr-non-associated-functions.stderr
similarity index 100%
rename from src/test/ui/test-attr-non-associated-functions.stderr
rename to src/test/ui/test-attrs/test-attr-non-associated-functions.stderr
diff --git a/src/test/ui/test-shadowing/test-cant-be-shadowed.rs b/src/test/ui/test-attrs/test-cant-be-shadowed.rs
similarity index 100%
rename from src/test/ui/test-shadowing/test-cant-be-shadowed.rs
rename to src/test/ui/test-attrs/test-cant-be-shadowed.rs
diff --git a/src/test/ui/test-fn-signature-verification-for-explicit-return-type.rs b/src/test/ui/test-attrs/test-fn-signature-verification-for-explicit-return-type.rs
similarity index 100%
rename from src/test/ui/test-fn-signature-verification-for-explicit-return-type.rs
rename to src/test/ui/test-attrs/test-fn-signature-verification-for-explicit-return-type.rs
diff --git a/src/test/ui/test-main-not-dead-attr.rs b/src/test/ui/test-attrs/test-main-not-dead-attr.rs
similarity index 100%
rename from src/test/ui/test-main-not-dead-attr.rs
rename to src/test/ui/test-attrs/test-main-not-dead-attr.rs
diff --git a/src/test/ui/test-main-not-dead.rs b/src/test/ui/test-attrs/test-main-not-dead.rs
similarity index 100%
rename from src/test/ui/test-main-not-dead.rs
rename to src/test/ui/test-attrs/test-main-not-dead.rs
diff --git a/src/test/ui/test-on-macro.rs b/src/test/ui/test-attrs/test-on-macro.rs
similarity index 100%
rename from src/test/ui/test-on-macro.rs
rename to src/test/ui/test-attrs/test-on-macro.rs
diff --git a/src/test/ui/test-on-macro.stderr b/src/test/ui/test-attrs/test-on-macro.stderr
similarity index 100%
rename from src/test/ui/test-on-macro.stderr
rename to src/test/ui/test-attrs/test-on-macro.stderr
diff --git a/src/test/ui/test-runner-hides-buried-main.rs b/src/test/ui/test-attrs/test-runner-hides-buried-main.rs
similarity index 100%
rename from src/test/ui/test-runner-hides-buried-main.rs
rename to src/test/ui/test-attrs/test-runner-hides-buried-main.rs
diff --git a/src/test/ui/test-runner-hides-main.rs b/src/test/ui/test-attrs/test-runner-hides-main.rs
similarity index 100%
rename from src/test/ui/test-runner-hides-main.rs
rename to src/test/ui/test-attrs/test-runner-hides-main.rs
diff --git a/src/test/ui/test-runner-hides-start.rs b/src/test/ui/test-attrs/test-runner-hides-start.rs
similarity index 100%
rename from src/test/ui/test-runner-hides-start.rs
rename to src/test/ui/test-attrs/test-runner-hides-start.rs
diff --git a/src/test/ui/test-should-fail-good-message.rs b/src/test/ui/test-attrs/test-should-fail-good-message.rs
similarity index 100%
rename from src/test/ui/test-should-fail-good-message.rs
rename to src/test/ui/test-attrs/test-should-fail-good-message.rs
diff --git a/src/test/ui/test-should-panic-attr.rs b/src/test/ui/test-attrs/test-should-panic-attr.rs
similarity index 100%
rename from src/test/ui/test-should-panic-attr.rs
rename to src/test/ui/test-attrs/test-should-panic-attr.rs
diff --git a/src/test/ui/test-should-panic-attr.stderr b/src/test/ui/test-attrs/test-should-panic-attr.stderr
similarity index 100%
rename from src/test/ui/test-should-panic-attr.stderr
rename to src/test/ui/test-attrs/test-should-panic-attr.stderr
diff --git a/src/test/ui/test-vs-cfg-test.rs b/src/test/ui/test-attrs/test-vs-cfg-test.rs
similarity index 100%
rename from src/test/ui/test-vs-cfg-test.rs
rename to src/test/ui/test-attrs/test-vs-cfg-test.rs
diff --git a/src/test/ui/test-warns-dead-code.rs b/src/test/ui/test-attrs/test-warns-dead-code.rs
similarity index 100%
rename from src/test/ui/test-warns-dead-code.rs
rename to src/test/ui/test-attrs/test-warns-dead-code.rs
diff --git a/src/test/ui/test-warns-dead-code.stderr b/src/test/ui/test-attrs/test-warns-dead-code.stderr
similarity index 100%
rename from src/test/ui/test-warns-dead-code.stderr
rename to src/test/ui/test-attrs/test-warns-dead-code.stderr
diff --git a/src/tools/rustbook/src/main.rs b/src/tools/rustbook/src/main.rs
index 95530b210afd6..d5dc9a79b5acb 100644
--- a/src/tools/rustbook/src/main.rs
+++ b/src/tools/rustbook/src/main.rs
@@ -1,18 +1,19 @@
-use clap::{crate_version};
+use clap::crate_version;
 
 use std::env;
 use std::path::{Path, PathBuf};
 
-use clap::{App, ArgMatches, SubCommand, AppSettings};
+use clap::{App, AppSettings, ArgMatches, SubCommand};
 
+use mdbook::errors::Result as Result3;
 use mdbook::MDBook;
-use mdbook::errors::{Result as Result3};
 
+#[cfg(feature = "linkcheck")]
+use failure::Error;
 #[cfg(feature = "linkcheck")]
 use mdbook::renderer::RenderContext;
 #[cfg(feature = "linkcheck")]
 use mdbook_linkcheck::{self, errors::BrokenLinks};
-use failure::Error;
 
 fn main() {
     let d_message = "-d, --dest-dir=[dest-dir]
@@ -21,18 +22,22 @@ fn main() {
 'A directory for your book{n}(Defaults to Current Directory when omitted)'";
 
     let matches = App::new("rustbook")
-                    .about("Build a book with mdBook")
-                    .author("Steve Klabnik <steve@steveklabnik.com>")
-                    .version(&*format!("v{}", crate_version!()))
-                    .setting(AppSettings::SubcommandRequired)
-                    .subcommand(SubCommand::with_name("build")
-                        .about("Build the book from the markdown files")
-                        .arg_from_usage(d_message)
-                        .arg_from_usage(dir_message))
-                    .subcommand(SubCommand::with_name("linkcheck")
-                        .about("Run linkcheck with mdBook 3")
-                        .arg_from_usage(dir_message))
-                    .get_matches();
+        .about("Build a book with mdBook")
+        .author("Steve Klabnik <steve@steveklabnik.com>")
+        .version(&*format!("v{}", crate_version!()))
+        .setting(AppSettings::SubcommandRequired)
+        .subcommand(
+            SubCommand::with_name("build")
+                .about("Build the book from the markdown files")
+                .arg_from_usage(d_message)
+                .arg_from_usage(dir_message),
+        )
+        .subcommand(
+            SubCommand::with_name("linkcheck")
+                .about("Run linkcheck with mdBook 3")
+                .arg_from_usage(dir_message),
+        )
+        .get_matches();
 
     // Check which subcomamnd the user ran...
     match matches.subcommand() {
@@ -46,23 +51,44 @@ fn main() {
 
                 ::std::process::exit(101);
             }
-        },
+        }
         ("linkcheck", Some(sub_matches)) => {
-            if let Err(err) = linkcheck(sub_matches) {
-                eprintln!("Error: {}", err);
-
-                #[cfg(feature = "linkcheck")]
-                {
-                    if let Ok(broken_links) = err.downcast::<BrokenLinks>() {
-                        for cause in broken_links.links().iter() {
-                            eprintln!("\tCaused By: {}", cause);
-                        }
+            #[cfg(feature = "linkcheck")]
+            {
+                if let Err(err) = linkcheck(sub_matches) {
+                    eprintln!("Error: {}", err);
+
+                    // HACK: ignore timeouts
+                    let actually_broken = err
+                        .downcast::<BrokenLinks>()
+                        .map(|broken_links| {
+                            broken_links
+                                .links()
+                                .iter()
+                                .inspect(|cause| eprintln!("\tCaused By: {}", cause))
+                                .fold(false, |already_broken, cause| {
+                                    already_broken || !format!("{}", cause).contains("timed out")
+                                })
+                        })
+                        .unwrap_or(false);
+
+                    if actually_broken {
+                        std::process::exit(101);
+                    } else {
+                        std::process::exit(0);
                     }
                 }
+            }
 
-                ::std::process::exit(101);
+            #[cfg(not(feature = "linkcheck"))]
+            {
+                // This avoids the `unused_binding` lint.
+                println!(
+                    "mdbook-linkcheck is disabled, but arguments were passed: {:?}",
+                    sub_matches
+                );
             }
-        },
+        }
         (_, _) => unreachable!(),
     };
 }
@@ -77,12 +103,6 @@ pub fn linkcheck(args: &ArgMatches<'_>) -> Result<(), Error> {
     mdbook_linkcheck::check_links(&render_ctx)
 }
 
-#[cfg(not(feature = "linkcheck"))]
-pub fn linkcheck(_args: &ArgMatches<'_>) -> Result<(), Error> {
-    println!("mdbook-linkcheck is disabled.");
-    Ok(())
-}
-
 // Build command implementation
 pub fn build(args: &ArgMatches<'_>) -> Result3<()> {
     let book_dir = get_book_dir(args);