From 63c471e10227944268ddf67973a584d68e314a8d Mon Sep 17 00:00:00 2001
From: Alex Crichton <alex@alexcrichton.com>
Date: Thu, 27 Sep 2018 15:50:17 -0700
Subject: [PATCH 01/30] rustc: Tweak filenames encoded into metadata

This commit is a fix for #54408 where on nightly right now whenever
generics are inlined the path name listed for the inlined function's
debuginfo is a relative path to the cwd, which surely doesn't exist!
Previously on beta/stable the debuginfo mentioned an absolute path which
still didn't exist, but more predictably didn't exist.

The change between stable/nightly is that nightly is now compiled with
`--remap-path-prefix` to give a deterministic prefix to all
rustc-generated paths in debuginfo. By using `--remap-path-prefix` the
previous logic would recognize that the cwd was remapped, causing the
original relative path name of the standard library to get emitted. If
`--remap-path-prefix` *wasn't* passed in then the logic would create an
absolute path name and then create a new source file entry.

The fix in this commit is to apply the "recreate the source file entry
with an absolute path" logic a bit more aggresively. If the source
file's name was remapped then we don't touch it, but otherwise we always
take the working dir (which may have been remapped) and then join it to
the file to ensure that we process all relative file names as well.

The end result is that the standard library should have an absolute path
for all file names in debuginfo (using our `--remap-path-prefix`
argument) as it does on stable after this patch.

Closes #54408
---
 src/librustc_metadata/encoder.rs              | 42 ++++++++-----------
 .../auxiliary/xcrate-generic.rs               | 16 +++++++
 .../remap_path_prefix/xcrate-generic.rs       | 25 +++++++++++
 3 files changed, 59 insertions(+), 24 deletions(-)
 create mode 100644 src/test/codegen/remap_path_prefix/auxiliary/xcrate-generic.rs
 create mode 100644 src/test/codegen/remap_path_prefix/xcrate-generic.rs

diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs
index c36ae02ab54e0..292bf1daee22f 100644
--- a/src/librustc_metadata/encoder.rs
+++ b/src/librustc_metadata/encoder.rs
@@ -340,7 +340,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         let source_map = self.tcx.sess.source_map();
         let all_source_files = source_map.files();
 
-        let (working_dir, working_dir_was_remapped) = self.tcx.sess.working_dir.clone();
+        let (working_dir, _cwd_remapped) = self.tcx.sess.working_dir.clone();
 
         let adapted = all_source_files.iter()
             .filter(|source_file| {
@@ -349,32 +349,26 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 !source_file.is_imported()
             })
             .map(|source_file| {
-                // When exporting SourceFiles, we expand all paths to absolute
-                // paths because any relative paths are potentially relative to
-                // a wrong directory.
-                // However, if a path has been modified via
-                // `--remap-path-prefix` we assume the user has already set
-                // things up the way they want and don't touch the path values
-                // anymore.
                 match source_file.name {
+                    // This path of this SourceFile has been modified by
+                    // path-remapping, so we use it verbatim (and avoid
+                    // cloning the whole map in the process).
+                    _  if source_file.name_was_remapped => source_file.clone(),
+
+                    // Otherwise expand all paths to absolute paths because
+                    // any relative paths are potentially relative to a
+                    // wrong directory.
                     FileName::Real(ref name) => {
-                        if source_file.name_was_remapped ||
-                        (name.is_relative() && working_dir_was_remapped) {
-                            // This path of this SourceFile has been modified by
-                            // path-remapping, so we use it verbatim (and avoid cloning
-                            // the whole map in the process).
-                            source_file.clone()
-                        } else {
-                            let mut adapted = (**source_file).clone();
-                            adapted.name = Path::new(&working_dir).join(name).into();
-                            adapted.name_hash = {
-                                let mut hasher: StableHasher<u128> = StableHasher::new();
-                                adapted.name.hash(&mut hasher);
-                                hasher.finish()
-                            };
-                            Lrc::new(adapted)
-                        }
+                        let mut adapted = (**source_file).clone();
+                        adapted.name = Path::new(&working_dir).join(name).into();
+                        adapted.name_hash = {
+                            let mut hasher: StableHasher<u128> = StableHasher::new();
+                            adapted.name.hash(&mut hasher);
+                            hasher.finish()
+                        };
+                        Lrc::new(adapted)
                     },
+
                     // expanded code, not from a file
                     _ => source_file.clone(),
                 }
diff --git a/src/test/codegen/remap_path_prefix/auxiliary/xcrate-generic.rs b/src/test/codegen/remap_path_prefix/auxiliary/xcrate-generic.rs
new file mode 100644
index 0000000000000..6c477a407812a
--- /dev/null
+++ b/src/test/codegen/remap_path_prefix/auxiliary/xcrate-generic.rs
@@ -0,0 +1,16 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-tidy-linelength
+// compile-flags: -g  --remap-path-prefix={{cwd}}=/the/aux-cwd --remap-path-prefix={{src-base}}/remap_path_prefix/auxiliary=/the/aux-src
+
+#![crate_type = "lib"]
+
+pub fn foo<T>() {}
diff --git a/src/test/codegen/remap_path_prefix/xcrate-generic.rs b/src/test/codegen/remap_path_prefix/xcrate-generic.rs
new file mode 100644
index 0000000000000..f206df9813165
--- /dev/null
+++ b/src/test/codegen/remap_path_prefix/xcrate-generic.rs
@@ -0,0 +1,25 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-windows
+// ignore-tidy-linelength
+// compile-flags: -g -C metadata=foo -C no-prepopulate-passes
+// aux-build:xcrate-generic.rs
+
+#![crate_type = "lib"]
+
+extern crate xcrate_generic;
+
+pub fn foo() {
+    xcrate_generic::foo::<u32>();
+}
+
+// Here we check that local debuginfo is mapped correctly.
+// CHECK: !DIFile(filename: "/the/aux-src/xcrate-generic.rs", directory: "")

From f81d1dd29401cd586a13b77db729f42076f07902 Mon Sep 17 00:00:00 2001
From: Wesley Wiser <wwiser@gmail.com>
Date: Tue, 11 Sep 2018 21:35:08 -0400
Subject: [PATCH 02/30] Rewrite the `UnconditionalRecursion` lint to use MIR

Part of #51002
---
 src/librustc/lint/builtin.rs                  |   7 +
 src/librustc_lint/builtin.rs                  | 277 ------------------
 src/librustc_lint/lib.rs                      |   1 -
 src/librustc_mir/build/mod.rs                 |   4 +
 src/librustc_mir/lib.rs                       |   1 +
 src/librustc_mir/lints.rs                     | 156 ++++++++++
 .../ui/did_you_mean/issue-31424.nll.stderr    |  14 +-
 src/test/ui/did_you_mean/issue-31424.rs       |   1 +
 src/test/ui/did_you_mean/issue-31424.stderr   |  14 +-
 src/test/ui/nll/issue-51191.rs                |   1 +
 src/test/ui/nll/issue-51191.stderr            |  22 +-
 ...-bound-on-closure-outlives-call.nll.stderr |  14 +-
 .../region-bound-on-closure-outlives-call.rs  |   1 +
 ...gion-bound-on-closure-outlives-call.stderr |  14 +-
 14 files changed, 240 insertions(+), 287 deletions(-)
 create mode 100644 src/librustc_mir/lints.rs

diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs
index 64056ece98770..cc493884f8488 100644
--- a/src/librustc/lint/builtin.rs
+++ b/src/librustc/lint/builtin.rs
@@ -233,6 +233,12 @@ declare_lint! {
     "detect mut variables which don't need to be mutable"
 }
 
+declare_lint! {
+    pub UNCONDITIONAL_RECURSION,
+    Warn,
+    "functions that cannot return without calling themselves"
+}
+
 declare_lint! {
     pub SINGLE_USE_LIFETIMES,
     Allow,
@@ -396,6 +402,7 @@ impl LintPass for HardwiredLints {
             DEPRECATED,
             UNUSED_UNSAFE,
             UNUSED_MUT,
+            UNCONDITIONAL_RECURSION,
             SINGLE_USE_LIFETIMES,
             UNUSED_LIFETIMES,
             UNUSED_LABELS,
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 7eda6e94dd071..4ca51e8ce1764 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -30,10 +30,7 @@
 
 use rustc::hir::def::Def;
 use rustc::hir::def_id::DefId;
-use rustc::cfg;
-use rustc::ty::subst::Substs;
 use rustc::ty::{self, Ty};
-use rustc::traits;
 use hir::Node;
 use util::nodemap::NodeSet;
 use lint::{LateContext, LintContext, LintArray};
@@ -844,279 +841,6 @@ impl EarlyLintPass for UnusedDocComment {
     }
 }
 
-declare_lint! {
-    pub UNCONDITIONAL_RECURSION,
-    Warn,
-    "functions that cannot return without calling themselves"
-}
-
-#[derive(Copy, Clone)]
-pub struct UnconditionalRecursion;
-
-
-impl LintPass for UnconditionalRecursion {
-    fn get_lints(&self) -> LintArray {
-        lint_array![UNCONDITIONAL_RECURSION]
-    }
-}
-
-impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnconditionalRecursion {
-    fn check_fn(&mut self,
-                cx: &LateContext,
-                fn_kind: FnKind,
-                _: &hir::FnDecl,
-                body: &hir::Body,
-                sp: Span,
-                id: ast::NodeId) {
-        let method = match fn_kind {
-            FnKind::ItemFn(..) => None,
-            FnKind::Method(..) => {
-                Some(cx.tcx.associated_item(cx.tcx.hir.local_def_id(id)))
-            }
-            // closures can't recur, so they don't matter.
-            FnKind::Closure(_) => return,
-        };
-
-        // Walk through this function (say `f`) looking to see if
-        // every possible path references itself, i.e. the function is
-        // called recursively unconditionally. This is done by trying
-        // to find a path from the entry node to the exit node that
-        // *doesn't* call `f` by traversing from the entry while
-        // pretending that calls of `f` are sinks (i.e. ignoring any
-        // exit edges from them).
-        //
-        // NB. this has an edge case with non-returning statements,
-        // like `loop {}` or `panic!()`: control flow never reaches
-        // the exit node through these, so one can have a function
-        // that never actually calls itself but is still picked up by
-        // this lint:
-        //
-        //     fn f(cond: bool) {
-        //         if !cond { panic!() } // could come from `assert!(cond)`
-        //         f(false)
-        //     }
-        //
-        // In general, functions of that form may be able to call
-        // itself a finite number of times and then diverge. The lint
-        // considers this to be an error for two reasons, (a) it is
-        // easier to implement, and (b) it seems rare to actually want
-        // to have behaviour like the above, rather than
-        // e.g. accidentally recursing after an assert.
-
-        let cfg = cfg::CFG::new(cx.tcx, &body);
-
-        let mut work_queue = vec![cfg.entry];
-        let mut reached_exit_without_self_call = false;
-        let mut self_call_spans = vec![];
-        let mut visited = FxHashSet::default();
-
-        while let Some(idx) = work_queue.pop() {
-            if idx == cfg.exit {
-                // found a path!
-                reached_exit_without_self_call = true;
-                break;
-            }
-
-            let cfg_id = idx.node_id();
-            if visited.contains(&cfg_id) {
-                // already done
-                continue;
-            }
-            visited.insert(cfg_id);
-
-            // is this a recursive call?
-            let local_id = cfg.graph.node_data(idx).id();
-            if local_id != hir::DUMMY_ITEM_LOCAL_ID {
-                let node_id = cx.tcx.hir.hir_to_node_id(hir::HirId {
-                    owner: body.value.hir_id.owner,
-                    local_id
-                });
-                let self_recursive = match method {
-                    Some(ref method) => expr_refers_to_this_method(cx, method, node_id),
-                    None => expr_refers_to_this_fn(cx, id, node_id),
-                };
-                if self_recursive {
-                    self_call_spans.push(cx.tcx.hir.span(node_id));
-                    // this is a self call, so we shouldn't explore past
-                    // this node in the CFG.
-                    continue;
-                }
-            }
-
-            // add the successors of this node to explore the graph further.
-            for (_, edge) in cfg.graph.outgoing_edges(idx) {
-                let target_idx = edge.target();
-                let target_cfg_id = target_idx.node_id();
-                if !visited.contains(&target_cfg_id) {
-                    work_queue.push(target_idx)
-                }
-            }
-        }
-
-        // Check the number of self calls because a function that
-        // doesn't return (e.g. calls a `-> !` function or `loop { /*
-        // no break */ }`) shouldn't be linted unless it actually
-        // recurs.
-        if !reached_exit_without_self_call && !self_call_spans.is_empty() {
-            let sp = cx.tcx.sess.source_map().def_span(sp);
-            let mut db = cx.struct_span_lint(UNCONDITIONAL_RECURSION,
-                                             sp,
-                                             "function cannot return without recursing");
-            db.span_label(sp, "cannot return without recursing");
-            // offer some help to the programmer.
-            for call in &self_call_spans {
-                db.span_label(*call, "recursive call site");
-            }
-            db.help("a `loop` may express intention better if this is on purpose");
-            db.emit();
-        }
-
-        // all done
-        return;
-
-        // Functions for identifying if the given Expr NodeId `id`
-        // represents a call to the function `fn_id`/method `method`.
-
-        fn expr_refers_to_this_fn(cx: &LateContext, fn_id: ast::NodeId, id: ast::NodeId) -> bool {
-            match cx.tcx.hir.get(id) {
-                Node::Expr(&hir::Expr { node: hir::ExprKind::Call(ref callee, _), .. }) => {
-                    let def = if let hir::ExprKind::Path(ref qpath) = callee.node {
-                        cx.tables.qpath_def(qpath, callee.hir_id)
-                    } else {
-                        return false;
-                    };
-                    match def {
-                        Def::Local(..) | Def::Upvar(..) => false,
-                        _ => def.def_id() == cx.tcx.hir.local_def_id(fn_id)
-                    }
-                }
-                _ => false,
-            }
-        }
-
-        // Check if the expression `id` performs a call to `method`.
-        fn expr_refers_to_this_method(cx: &LateContext,
-                                      method: &ty::AssociatedItem,
-                                      id: ast::NodeId)
-                                      -> bool {
-            use rustc::ty::adjustment::*;
-
-            // Ignore non-expressions.
-            let expr = if let Node::Expr(e) = cx.tcx.hir.get(id) {
-                e
-            } else {
-                return false;
-            };
-
-            // Check for overloaded autoderef method calls.
-            let mut source = cx.tables.expr_ty(expr);
-            for adjustment in cx.tables.expr_adjustments(expr) {
-                if let Adjust::Deref(Some(deref)) = adjustment.kind {
-                    let (def_id, substs) = deref.method_call(cx.tcx, source);
-                    if method_call_refers_to_method(cx, method, def_id, substs, id) {
-                        return true;
-                    }
-                }
-                source = adjustment.target;
-            }
-
-            // Check for method calls and overloaded operators.
-            if cx.tables.is_method_call(expr) {
-                let hir_id = cx.tcx.hir.definitions().node_to_hir_id(id);
-                if let Some(def) = cx.tables.type_dependent_defs().get(hir_id) {
-                    let def_id = def.def_id();
-                    let substs = cx.tables.node_substs(hir_id);
-                    if method_call_refers_to_method(cx, method, def_id, substs, id) {
-                        return true;
-                    }
-                } else {
-                    cx.tcx.sess.delay_span_bug(expr.span,
-                                               "no type-dependent def for method call");
-                }
-            }
-
-            // Check for calls to methods via explicit paths (e.g. `T::method()`).
-            match expr.node {
-                hir::ExprKind::Call(ref callee, _) => {
-                    let def = if let hir::ExprKind::Path(ref qpath) = callee.node {
-                        cx.tables.qpath_def(qpath, callee.hir_id)
-                    } else {
-                        return false;
-                    };
-                    match def {
-                        Def::Method(def_id) => {
-                            let substs = cx.tables.node_substs(callee.hir_id);
-                            method_call_refers_to_method(cx, method, def_id, substs, id)
-                        }
-                        _ => false,
-                    }
-                }
-                _ => false,
-            }
-        }
-
-        // Check if the method call to the method with the ID `callee_id`
-        // and instantiated with `callee_substs` refers to method `method`.
-        fn method_call_refers_to_method<'a, 'tcx>(cx: &LateContext<'a, 'tcx>,
-                                                  method: &ty::AssociatedItem,
-                                                  callee_id: DefId,
-                                                  callee_substs: &Substs<'tcx>,
-                                                  expr_id: ast::NodeId)
-                                                  -> bool {
-            let tcx = cx.tcx;
-            let callee_item = tcx.associated_item(callee_id);
-
-            match callee_item.container {
-                // This is an inherent method, so the `def_id` refers
-                // directly to the method definition.
-                ty::ImplContainer(_) => callee_id == method.def_id,
-
-                // A trait method, from any number of possible sources.
-                // Attempt to select a concrete impl before checking.
-                ty::TraitContainer(trait_def_id) => {
-                    let trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, callee_substs);
-                    let trait_ref = ty::Binder::bind(trait_ref);
-                    let span = tcx.hir.span(expr_id);
-                    let obligation =
-                        traits::Obligation::new(traits::ObligationCause::misc(span, expr_id),
-                                                cx.param_env,
-                                                trait_ref.to_poly_trait_predicate());
-
-                    tcx.infer_ctxt().enter(|infcx| {
-                        let mut selcx = traits::SelectionContext::new(&infcx);
-                        match selcx.select(&obligation) {
-                            // The method comes from a `T: Trait` bound.
-                            // If `T` is `Self`, then this call is inside
-                            // a default method definition.
-                            Ok(Some(traits::VtableParam(_))) => {
-                                let on_self = trait_ref.self_ty().is_self();
-                                // We can only be recursing in a default
-                                // method if we're being called literally
-                                // on the `Self` type.
-                                on_self && callee_id == method.def_id
-                            }
-
-                            // The `impl` is known, so we check that with a
-                            // special case:
-                            Ok(Some(traits::VtableImpl(vtable_impl))) => {
-                                let container = ty::ImplContainer(vtable_impl.impl_def_id);
-                                // It matches if it comes from the same impl,
-                                // and has the same method name.
-                                container == method.container &&
-                                callee_item.ident.name == method.ident.name
-                            }
-
-                            // There's no way to know if this call is
-                            // recursive, so we assume it's not.
-                            _ => false,
-                        }
-                    })
-                }
-            }
-        }
-    }
-}
-
 declare_lint! {
     PLUGIN_AS_LIBRARY,
     Warn,
@@ -1783,7 +1507,6 @@ impl LintPass for SoftLints {
             MISSING_DEBUG_IMPLEMENTATIONS,
             ANONYMOUS_PARAMETERS,
             UNUSED_DOC_COMMENTS,
-            UNCONDITIONAL_RECURSION,
             PLUGIN_AS_LIBRARY,
             PRIVATE_NO_MANGLE_FNS,
             PRIVATE_NO_MANGLE_STATICS,
diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs
index 98d4c87dc3b5e..63ecb052924c4 100644
--- a/src/librustc_lint/lib.rs
+++ b/src/librustc_lint/lib.rs
@@ -144,7 +144,6 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
         UnusedAllocation: UnusedAllocation,
         MissingCopyImplementations: MissingCopyImplementations,
         UnstableFeatures: UnstableFeatures,
-        UnconditionalRecursion: UnconditionalRecursion,
         InvalidNoMangleItems: InvalidNoMangleItems,
         PluginAsLibrary: PluginAsLibrary,
         MutableTransmutes: MutableTransmutes,
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index 3dbd3bbb41573..b899918d2949e 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -35,6 +35,8 @@ use syntax_pos::Span;
 use transform::MirSource;
 use util as mir_util;
 
+use super::lints;
+
 /// Construct the MIR for a given def-id.
 pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'tcx> {
     let id = tcx.hir.as_local_node_id(def_id).unwrap();
@@ -176,6 +178,8 @@ pub fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Mir<'t
         mir_util::dump_mir(tcx, None, "mir_map", &0,
                            MirSource::item(def_id), &mir, |_, _| Ok(()) );
 
+        lints::check(tcx, &mir, def_id);
+
         mir
     })
 }
diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs
index b3ef9eab8017d..257e4d1db80c3 100644
--- a/src/librustc_mir/lib.rs
+++ b/src/librustc_mir/lib.rs
@@ -79,6 +79,7 @@ mod borrow_check;
 mod build;
 mod dataflow;
 mod hair;
+mod lints;
 mod shim;
 pub mod transform;
 pub mod util;
diff --git a/src/librustc_mir/lints.rs b/src/librustc_mir/lints.rs
new file mode 100644
index 0000000000000..4c7938504c10f
--- /dev/null
+++ b/src/librustc_mir/lints.rs
@@ -0,0 +1,156 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use rustc_data_structures::bit_set::BitSet;
+use rustc::hir::def_id::DefId;
+use rustc::hir::intravisit::FnKind;
+use rustc::hir::map::blocks::FnLikeNode;
+use rustc::lint::builtin::UNCONDITIONAL_RECURSION;
+use rustc::mir::{self, Mir, TerminatorKind};
+use rustc::ty::{AssociatedItem, AssociatedItemContainer, Instance, TyCtxt, TyKind};
+use rustc::ty::subst::Substs;
+
+pub fn check(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+             mir: &Mir<'tcx>,
+             def_id: DefId) {
+    let node_id = tcx.hir.as_local_node_id(def_id).unwrap();
+
+    if let Some(fn_like_node) = FnLikeNode::from_node(tcx.hir.get(node_id)) {
+        check_fn_for_unconditional_recursion(tcx, fn_like_node.kind(), mir, def_id);
+    }
+}
+
+fn check_fn_for_unconditional_recursion(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                        fn_kind: FnKind,
+                                        mir: &Mir<'tcx>,
+                                        def_id: DefId) {
+    if let FnKind::Closure(_) = fn_kind {
+        // closures can't recur, so they don't matter.
+        return;
+    }
+
+    //FIXME(#54444) rewrite this lint to use the dataflow framework
+
+    // Walk through this function (say `f`) looking to see if
+    // every possible path references itself, i.e. the function is
+    // called recursively unconditionally. This is done by trying
+    // to find a path from the entry node to the exit node that
+    // *doesn't* call `f` by traversing from the entry while
+    // pretending that calls of `f` are sinks (i.e. ignoring any
+    // exit edges from them).
+    //
+    // NB. this has an edge case with non-returning statements,
+    // like `loop {}` or `panic!()`: control flow never reaches
+    // the exit node through these, so one can have a function
+    // that never actually calls itself but is still picked up by
+    // this lint:
+    //
+    //     fn f(cond: bool) {
+    //         if !cond { panic!() } // could come from `assert!(cond)`
+    //         f(false)
+    //     }
+    //
+    // In general, functions of that form may be able to call
+    // itself a finite number of times and then diverge. The lint
+    // considers this to be an error for two reasons, (a) it is
+    // easier to implement, and (b) it seems rare to actually want
+    // to have behaviour like the above, rather than
+    // e.g. accidentally recursing after an assert.
+
+    let basic_blocks = mir.basic_blocks();
+    let mut reachable_without_self_call_queue = vec![mir::START_BLOCK];
+    let mut reached_exit_without_self_call = false;
+    let mut self_call_locations = vec![];
+    let mut visited = BitSet::new_empty(basic_blocks.len());
+
+    let param_env = tcx.param_env(def_id);
+    let trait_substs_count =
+        match tcx.opt_associated_item(def_id) {
+            Some(AssociatedItem {
+                container: AssociatedItemContainer::TraitContainer(trait_def_id),
+                ..
+            }) => tcx.generics_of(trait_def_id).count(),
+            _ => 0
+        };
+    let caller_substs = &Substs::identity_for_item(tcx, def_id)[..trait_substs_count];
+
+    while let Some(bb) = reachable_without_self_call_queue.pop() {
+        if visited.contains(bb) {
+            //already done
+            continue;
+        }
+
+        visited.insert(bb);
+
+        let block = &basic_blocks[bb];
+
+        if let Some(ref terminator) = block.terminator {
+            match terminator.kind {
+                TerminatorKind::Call { ref func, .. } => {
+                    let func_ty = func.ty(mir, tcx);
+
+                    if let TyKind::FnDef(fn_def_id, substs) = func_ty.sty {
+                        let (call_fn_id, call_substs) =
+                            if let Some(instance) = Instance::resolve(tcx,
+                                                                        param_env,
+                                                                        fn_def_id,
+                                                                        substs) {
+                                (instance.def_id(), instance.substs)
+                            } else {
+                                (fn_def_id, substs)
+                            };
+
+                        let is_self_call =
+                            call_fn_id == def_id &&
+                                &call_substs[..caller_substs.len()] == caller_substs;
+
+                        if is_self_call {
+                            self_call_locations.push(terminator.source_info);
+
+                            //this is a self call so we shouldn't explore
+                            //further down this path
+                            continue;
+                        }
+                    }
+                },
+                TerminatorKind::Abort | TerminatorKind::Return => {
+                    //found a path!
+                    reached_exit_without_self_call = true;
+                    break;
+                }
+                _ => {}
+            }
+
+            for successor in terminator.successors() {
+                reachable_without_self_call_queue.push(*successor);
+            }
+        }
+    }
+
+    // Check the number of self calls because a function that
+    // doesn't return (e.g. calls a `-> !` function or `loop { /*
+    // no break */ }`) shouldn't be linted unless it actually
+    // recurs.
+    if !reached_exit_without_self_call && !self_call_locations.is_empty() {
+        let node_id = tcx.hir.as_local_node_id(def_id).unwrap();
+        let sp = tcx.sess.source_map().def_span(tcx.hir.span(node_id));
+        let mut db = tcx.struct_span_lint_node(UNCONDITIONAL_RECURSION,
+                                                node_id,
+                                                sp,
+                                                "function cannot return without recursing");
+        db.span_label(sp, "cannot return without recursing");
+        // offer some help to the programmer.
+        for location in &self_call_locations {
+            db.span_label(location.span, "recursive call site");
+        }
+        db.help("a `loop` may express intention better if this is on purpose");
+        db.emit();
+    }
+}
diff --git a/src/test/ui/did_you_mean/issue-31424.nll.stderr b/src/test/ui/did_you_mean/issue-31424.nll.stderr
index 15139e4e8ae36..fca29c9a9f644 100644
--- a/src/test/ui/did_you_mean/issue-31424.nll.stderr
+++ b/src/test/ui/did_you_mean/issue-31424.nll.stderr
@@ -7,8 +7,20 @@ LL |         (&mut self).bar(); //~ ERROR cannot borrow
    |         cannot borrow as mutable
    |         try removing `&mut` here
 
+warning: function cannot return without recursing
+  --> $DIR/issue-31424.rs:22:5
+   |
+LL |     fn bar(self: &mut Self) {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
+LL |         //~^ WARN function cannot return without recursing
+LL |         (&mut self).bar(); //~ ERROR cannot borrow
+   |         ----------------- recursive call site
+   |
+   = note: #[warn(unconditional_recursion)] on by default
+   = help: a `loop` may express intention better if this is on purpose
+
 error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable
-  --> $DIR/issue-31424.rs:23:9
+  --> $DIR/issue-31424.rs:24:9
    |
 LL |         (&mut self).bar(); //~ ERROR cannot borrow
    |         ^^^^^^^^^^^
diff --git a/src/test/ui/did_you_mean/issue-31424.rs b/src/test/ui/did_you_mean/issue-31424.rs
index 1b31e064337e2..903a76a824338 100644
--- a/src/test/ui/did_you_mean/issue-31424.rs
+++ b/src/test/ui/did_you_mean/issue-31424.rs
@@ -20,6 +20,7 @@ impl Struct {
     // In this case we could keep the suggestion, but to distinguish the
     // two cases is pretty hard. It's an obscure case anyway.
     fn bar(self: &mut Self) {
+        //~^ WARN function cannot return without recursing
         (&mut self).bar(); //~ ERROR cannot borrow
     }
 }
diff --git a/src/test/ui/did_you_mean/issue-31424.stderr b/src/test/ui/did_you_mean/issue-31424.stderr
index 9d0ab21ffaf0e..2e4bcc7f95947 100644
--- a/src/test/ui/did_you_mean/issue-31424.stderr
+++ b/src/test/ui/did_you_mean/issue-31424.stderr
@@ -7,8 +7,20 @@ LL |         (&mut self).bar(); //~ ERROR cannot borrow
    |               cannot reborrow mutably
    |               try removing `&mut` here
 
+warning: function cannot return without recursing
+  --> $DIR/issue-31424.rs:22:5
+   |
+LL |     fn bar(self: &mut Self) {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
+LL |         //~^ WARN function cannot return without recursing
+LL |         (&mut self).bar(); //~ ERROR cannot borrow
+   |         ----------------- recursive call site
+   |
+   = note: #[warn(unconditional_recursion)] on by default
+   = help: a `loop` may express intention better if this is on purpose
+
 error[E0596]: cannot borrow immutable argument `self` as mutable
-  --> $DIR/issue-31424.rs:23:15
+  --> $DIR/issue-31424.rs:24:15
    |
 LL |         (&mut self).bar(); //~ ERROR cannot borrow
    |               ^^^^ cannot borrow mutably
diff --git a/src/test/ui/nll/issue-51191.rs b/src/test/ui/nll/issue-51191.rs
index 87ec3e5df0b81..0f8372e094d6a 100644
--- a/src/test/ui/nll/issue-51191.rs
+++ b/src/test/ui/nll/issue-51191.rs
@@ -14,6 +14,7 @@ struct Struct;
 
 impl Struct {
     fn bar(self: &mut Self) {
+        //~^ WARN function cannot return without recursing
         (&mut self).bar();
         //~^ ERROR cannot borrow `self` as mutable, as it is not declared as mutable [E0596]
     }
diff --git a/src/test/ui/nll/issue-51191.stderr b/src/test/ui/nll/issue-51191.stderr
index c5b5218f173ac..88c653effb405 100644
--- a/src/test/ui/nll/issue-51191.stderr
+++ b/src/test/ui/nll/issue-51191.stderr
@@ -1,5 +1,17 @@
+warning: function cannot return without recursing
+  --> $DIR/issue-51191.rs:16:5
+   |
+LL |     fn bar(self: &mut Self) {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
+LL |         //~^ WARN function cannot return without recursing
+LL |         (&mut self).bar();
+   |         ----------------- recursive call site
+   |
+   = note: #[warn(unconditional_recursion)] on by default
+   = help: a `loop` may express intention better if this is on purpose
+
 error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable
-  --> $DIR/issue-51191.rs:17:9
+  --> $DIR/issue-51191.rs:18:9
    |
 LL |         (&mut self).bar();
    |         ^^^^^^^^^^^
@@ -8,7 +20,7 @@ LL |         (&mut self).bar();
    |         try removing `&mut` here
 
 error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable
-  --> $DIR/issue-51191.rs:22:9
+  --> $DIR/issue-51191.rs:23:9
    |
 LL |     fn imm(self) {
    |            ---- help: consider changing this to be mutable: `mut self`
@@ -16,19 +28,19 @@ LL |         (&mut self).bar();
    |         ^^^^^^^^^^^ cannot borrow as mutable
 
 error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable
-  --> $DIR/issue-51191.rs:31:9
+  --> $DIR/issue-51191.rs:32:9
    |
 LL |         (&mut self).bar();
    |         ^^^^^^^^^^^ cannot borrow as mutable
 
 error[E0596]: cannot borrow data in a `&` reference as mutable
-  --> $DIR/issue-51191.rs:31:9
+  --> $DIR/issue-51191.rs:32:9
    |
 LL |         (&mut self).bar();
    |         ^^^^^^^^^^^ cannot borrow as mutable
 
 error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable
-  --> $DIR/issue-51191.rs:37:9
+  --> $DIR/issue-51191.rs:38:9
    |
 LL |         (&mut self).bar();
    |         ^^^^^^^^^^^
diff --git a/src/test/ui/regions/region-bound-on-closure-outlives-call.nll.stderr b/src/test/ui/regions/region-bound-on-closure-outlives-call.nll.stderr
index da6ebaaefadf1..f0fb57039c465 100644
--- a/src/test/ui/regions/region-bound-on-closure-outlives-call.nll.stderr
+++ b/src/test/ui/regions/region-bound-on-closure-outlives-call.nll.stderr
@@ -1,5 +1,17 @@
+warning: function cannot return without recursing
+  --> $DIR/region-bound-on-closure-outlives-call.rs:11:1
+   |
+LL | fn call_rec<F>(mut f: F) -> usize where F: FnMut(usize) -> usize {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
+LL |     //~^ WARN function cannot return without recursing
+LL |     (|x| f(x))(call_rec(f)) //~ ERROR cannot move out of `f`
+   |                ----------- recursive call site
+   |
+   = note: #[warn(unconditional_recursion)] on by default
+   = help: a `loop` may express intention better if this is on purpose
+
 error[E0505]: cannot move out of `f` because it is borrowed
-  --> $DIR/region-bound-on-closure-outlives-call.rs:12:25
+  --> $DIR/region-bound-on-closure-outlives-call.rs:13:25
    |
 LL |     (|x| f(x))(call_rec(f)) //~ ERROR cannot move out of `f`
    |     --------------------^--
diff --git a/src/test/ui/regions/region-bound-on-closure-outlives-call.rs b/src/test/ui/regions/region-bound-on-closure-outlives-call.rs
index b73c283fa515e..f931e281c83fc 100644
--- a/src/test/ui/regions/region-bound-on-closure-outlives-call.rs
+++ b/src/test/ui/regions/region-bound-on-closure-outlives-call.rs
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 fn call_rec<F>(mut f: F) -> usize where F: FnMut(usize) -> usize {
+    //~^ WARN function cannot return without recursing
     (|x| f(x))(call_rec(f)) //~ ERROR cannot move out of `f`
 }
 
diff --git a/src/test/ui/regions/region-bound-on-closure-outlives-call.stderr b/src/test/ui/regions/region-bound-on-closure-outlives-call.stderr
index 7adf68ee9b6d1..6465f1ccf3345 100644
--- a/src/test/ui/regions/region-bound-on-closure-outlives-call.stderr
+++ b/src/test/ui/regions/region-bound-on-closure-outlives-call.stderr
@@ -1,5 +1,17 @@
+warning: function cannot return without recursing
+  --> $DIR/region-bound-on-closure-outlives-call.rs:11:1
+   |
+LL | fn call_rec<F>(mut f: F) -> usize where F: FnMut(usize) -> usize {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
+LL |     //~^ WARN function cannot return without recursing
+LL |     (|x| f(x))(call_rec(f)) //~ ERROR cannot move out of `f`
+   |                ----------- recursive call site
+   |
+   = note: #[warn(unconditional_recursion)] on by default
+   = help: a `loop` may express intention better if this is on purpose
+
 error[E0505]: cannot move out of `f` because it is borrowed
-  --> $DIR/region-bound-on-closure-outlives-call.rs:12:25
+  --> $DIR/region-bound-on-closure-outlives-call.rs:13:25
    |
 LL |     (|x| f(x))(call_rec(f)) //~ ERROR cannot move out of `f`
    |      ---                ^ move out of `f` occurs here

From 7d3d835c58e789127744076c97110664ea9c6087 Mon Sep 17 00:00:00 2001
From: Philip Munksgaard <pmunksgaard@gmail.com>
Date: Thu, 4 Oct 2018 22:17:01 +0200
Subject: [PATCH 03/30] replace escape-rust-expr test with
 dont-show-const-contents

The old test was supposed to check for proper html escaping when showing the
contents of constants. This was changed as part of #53409. The revised test
asserts that the contents of the constant is not shown as part of the generated
documentation.
---
 .../{escape-rust-expr.rs => dont-show-const-contents.rs}  | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
 rename src/test/rustdoc/{escape-rust-expr.rs => dont-show-const-contents.rs} (64%)

diff --git a/src/test/rustdoc/escape-rust-expr.rs b/src/test/rustdoc/dont-show-const-contents.rs
similarity index 64%
rename from src/test/rustdoc/escape-rust-expr.rs
rename to src/test/rustdoc/dont-show-const-contents.rs
index 4594eb95ea18e..1392c62b4abb1 100644
--- a/src/test/rustdoc/escape-rust-expr.rs
+++ b/src/test/rustdoc/dont-show-const-contents.rs
@@ -8,8 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// Test that we HTML-escape Rust expressions, where HTML special chars
-// can occur, and we know it's definitely not markup.
+// Test that the contents of constants are not displayed as part of the
+// documentation.
 
-// @!has escape_rust_expr/constant.CONST_S.html '//pre[@class="rust const"]' '"<script>"'
-pub const CONST_S: &'static str = "<script>";
+// @!has dont_show_const_contents/constant.CONST_S.html 'dont show this'
+pub const CONST_S: &'static str = "dont show this";

From f97e094b198a0eb6115251f42e3f6e5dcec731f8 Mon Sep 17 00:00:00 2001
From: Philip Munksgaard <pmunksgaard@gmail.com>
Date: Thu, 4 Oct 2018 22:23:19 +0200
Subject: [PATCH 04/30] Add compile flags to playground-empty test

"Run" links to the playground are not added to the generated documentation if
the unstable `--playground-url` argument is not passed to rustdoc. Therefore,
without specifying `--playground-url` as a compile-flag, the test doesn't
correctly assert that `#![doc(html_playground_url = "")]` removes playground
links.
---
 src/test/rustdoc/playground-empty.rs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/test/rustdoc/playground-empty.rs b/src/test/rustdoc/playground-empty.rs
index 00881a62dd0a8..2b3120fa5b4a1 100644
--- a/src/test/rustdoc/playground-empty.rs
+++ b/src/test/rustdoc/playground-empty.rs
@@ -12,6 +12,8 @@
 
 #![doc(html_playground_url = "")]
 
+// compile-flags:-Z unstable-options --playground-url https://play.rust-lang.org/
+
 //! module docs
 //!
 //! ```

From 46ccce0803386db3d7c0dbdd30d31474889b64a0 Mon Sep 17 00:00:00 2001
From: Philip Munksgaard <pmunksgaard@gmail.com>
Date: Fri, 5 Oct 2018 18:24:25 +0200
Subject: [PATCH 05/30] Remove duplicate test line

---
 src/test/rustdoc/manual_impl.rs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/test/rustdoc/manual_impl.rs b/src/test/rustdoc/manual_impl.rs
index 54a8a76483341..db48a6525230c 100644
--- a/src/test/rustdoc/manual_impl.rs
+++ b/src/test/rustdoc/manual_impl.rs
@@ -35,7 +35,6 @@ pub trait T {
 // @has  - '//*[@class="docblock"]' 'Docs associated with the S1 trait a_method implementation.'
 // @!has - '//*[@class="docblock"]' 'Docs associated with the trait a_method definition.'
 // @has - '//*[@class="docblock"]' 'Docs associated with the trait b_method definition.'
-// @has - '//*[@class="docblock"]' 'Docs associated with the trait b_method definition.'
 // @has - '//*[@class="docblock"]' 'Docs associated with the trait c_method definition.'
 // @!has - '//*[@class="docblock"]' 'There is another line'
 // @has - '//*[@class="docblock"]' 'Read more'

From 5f8dc0ddf47274e0a98111edc816b825484d0d1a Mon Sep 17 00:00:00 2001
From: Philip Munksgaard <pmunksgaard@gmail.com>
Date: Fri, 5 Oct 2018 19:08:52 +0200
Subject: [PATCH 06/30] Fix unneeded-trait-implementations-title test

The `@!has` command does not support specifying just a PATH and an XPATH. That
means that the previous test searched for the literal string
`//h2[@id="implementations"]` within the generated output, which obviously
didn't exist. Even after adding a trait implementation, the test still passed.
The correct way to check for the existence of a DOM element with the id
`implementations` is to use the `@count` keyword.
---
 src/test/rustdoc/unneeded-trait-implementations-title.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/test/rustdoc/unneeded-trait-implementations-title.rs b/src/test/rustdoc/unneeded-trait-implementations-title.rs
index 2c074179e32b5..c87b533b9d474 100644
--- a/src/test/rustdoc/unneeded-trait-implementations-title.rs
+++ b/src/test/rustdoc/unneeded-trait-implementations-title.rs
@@ -12,4 +12,4 @@
 
 pub struct Bar;
 
-// @!has foo/struct.Bar.html '//*[@id="implementations"]'
+// @count foo/struct.Bar.html '//*[@id="implementations"]' 0

From a9a26de8b3b8a0acda3b7716f6e9b4b1bfb799dd Mon Sep 17 00:00:00 2001
From: Philip Munksgaard <pmunksgaard@gmail.com>
Date: Fri, 5 Oct 2018 22:55:13 +0200
Subject: [PATCH 07/30] Fix redirect.rs test

Struct names are no longer encapsulated in `<code>` tags, which meant that the
test was not correctly verifying that it wasn't being show. I've also added a
check to make sure the documentation page for the redirect::Qux struct is not
generated at all.
---
 src/test/rustdoc/redirect.rs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/test/rustdoc/redirect.rs b/src/test/rustdoc/redirect.rs
index 98e66e8c024bd..c20dd815d8ce7 100644
--- a/src/test/rustdoc/redirect.rs
+++ b/src/test/rustdoc/redirect.rs
@@ -43,6 +43,7 @@ mod private_no_inline {
 
 // @has redirect/index.html
 // @has - '//code' 'pub use private_no_inline::Qux'
-// @!has - '//code/a' 'Qux'
+// @!has - '//a' 'Qux'
+// @!has redirect/struct.Qux.html
 #[doc(no_inline)]
 pub use private_no_inline::Qux;

From 28596ade7169abd483410b22a4bd8703c4ccc8f7 Mon Sep 17 00:00:00 2001
From: Philip Munksgaard <pmunksgaard@gmail.com>
Date: Fri, 5 Oct 2018 23:28:08 +0200
Subject: [PATCH 08/30] Fix empty-section.rs test

The Auto Trait Implementation section is not wrapped in a
`synthetic-implementations` class. In fact, it is wrapped in a
`synthetic-implementations` id. However, we can generalize and completely remove
the `synthetic-implementations` requirement. We just have to verify that there's
no mention of "Auto Trait Implementations" anywhere.
---
 src/test/rustdoc/empty-section.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/test/rustdoc/empty-section.rs b/src/test/rustdoc/empty-section.rs
index 3748313593fc2..11a027a13f7e3 100644
--- a/src/test/rustdoc/empty-section.rs
+++ b/src/test/rustdoc/empty-section.rs
@@ -15,6 +15,6 @@
 pub struct Foo;
 
 // @has foo/struct.Foo.html
-// @!has - '//*[@class="synthetic-implementations"]' 'Auto Trait Implementations'
+// @!has - 'Auto Trait Implementations'
 impl !Send for Foo {}
 impl !Sync for Foo {}

From e961d397cab900c55f8d8c104648852e2b63664e Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date: Mon, 8 Oct 2018 22:51:37 +0200
Subject: [PATCH 09/30] Add line numbers option to rustdoc

---
 src/librustdoc/html/highlight.rs            | 13 ++++---
 src/librustdoc/html/render.rs               |  1 +
 src/librustdoc/html/static/main.js          | 42 ++++++++++++++++-----
 src/librustdoc/html/static/rustdoc.css      | 18 +++++++++
 src/librustdoc/html/static/themes/dark.css  |  4 ++
 src/librustdoc/html/static/themes/light.css |  4 ++
 6 files changed, 68 insertions(+), 14 deletions(-)

diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index 5df4862290e3c..bd4cfe51ea72f 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -28,9 +28,12 @@ use syntax::parse;
 use syntax_pos::{Span, FileName};
 
 /// Highlights `src`, returning the HTML output.
-pub fn render_with_highlighting(src: &str, class: Option<&str>,
-                                extension: Option<&str>,
-                                tooltip: Option<(&str, &str)>) -> String {
+pub fn render_with_highlighting(
+    src: &str,
+    class: Option<&str>,
+    extension: Option<&str>,
+    tooltip: Option<(&str, &str)>,
+) -> String {
     debug!("highlighting: ================\n{}\n==============", src);
     let sess = parse::ParseSess::new(FilePathMapping::empty());
     let fm = sess.source_map().new_source_file(FileName::Custom("stdin".to_string()),
@@ -373,9 +376,9 @@ impl Class {
 }
 
 fn write_header(class: Option<&str>, out: &mut dyn Write) -> io::Result<()> {
-    write!(out, "<pre class=\"rust {}\">\n", class.unwrap_or(""))
+    write!(out, "<div class=\"example-wrap\"><pre class=\"rust {}\">\n", class.unwrap_or(""))
 }
 
 fn write_footer(out: &mut dyn Write) -> io::Result<()> {
-    write!(out, "</pre>\n")
+    write!(out, "</pre></div>\n")
 }
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 1c61e73fae03c..c83c5d382ddcc 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -1709,6 +1709,7 @@ impl<'a> Settings<'a> {
                 ("method-docs", "Auto-hide item methods' documentation", false),
                 ("go-to-only-result", "Directly go to item in search if there is only one result",
                  false),
+                ("line-numbers", "Show line numbers on code examples", false),
             ],
             root_path,
             suffix,
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index 6307dda454da8..9d1a5c3837830 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -2093,6 +2093,7 @@
         return wrapper;
     }
 
+    var hideItemDeclarations = getCurrentValue('rustdoc-item-declarations') === "false";
     onEach(document.getElementsByClassName('docblock'), function(e) {
         if (hasClass(e, 'autohide')) {
             var wrap = e.previousElementSibling;
@@ -2116,16 +2117,14 @@
             }
         }
         if (e.parentNode.id === "main") {
-            var otherMessage;
+            var otherMessage = '';
             var fontSize;
             var extraClass;
-            var show = true;
 
             if (hasClass(e, "type-decl")) {
                 fontSize = "20px";
                 otherMessage = '&nbsp;Show&nbsp;declaration';
-                show = getCurrentValue('rustdoc-item-declarations') === "false";
-                if (!show) {
+                if (hideItemDeclarations === false) {
                     extraClass = 'collapsed';
                 }
             } else if (hasClass(e, "non-exhaustive")) {
@@ -2142,8 +2141,12 @@
                 extraClass = "marg-left";
             }
 
-            e.parentNode.insertBefore(createToggle(otherMessage, fontSize, extraClass, show), e);
-            if (otherMessage && show) {
+            e.parentNode.insertBefore(createToggle(otherMessage,
+                                                   fontSize,
+                                                   extraClass,
+                                                   hideItemDeclarations),
+                                      e);
+            if (otherMessage.length > 0 && hideItemDeclarations === true) {
                 collapseDocs(e.previousSibling.childNodes[0], "toggle");
             }
         }
@@ -2186,13 +2189,33 @@
         });
     }
 
+    // To avoid checking on "rustdoc-item-attributes" value on every loop...
+    var itemAttributesFunc = function() {};
+    if (getCurrentValue("rustdoc-item-attributes") !== "false") {
+        itemAttributesFunc = function(x) {
+            collapseDocs(x.previousSibling.childNodes[0], "toggle");
+        };
+    }
     onEach(document.getElementById('main').getElementsByClassName('attributes'), function(i_e) {
         i_e.parentNode.insertBefore(createToggleWrapper(toggle.cloneNode(true)), i_e);
-        if (getCurrentValue("rustdoc-item-attributes") !== "false") {
-            collapseDocs(i_e.previousSibling.childNodes[0], "toggle");
-        }
+        itemAttributesFunc(i_e);
     });
 
+    // To avoid checking on "rustdoc-line-numbers" value on every loop...
+    var lineNumbersFunc = function() {};
+    if (getCurrentValue("rustdoc-line-numbers") === "true") {
+        lineNumbersFunc = function(x) {
+            var count = x.textContent.split('\n').length;
+            var elems = [];
+            for (var i = 0; i < count; ++i) {
+                elems.push(i + 1);
+            }
+            var node = document.createElement('pre');
+            addClass(node, 'line-number');
+            node.innerHTML = elems.join('\n');
+            x.parentNode.insertBefore(node, x);
+        };
+    }
     onEach(document.getElementsByClassName('rust-example-rendered'), function(e) {
         if (hasClass(e, 'compile_fail')) {
             e.addEventListener("mouseover", function(event) {
@@ -2209,6 +2232,7 @@
                 e.previousElementSibling.childNodes[0].style.color = '';
             });
         }
+        lineNumbersFunc(e);
     });
 
     function showModal(content) {
diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css
index ee811f3379239..5817a22574953 100644
--- a/src/librustdoc/html/static/rustdoc.css
+++ b/src/librustdoc/html/static/rustdoc.css
@@ -283,6 +283,24 @@ nav.sub {
 	padding-left: 0;
 }
 
+.example-wrap {
+	display: inline-flex;
+	width: 100%;
+}
+
+.example-wrap > pre.line-number {
+	overflow: initial;
+	border: 1px solid;
+	border-top-left-radius: 5px;
+	border-bottom-left-radius: 5px;
+	padding: 13px 8px;
+	text-align: right;
+}
+
+.example-wrap > pre.rust {
+	width: 100%;
+}
+
 #search {
 	margin-left: 230px;
 	position: relative;
diff --git a/src/librustdoc/html/static/themes/dark.css b/src/librustdoc/html/static/themes/dark.css
index 34a1d71beecfc..e179c4586bc74 100644
--- a/src/librustdoc/html/static/themes/dark.css
+++ b/src/librustdoc/html/static/themes/dark.css
@@ -233,6 +233,10 @@ pre.rust .question-mark {
 	color: #ff9011;
 }
 
+.example-wrap > pre.line-number {
+	border-color: #4a4949;
+}
+
 a.test-arrow {
 	background-color: rgba(78, 139, 202, 0.2);
 }
diff --git a/src/librustdoc/html/static/themes/light.css b/src/librustdoc/html/static/themes/light.css
index 8218b1b371ea7..6ef9875ea2d4d 100644
--- a/src/librustdoc/html/static/themes/light.css
+++ b/src/librustdoc/html/static/themes/light.css
@@ -227,6 +227,10 @@ pre.rust .question-mark {
 	color: #ff9011;
 }
 
+.example-wrap > pre.line-number {
+	border-color: #c7c7c7;
+}
+
 a.test-arrow {
 	background-color: rgba(78, 139, 202, 0.2);
 }

From e8bc04e1c8e0e27bcc3e5915d465ca12b0254565 Mon Sep 17 00:00:00 2001
From: Philip Munksgaard <pmunksgaard@gmail.com>
Date: Wed, 10 Oct 2018 21:04:52 +0200
Subject: [PATCH 10/30] Use XPATH notation to match against flattened nodes

The generated code would look like `<code>impl <a href="...">Foo</a></code>`
which the plain text matcher doesn't match. But by using the XPATH notation, the
nodes are flattened and we can correctly assert that `impl Foo` does not occur
in the generated docs.
---
 src/test/rustdoc/hidden-methods.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/test/rustdoc/hidden-methods.rs b/src/test/rustdoc/hidden-methods.rs
index 18f5f086cd1a4..aea5f44e2a723 100644
--- a/src/test/rustdoc/hidden-methods.rs
+++ b/src/test/rustdoc/hidden-methods.rs
@@ -28,12 +28,12 @@ pub mod hidden {
 
 // @has foo/struct.Foo.html
 // @!has - 'Methods'
-// @!has - 'impl Foo'
+// @!has - '//code' 'impl Foo'
 // @!has - 'this_should_be_hidden'
 pub use hidden::Foo;
 
 // @has foo/struct.Bar.html
 // @!has - 'Methods'
-// @!has - 'impl Bar'
+// @!has - '//code' 'impl Bar'
 // @!has - 'this_should_be_hidden'
 pub use hidden::Bar;

From 9dbe902800d5ba87b08b15adb157adbe9be02e75 Mon Sep 17 00:00:00 2001
From: Philip Munksgaard <pmunksgaard@gmail.com>
Date: Wed, 10 Oct 2018 21:18:08 +0200
Subject: [PATCH 11/30] Fix issue-46767 test

The link that is matched against is not the same as would be generated by
rustdoc. We should also check that the `foo/private` directory is not generated
at all.
---
 src/test/rustdoc/issue-46767.rs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/test/rustdoc/issue-46767.rs b/src/test/rustdoc/issue-46767.rs
index 855de150b0a37..1e6b697cd5fd9 100644
--- a/src/test/rustdoc/issue-46767.rs
+++ b/src/test/rustdoc/issue-46767.rs
@@ -15,4 +15,5 @@ mod private {
 }
 pub use self::private::Enum::*;
 
-// @!has foo/index.html '//a/@href' './private/index.html'
+// @!has-dir foo/private
+// @!has foo/index.html '//a/@href' 'private/index.html'

From df79da96614467e97a8f8ddd58106b456e44dee1 Mon Sep 17 00:00:00 2001
From: Philip Munksgaard <pmunksgaard@gmail.com>
Date: Wed, 10 Oct 2018 22:01:05 +0200
Subject: [PATCH 12/30] Fix typo in deprecated_impls

The function is called `fn_def_with_doc`, not `fn_def_with`.
---
 src/test/rustdoc/deprecated-impls.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/test/rustdoc/deprecated-impls.rs b/src/test/rustdoc/deprecated-impls.rs
index bcf0645766b30..3fb83bff91604 100644
--- a/src/test/rustdoc/deprecated-impls.rs
+++ b/src/test/rustdoc/deprecated-impls.rs
@@ -109,7 +109,7 @@ impl Bar for Foo2 {
 
     // @has - '//*[@class="stab deprecated"]' 'Deprecated since 1.0.5: fn_def_with_doc'
     // @has - 'fn_def_with_doc short'
-    // @!has - 'fn_def_with full'
+    // @!has - 'fn_def_with_doc full'
     fn fn_def_with_doc() {}
 
     // @has - '//*[@class="stab deprecated"]' 'Deprecated since 1.0.6: fn_def_without_doc'

From a9217e316c7fd35ef5be4b0c1e84f637ec0fa813 Mon Sep 17 00:00:00 2001
From: Philip Munksgaard <pmunksgaard@gmail.com>
Date: Wed, 10 Oct 2018 22:30:15 +0200
Subject: [PATCH 13/30] Fix typo in issue-13698.rs

---
 src/test/rustdoc/issue-13698.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/test/rustdoc/issue-13698.rs b/src/test/rustdoc/issue-13698.rs
index cf9b30a0fe987..b0efee511bc7d 100644
--- a/src/test/rustdoc/issue-13698.rs
+++ b/src/test/rustdoc/issue-13698.rs
@@ -22,5 +22,5 @@ pub trait Bar {
     fn bar(&self) {}
 }
 
-// @!has issue_13698/struct.Foo.html '//*[@id="method.foo"]' 'fn bar'
+// @!has issue_13698/struct.Foo.html '//*[@id="method.bar"]' 'fn bar'
 impl Bar for Foo {}

From a75f7810a7118e3bb103058091c1d6414b515786 Mon Sep 17 00:00:00 2001
From: "Felix S. Klock II" <pnkfelix@pnkfx.org>
Date: Wed, 17 Oct 2018 16:31:22 +0200
Subject: [PATCH 14/30] AST-borrowck: add separate mem category for
 thread-locals, as they are not quite rvalues (and of course they are not
 quite statics either).

Namely, they *do* have a restricted region (like rvalues), but they
also cannot be moved out of (like statics).
---
 src/librustc/middle/mem_categorization.rs     | 29 ++++++++++++++-----
 src/librustc_borrowck/borrowck/check_loans.rs |  1 +
 .../borrowck/gather_loans/gather_moves.rs     |  1 +
 .../borrowck/gather_loans/lifetime.rs         |  2 ++
 .../borrowck/gather_loans/move_error.rs       |  7 ++++-
 .../borrowck/gather_loans/restrictions.rs     |  6 ++++
 src/librustc_borrowck/borrowck/mod.rs         |  1 +
 src/librustc_passes/rvalue_promotion.rs       |  1 +
 src/librustc_typeck/check/regionck.rs         |  1 +
 src/librustc_typeck/check/upvar.rs            |  2 ++
 10 files changed, 42 insertions(+), 9 deletions(-)

diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index 13e6f7a4c745a..9d3f37bc36a9d 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -93,6 +93,7 @@ use util::nodemap::ItemLocalSet;
 #[derive(Clone, Debug, PartialEq)]
 pub enum Categorization<'tcx> {
     Rvalue(ty::Region<'tcx>),            // temporary val, argument is its scope
+    ThreadLocal(ty::Region<'tcx>),       // value that cannot move, but still restricted in scope
     StaticItem,
     Upvar(Upvar),                        // upvar referenced by closure env
     Local(ast::NodeId),                  // local variable
@@ -268,6 +269,7 @@ impl<'tcx> cmt_<'tcx> {
             Categorization::Deref(ref base_cmt, _) => {
                 base_cmt.immutability_blame()
             }
+            Categorization::ThreadLocal(..) |
             Categorization::StaticItem => {
                 // Do we want to do something here?
                 None
@@ -715,17 +717,23 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
             }
 
             Def::Static(def_id, mutbl) => {
-                // `#[thread_local]` statics may not outlive the current function.
-                for attr in &self.tcx.get_attrs(def_id)[..] {
-                    if attr.check_name("thread_local") {
-                        return Ok(self.cat_rvalue_node(hir_id, span, expr_ty));
-                    }
-                }
+                // `#[thread_local]` statics may not outlive the current function, but
+                // they also cannot be moved out of.
+                let is_thread_local = self.tcx.get_attrs(def_id)[..]
+                    .iter()
+                    .any(|attr| attr.check_name("thread_local"));
+
+                let cat = if is_thread_local {
+                    let re = self.temporary_scope(hir_id.local_id);
+                    Categorization::ThreadLocal(re)
+                } else {
+                    Categorization::StaticItem
+                };
 
                 Ok(cmt_ {
                     hir_id,
-                    span:span,
-                    cat:Categorization::StaticItem,
+                    span,
+                    cat,
                     mutbl: if mutbl { McDeclared } else { McImmutable},
                     ty:expr_ty,
                     note: NoteNone
@@ -1408,6 +1416,7 @@ impl<'tcx> cmt_<'tcx> {
         match self.cat {
             Categorization::Rvalue(..) |
             Categorization::StaticItem |
+            Categorization::ThreadLocal(..) |
             Categorization::Local(..) |
             Categorization::Deref(_, UnsafePtr(..)) |
             Categorization::Deref(_, BorrowedPtr(..)) |
@@ -1439,6 +1448,7 @@ impl<'tcx> cmt_<'tcx> {
             }
 
             Categorization::Rvalue(..) |
+            Categorization::ThreadLocal(..) |
             Categorization::Local(..) |
             Categorization::Upvar(..) |
             Categorization::Deref(_, UnsafePtr(..)) => { // yes, it's aliasable, but...
@@ -1485,6 +1495,9 @@ impl<'tcx> cmt_<'tcx> {
             Categorization::StaticItem => {
                 "static item".into()
             }
+            Categorization::ThreadLocal(..) => {
+                "thread-local static item".into()
+            }
             Categorization::Rvalue(..) => {
                 "non-place".into()
             }
diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs
index 34ee03b895f9f..033ed8008edc2 100644
--- a/src/librustc_borrowck/borrowck/check_loans.rs
+++ b/src/librustc_borrowck/borrowck/check_loans.rs
@@ -377,6 +377,7 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
                 // by-move upvars, which is local data for generators
                 Categorization::Upvar(..) => true,
 
+                Categorization::ThreadLocal(region) |
                 Categorization::Rvalue(region) => {
                     // Rvalues promoted to 'static are no longer local
                     if let RegionKind::ReStatic = *region {
diff --git a/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs b/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs
index ffc4fbfb4c9cb..7bb5f411752fe 100644
--- a/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs
+++ b/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs
@@ -177,6 +177,7 @@ fn check_and_get_illegal_move_origin<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
     match cmt.cat {
         Categorization::Deref(_, mc::BorrowedPtr(..)) |
         Categorization::Deref(_, mc::UnsafePtr(..)) |
+        Categorization::ThreadLocal(..) |
         Categorization::StaticItem => {
             Some(cmt.clone())
         }
diff --git a/src/librustc_borrowck/borrowck/gather_loans/lifetime.rs b/src/librustc_borrowck/borrowck/gather_loans/lifetime.rs
index c9dcc0d9fa266..6ef5d65d10dca 100644
--- a/src/librustc_borrowck/borrowck/gather_loans/lifetime.rs
+++ b/src/librustc_borrowck/borrowck/gather_loans/lifetime.rs
@@ -70,6 +70,7 @@ impl<'a, 'tcx> GuaranteeLifetimeContext<'a, 'tcx> {
 
         match cmt.cat {
             Categorization::Rvalue(..) |
+            Categorization::ThreadLocal(..) |
             Categorization::Local(..) |                     // L-Local
             Categorization::Upvar(..) |
             Categorization::Deref(_, mc::BorrowedPtr(..)) | // L-Deref-Borrowed
@@ -105,6 +106,7 @@ impl<'a, 'tcx> GuaranteeLifetimeContext<'a, 'tcx> {
         //! rooting etc, and presuming `cmt` is not mutated.
 
         match cmt.cat {
+            Categorization::ThreadLocal(temp_scope) |
             Categorization::Rvalue(temp_scope) => {
                 temp_scope
             }
diff --git a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs
index b29ab55f9ba78..e1a4473539c8c 100644
--- a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs
+++ b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs
@@ -145,6 +145,8 @@ fn report_cannot_move_out_of<'a, 'tcx>(bccx: &'a BorrowckCtxt<'a, 'tcx>,
     match move_from.cat {
         Categorization::Deref(_, mc::BorrowedPtr(..)) |
         Categorization::Deref(_, mc::UnsafePtr(..)) |
+        Categorization::Deref(_, mc::Unique) |
+        Categorization::ThreadLocal(..) |
         Categorization::StaticItem => {
             bccx.cannot_move_out_of(
                 move_from.span, &move_from.descriptive_string(bccx.tcx), Origin::Ast)
@@ -166,7 +168,10 @@ fn report_cannot_move_out_of<'a, 'tcx>(bccx: &'a BorrowckCtxt<'a, 'tcx>,
                 }
             }
         }
-        _ => {
+
+        Categorization::Rvalue(..) |
+        Categorization::Local(..) |
+        Categorization::Upvar(..) => {
             span_bug!(move_from.span, "this path should not cause illegal move");
         }
     }
diff --git a/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs b/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs
index d9784cc2177fd..52c7ebb4beb02 100644
--- a/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs
+++ b/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs
@@ -70,6 +70,12 @@ impl<'a, 'tcx> RestrictionsContext<'a, 'tcx> {
                 RestrictionResult::Safe
             }
 
+            Categorization::ThreadLocal(..) => {
+                // Thread-locals are statics that have a scope, with
+                // no underlying structure to provide restrictions.
+                RestrictionResult::Safe
+            }
+
             Categorization::Local(local_id) => {
                 // R-Variable, locally declared
                 let lp = new_lp(LpVar(local_id));
diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs
index 0ef0d284770f5..d56eb188ccd2d 100644
--- a/src/librustc_borrowck/borrowck/mod.rs
+++ b/src/librustc_borrowck/borrowck/mod.rs
@@ -520,6 +520,7 @@ pub fn opt_loan_path_is_field<'tcx>(cmt: &mc::cmt_<'tcx>) -> (Option<Rc<LoanPath
 
     match cmt.cat {
         Categorization::Rvalue(..) |
+        Categorization::ThreadLocal(..) |
         Categorization::StaticItem => {
             (None, false)
         }
diff --git a/src/librustc_passes/rvalue_promotion.rs b/src/librustc_passes/rvalue_promotion.rs
index 5e9169e86a98d..ca58239df8eac 100644
--- a/src/librustc_passes/rvalue_promotion.rs
+++ b/src/librustc_passes/rvalue_promotion.rs
@@ -663,6 +663,7 @@ impl<'a, 'gcx, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'gcx> {
         let mut cur = cmt;
         loop {
             match cur.cat {
+                Categorization::ThreadLocal(..) |
                 Categorization::Rvalue(..) => {
                     if loan_cause == euv::MatchDiscriminant {
                         // Ignore the dummy immutable borrow created by EUV.
diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs
index 80b4ba6240d33..212ee2698e012 100644
--- a/src/librustc_typeck/check/regionck.rs
+++ b/src/librustc_typeck/check/regionck.rs
@@ -1243,6 +1243,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
                 | Categorization::StaticItem
                 | Categorization::Upvar(..)
                 | Categorization::Local(..)
+                | Categorization::ThreadLocal(..)
                 | Categorization::Rvalue(..) => {
                     // These are all "base cases" with independent lifetimes
                     // that are not subject to inference
diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs
index 99effce4ee08d..df994ad9e55c4 100644
--- a/src/librustc_typeck/check/upvar.rs
+++ b/src/librustc_typeck/check/upvar.rs
@@ -401,6 +401,7 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> {
 
             Categorization::Deref(_, mc::UnsafePtr(..)) |
             Categorization::StaticItem |
+            Categorization::ThreadLocal(..) |
             Categorization::Rvalue(..) |
             Categorization::Local(_) |
             Categorization::Upvar(..) => {
@@ -431,6 +432,7 @@ impl<'a, 'gcx, 'tcx> InferBorrowKind<'a, 'gcx, 'tcx> {
 
             Categorization::Deref(_, mc::UnsafePtr(..)) |
             Categorization::StaticItem |
+            Categorization::ThreadLocal(..) |
             Categorization::Rvalue(..) |
             Categorization::Local(_) |
             Categorization::Upvar(..) => {}

From e6e4fe6209d0b31c3d119fb75744531f8fad2af8 Mon Sep 17 00:00:00 2001
From: "Felix S. Klock II" <pnkfelix@pnkfx.org>
Date: Wed, 17 Oct 2018 16:32:03 +0200
Subject: [PATCH 15/30] Update existing tests and .stderr files to reflect
 introduction of thread-local mem category.

---
 ...borrowck-thread-local-static-borrow-outlives-fn.ast.stderr | 4 ++--
 src/test/ui/issues/issue-17954.rs                             | 2 +-
 src/test/ui/issues/issue-17954.stderr                         | 4 ++--
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/test/ui/borrowck/borrowck-thread-local-static-borrow-outlives-fn.ast.stderr b/src/test/ui/borrowck/borrowck-thread-local-static-borrow-outlives-fn.ast.stderr
index 27e0dba5095b2..505bcfb1e6e14 100644
--- a/src/test/ui/borrowck/borrowck-thread-local-static-borrow-outlives-fn.ast.stderr
+++ b/src/test/ui/borrowck/borrowck-thread-local-static-borrow-outlives-fn.ast.stderr
@@ -2,9 +2,9 @@ error[E0597]: borrowed value does not live long enough
   --> $DIR/borrowck-thread-local-static-borrow-outlives-fn.rs:21:21
    |
 LL |      assert_static(&FOO); //[ast]~ ERROR [E0597]
-   |                     ^^^ - temporary value only lives until here
+   |                     ^^^ - borrowed value only lives until here
    |                     |
-   |                     temporary value does not live long enough
+   |                     borrowed value does not live long enough
    |
    = note: borrowed value must be valid for the static lifetime...
 
diff --git a/src/test/ui/issues/issue-17954.rs b/src/test/ui/issues/issue-17954.rs
index ce554a7254812..5f003436951ce 100644
--- a/src/test/ui/issues/issue-17954.rs
+++ b/src/test/ui/issues/issue-17954.rs
@@ -23,4 +23,4 @@ fn main() {
         println!("{}", a);
     });
 }
-//~^ NOTE temporary value only lives until here
+//~^ NOTE borrowed value only lives until here
diff --git a/src/test/ui/issues/issue-17954.stderr b/src/test/ui/issues/issue-17954.stderr
index 76858a9b097b2..020e544ad10ff 100644
--- a/src/test/ui/issues/issue-17954.stderr
+++ b/src/test/ui/issues/issue-17954.stderr
@@ -2,10 +2,10 @@ error[E0597]: borrowed value does not live long enough
   --> $DIR/issue-17954.rs:17:14
    |
 LL |     let a = &FOO;
-   |              ^^^ temporary value does not live long enough
+   |              ^^^ borrowed value does not live long enough
 ...
 LL | }
-   | - temporary value only lives until here
+   | - borrowed value only lives until here
    |
    = note: borrowed value must be valid for the static lifetime...
 

From 1d46ce5a7229111850e2619bf297a0561b878cec Mon Sep 17 00:00:00 2001
From: "Felix S. Klock II" <pnkfelix@pnkfx.org>
Date: Wed, 17 Oct 2018 16:32:43 +0200
Subject: [PATCH 16/30] Add regression test for issue 47215.

---
 .../issue-47215-ice-from-drop-elab.nll.stderr | 12 +++++++++++
 .../issue-47215-ice-from-drop-elab.rs         | 20 +++++++++++++++++++
 .../issue-47215-ice-from-drop-elab.stderr     | 12 +++++++++++
 3 files changed, 44 insertions(+)
 create mode 100644 src/test/ui/borrowck/issue-47215-ice-from-drop-elab.nll.stderr
 create mode 100644 src/test/ui/borrowck/issue-47215-ice-from-drop-elab.rs
 create mode 100644 src/test/ui/borrowck/issue-47215-ice-from-drop-elab.stderr

diff --git a/src/test/ui/borrowck/issue-47215-ice-from-drop-elab.nll.stderr b/src/test/ui/borrowck/issue-47215-ice-from-drop-elab.nll.stderr
new file mode 100644
index 0000000000000..255eb757a4989
--- /dev/null
+++ b/src/test/ui/borrowck/issue-47215-ice-from-drop-elab.nll.stderr
@@ -0,0 +1,12 @@
+error[E0507]: cannot move out of static item
+  --> $DIR/issue-47215-ice-from-drop-elab.rs:17:21
+   |
+LL |         let mut x = X; //~ ERROR cannot move out of thread-local static item [E0507]
+   |                     ^
+   |                     |
+   |                     cannot move out of static item
+   |                     help: consider borrowing here: `&X`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/borrowck/issue-47215-ice-from-drop-elab.rs b/src/test/ui/borrowck/issue-47215-ice-from-drop-elab.rs
new file mode 100644
index 0000000000000..670c6bb869d59
--- /dev/null
+++ b/src/test/ui/borrowck/issue-47215-ice-from-drop-elab.rs
@@ -0,0 +1,20 @@
+// rust-lang/rust#47215: at one time, the compiler categorized
+// thread-local statics as a temporary rvalue, as a way to enforce
+// that they are only valid for a given lifetime.
+//
+// The problem with this is that you cannot move out of static items,
+// but you *can* move temporary rvalues. I.e., the categorization
+// above only solves half of the problem presented by thread-local
+// statics.
+
+#![feature(thread_local)]
+
+#[thread_local]
+static mut X: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::ATOMIC_USIZE_INIT;
+
+fn main() {
+    unsafe {
+        let mut x = X; //~ ERROR cannot move out of thread-local static item [E0507]
+        let _y = x.get_mut();
+    }
+}
diff --git a/src/test/ui/borrowck/issue-47215-ice-from-drop-elab.stderr b/src/test/ui/borrowck/issue-47215-ice-from-drop-elab.stderr
new file mode 100644
index 0000000000000..219a1fd2e7727
--- /dev/null
+++ b/src/test/ui/borrowck/issue-47215-ice-from-drop-elab.stderr
@@ -0,0 +1,12 @@
+error[E0507]: cannot move out of thread-local static item
+  --> $DIR/issue-47215-ice-from-drop-elab.rs:17:21
+   |
+LL |         let mut x = X; //~ ERROR cannot move out of thread-local static item [E0507]
+   |                     ^
+   |                     |
+   |                     cannot move out of thread-local static item
+   |                     help: consider using a reference instead: `&X`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0507`.

From 9696882b1b2b0ce5081e8f3ae324f7f2f54eba89 Mon Sep 17 00:00:00 2001
From: Alex Crichton <alex@alexcrichton.com>
Date: Sat, 20 Oct 2018 19:04:42 -0700
Subject: [PATCH 17/30] Remove all jemalloc-related content

This commit removes all jemalloc related submodules, configuration, etc,
from the bootstrap, from the standard library, and from the compiler.
This will be followed up with a change to use jemalloc specifically as
part of rustc on blessed platforms.
---
 .gitmodules                                  |   5 +-
 config.toml.example                          |  10 --
 src/Cargo.lock                               |  12 --
 src/bootstrap/bootstrap.py                   |   5 -
 src/bootstrap/compile.rs                     |  16 +-
 src/bootstrap/config.rs                      |  14 --
 src/bootstrap/configure.py                   |   5 -
 src/bootstrap/dist.rs                        |   3 -
 src/bootstrap/lib.rs                         |  14 +-
 src/bootstrap/sanity.rs                      |   6 -
 src/bootstrap/test.rs                        |   5 +-
 src/jemalloc                                 |   1 -
 src/liballoc/tests/heap.rs                   |   3 -
 src/liballoc_jemalloc/Cargo.toml             |  24 ---
 src/liballoc_jemalloc/build.rs               | 151 -------------------
 src/liballoc_jemalloc/lib.rs                 | 127 ----------------
 src/liballoc_jemalloc/pthread_atfork_dummy.c |  16 --
 src/librustc_target/Cargo.toml               |   3 -
 src/librustc_target/spec/apple_base.rs       |   1 -
 src/librustc_target/spec/apple_ios_base.rs   |   4 -
 src/librustc_target/spec/cloudabi_base.rs    |   1 -
 src/librustc_target/spec/dragonfly_base.rs   |   1 -
 src/librustc_target/spec/freebsd_base.rs     |   1 -
 src/librustc_target/spec/linux_base.rs       |   1 -
 src/librustc_target/spec/mod.rs              |   8 -
 src/librustc_target/spec/solaris_base.rs     |   1 -
 src/libstd/Cargo.toml                        |   4 -
 src/libstd/alloc.rs                          |   9 +-
 src/libstd/lib.rs                            |   9 +-
 src/rustc/Cargo.toml                         |   3 -
 src/tools/tidy/src/cargo.rs                  |   1 -
 src/tools/tidy/src/lib.rs                    |   1 -
 src/tools/tidy/src/pal.rs                    |   2 -
 33 files changed, 9 insertions(+), 458 deletions(-)
 delete mode 160000 src/jemalloc
 delete mode 100644 src/liballoc_jemalloc/Cargo.toml
 delete mode 100644 src/liballoc_jemalloc/build.rs
 delete mode 100644 src/liballoc_jemalloc/lib.rs
 delete mode 100644 src/liballoc_jemalloc/pthread_atfork_dummy.c

diff --git a/.gitmodules b/.gitmodules
index 038237aa179a9..bf9bdd9a5b4b0 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -2,9 +2,6 @@
 	path = src/llvm
 	url = https://github.com/rust-lang/llvm.git
 	branch = master
-[submodule "src/jemalloc"]
-	path = src/jemalloc
-	url = https://github.com/rust-lang/jemalloc.git
 [submodule "src/rust-installer"]
 	path = src/tools/rust-installer
 	url = https://github.com/rust-lang/rust-installer.git
@@ -64,4 +61,4 @@
 	path = src/tools/clang
 	url = https://github.com/rust-lang-nursery/clang.git
 	branch = rust-release-80-v1
-  
\ No newline at end of file
+
diff --git a/config.toml.example b/config.toml.example
index e8cb0cba6b1f9..42234c261cf8d 100644
--- a/config.toml.example
+++ b/config.toml.example
@@ -296,12 +296,6 @@
 # Adding debuginfo makes them several times larger.
 #debuginfo-tools = false
 
-# Whether or not jemalloc is built and enabled
-#use-jemalloc = true
-
-# Whether or not jemalloc is built with its debug option set
-#debug-jemalloc = false
-
 # Whether or not `panic!`s generate backtraces (RUST_BACKTRACE)
 #backtrace = true
 
@@ -437,10 +431,6 @@
 # not, you can specify an explicit file name for it.
 #llvm-filecheck = "/path/to/FileCheck"
 
-# Path to the custom jemalloc static library to link into the standard library
-# by default. This is only used if jemalloc is still enabled above
-#jemalloc = "/path/to/jemalloc/libjemalloc_pic.a"
-
 # If this target is for Android, this option will be required to specify where
 # the NDK for the target lives. This is used to find the C compiler to link and
 # build native code.
diff --git a/src/Cargo.lock b/src/Cargo.lock
index 3361e81ecfe6d..7264b92a96828 100644
--- a/src/Cargo.lock
+++ b/src/Cargo.lock
@@ -15,17 +15,6 @@ dependencies = [
  "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
-[[package]]
-name = "alloc_jemalloc"
-version = "0.0.0"
-dependencies = [
- "build_helper 0.1.0",
- "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
- "compiler_builtins 0.0.0",
- "core 0.0.0",
- "libc 0.0.0",
-]
-
 [[package]]
 name = "alloc_system"
 version = "0.0.0"
@@ -2678,7 +2667,6 @@ name = "std"
 version = "0.0.0"
 dependencies = [
  "alloc 0.0.0",
- "alloc_jemalloc 0.0.0",
  "alloc_system 0.0.0",
  "build_helper 0.1.0",
  "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py
index c27f4f056d747..2b58fef84f49a 100644
--- a/src/bootstrap/bootstrap.py
+++ b/src/bootstrap/bootstrap.py
@@ -712,11 +712,6 @@ def update_submodules(self):
                 backends = self.get_toml('codegen-backends')
                 if backends is None or not 'emscripten' in backends:
                     continue
-            if module.endswith("jemalloc"):
-                if self.get_toml('use-jemalloc') == 'false':
-                    continue
-                if self.get_toml('jemalloc'):
-                    continue
             if module.endswith("lld"):
                 config = self.get_toml('lld')
                 if config is None or config == 'false':
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index 69d45acdedaf9..25f12ea34f203 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -158,16 +158,7 @@ pub fn std_cargo(builder: &Builder,
             .arg("--manifest-path")
             .arg(builder.src.join("src/rustc/compiler_builtins_shim/Cargo.toml"));
     } else {
-        let mut features = builder.std_features();
-
-        // When doing a local rebuild we tell cargo that we're stage1 rather than
-        // stage0. This works fine if the local rust and being-built rust have the
-        // same view of what the default allocator is, but fails otherwise. Since
-        // we don't have a way to express an allocator preference yet, work
-        // around the issue in the case of a local rebuild with jemalloc disabled.
-        if compiler.stage == 0 && builder.local_rebuild && !builder.config.use_jemalloc {
-            features.push_str(" force_alloc_system");
-        }
+        let features = builder.std_features();
 
         if compiler.stage != 0 && builder.config.sanitizers {
             // This variable is used by the sanitizer runtime crates, e.g.
@@ -188,11 +179,6 @@ pub fn std_cargo(builder: &Builder,
             .arg("--manifest-path")
             .arg(builder.src.join("src/libstd/Cargo.toml"));
 
-        if let Some(target) = builder.config.target_config.get(&target) {
-            if let Some(ref jemalloc) = target.jemalloc {
-                cargo.env("JEMALLOC_OVERRIDE", jemalloc);
-            }
-        }
         if target.contains("musl") {
             if let Some(p) = builder.musl_root(target) {
                 cargo.env("MUSL_ROOT", p);
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index a9d330e06a15d..9c68e12354509 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -122,8 +122,6 @@ pub struct Config {
     pub dist_gpg_password_file: Option<PathBuf>,
 
     // libstd features
-    pub debug_jemalloc: bool,
-    pub use_jemalloc: bool,
     pub backtrace: bool, // support for RUST_BACKTRACE
     pub wasm_syscall: bool,
 
@@ -165,7 +163,6 @@ pub struct Target {
     pub llvm_config: Option<PathBuf>,
     /// Some(path to FileCheck) if one was specified.
     pub llvm_filecheck: Option<PathBuf>,
-    pub jemalloc: Option<PathBuf>,
     pub cc: Option<PathBuf>,
     pub cxx: Option<PathBuf>,
     pub ar: Option<PathBuf>,
@@ -300,8 +297,6 @@ struct Rust {
     debuginfo_only_std: Option<bool>,
     debuginfo_tools: Option<bool>,
     experimental_parallel_queries: Option<bool>,
-    debug_jemalloc: Option<bool>,
-    use_jemalloc: Option<bool>,
     backtrace: Option<bool>,
     default_linker: Option<String>,
     channel: Option<String>,
@@ -335,7 +330,6 @@ struct Rust {
 struct TomlTarget {
     llvm_config: Option<String>,
     llvm_filecheck: Option<String>,
-    jemalloc: Option<String>,
     cc: Option<String>,
     cxx: Option<String>,
     ar: Option<String>,
@@ -361,7 +355,6 @@ impl Config {
         config.llvm_enabled = true;
         config.llvm_optimize = true;
         config.llvm_version_check = true;
-        config.use_jemalloc = true;
         config.backtrace = true;
         config.rust_optimize = true;
         config.rust_optimize_tests = true;
@@ -497,7 +490,6 @@ impl Config {
         let mut debuginfo_only_std = None;
         let mut debuginfo_tools = None;
         let mut debug = None;
-        let mut debug_jemalloc = None;
         let mut debuginfo = None;
         let mut debug_assertions = None;
         let mut optimize = None;
@@ -539,12 +531,10 @@ impl Config {
             debuginfo_tools = rust.debuginfo_tools;
             optimize = rust.optimize;
             ignore_git = rust.ignore_git;
-            debug_jemalloc = rust.debug_jemalloc;
             set(&mut config.rust_optimize_tests, rust.optimize_tests);
             set(&mut config.rust_debuginfo_tests, rust.debuginfo_tests);
             set(&mut config.codegen_tests, rust.codegen_tests);
             set(&mut config.rust_rpath, rust.rpath);
-            set(&mut config.use_jemalloc, rust.use_jemalloc);
             set(&mut config.backtrace, rust.backtrace);
             set(&mut config.channel, rust.channel.clone());
             set(&mut config.rust_dist_src, rust.dist_src);
@@ -592,9 +582,6 @@ impl Config {
                 if let Some(ref s) = cfg.llvm_filecheck {
                     target.llvm_filecheck = Some(config.src.join(s));
                 }
-                if let Some(ref s) = cfg.jemalloc {
-                    target.jemalloc = Some(config.src.join(s));
-                }
                 if let Some(ref s) = cfg.android_ndk {
                     target.ndk = Some(config.src.join(s));
                 }
@@ -640,7 +627,6 @@ impl Config {
         config.rust_debuginfo_tools = debuginfo_tools.unwrap_or(false);
 
         let default = debug == Some(true);
-        config.debug_jemalloc = debug_jemalloc.unwrap_or(default);
         config.rust_debuginfo = debuginfo.unwrap_or(default);
         config.rust_debug_assertions = debug_assertions.unwrap_or(default);
 
diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py
index 0cf84a62986a5..78e6ad1a5d202 100755
--- a/src/bootstrap/configure.py
+++ b/src/bootstrap/configure.py
@@ -82,7 +82,6 @@ def v(*args):
 o("debuginfo-lines", "rust.debuginfo-lines", "build with line number debugger metadata")
 o("debuginfo-only-std", "rust.debuginfo-only-std", "build only libstd with debugging information")
 o("debuginfo-tools", "rust.debuginfo-tools", "build extended tools with debugging information")
-o("debug-jemalloc", "rust.debug-jemalloc", "build jemalloc with --enable-debug --enable-fill")
 v("save-toolstates", "rust.save-toolstates", "save build and test status of external tools into this file")
 
 v("prefix", "install.prefix", "set installation prefix")
@@ -99,7 +98,6 @@ def v(*args):
 v("llvm-config", None, "set path to llvm-config")
 v("llvm-filecheck", None, "set path to LLVM's FileCheck utility")
 v("python", "build.python", "set path to python")
-v("jemalloc-root", None, "set directory where libjemalloc_pic.a is located")
 v("android-cross-path", "target.arm-linux-androideabi.android-ndk",
   "Android NDK standalone path (deprecated)")
 v("i686-linux-android-ndk", "target.i686-linux-android.android-ndk",
@@ -148,7 +146,6 @@ def v(*args):
 # Many of these are saved below during the "writing configuration" step
 # (others are conditionally saved).
 o("manage-submodules", "build.submodules", "let the build manage the git submodules")
-o("jemalloc", "rust.use-jemalloc", "build liballoc with jemalloc")
 o("full-bootstrap", "build.full-bootstrap", "build three compilers instead of two")
 o("extended", "build.extended", "build an extended rust tool set")
 
@@ -330,8 +327,6 @@ def set(key, value):
         set('target.{}.llvm-config'.format(build()), value)
     elif option.name == 'llvm-filecheck':
         set('target.{}.llvm-filecheck'.format(build()), value)
-    elif option.name == 'jemalloc-root':
-        set('target.{}.jemalloc'.format(build()), value + '/libjemalloc_pic.a')
     elif option.name == 'tools':
         set('build.tools', value.split(','))
     elif option.name == 'host':
diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs
index 4de899ee2d599..850b0494ea525 100644
--- a/src/bootstrap/dist.rs
+++ b/src/bootstrap/dist.rs
@@ -859,7 +859,6 @@ impl Step for Src {
             "src/build_helper",
             "src/dlmalloc",
             "src/liballoc",
-            "src/liballoc_jemalloc",
             "src/liballoc_system",
             "src/libbacktrace",
             "src/libcompiler_builtins",
@@ -878,13 +877,11 @@ impl Step for Src {
             "src/rustc/dlmalloc_shim",
             "src/libtest",
             "src/libterm",
-            "src/jemalloc",
             "src/libprofiler_builtins",
             "src/stdsimd",
         ];
         let std_src_dirs_exclude = [
             "src/libcompiler_builtins/compiler-rt/test",
-            "src/jemalloc/test/unit",
         ];
 
         copy_src_dirs(builder, &std_src_dirs[..], &std_src_dirs_exclude[..], &dst_src);
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index 4e26c98a62759..5cf9ccaa71500 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -516,12 +516,6 @@ impl Build {
     fn std_features(&self) -> String {
         let mut features = "panic-unwind".to_string();
 
-        if self.config.debug_jemalloc {
-            features.push_str(" debug-jemalloc");
-        }
-        if self.config.use_jemalloc {
-            features.push_str(" jemalloc");
-        }
         if self.config.backtrace {
             features.push_str(" backtrace");
         }
@@ -536,11 +530,7 @@ impl Build {
 
     /// Get the space-separated set of activated features for the compiler.
     fn rustc_features(&self) -> String {
-        let mut features = String::new();
-        if self.config.use_jemalloc {
-            features.push_str(" jemalloc");
-        }
-        features
+        String::new()
     }
 
     /// Component directory that Cargo will produce output into (e.g.
@@ -791,7 +781,7 @@ impl Build {
         // If we're compiling on macOS then we add a few unconditional flags
         // indicating that we want libc++ (more filled out than libstdc++) and
         // we want to compile for 10.7. This way we can ensure that
-        // LLVM/jemalloc/etc are all properly compiled.
+        // LLVM/etc are all properly compiled.
         if target.contains("apple-darwin") {
             base.push("-stdlib=libc++".into());
         }
diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs
index 724cb5841f48f..ed8956ce3e051 100644
--- a/src/bootstrap/sanity.rs
+++ b/src/bootstrap/sanity.rs
@@ -152,12 +152,6 @@ pub fn check(build: &mut Build) {
         if !build.config.dry_run {
             cmd_finder.must_have(build.cxx(*host).unwrap());
         }
-
-        // The msvc hosts don't use jemalloc, turn it off globally to
-        // avoid packaging the dummy liballoc_jemalloc on that platform.
-        if host.contains("msvc") {
-            build.config.use_jemalloc = false;
-        }
     }
 
     // Externally configured LLVM requires FileCheck to exist
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index 6e6f772a7f351..9d771db94d4ca 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -1510,8 +1510,7 @@ impl Step for CrateNotDefault {
     type Output = ();
 
     fn should_run(run: ShouldRun) -> ShouldRun {
-        run.path("src/liballoc_jemalloc")
-            .path("src/librustc_asan")
+        run.path("src/librustc_asan")
             .path("src/librustc_lsan")
             .path("src/librustc_msan")
             .path("src/librustc_tsan")
@@ -1528,7 +1527,6 @@ impl Step for CrateNotDefault {
             target: run.target,
             test_kind,
             krate: match run.path {
-                _ if run.path.ends_with("src/liballoc_jemalloc") => "alloc_jemalloc",
                 _ if run.path.ends_with("src/librustc_asan") => "rustc_asan",
                 _ if run.path.ends_with("src/librustc_lsan") => "rustc_lsan",
                 _ if run.path.ends_with("src/librustc_msan") => "rustc_msan",
@@ -1567,7 +1565,6 @@ impl Step for Crate {
         run = run.krate("test");
         for krate in run.builder.in_tree_crates("std") {
             if krate.is_local(&run.builder)
-                && !krate.name.contains("jemalloc")
                 && !(krate.name.starts_with("rustc_") && krate.name.ends_with("san"))
                 && krate.name != "dlmalloc"
             {
diff --git a/src/jemalloc b/src/jemalloc
deleted file mode 160000
index 1f5a28755e301..0000000000000
--- a/src/jemalloc
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 1f5a28755e301ac581e2048011e4e0ff3da482ef
diff --git a/src/liballoc/tests/heap.rs b/src/liballoc/tests/heap.rs
index 6fa88ce969a0e..b6be38107da7f 100644
--- a/src/liballoc/tests/heap.rs
+++ b/src/liballoc/tests/heap.rs
@@ -12,9 +12,6 @@ use alloc_system::System;
 use std::alloc::{Global, Alloc, Layout};
 
 /// https://github.com/rust-lang/rust/issues/45955
-///
-/// Note that `#[global_allocator]` is not used,
-/// so `liballoc_jemalloc` is linked (on some platforms).
 #[test]
 fn alloc_system_overaligned_request() {
     check_overalign_requests(System)
diff --git a/src/liballoc_jemalloc/Cargo.toml b/src/liballoc_jemalloc/Cargo.toml
deleted file mode 100644
index 7986d5dd2eb54..0000000000000
--- a/src/liballoc_jemalloc/Cargo.toml
+++ /dev/null
@@ -1,24 +0,0 @@
-[package]
-authors = ["The Rust Project Developers"]
-name = "alloc_jemalloc"
-version = "0.0.0"
-build = "build.rs"
-links = "jemalloc"
-
-[lib]
-name = "alloc_jemalloc"
-path = "lib.rs"
-test = false
-doc = false
-
-[dependencies]
-core = { path = "../libcore" }
-libc = { path = "../rustc/libc_shim" }
-compiler_builtins = { path = "../rustc/compiler_builtins_shim" }
-
-[build-dependencies]
-build_helper = { path = "../build_helper" }
-cc = "1.0.1"
-
-[features]
-debug = []
diff --git a/src/liballoc_jemalloc/build.rs b/src/liballoc_jemalloc/build.rs
deleted file mode 100644
index fbda425a70bf5..0000000000000
--- a/src/liballoc_jemalloc/build.rs
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-#![deny(warnings)]
-
-extern crate build_helper;
-extern crate cc;
-
-use std::env;
-use std::path::PathBuf;
-use std::process::Command;
-use build_helper::{run, native_lib_boilerplate};
-
-fn main() {
-    // FIXME: This is a hack to support building targets that don't
-    // support jemalloc alongside hosts that do. The jemalloc build is
-    // controlled by a feature of the std crate, and if that feature
-    // changes between targets, it invalidates the fingerprint of
-    // std's build script (this is a cargo bug); so we must ensure
-    // that the feature set used by std is the same across all
-    // targets, which means we have to build the alloc_jemalloc crate
-    // for targets like emscripten, even if we don't use it.
-    let target = env::var("TARGET").expect("TARGET was not set");
-    let host = env::var("HOST").expect("HOST was not set");
-    if target.contains("bitrig") || target.contains("emscripten") || target.contains("fuchsia") ||
-       target.contains("msvc") || target.contains("openbsd") || target.contains("redox") ||
-       target.contains("rumprun") || target.contains("wasm32") {
-        println!("cargo:rustc-cfg=dummy_jemalloc");
-        return;
-    }
-
-    // CloudABI ships with a copy of jemalloc that has been patched to
-    // work well with sandboxing. Don't attempt to build our own copy,
-    // as it won't build.
-    if target.contains("cloudabi") {
-        return;
-    }
-
-    if target.contains("android") {
-        println!("cargo:rustc-link-lib=gcc");
-    } else if !target.contains("windows") && !target.contains("musl") {
-        println!("cargo:rustc-link-lib=pthread");
-    }
-
-    if let Some(jemalloc) = env::var_os("JEMALLOC_OVERRIDE") {
-        let jemalloc = PathBuf::from(jemalloc);
-        println!("cargo:rustc-link-search=native={}",
-                 jemalloc.parent().unwrap().display());
-        let stem = jemalloc.file_stem().unwrap().to_str().unwrap();
-        let name = jemalloc.file_name().unwrap().to_str().unwrap();
-        let kind = if name.ends_with(".a") {
-            "static"
-        } else {
-            "dylib"
-        };
-        println!("cargo:rustc-link-lib={}={}", kind, &stem[3..]);
-        return;
-    }
-
-    let link_name = if target.contains("windows") { "jemalloc" } else { "jemalloc_pic" };
-    let native = match native_lib_boilerplate("jemalloc", "jemalloc", link_name, "lib") {
-        Ok(native) => native,
-        _ => return,
-    };
-
-    let mut cmd = Command::new("sh");
-    cmd.arg(native.src_dir.join("configure")
-                          .to_str()
-                          .unwrap()
-                          .replace("C:\\", "/c/")
-                          .replace("\\", "/"))
-       .current_dir(&native.out_dir)
-       // jemalloc generates Makefile deps using GCC's "-MM" flag. This means
-       // that GCC will run the preprocessor, and only the preprocessor, over
-       // jemalloc's source files. If we don't specify CPPFLAGS, then at least
-       // on ARM that step fails with a "Missing implementation for 32-bit
-       // atomic operations" error. This is because no "-march" flag will be
-       // passed to GCC, and then GCC won't define the
-       // "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4" macro that jemalloc needs to
-       // select an atomic operation implementation.
-       .env("CPPFLAGS", env::var_os("CFLAGS").unwrap_or_default());
-
-    if target.contains("ios") {
-        cmd.arg("--disable-tls");
-    } else if target.contains("android") {
-        // We force android to have prefixed symbols because apparently
-        // replacement of the libc allocator doesn't quite work. When this was
-        // tested (unprefixed symbols), it was found that the `realpath`
-        // function in libc would allocate with libc malloc (not jemalloc
-        // malloc), and then the standard library would free with jemalloc free,
-        // causing a segfault.
-        //
-        // If the test suite passes, however, without symbol prefixes then we
-        // should be good to go!
-        cmd.arg("--with-jemalloc-prefix=je_");
-        cmd.arg("--disable-tls");
-    } else if target.contains("dragonfly") || target.contains("musl") {
-        cmd.arg("--with-jemalloc-prefix=je_");
-    }
-
-    if cfg!(feature = "debug") {
-        // Enable jemalloc assertions.
-        cmd.arg("--enable-debug");
-    }
-
-    cmd.arg(format!("--host={}", build_helper::gnu_target(&target)));
-    cmd.arg(format!("--build={}", build_helper::gnu_target(&host)));
-
-    // for some reason, jemalloc configure doesn't detect this value
-    // automatically for this target
-    if target == "sparc64-unknown-linux-gnu" {
-        cmd.arg("--with-lg-quantum=4");
-    }
-
-    run(&mut cmd);
-
-    let mut make = Command::new(build_helper::make(&host));
-    make.current_dir(&native.out_dir)
-        .arg("build_lib_static");
-
-    // These are intended for mingw32-make which we don't use
-    if cfg!(windows) {
-        make.env_remove("MAKEFLAGS").env_remove("MFLAGS");
-    }
-
-    // mingw make seems... buggy? unclear...
-    if !host.contains("windows") {
-        make.arg("-j")
-            .arg(env::var("NUM_JOBS").expect("NUM_JOBS was not set"));
-    }
-
-    run(&mut make);
-
-    // The pthread_atfork symbols is used by jemalloc on android but the really
-    // old android we're building on doesn't have them defined, so just make
-    // sure the symbols are available.
-    if target.contains("androideabi") {
-        println!("cargo:rerun-if-changed=pthread_atfork_dummy.c");
-        cc::Build::new()
-            .flag("-fvisibility=hidden")
-            .file("pthread_atfork_dummy.c")
-            .compile("pthread_atfork_dummy");
-    }
-}
diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs
deleted file mode 100644
index 0065e84a7ad1a..0000000000000
--- a/src/liballoc_jemalloc/lib.rs
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-#![no_std]
-#![allow(unused_attributes)]
-#![unstable(feature = "alloc_jemalloc",
-            reason = "implementation detail of std, does not provide any public API",
-            issue = "0")]
-#![feature(core_intrinsics)]
-#![feature(libc)]
-#![feature(linkage)]
-#![feature(nll)]
-#![feature(staged_api)]
-#![feature(rustc_attrs)]
-#![cfg_attr(dummy_jemalloc, allow(dead_code, unused_extern_crates))]
-#![cfg_attr(not(dummy_jemalloc), feature(allocator_api))]
-#![rustc_alloc_kind = "exe"]
-
-extern crate libc;
-
-#[cfg(not(dummy_jemalloc))]
-pub use contents::*;
-#[cfg(not(dummy_jemalloc))]
-mod contents {
-    use libc::{c_int, c_void, size_t};
-
-    // Note that the symbols here are prefixed by default on macOS and Windows (we
-    // don't explicitly request it), and on Android and DragonFly we explicitly
-    // request it as unprefixing cause segfaults (mismatches in allocators).
-    extern "C" {
-        #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
-                       target_os = "dragonfly", target_os = "windows", target_env = "musl"),
-                   link_name = "je_mallocx")]
-        fn mallocx(size: size_t, flags: c_int) -> *mut c_void;
-        #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
-                       target_os = "dragonfly", target_os = "windows", target_env = "musl"),
-                   link_name = "je_calloc")]
-        fn calloc(size: size_t, flags: c_int) -> *mut c_void;
-        #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
-                       target_os = "dragonfly", target_os = "windows", target_env = "musl"),
-                   link_name = "je_rallocx")]
-        fn rallocx(ptr: *mut c_void, size: size_t, flags: c_int) -> *mut c_void;
-        #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
-                       target_os = "dragonfly", target_os = "windows", target_env = "musl"),
-                   link_name = "je_sdallocx")]
-        fn sdallocx(ptr: *mut c_void, size: size_t, flags: c_int);
-    }
-
-    const MALLOCX_ZERO: c_int = 0x40;
-
-    // The minimum alignment guaranteed by the architecture. This value is used to
-    // add fast paths for low alignment values.
-    #[cfg(all(any(target_arch = "arm",
-                  target_arch = "mips",
-                  target_arch = "powerpc")))]
-    const MIN_ALIGN: usize = 8;
-    #[cfg(all(any(target_arch = "x86",
-                  target_arch = "x86_64",
-                  target_arch = "aarch64",
-                  target_arch = "powerpc64",
-                  target_arch = "mips64",
-                  target_arch = "s390x",
-                  target_arch = "sparc64")))]
-    const MIN_ALIGN: usize = 16;
-
-    // MALLOCX_ALIGN(a) macro
-    fn mallocx_align(a: usize) -> c_int {
-        a.trailing_zeros() as c_int
-    }
-
-    fn align_to_flags(align: usize, size: usize) -> c_int {
-        if align <= MIN_ALIGN && align <= size {
-            0
-        } else {
-            mallocx_align(align)
-        }
-    }
-
-    // for symbol names src/librustc/middle/allocator.rs
-    // for signatures src/librustc_allocator/lib.rs
-
-    // linkage directives are provided as part of the current compiler allocator
-    // ABI
-
-    #[rustc_std_internal_symbol]
-    pub unsafe extern fn __rde_alloc(size: usize, align: usize) -> *mut u8 {
-        let flags = align_to_flags(align, size);
-        let ptr = mallocx(size as size_t, flags) as *mut u8;
-        ptr
-    }
-
-    #[rustc_std_internal_symbol]
-    pub unsafe extern fn __rde_dealloc(ptr: *mut u8,
-                                       size: usize,
-                                       align: usize) {
-        let flags = align_to_flags(align, size);
-        sdallocx(ptr as *mut c_void, size, flags);
-    }
-
-    #[rustc_std_internal_symbol]
-    pub unsafe extern fn __rde_realloc(ptr: *mut u8,
-                                       _old_size: usize,
-                                       align: usize,
-                                       new_size: usize) -> *mut u8 {
-        let flags = align_to_flags(align, new_size);
-        let ptr = rallocx(ptr as *mut c_void, new_size, flags) as *mut u8;
-        ptr
-    }
-
-    #[rustc_std_internal_symbol]
-    pub unsafe extern fn __rde_alloc_zeroed(size: usize, align: usize) -> *mut u8 {
-        let ptr = if align <= MIN_ALIGN && align <= size {
-            calloc(size as size_t, 1) as *mut u8
-        } else {
-            let flags = align_to_flags(align, size) | MALLOCX_ZERO;
-            mallocx(size as size_t, flags) as *mut u8
-        };
-        ptr
-    }
-}
diff --git a/src/liballoc_jemalloc/pthread_atfork_dummy.c b/src/liballoc_jemalloc/pthread_atfork_dummy.c
deleted file mode 100644
index 4e3df0ab26c37..0000000000000
--- a/src/liballoc_jemalloc/pthread_atfork_dummy.c
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// See comments in build.rs for why this exists
-int pthread_atfork(void* prefork,
-                   void* postfork_parent,
-                   void* postfork_child) {
-  return 0;
-}
diff --git a/src/librustc_target/Cargo.toml b/src/librustc_target/Cargo.toml
index bb686e914a048..684ea4c78978c 100644
--- a/src/librustc_target/Cargo.toml
+++ b/src/librustc_target/Cargo.toml
@@ -13,6 +13,3 @@ bitflags = "1.0"
 log = "0.4"
 rustc_cratesio_shim = { path = "../librustc_cratesio_shim" }
 serialize = { path = "../libserialize" }
-
-[features]
-jemalloc = []
diff --git a/src/librustc_target/spec/apple_base.rs b/src/librustc_target/spec/apple_base.rs
index 38b3f2528fe86..8774c15ff0121 100644
--- a/src/librustc_target/spec/apple_base.rs
+++ b/src/librustc_target/spec/apple_base.rs
@@ -44,7 +44,6 @@ pub fn opts() -> TargetOptions {
         dll_suffix: ".dylib".to_string(),
         archive_format: "bsd".to_string(),
         pre_link_args: LinkArgs::new(),
-        exe_allocation_crate: super::maybe_jemalloc(),
         has_elf_tls: version >= (10, 7),
         abi_return_struct_as_int: true,
         emit_debug_gdb_scripts: false,
diff --git a/src/librustc_target/spec/apple_ios_base.rs b/src/librustc_target/spec/apple_ios_base.rs
index 296eaca7c7df0..e926e4913d634 100644
--- a/src/librustc_target/spec/apple_ios_base.rs
+++ b/src/librustc_target/spec/apple_ios_base.rs
@@ -99,10 +99,6 @@ pub fn opts(arch: Arch) -> Result<TargetOptions, String> {
         pre_link_args,
         has_elf_tls: false,
         eliminate_frame_pointer: false,
-        // The following line is a workaround for jemalloc 4.5 being broken on
-        // ios. jemalloc 5.0 is supposed to fix this.
-        // see https://github.com/rust-lang/rust/issues/45262
-        exe_allocation_crate: None,
         .. super::apple_base::opts()
     })
 }
diff --git a/src/librustc_target/spec/cloudabi_base.rs b/src/librustc_target/spec/cloudabi_base.rs
index 2ffa74e737fd5..fb78cf495e22a 100644
--- a/src/librustc_target/spec/cloudabi_base.rs
+++ b/src/librustc_target/spec/cloudabi_base.rs
@@ -38,7 +38,6 @@ pub fn opts() -> TargetOptions {
         // dynamic linking.
         tls_model: "local-exec".to_string(),
         relro_level: RelroLevel::Full,
-        exe_allocation_crate: super::maybe_jemalloc(),
         .. Default::default()
     }
 }
diff --git a/src/librustc_target/spec/dragonfly_base.rs b/src/librustc_target/spec/dragonfly_base.rs
index 32eac8663afac..a9e317b7cb8a7 100644
--- a/src/librustc_target/spec/dragonfly_base.rs
+++ b/src/librustc_target/spec/dragonfly_base.rs
@@ -33,7 +33,6 @@ pub fn opts() -> TargetOptions {
         pre_link_args: args,
         position_independent_executables: true,
         relro_level: RelroLevel::Full,
-        exe_allocation_crate: super::maybe_jemalloc(),
         .. Default::default()
     }
 }
diff --git a/src/librustc_target/spec/freebsd_base.rs b/src/librustc_target/spec/freebsd_base.rs
index 04b8a6e706064..c8a2946da50a5 100644
--- a/src/librustc_target/spec/freebsd_base.rs
+++ b/src/librustc_target/spec/freebsd_base.rs
@@ -34,7 +34,6 @@ pub fn opts() -> TargetOptions {
         position_independent_executables: true,
         eliminate_frame_pointer: false, // FIXME 43575
         relro_level: RelroLevel::Full,
-        exe_allocation_crate: super::maybe_jemalloc(),
         abi_return_struct_as_int: true,
         .. Default::default()
     }
diff --git a/src/librustc_target/spec/linux_base.rs b/src/librustc_target/spec/linux_base.rs
index 4a9cd9e2f3233..01f65d5736322 100644
--- a/src/librustc_target/spec/linux_base.rs
+++ b/src/librustc_target/spec/linux_base.rs
@@ -36,7 +36,6 @@ pub fn opts() -> TargetOptions {
         pre_link_args: args,
         position_independent_executables: true,
         relro_level: RelroLevel::Full,
-        exe_allocation_crate: super::maybe_jemalloc(),
         has_elf_tls: true,
         .. Default::default()
     }
diff --git a/src/librustc_target/spec/mod.rs b/src/librustc_target/spec/mod.rs
index 9ee4582fabf7b..91cb9b1a9dc1d 100644
--- a/src/librustc_target/spec/mod.rs
+++ b/src/librustc_target/spec/mod.rs
@@ -1270,14 +1270,6 @@ impl ToJson for Target {
     }
 }
 
-fn maybe_jemalloc() -> Option<String> {
-    if cfg!(feature = "jemalloc") {
-        Some("alloc_jemalloc".to_string())
-    } else {
-        None
-    }
-}
-
 /// Either a target triple string or a path to a JSON file.
 #[derive(PartialEq, Clone, Debug, Hash, RustcEncodable, RustcDecodable)]
 pub enum TargetTriple {
diff --git a/src/librustc_target/spec/solaris_base.rs b/src/librustc_target/spec/solaris_base.rs
index c14cc3f5bc3be..93b889d5d399e 100644
--- a/src/librustc_target/spec/solaris_base.rs
+++ b/src/librustc_target/spec/solaris_base.rs
@@ -18,7 +18,6 @@ pub fn opts() -> TargetOptions {
         has_rpath: true,
         target_family: Some("unix".to_string()),
         is_like_solaris: true,
-        exe_allocation_crate: super::maybe_jemalloc(),
 
         .. Default::default()
     }
diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml
index cd1e3438fc372..0f22459b34349 100644
--- a/src/libstd/Cargo.toml
+++ b/src/libstd/Cargo.toml
@@ -14,7 +14,6 @@ crate-type = ["dylib", "rlib"]
 
 [dependencies]
 alloc = { path = "../liballoc" }
-alloc_jemalloc = { path = "../liballoc_jemalloc", optional = true }
 alloc_system = { path = "../liballoc_system" }
 panic_unwind = { path = "../libpanic_unwind", optional = true }
 panic_abort = { path = "../libpanic_abort" }
@@ -43,9 +42,6 @@ build_helper = { path = "../build_helper" }
 
 [features]
 backtrace = []
-debug-jemalloc = ["alloc_jemalloc/debug"]
-jemalloc = ["alloc_jemalloc"]
-force_alloc_system = []
 panic-unwind = ["panic_unwind"]
 profiler = ["profiler_builtins"]
 
diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs
index 31fc9ed3f772d..1ff342fa7a7be 100644
--- a/src/libstd/alloc.rs
+++ b/src/libstd/alloc.rs
@@ -13,13 +13,10 @@
 //! In a given program, the standard library has one “global” memory allocator
 //! that is used for example by `Box<T>` and `Vec<T>`.
 //!
-//! Currently the default global allocator is unspecified.
-//! The compiler may link to a version of [jemalloc] on some platforms,
-//! but this is not guaranteed.
-//! Libraries, however, like `cdylib`s and `staticlib`s are guaranteed
-//! to use the [`System`] by default.
+//! Currently the default global allocator is unspecified. Libraries, however,
+//! like `cdylib`s and `staticlib`s are guaranteed to use the [`System`] by
+//! default.
 //!
-//! [jemalloc]: https://github.com/jemalloc/jemalloc
 //! [`System`]: struct.System.html
 //!
 //! # The `#[global_allocator]` attribute
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index a4db879680566..935ddb791fb9b 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -313,14 +313,7 @@
 
 #![default_lib_allocator]
 
-// Always use alloc_system during stage0 since we don't know if the alloc_*
-// crate the stage0 compiler will pick by default is enabled (e.g.
-// if the user has disabled jemalloc in `./configure`).
-// `force_alloc_system` is *only* intended as a workaround for local rebuilds
-// with a rustc without jemalloc.
-// FIXME(#44236) shouldn't need MSVC logic
-#[cfg(all(not(target_env = "msvc"),
-          any(all(stage0, not(test)), feature = "force_alloc_system")))]
+#[cfg(stage0)]
 #[global_allocator]
 static ALLOC: alloc_system::System = alloc_system::System;
 
diff --git a/src/rustc/Cargo.toml b/src/rustc/Cargo.toml
index 9ccd37a6a4592..7a31afed8c322 100644
--- a/src/rustc/Cargo.toml
+++ b/src/rustc/Cargo.toml
@@ -10,6 +10,3 @@ path = "rustc.rs"
 [dependencies]
 rustc_target = { path = "../librustc_target" }
 rustc_driver = { path = "../librustc_driver" }
-
-[features]
-jemalloc = ["rustc_target/jemalloc"]
diff --git a/src/tools/tidy/src/cargo.rs b/src/tools/tidy/src/cargo.rs
index 69f61bc248dbb..da0cd8b665207 100644
--- a/src/tools/tidy/src/cargo.rs
+++ b/src/tools/tidy/src/cargo.rs
@@ -84,7 +84,6 @@ fn verify(tomlfile: &Path, libfile: &Path, bad: &mut bool) {
 
         // This is intentional, this dependency just makes the crate available
         // for others later on. Cover cases
-        let whitelisted = krate == "alloc_jemalloc";
         let whitelisted = whitelisted || krate.starts_with("panic");
         if toml.contains("name = \"std\"") && whitelisted {
             continue
diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs
index c5f5896d286c3..e235de9c5e138 100644
--- a/src/tools/tidy/src/lib.rs
+++ b/src/tools/tidy/src/lib.rs
@@ -57,7 +57,6 @@ pub mod libcoretest;
 fn filter_dirs(path: &Path) -> bool {
     let skip = [
         "src/dlmalloc",
-        "src/jemalloc",
         "src/llvm",
         "src/llvm-emscripten",
         "src/libbacktrace",
diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs
index 69a4c09c2285d..ff59532ed629d 100644
--- a/src/tools/tidy/src/pal.rs
+++ b/src/tools/tidy/src/pal.rs
@@ -28,7 +28,6 @@
 //! - core may not have platform-specific code
 //! - libcompiler_builtins may have platform-specific code
 //! - liballoc_system may have platform-specific code
-//! - liballoc_jemalloc may have platform-specific code
 //! - libpanic_abort may have platform-specific code
 //! - libpanic_unwind may have platform-specific code
 //! - libunwind may have platform-specific code
@@ -52,7 +51,6 @@ use std::iter::Iterator;
 // Paths that may contain platform-specific code
 const EXCEPTION_PATHS: &[&str] = &[
     // std crates
-    "src/liballoc_jemalloc",
     "src/liballoc_system",
     "src/libcompiler_builtins",
     "src/liblibc",

From 94cb1059dd33755541c67f23fe5452cdfdba70e6 Mon Sep 17 00:00:00 2001
From: Alex Crichton <alex@alexcrichton.com>
Date: Sat, 20 Oct 2018 19:15:06 -0700
Subject: [PATCH 18/30] Use `jemalloc-sys` on Linux and OSX compilers

This commit adds opt-in support to the compiler to link to `jemalloc` in
the compiler. When activated the compiler will depend on `jemalloc-sys`,
instruct jemalloc to unprefix its symbols, and then link to it. The
feature is activated by default on Linux/OSX compilers for x86_64/i686
platforms, and it's not enabled anywhere else for now. We may be able to
opt-in other platforms in the future! Also note that the opt-in only
happens on CI, it's otherwise unconditionally turned off by default.

Closes #36963
---
 .travis.yml                                | 10 +++++-----
 config.toml.example                        |  4 ++++
 src/Cargo.lock                             | 18 ++++++++++++++++++
 src/bootstrap/config.rs                    |  5 ++++-
 src/bootstrap/lib.rs                       |  6 +++++-
 src/ci/docker/dist-i686-linux/Dockerfile   |  3 ++-
 src/ci/docker/dist-x86_64-linux/Dockerfile |  3 ++-
 src/librustc_driver/Cargo.toml             |  5 +++++
 src/librustc_driver/lib.rs                 |  8 ++++++++
 src/rustc/Cargo.toml                       |  3 +++
 src/tools/tidy/src/cargo.rs                |  2 +-
 11 files changed, 57 insertions(+), 10 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index ec8060b9f56aa..3457e8a8043d0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -30,7 +30,7 @@ matrix:
 
     - env: >
         RUST_CHECK_TARGET=dist
-        RUST_CONFIGURE_ARGS="--enable-extended --enable-profiler --enable-lldb"
+        RUST_CONFIGURE_ARGS="--enable-extended --enable-profiler --enable-lldb --set rust.jemalloc"
         SRC=.
         DEPLOY_ALT=1
         RUSTC_RETRY_LINKER_ON_SEGFAULT=1
@@ -53,7 +53,7 @@ matrix:
     # version that we're using, 8.2, cannot compile LLVM for OSX 10.7.
     - env: >
         RUST_CHECK_TARGET=check
-        RUST_CONFIGURE_ARGS="--build=x86_64-apple-darwin --enable-sanitizers --enable-profiler"
+        RUST_CONFIGURE_ARGS="--build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc"
         SRC=.
         RUSTC_RETRY_LINKER_ON_SEGFAULT=1
         MACOSX_DEPLOYMENT_TARGET=10.8
@@ -67,7 +67,7 @@ matrix:
 
     - env: >
         RUST_CHECK_TARGET=check
-        RUST_CONFIGURE_ARGS=--build=i686-apple-darwin
+        RUST_CONFIGURE_ARGS="--build=i686-apple-darwin --set rust.jemalloc"
         SRC=.
         RUSTC_RETRY_LINKER_ON_SEGFAULT=1
         MACOSX_DEPLOYMENT_TARGET=10.8
@@ -87,7 +87,7 @@ matrix:
     # OSX 10.7 and `xcode7` is the latest Xcode able to compile LLVM for 10.7.
     - env: >
         RUST_CHECK_TARGET=dist
-        RUST_CONFIGURE_ARGS="--build=i686-apple-darwin --enable-full-tools --enable-profiler --enable-lldb"
+        RUST_CONFIGURE_ARGS="--build=i686-apple-darwin --enable-full-tools --enable-profiler --enable-lldb --set rust.jemalloc"
         SRC=.
         DEPLOY=1
         RUSTC_RETRY_LINKER_ON_SEGFAULT=1
@@ -102,7 +102,7 @@ matrix:
 
     - env: >
         RUST_CHECK_TARGET=dist
-        RUST_CONFIGURE_ARGS="--target=aarch64-apple-ios,armv7-apple-ios,armv7s-apple-ios,i386-apple-ios,x86_64-apple-ios --enable-full-tools --enable-sanitizers --enable-profiler --enable-lldb"
+        RUST_CONFIGURE_ARGS="--target=aarch64-apple-ios,armv7-apple-ios,armv7s-apple-ios,i386-apple-ios,x86_64-apple-ios --enable-full-tools --enable-sanitizers --enable-profiler --enable-lldb --set rust.jemalloc"
         SRC=.
         DEPLOY=1
         RUSTC_RETRY_LINKER_ON_SEGFAULT=1
diff --git a/config.toml.example b/config.toml.example
index 42234c261cf8d..247b2d3ce80f9 100644
--- a/config.toml.example
+++ b/config.toml.example
@@ -392,6 +392,10 @@
 # generally only set for releases
 #remap-debuginfo = false
 
+# Link the compiler against `jemalloc`, where on Linux and OSX it should
+# override the default allocator for rustc and LLVM.
+#jemalloc = false
+
 # =============================================================================
 # Options for specific targets
 #
diff --git a/src/Cargo.lock b/src/Cargo.lock
index 7264b92a96828..6d070cf1b47fa 100644
--- a/src/Cargo.lock
+++ b/src/Cargo.lock
@@ -772,6 +772,11 @@ dependencies = [
  "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "fs_extra"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "fst"
 version = "0.3.0"
@@ -983,6 +988,16 @@ name = "itoa"
 version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "jemalloc-sys"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "jobserver"
 version = "0.1.11"
@@ -2168,6 +2183,7 @@ dependencies = [
  "arena 0.0.0",
  "env_logger 0.5.12 (registry+https://github.com/rust-lang/crates.io-index)",
  "graphviz 0.0.0",
+ "jemalloc-sys 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc 0.0.0",
  "rustc-rayon 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3223,6 +3239,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
 "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
 "checksum fs2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
+"checksum fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5f2a4a2034423744d2cc7ca2068453168dcdb82c438419e639a26bd87839c674"
 "checksum fst 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d94485a00b1827b861dd9d1a2cc9764f9044d4c535514c0760a5a2012ef3399f"
 "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
 "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
@@ -3245,6 +3262,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum is-match 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7e5b386aef33a1c677be65237cb9d32c3f3ef56bd035949710c4bb13083eb053"
 "checksum itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "f58856976b776fedd95533137617a02fb25719f40e7d9b01c7043cd65474f450"
 "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
+"checksum jemalloc-sys 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "bfc62c8e50e381768ce8ee0428ee53741929f7ebd73e4d83f669bcf7693e00ae"
 "checksum jobserver 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "60af5f849e1981434e4a31d3d782c4774ae9b434ce55b101a96ecfd09147e8be"
 "checksum json 0.11.13 (registry+https://github.com/rust-lang/crates.io-index)" = "9ad0485404155f45cce53a40d4b2d6ac356418300daed05273d9e26f91c390be"
 "checksum jsonrpc-core 8.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ddf83704f4e79979a424d1082dd2c1e52683058056c9280efa19ac5f6bc9033c"
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index 9c68e12354509..2a75265e88381 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -115,6 +115,7 @@ pub struct Config {
     pub hosts: Vec<Interned<String>>,
     pub targets: Vec<Interned<String>>,
     pub local_rebuild: bool,
+    pub jemalloc: bool,
 
     // dist misc
     pub dist_sign_folder: Option<PathBuf>,
@@ -259,7 +260,7 @@ struct Llvm {
     link_jobs: Option<u32>,
     link_shared: Option<bool>,
     version_suffix: Option<String>,
-    clang_cl: Option<String>
+    clang_cl: Option<String>,
 }
 
 #[derive(Deserialize, Default, Clone)]
@@ -322,6 +323,7 @@ struct Rust {
     backtrace_on_ice: Option<bool>,
     verify_llvm_ir: Option<bool>,
     remap_debuginfo: Option<bool>,
+    jemalloc: Option<bool>,
 }
 
 /// TOML representation of how each build target is configured.
@@ -535,6 +537,7 @@ impl Config {
             set(&mut config.rust_debuginfo_tests, rust.debuginfo_tests);
             set(&mut config.codegen_tests, rust.codegen_tests);
             set(&mut config.rust_rpath, rust.rpath);
+            set(&mut config.jemalloc, rust.jemalloc);
             set(&mut config.backtrace, rust.backtrace);
             set(&mut config.channel, rust.channel.clone());
             set(&mut config.rust_dist_src, rust.dist_src);
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index 5cf9ccaa71500..c225ef3827180 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -530,7 +530,11 @@ impl Build {
 
     /// Get the space-separated set of activated features for the compiler.
     fn rustc_features(&self) -> String {
-        String::new()
+        let mut features = String::new();
+        if self.config.jemalloc {
+            features.push_str("jemalloc");
+        }
+        features
     }
 
     /// Component directory that Cargo will produce output into (e.g.
diff --git a/src/ci/docker/dist-i686-linux/Dockerfile b/src/ci/docker/dist-i686-linux/Dockerfile
index d99e409e42671..8df49f364a372 100644
--- a/src/ci/docker/dist-i686-linux/Dockerfile
+++ b/src/ci/docker/dist-i686-linux/Dockerfile
@@ -98,7 +98,8 @@ ENV RUST_CONFIGURE_ARGS \
       --enable-sanitizers \
       --enable-profiler \
       --set target.i686-unknown-linux-gnu.linker=clang \
-      --build=i686-unknown-linux-gnu
+      --build=i686-unknown-linux-gnu \
+      --set rust.jemalloc
 ENV SCRIPT python2.7 ../x.py dist --build $HOSTS --host $HOSTS --target $HOSTS
 ENV CARGO_TARGET_I686_UNKNOWN_LINUX_GNU_LINKER=clang
 
diff --git a/src/ci/docker/dist-x86_64-linux/Dockerfile b/src/ci/docker/dist-x86_64-linux/Dockerfile
index 8696f72e0e388..0a2dae72f7382 100644
--- a/src/ci/docker/dist-x86_64-linux/Dockerfile
+++ b/src/ci/docker/dist-x86_64-linux/Dockerfile
@@ -101,7 +101,8 @@ ENV RUST_CONFIGURE_ARGS \
       --set target.x86_64-unknown-linux-gnu.linker=clang \
       --set target.x86_64-unknown-linux-gnu.ar=/rustroot/bin/llvm-ar \
       --set target.x86_64-unknown-linux-gnu.ranlib=/rustroot/bin/llvm-ranlib \
-      --set llvm.thin-lto=true
+      --set llvm.thin-lto=true \
+      --set rust.jemalloc
 ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS
 ENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=clang
 
diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml
index 470c8b03d0bca..1e32f5ef6f0b6 100644
--- a/src/librustc_driver/Cargo.toml
+++ b/src/librustc_driver/Cargo.toml
@@ -38,3 +38,8 @@ syntax = { path = "../libsyntax" }
 smallvec = { version = "0.6.5", features = ["union"] }
 syntax_ext = { path = "../libsyntax_ext" }
 syntax_pos = { path = "../libsyntax_pos" }
+
+[dependencies.jemalloc-sys]
+version = '0.1.8'
+optional = true
+features = ['unprefixed_malloc_on_supported_platforms']
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index 276b7290c2ef0..e8fdaddaeb89c 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -64,6 +64,14 @@ extern crate syntax;
 extern crate syntax_ext;
 extern crate syntax_pos;
 
+// Note that the linkage here should be all that we need, on Linux we're not
+// prefixing the symbols here so this should naturally override our default
+// allocator. On OSX it should override via the zone allocator. We shouldn't
+// enable this by default on other platforms, so other platforms aren't handled
+// here yet.
+#[cfg(feature = "jemalloc-sys")]
+extern crate jemalloc_sys;
+
 use driver::CompileController;
 use pretty::{PpMode, UserIdentifiedItem};
 
diff --git a/src/rustc/Cargo.toml b/src/rustc/Cargo.toml
index 7a31afed8c322..ec822fddef3eb 100644
--- a/src/rustc/Cargo.toml
+++ b/src/rustc/Cargo.toml
@@ -10,3 +10,6 @@ path = "rustc.rs"
 [dependencies]
 rustc_target = { path = "../librustc_target" }
 rustc_driver = { path = "../librustc_driver" }
+
+[features]
+jemalloc = ['rustc_driver/jemalloc-sys']
diff --git a/src/tools/tidy/src/cargo.rs b/src/tools/tidy/src/cargo.rs
index da0cd8b665207..466d2fa0d2bd6 100644
--- a/src/tools/tidy/src/cargo.rs
+++ b/src/tools/tidy/src/cargo.rs
@@ -84,7 +84,7 @@ fn verify(tomlfile: &Path, libfile: &Path, bad: &mut bool) {
 
         // This is intentional, this dependency just makes the crate available
         // for others later on. Cover cases
-        let whitelisted = whitelisted || krate.starts_with("panic");
+        let whitelisted = krate.starts_with("panic");
         if toml.contains("name = \"std\"") && whitelisted {
             continue
         }

From b37572844e0edd8098b7bab5c4601df439f2798d Mon Sep 17 00:00:00 2001
From: Matthew Jasper <mjjasper1@gmail.com>
Date: Sat, 20 Oct 2018 10:08:36 +0100
Subject: [PATCH 19/30] Don't emit cannot move errors twice in migrate mode

---
 src/librustc_mir/borrow_check/mod.rs          | 14 ++--
 .../borrow_check/mutability_errors.rs         |  6 +-
 .../ui/access-mode-in-closures.nll.stderr     | 14 +---
 .../ui/binop/binop-move-semantics.nll.stderr  | 14 +---
 .../borrowck-fn-in-const-a.ast.nll.stderr     | 20 ------
 ...or-loop-correct-cmt-for-pattern.nll.stderr | 32 +--------
 .../ui/borrowck/borrowck-in-static.nll.stderr | 17 +----
 .../borrowck/borrowck-issue-2657-2.nll.stderr | 13 +---
 .../borrowck-migrate-to-nll.edition.stderr    | 14 ----
 .../borrowck-migrate-to-nll.zflag.stderr      | 14 ----
 .../borrowck-move-error-with-note.nll.stderr  | 50 +------------
 .../borrowck-move-from-unsafe-ptr.nll.stderr  | 13 +---
 ...orrowck-move-in-irrefut-pat.ast.nll.stderr | 29 +-------
 ...ut-of-overloaded-auto-deref.ast.nll.stderr | 11 +--
 ...ck-move-out-of-overloaded-deref.nll.stderr | 11 +--
 ...wck-move-out-of-static-item.ast.nll.stderr | 18 -----
 .../borrowck-move-out-of-vec-tail.nll.stderr  | 29 +-------
 ...-overloaded-index-move-from-vec.nll.stderr | 11 +--
 src/test/ui/borrowck/issue-51415.nll.stderr   | 11 +--
 ...tic-initializer-issue-38520.ast.nll.stderr | 33 ---------
 ...upvar-from-non-once-ref-closure.nll.stderr | 21 +-----
 .../ui/by-move-pattern-binding.nll.stderr     | 11 +--
 ...check-static-values-constraints.nll.stderr | 11 +--
 src/test/ui/dst/dst-index.nll.stderr          | 20 +-----
 src/test/ui/dst/dst-rvalue.nll.stderr         | 22 +-----
 src/test/ui/error-codes/E0507.nll.stderr      | 18 -----
 src/test/ui/issues/issue-12567.nll.stderr     | 41 +----------
 .../issues/issue-17718-static-move.nll.stderr | 11 +--
 src/test/ui/issues/issue-20801.nll.stderr     | 20 +-----
 src/test/ui/issues/issue-2590.nll.stderr      | 13 +---
 src/test/ui/issues/issue-30355.nll.stderr     | 13 +---
 .../issue-40402-1.nll.stderr                  | 11 +--
 .../issue-40402-2.nll.stderr                  | 20 +-----
 src/test/ui/issues/issue-4335.nll.stderr      | 11 +--
 .../moves-based-on-type-block-bad.nll.stderr  | 14 +---
 ...e-out-of-closure-env-issue-1965.nll.stderr | 17 +----
 .../ui/nll/cannot-move-block-spans.nll.stderr | 72 +------------------
 .../match-guards-always-borrow.ast.nll.stderr | 14 ----
 .../static/static-items-cant-move.nll.stderr  | 18 -----
 src/test/ui/std-uncopyable-atomics.nll.stderr | 38 +---------
 .../trivial-bounds-leak-copy.nll.stderr       | 20 ------
 .../unboxed-closure-illegal-move.nll.stderr   | 32 +--------
 src/test/ui/unop-move-semantics.nll.stderr    | 14 +---
 43 files changed, 46 insertions(+), 810 deletions(-)
 delete mode 100644 src/test/ui/borrowck/borrowck-fn-in-const-a.ast.nll.stderr
 delete mode 100644 src/test/ui/borrowck/borrowck-move-out-of-static-item.ast.nll.stderr
 delete mode 100644 src/test/ui/borrowck/move-in-static-initializer-issue-38520.ast.nll.stderr
 delete mode 100644 src/test/ui/error-codes/E0507.nll.stderr
 delete mode 100644 src/test/ui/static/static-items-cant-move.nll.stderr
 delete mode 100644 src/test/ui/trivial-bounds/trivial-bounds-leak-copy.nll.stderr

diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index 6ef8b15545872..ddea7e5226024 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -1831,7 +1831,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             | Write(wk @ WriteKind::StorageDeadOrDrop)
             | Write(wk @ WriteKind::MutableBorrow(BorrowKind::Shared))
             | Write(wk @ WriteKind::MutableBorrow(BorrowKind::Shallow)) => {
-                if let Err(_place_err) = self.is_mutable(place, is_local_mutation_allowed) {
+                if let (Err(_place_err), true) = (
+                    self.is_mutable(place, is_local_mutation_allowed),
+                    self.errors_buffer.is_empty()
+                ) {
                     if self.infcx.tcx.migrate_borrowck() {
                         // rust-lang/rust#46908: In pure NLL mode this
                         // code path should be unreachable (and thus
@@ -1855,12 +1858,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                             location,
                         );
                     } else {
-                        self.infcx.tcx.sess.delay_span_bug(
+                        span_bug!(
                             span,
-                            &format!(
-                                "Accessing `{:?}` with the kind `{:?}` shouldn't be possible",
-                                place, kind
-                            ),
+                            "Accessing `{:?}` with the kind `{:?}` shouldn't be possible",
+                            place,
+                            kind,
                         );
                     }
                 }
diff --git a/src/librustc_mir/borrow_check/mutability_errors.rs b/src/librustc_mir/borrow_check/mutability_errors.rs
index b71b131570d7b..7afe2c67adc9b 100644
--- a/src/librustc_mir/borrow_check/mutability_errors.rs
+++ b/src/librustc_mir/borrow_check/mutability_errors.rs
@@ -180,9 +180,9 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
             AccessKind::Move => {
                 err = self.infcx.tcx
                     .cannot_move_out_of(span, &(item_msg + &reason), Origin::Mir);
-                act = "move";
-                acted_on = "moved";
-                span
+                err.span_label(span, "cannot move");
+                err.buffer(&mut self.errors_buffer);
+                return;
             }
             AccessKind::Mutate => {
                 err = self.infcx.tcx
diff --git a/src/test/ui/access-mode-in-closures.nll.stderr b/src/test/ui/access-mode-in-closures.nll.stderr
index 3366f0639caa5..b9de60f43f703 100644
--- a/src/test/ui/access-mode-in-closures.nll.stderr
+++ b/src/test/ui/access-mode-in-closures.nll.stderr
@@ -13,18 +13,6 @@ note: move occurs because `v` has type `std::vec::Vec<isize>`, which does not im
 LL |         match *s { sty(v) => v } //~ ERROR cannot move out
    |                        ^
 
-error[E0507]: cannot move out of `s.0` which is behind a `&` reference
-  --> $DIR/access-mode-in-closures.rs:19:24
-   |
-LL |     let _foo = unpack(|s| {
-   |                        - help: consider changing this to be a mutable reference: `&mut sty`
-LL |         // Test that `s` is moved here.
-LL |         match *s { sty(v) => v } //~ ERROR cannot move out
-   |                        ^
-   |                        |
-   |                        cannot move out of `s.0` which is behind a `&` reference
-   |                        `s` is a `&` reference, so the data it refers to cannot be moved
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/binop/binop-move-semantics.nll.stderr b/src/test/ui/binop/binop-move-semantics.nll.stderr
index 94676f0e5ad77..545a60f6770d9 100644
--- a/src/test/ui/binop/binop-move-semantics.nll.stderr
+++ b/src/test/ui/binop/binop-move-semantics.nll.stderr
@@ -32,18 +32,6 @@ error[E0507]: cannot move out of borrowed content
 LL |     *n;  //~ ERROR: cannot move out of borrowed content
    |     ^^ cannot move out of borrowed content
 
-error[E0507]: cannot move out of `*n` which is behind a `&` reference
-  --> $DIR/binop-move-semantics.rs:42:5
-   |
-LL |     let n = &y;
-   |             -- help: consider changing this to be a mutable reference: `&mut y`
-...
-LL |     *n;  //~ ERROR: cannot move out of borrowed content
-   |     ^^
-   |     |
-   |     cannot move out of `*n` which is behind a `&` reference
-   |     `n` is a `&` reference, so the data it refers to cannot be moved
-
 error[E0502]: cannot borrow `f` as immutable because it is also borrowed as mutable
   --> $DIR/binop-move-semantics.rs:64:5
    |
@@ -74,7 +62,7 @@ LL | |     &mut f;  //~ ERROR: cannot borrow `f` as mutable because it is also b
    |       |    immutable borrow later used here
    |       mutable borrow occurs here
 
-error: aborting due to 7 previous errors
+error: aborting due to 6 previous errors
 
 Some errors occurred: E0382, E0502, E0507.
 For more information about an error, try `rustc --explain E0382`.
diff --git a/src/test/ui/borrowck/borrowck-fn-in-const-a.ast.nll.stderr b/src/test/ui/borrowck/borrowck-fn-in-const-a.ast.nll.stderr
deleted file mode 100644
index b171a48ac5081..0000000000000
--- a/src/test/ui/borrowck/borrowck-fn-in-const-a.ast.nll.stderr
+++ /dev/null
@@ -1,20 +0,0 @@
-error[E0507]: cannot move out of borrowed content
-  --> $DIR/borrowck-fn-in-const-a.rs:19:16
-   |
-LL |         return *x //[ast]~ ERROR cannot move out of borrowed content [E0507]
-   |                ^^ cannot move out of borrowed content
-
-error[E0507]: cannot move out of `*x` which is behind a `&` reference
-  --> $DIR/borrowck-fn-in-const-a.rs:19:16
-   |
-LL |     fn broken(x: &String) -> String {
-   |                  ------- help: consider changing this to be a mutable reference: `&mut std::string::String`
-LL |         return *x //[ast]~ ERROR cannot move out of borrowed content [E0507]
-   |                ^^
-   |                |
-   |                cannot move out of `*x` which is behind a `&` reference
-   |                `x` is a `&` reference, so the data it refers to cannot be moved
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/borrowck/borrowck-for-loop-correct-cmt-for-pattern.nll.stderr b/src/test/ui/borrowck/borrowck-for-loop-correct-cmt-for-pattern.nll.stderr
index 0ab3d3ea5cdfe..25eb69ad9377d 100644
--- a/src/test/ui/borrowck/borrowck-for-loop-correct-cmt-for-pattern.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-for-loop-correct-cmt-for-pattern.nll.stderr
@@ -1,13 +1,3 @@
-error[E0507]: cannot move out of `*__next` which is behind a `&` reference
-  --> $DIR/borrowck-for-loop-correct-cmt-for-pattern.rs:22:10
-   |
-LL |     for &a in x.iter() {    //~ ERROR cannot move out
-   |         -^
-   |         ||
-   |         |cannot move out of `*__next` which is behind a `&` reference
-   |         |`__next` is a `&` reference, so the data it refers to cannot be moved
-   |         help: consider changing this to be a mutable reference: `&mut a`
-
 error[E0507]: cannot move out of borrowed content
   --> $DIR/borrowck-for-loop-correct-cmt-for-pattern.rs:22:15
    |
@@ -23,16 +13,6 @@ note: move occurs because `a` has type `&mut i32`, which does not implement the
 LL |     for &a in x.iter() {    //~ ERROR cannot move out
    |          ^
 
-error[E0507]: cannot move out of `*__next` which is behind a `&` reference
-  --> $DIR/borrowck-for-loop-correct-cmt-for-pattern.rs:28:10
-   |
-LL |     for &a in &f.a {  //~ ERROR cannot move out
-   |         -^
-   |         ||
-   |         |cannot move out of `*__next` which is behind a `&` reference
-   |         |`__next` is a `&` reference, so the data it refers to cannot be moved
-   |         help: consider changing this to be a mutable reference: `&mut a`
-
 error[E0507]: cannot move out of borrowed content
   --> $DIR/borrowck-for-loop-correct-cmt-for-pattern.rs:28:15
    |
@@ -48,16 +28,6 @@ note: move occurs because `a` has type `std::boxed::Box<isize>`, which does not
 LL |     for &a in &f.a {  //~ ERROR cannot move out
    |          ^
 
-error[E0507]: cannot move out of `*__next` which is behind a `&` reference
-  --> $DIR/borrowck-for-loop-correct-cmt-for-pattern.rs:32:10
-   |
-LL |     for &a in x.iter() {    //~ ERROR cannot move out
-   |         -^
-   |         ||
-   |         |cannot move out of `*__next` which is behind a `&` reference
-   |         |`__next` is a `&` reference, so the data it refers to cannot be moved
-   |         help: consider changing this to be a mutable reference: `&mut a`
-
 error[E0507]: cannot move out of borrowed content
   --> $DIR/borrowck-for-loop-correct-cmt-for-pattern.rs:32:15
    |
@@ -73,6 +43,6 @@ note: move occurs because `a` has type `std::boxed::Box<i32>`, which does not im
 LL |     for &a in x.iter() {    //~ ERROR cannot move out
    |          ^
 
-error: aborting due to 6 previous errors
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/borrowck/borrowck-in-static.nll.stderr b/src/test/ui/borrowck/borrowck-in-static.nll.stderr
index ba42dc2436377..45fa1764f7027 100644
--- a/src/test/ui/borrowck/borrowck-in-static.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-in-static.nll.stderr
@@ -6,21 +6,6 @@ LL |     let x = Box::new(0);
 LL |     Box::new(|| x) //~ ERROR cannot move out of captured outer variable
    |                 ^ cannot move out of captured variable in an `Fn` closure
 
-error[E0507]: cannot move out of `x`, as it is a captured variable in a `Fn` closure
-  --> $DIR/borrowck-in-static.rs:15:17
-   |
-LL |     Box::new(|| x) //~ ERROR cannot move out of captured outer variable
-   |                 ^
-   |                 |
-   |                 cannot move out of `x`, as it is a captured variable in a `Fn` closure
-   |                 cannot move
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/borrowck-in-static.rs:15:14
-   |
-LL |     Box::new(|| x) //~ ERROR cannot move out of captured outer variable
-   |              ^^^^
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/borrowck/borrowck-issue-2657-2.nll.stderr b/src/test/ui/borrowck/borrowck-issue-2657-2.nll.stderr
index 0445a75d61a9d..cdbfab8bd054a 100644
--- a/src/test/ui/borrowck/borrowck-issue-2657-2.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-issue-2657-2.nll.stderr
@@ -7,17 +7,6 @@ LL |         let _b = *y; //~ ERROR cannot move out
    |                  cannot move out of borrowed content
    |                  help: consider removing the `*`: `y`
 
-error[E0507]: cannot move out of `*y` which is behind a `&` reference
-  --> $DIR/borrowck-issue-2657-2.rs:17:18
-   |
-LL |       Some(ref y) => {
-   |            ----- help: consider changing this to be a mutable reference: `ref mut y`
-LL |         let _b = *y; //~ ERROR cannot move out
-   |                  ^^
-   |                  |
-   |                  cannot move out of `*y` which is behind a `&` reference
-   |                  `y` is a `&` reference, so the data it refers to cannot be moved
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/borrowck/borrowck-migrate-to-nll.edition.stderr b/src/test/ui/borrowck/borrowck-migrate-to-nll.edition.stderr
index f5a9db364065f..e7dbc6f6e3ba0 100644
--- a/src/test/ui/borrowck/borrowck-migrate-to-nll.edition.stderr
+++ b/src/test/ui/borrowck/borrowck-migrate-to-nll.edition.stderr
@@ -8,17 +8,3 @@ LL |                 (|| { let bar = foo; bar.take() })();
            It represents potential unsoundness in your code.
            This warning will become a hard error in the future.
 
-warning[E0507]: cannot move out of `foo`, as it is immutable for the pattern guard
-  --> $DIR/borrowck-migrate-to-nll.rs:35:17
-   |
-LL |                 (|| { let bar = foo; bar.take() })();
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |                 |
-   |                 cannot move out of `foo`, as it is immutable for the pattern guard
-   |                 cannot move
-   |
-   = note: variables bound in patterns are immutable until the end of the pattern guard
-   = warning: This error has been downgraded to a warning for backwards compatibility with previous releases.
-           It represents potential unsoundness in your code.
-           This warning will become a hard error in the future.
-
diff --git a/src/test/ui/borrowck/borrowck-migrate-to-nll.zflag.stderr b/src/test/ui/borrowck/borrowck-migrate-to-nll.zflag.stderr
index f5a9db364065f..e7dbc6f6e3ba0 100644
--- a/src/test/ui/borrowck/borrowck-migrate-to-nll.zflag.stderr
+++ b/src/test/ui/borrowck/borrowck-migrate-to-nll.zflag.stderr
@@ -8,17 +8,3 @@ LL |                 (|| { let bar = foo; bar.take() })();
            It represents potential unsoundness in your code.
            This warning will become a hard error in the future.
 
-warning[E0507]: cannot move out of `foo`, as it is immutable for the pattern guard
-  --> $DIR/borrowck-migrate-to-nll.rs:35:17
-   |
-LL |                 (|| { let bar = foo; bar.take() })();
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |                 |
-   |                 cannot move out of `foo`, as it is immutable for the pattern guard
-   |                 cannot move
-   |
-   = note: variables bound in patterns are immutable until the end of the pattern guard
-   = warning: This error has been downgraded to a warning for backwards compatibility with previous releases.
-           It represents potential unsoundness in your code.
-           This warning will become a hard error in the future.
-
diff --git a/src/test/ui/borrowck/borrowck-move-error-with-note.nll.stderr b/src/test/ui/borrowck/borrowck-move-error-with-note.nll.stderr
index 99f69515a4732..2df520a936c9d 100644
--- a/src/test/ui/borrowck/borrowck-move-error-with-note.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-move-error-with-note.nll.stderr
@@ -24,42 +24,6 @@ LL |                   num2) => (),
 LL |         Foo::Foo2(num) => (),
    |                   ^^^
 
-error[E0507]: cannot move out of `f.0` which is behind a `&` reference
-  --> $DIR/borrowck-move-error-with-note.rs:23:19
-   |
-LL |     let f = &Foo::Foo1(box 1, box 2);
-   |             ------------------------ help: consider changing this to be a mutable reference: `&mut Foo::Foo1(box 1, box 2)`
-...
-LL |         Foo::Foo1(num1,
-   |                   ^^^^
-   |                   |
-   |                   cannot move out of `f.0` which is behind a `&` reference
-   |                   `f` is a `&` reference, so the data it refers to cannot be moved
-
-error[E0507]: cannot move out of `f.1` which is behind a `&` reference
-  --> $DIR/borrowck-move-error-with-note.rs:24:19
-   |
-LL |     let f = &Foo::Foo1(box 1, box 2);
-   |             ------------------------ help: consider changing this to be a mutable reference: `&mut Foo::Foo1(box 1, box 2)`
-...
-LL |                   num2) => (),
-   |                   ^^^^
-   |                   |
-   |                   cannot move out of `f.1` which is behind a `&` reference
-   |                   `f` is a `&` reference, so the data it refers to cannot be moved
-
-error[E0507]: cannot move out of `f.0` which is behind a `&` reference
-  --> $DIR/borrowck-move-error-with-note.rs:25:19
-   |
-LL |     let f = &Foo::Foo1(box 1, box 2);
-   |             ------------------------ help: consider changing this to be a mutable reference: `&mut Foo::Foo1(box 1, box 2)`
-...
-LL |         Foo::Foo2(num) => (),
-   |                   ^^^
-   |                   |
-   |                   cannot move out of `f.0` which is behind a `&` reference
-   |                   `f` is a `&` reference, so the data it refers to cannot be moved
-
 error[E0509]: cannot move out of type `S`, which implements the `Drop` trait
   --> $DIR/borrowck-move-error-with-note.rs:39:11
    |
@@ -97,19 +61,7 @@ note: move occurs because `n` has type `std::boxed::Box<isize>`, which does not
 LL |         n => {
    |         ^
 
-error[E0507]: cannot move out of `a.a` which is behind a `&` reference
-  --> $DIR/borrowck-move-error-with-note.rs:59:9
-   |
-LL |     let a = &A { a: box 1 };
-   |             --------------- help: consider changing this to be a mutable reference: `&mut A { a: box 1 }`
-...
-LL |         n => {
-   |         ^
-   |         |
-   |         cannot move out of `a.a` which is behind a `&` reference
-   |         `a` is a `&` reference, so the data it refers to cannot be moved
-
-error: aborting due to 7 previous errors
+error: aborting due to 3 previous errors
 
 Some errors occurred: E0507, E0509.
 For more information about an error, try `rustc --explain E0507`.
diff --git a/src/test/ui/borrowck/borrowck-move-from-unsafe-ptr.nll.stderr b/src/test/ui/borrowck/borrowck-move-from-unsafe-ptr.nll.stderr
index 83c5b82957dea..c3a2180b9f082 100644
--- a/src/test/ui/borrowck/borrowck-move-from-unsafe-ptr.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-move-from-unsafe-ptr.nll.stderr
@@ -7,17 +7,6 @@ LL |     let y = *x; //~ ERROR cannot move out of dereference of raw pointer
    |             cannot move out of dereference of raw pointer
    |             help: consider removing the `*`: `x`
 
-error[E0507]: cannot move out of `*x` which is behind a `*const` pointer
-  --> $DIR/borrowck-move-from-unsafe-ptr.rs:13:13
-   |
-LL | unsafe fn foo(x: *const Box<isize>) -> Box<isize> {
-   |                  ----------------- help: consider changing this to be a mutable pointer: `*mut std::boxed::Box<isize>`
-LL |     let y = *x; //~ ERROR cannot move out of dereference of raw pointer
-   |             ^^
-   |             |
-   |             cannot move out of `*x` which is behind a `*const` pointer
-   |             `x` is a `*const` pointer, so the data it refers to cannot be moved
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/borrowck/borrowck-move-in-irrefut-pat.ast.nll.stderr b/src/test/ui/borrowck/borrowck-move-in-irrefut-pat.ast.nll.stderr
index f22aca9994986..49c2ec0dcf4e7 100644
--- a/src/test/ui/borrowck/borrowck-move-in-irrefut-pat.ast.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-move-in-irrefut-pat.ast.nll.stderr
@@ -14,15 +14,6 @@ note: move occurs because `_x` has type `std::string::String`, which does not im
 LL | fn arg_item(&_x: &String) {}
    |              ^^
 
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/borrowck-move-in-irrefut-pat.rs:16:14
-   |
-LL | fn arg_item(&_x: &String) {}
-   |              ^^
-   |              |
-   |              cannot move out of data in a `&` reference
-   |              cannot move
-
 error[E0507]: cannot move out of borrowed content
   --> $DIR/borrowck-move-in-irrefut-pat.rs:21:11
    |
@@ -39,24 +30,6 @@ note: move occurs because `_x` has type `std::string::String`, which does not im
 LL |     with(|&_x| ())
    |            ^^
 
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/borrowck-move-in-irrefut-pat.rs:21:12
-   |
-LL |     with(|&_x| ())
-   |            ^^
-   |            |
-   |            cannot move out of data in a `&` reference
-   |            cannot move
-
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/borrowck-move-in-irrefut-pat.rs:27:10
-   |
-LL |     let &_x = &"hi".to_string();
-   |          ^^
-   |          |
-   |          cannot move out of data in a `&` reference
-   |          cannot move
-
 error[E0507]: cannot move out of borrowed content
   --> $DIR/borrowck-move-in-irrefut-pat.rs:27:15
    |
@@ -72,6 +45,6 @@ note: move occurs because `_x` has type `std::string::String`, which does not im
 LL |     let &_x = &"hi".to_string();
    |          ^^
 
-error: aborting due to 6 previous errors
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.ast.nll.stderr b/src/test/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.ast.nll.stderr
index ba74cd2514e1a..d58beabb3038a 100644
--- a/src/test/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.ast.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.ast.nll.stderr
@@ -4,15 +4,6 @@ error[E0507]: cannot move out of an `Rc`
 LL |     let _x = Rc::new(vec![1, 2]).into_iter();
    |              ^^^^^^^^^^^^^^^^^^^ cannot move out of an `Rc`
 
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/borrowck-move-out-of-overloaded-auto-deref.rs:17:14
-   |
-LL |     let _x = Rc::new(vec![1, 2]).into_iter();
-   |              ^^^^^^^^^^^^^^^^^^^
-   |              |
-   |              cannot move out of data in a `&` reference
-   |              cannot move
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/borrowck/borrowck-move-out-of-overloaded-deref.nll.stderr b/src/test/ui/borrowck/borrowck-move-out-of-overloaded-deref.nll.stderr
index 205044bb36885..c9c8cf104ce2e 100644
--- a/src/test/ui/borrowck/borrowck-move-out-of-overloaded-deref.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-move-out-of-overloaded-deref.nll.stderr
@@ -7,15 +7,6 @@ LL |     let _x = *Rc::new("hi".to_string());
    |              cannot move out of an `Rc`
    |              help: consider removing the `*`: `Rc::new("hi".to_string())`
 
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/borrowck-move-out-of-overloaded-deref.rs:14:14
-   |
-LL |     let _x = *Rc::new("hi".to_string());
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |              |
-   |              cannot move out of data in a `&` reference
-   |              cannot move
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/borrowck/borrowck-move-out-of-static-item.ast.nll.stderr b/src/test/ui/borrowck/borrowck-move-out-of-static-item.ast.nll.stderr
deleted file mode 100644
index 675458d8c2b85..0000000000000
--- a/src/test/ui/borrowck/borrowck-move-out-of-static-item.ast.nll.stderr
+++ /dev/null
@@ -1,18 +0,0 @@
-error[E0507]: cannot move out of static item
-  --> $DIR/borrowck-move-out-of-static-item.rs:28:10
-   |
-LL |     test(BAR); //[ast]~ ERROR cannot move out of static item [E0507]
-   |          ^^^ cannot move out of static item
-
-error[E0507]: cannot move out of immutable static item `BAR`
-  --> $DIR/borrowck-move-out-of-static-item.rs:28:10
-   |
-LL |     test(BAR); //[ast]~ ERROR cannot move out of static item [E0507]
-   |          ^^^
-   |          |
-   |          cannot move out of immutable static item `BAR`
-   |          cannot move
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/borrowck/borrowck-move-out-of-vec-tail.nll.stderr b/src/test/ui/borrowck/borrowck-move-out-of-vec-tail.nll.stderr
index dea42c53992b1..f3430ba4e06c9 100644
--- a/src/test/ui/borrowck/borrowck-move-out-of-vec-tail.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-move-out-of-vec-tail.nll.stderr
@@ -26,31 +26,6 @@ LL |                 //~| to prevent move
 LL |                   Foo { string: b }] => {
    |
 
-error[E0507]: cannot move out of `tail[..].string` which is behind a `&` reference
-  --> $DIR/borrowck-move-out-of-vec-tail.rs:30:33
-   |
-LL |         [_, ref tail..] => {
-   |             -------- help: consider changing this to be a mutable reference: `ref mut tail`
-LL |             match tail {
-LL |                 &[Foo { string: a },
-   |                                 ^
-   |                                 |
-   |                                 cannot move out of `tail[..].string` which is behind a `&` reference
-   |                                 `tail` is a `&` reference, so the data it refers to cannot be moved
-
-error[E0507]: cannot move out of `tail[..].string` which is behind a `&` reference
-  --> $DIR/borrowck-move-out-of-vec-tail.rs:34:33
-   |
-LL |         [_, ref tail..] => {
-   |             -------- help: consider changing this to be a mutable reference: `ref mut tail`
-...
-LL |                   Foo { string: b }] => {
-   |                                 ^
-   |                                 |
-   |                                 cannot move out of `tail[..].string` which is behind a `&` reference
-   |                                 `tail` is a `&` reference, so the data it refers to cannot be moved
-
-error: aborting due to 3 previous errors
+error: aborting due to previous error
 
-Some errors occurred: E0507, E0508.
-For more information about an error, try `rustc --explain E0507`.
+For more information about this error, try `rustc --explain E0508`.
diff --git a/src/test/ui/borrowck/borrowck-overloaded-index-move-from-vec.nll.stderr b/src/test/ui/borrowck/borrowck-overloaded-index-move-from-vec.nll.stderr
index 05fd6d71520c7..92e10c258c269 100644
--- a/src/test/ui/borrowck/borrowck-overloaded-index-move-from-vec.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-overloaded-index-move-from-vec.nll.stderr
@@ -7,15 +7,6 @@ LL |     let bad = v[0];
    |               cannot move out of borrowed content
    |               help: consider borrowing here: `&v[0]`
 
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/borrowck-overloaded-index-move-from-vec.rs:30:15
-   |
-LL |     let bad = v[0];
-   |               ^^^^
-   |               |
-   |               cannot move out of data in a `&` reference
-   |               cannot move
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/borrowck/issue-51415.nll.stderr b/src/test/ui/borrowck/issue-51415.nll.stderr
index ee7e3e71962e8..d4340938eebc1 100644
--- a/src/test/ui/borrowck/issue-51415.nll.stderr
+++ b/src/test/ui/borrowck/issue-51415.nll.stderr
@@ -13,15 +13,6 @@ note: move occurs because `s` has type `std::string::String`, which does not imp
 LL |     let opt = a.iter().enumerate().find(|(_, &s)| {
    |                                               ^
 
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/issue-51415.rs:16:47
-   |
-LL |     let opt = a.iter().enumerate().find(|(_, &s)| {
-   |                                               ^
-   |                                               |
-   |                                               cannot move out of data in a `&` reference
-   |                                               cannot move
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/borrowck/move-in-static-initializer-issue-38520.ast.nll.stderr b/src/test/ui/borrowck/move-in-static-initializer-issue-38520.ast.nll.stderr
deleted file mode 100644
index d25fc75977542..0000000000000
--- a/src/test/ui/borrowck/move-in-static-initializer-issue-38520.ast.nll.stderr
+++ /dev/null
@@ -1,33 +0,0 @@
-error[E0507]: cannot move out of borrowed content
-  --> $DIR/move-in-static-initializer-issue-38520.rs:25:23
-   |
-LL | static Y: usize = get(*&X); //[ast]~ ERROR E0507
-   |                       ^^^ cannot move out of borrowed content
-
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/move-in-static-initializer-issue-38520.rs:25:23
-   |
-LL | static Y: usize = get(*&X); //[ast]~ ERROR E0507
-   |                       ^^^
-   |                       |
-   |                       cannot move out of data in a `&` reference
-   |                       cannot move
-
-error[E0507]: cannot move out of borrowed content
-  --> $DIR/move-in-static-initializer-issue-38520.rs:27:22
-   |
-LL | const Z: usize = get(*&X); //[ast]~ ERROR E0507
-   |                      ^^^ cannot move out of borrowed content
-
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/move-in-static-initializer-issue-38520.rs:27:22
-   |
-LL | const Z: usize = get(*&X); //[ast]~ ERROR E0507
-   |                      ^^^
-   |                      |
-   |                      cannot move out of data in a `&` reference
-   |                      cannot move
-
-error: aborting due to 4 previous errors
-
-For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.nll.stderr b/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.nll.stderr
index 0844ac32b8b60..0eb5fc8c32435 100644
--- a/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.nll.stderr
+++ b/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.nll.stderr
@@ -7,25 +7,6 @@ LL |     call(|| {
 LL |         y.into_iter();
    |         ^ cannot move out of captured variable in an `Fn` closure
 
-error[E0507]: cannot move out of `y`, as it is a captured variable in a `Fn` closure
-  --> $DIR/unboxed-closures-move-upvar-from-non-once-ref-closure.rs:21:9
-   |
-LL |         y.into_iter();
-   |         ^
-   |         |
-   |         cannot move out of `y`, as it is a captured variable in a `Fn` closure
-   |         cannot move
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/unboxed-closures-move-upvar-from-non-once-ref-closure.rs:20:10
-   |
-LL |       call(|| {
-   |  __________^
-LL | |         y.into_iter();
-LL | |         //~^ ERROR cannot move out of captured outer variable in an `Fn` closure
-LL | |     });
-   | |_____^
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/by-move-pattern-binding.nll.stderr b/src/test/ui/by-move-pattern-binding.nll.stderr
index 4098795811589..491b5b5bd74ab 100644
--- a/src/test/ui/by-move-pattern-binding.nll.stderr
+++ b/src/test/ui/by-move-pattern-binding.nll.stderr
@@ -16,15 +16,6 @@ note: move occurs because `identifier` has type `std::string::String`, which doe
 LL |         &E::Bar(identifier) => f(identifier.clone())  //~ ERROR cannot move
    |                 ^^^^^^^^^^
 
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/by-move-pattern-binding.rs:26:17
-   |
-LL |         &E::Bar(identifier) => f(identifier.clone())  //~ ERROR cannot move
-   |                 ^^^^^^^^^^
-   |                 |
-   |                 cannot move out of data in a `&` reference
-   |                 cannot move
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/check-static-values-constraints.nll.stderr b/src/test/ui/check-static-values-constraints.nll.stderr
index 576322ff5c195..5522e22fb1fa2 100644
--- a/src/test/ui/check-static-values-constraints.nll.stderr
+++ b/src/test/ui/check-static-values-constraints.nll.stderr
@@ -58,22 +58,13 @@ LL |     let y = { static x: Box<isize> = box 3; x };
    |                                             cannot move out of static item
    |                                             help: consider borrowing here: `&x`
 
-error[E0507]: cannot move out of immutable static item `x`
-  --> $DIR/check-static-values-constraints.rs:120:45
-   |
-LL |     let y = { static x: Box<isize> = box 3; x };
-   |                                             ^
-   |                                             |
-   |                                             cannot move out of immutable static item `x`
-   |                                             cannot move
-
 error[E0010]: allocations are not allowed in statics
   --> $DIR/check-static-values-constraints.rs:120:38
    |
 LL |     let y = { static x: Box<isize> = box 3; x };
    |                                      ^^^^^ allocation not allowed in statics
 
-error: aborting due to 11 previous errors
+error: aborting due to 10 previous errors
 
 Some errors occurred: E0010, E0015, E0493, E0507.
 For more information about an error, try `rustc --explain E0010`.
diff --git a/src/test/ui/dst/dst-index.nll.stderr b/src/test/ui/dst/dst-index.nll.stderr
index d14760b707db2..0aa85d3ed7a3d 100644
--- a/src/test/ui/dst/dst-index.nll.stderr
+++ b/src/test/ui/dst/dst-index.nll.stderr
@@ -16,31 +16,13 @@ error[E0507]: cannot move out of borrowed content
 LL |     S[0];
    |     ^^^^ cannot move out of borrowed content
 
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/dst-index.rs:41:5
-   |
-LL |     S[0];
-   |     ^^^^
-   |     |
-   |     cannot move out of data in a `&` reference
-   |     cannot move
-
 error[E0507]: cannot move out of borrowed content
   --> $DIR/dst-index.rs:44:5
    |
 LL |     T[0];
    |     ^^^^ cannot move out of borrowed content
 
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/dst-index.rs:44:5
-   |
-LL |     T[0];
-   |     ^^^^
-   |     |
-   |     cannot move out of data in a `&` reference
-   |     cannot move
-
-error: aborting due to 6 previous errors
+error: aborting due to 4 previous errors
 
 Some errors occurred: E0161, E0507.
 For more information about an error, try `rustc --explain E0161`.
diff --git a/src/test/ui/dst/dst-rvalue.nll.stderr b/src/test/ui/dst/dst-rvalue.nll.stderr
index 537ece48e012c..b120da773a24a 100644
--- a/src/test/ui/dst/dst-rvalue.nll.stderr
+++ b/src/test/ui/dst/dst-rvalue.nll.stderr
@@ -16,33 +16,13 @@ error[E0507]: cannot move out of borrowed content
 LL |     let _x: Box<str> = box *"hello world";
    |                            ^^^^^^^^^^^^^^ cannot move out of borrowed content
 
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/dst-rvalue.rs:16:28
-   |
-LL |     let _x: Box<str> = box *"hello world";
-   |                            ^^^^^^^^^^^^^^
-   |                            |
-   |                            cannot move out of data in a `&` reference
-   |                            cannot move
-
 error[E0508]: cannot move out of type `[isize]`, a non-copy slice
   --> $DIR/dst-rvalue.rs:21:32
    |
 LL |     let _x: Box<[isize]> = box *array;
    |                                ^^^^^^ cannot move out of here
 
-error[E0507]: cannot move out of `*array` which is behind a `&` reference
-  --> $DIR/dst-rvalue.rs:21:32
-   |
-LL |     let array: &[isize] = &[1, 2, 3];
-   |                           ---------- help: consider changing this to be a mutable reference: `&mut [1, 2, 3]`
-LL |     let _x: Box<[isize]> = box *array;
-   |                                ^^^^^^
-   |                                |
-   |                                cannot move out of `*array` which is behind a `&` reference
-   |                                `array` is a `&` reference, so the data it refers to cannot be moved
-
-error: aborting due to 6 previous errors
+error: aborting due to 4 previous errors
 
 Some errors occurred: E0161, E0507, E0508.
 For more information about an error, try `rustc --explain E0161`.
diff --git a/src/test/ui/error-codes/E0507.nll.stderr b/src/test/ui/error-codes/E0507.nll.stderr
deleted file mode 100644
index 43795e5224ddc..0000000000000
--- a/src/test/ui/error-codes/E0507.nll.stderr
+++ /dev/null
@@ -1,18 +0,0 @@
-error[E0507]: cannot move out of borrowed content
-  --> $DIR/E0507.rs:22:5
-   |
-LL |     x.borrow().nothing_is_true(); //~ ERROR E0507
-   |     ^^^^^^^^^^ cannot move out of borrowed content
-
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/E0507.rs:22:5
-   |
-LL |     x.borrow().nothing_is_true(); //~ ERROR E0507
-   |     ^^^^^^^^^^
-   |     |
-   |     cannot move out of data in a `&` reference
-   |     cannot move
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/issues/issue-12567.nll.stderr b/src/test/ui/issues/issue-12567.nll.stderr
index bb844c4a69f22..72d21d47d86fa 100644
--- a/src/test/ui/issues/issue-12567.nll.stderr
+++ b/src/test/ui/issues/issue-12567.nll.stderr
@@ -40,43 +40,6 @@ LL |         (&[], &[hd, ..]) | (&[hd, ..], &[])
 LL |         (&[hd1, ..], &[hd2, ..])
    |            ^^^
 
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/issue-12567.rs:16:17
-   |
-LL |         (&[], &[hd, ..]) | (&[hd, ..], &[])
-   |                 ^^
-   |                 |
-   |                 cannot move out of data in a `&` reference
-   |                 cannot move
-
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/issue-12567.rs:16:31
-   |
-LL |         (&[], &[hd, ..]) | (&[hd, ..], &[])
-   |                               ^^
-   |                               |
-   |                               cannot move out of data in a `&` reference
-   |                               cannot move
-
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/issue-12567.rs:20:12
-   |
-LL |         (&[hd1, ..], &[hd2, ..])
-   |            ^^^
-   |            |
-   |            cannot move out of data in a `&` reference
-   |            cannot move
-
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/issue-12567.rs:20:24
-   |
-LL |         (&[hd1, ..], &[hd2, ..])
-   |                        ^^^
-   |                        |
-   |                        cannot move out of data in a `&` reference
-   |                        cannot move
-
-error: aborting due to 6 previous errors
+error: aborting due to 2 previous errors
 
-Some errors occurred: E0507, E0508.
-For more information about an error, try `rustc --explain E0507`.
+For more information about this error, try `rustc --explain E0508`.
diff --git a/src/test/ui/issues/issue-17718-static-move.nll.stderr b/src/test/ui/issues/issue-17718-static-move.nll.stderr
index 66fba7749f479..f8da3c3d5989e 100644
--- a/src/test/ui/issues/issue-17718-static-move.nll.stderr
+++ b/src/test/ui/issues/issue-17718-static-move.nll.stderr
@@ -7,15 +7,6 @@ LL |     let _a = FOO; //~ ERROR: cannot move out of static item
    |              cannot move out of static item
    |              help: consider borrowing here: `&FOO`
 
-error[E0507]: cannot move out of immutable static item `FOO`
-  --> $DIR/issue-17718-static-move.rs:16:14
-   |
-LL |     let _a = FOO; //~ ERROR: cannot move out of static item
-   |              ^^^
-   |              |
-   |              cannot move out of immutable static item `FOO`
-   |              cannot move
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/issues/issue-20801.nll.stderr b/src/test/ui/issues/issue-20801.nll.stderr
index 60e3056b8c4d4..362778b26c861 100644
--- a/src/test/ui/issues/issue-20801.nll.stderr
+++ b/src/test/ui/issues/issue-20801.nll.stderr
@@ -16,15 +16,6 @@ LL |     let b = unsafe { *imm_ref() };
    |                      cannot move out of borrowed content
    |                      help: consider removing the `*`: `imm_ref()`
 
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/issue-20801.rs:39:22
-   |
-LL |     let b = unsafe { *imm_ref() };
-   |                      ^^^^^^^^^^
-   |                      |
-   |                      cannot move out of data in a `&` reference
-   |                      cannot move
-
 error[E0507]: cannot move out of dereference of raw pointer
   --> $DIR/issue-20801.rs:42:22
    |
@@ -43,15 +34,6 @@ LL |     let d = unsafe { *const_ptr() };
    |                      cannot move out of dereference of raw pointer
    |                      help: consider removing the `*`: `const_ptr()`
 
-error[E0507]: cannot move out of data in a `*const` pointer
-  --> $DIR/issue-20801.rs:45:22
-   |
-LL |     let d = unsafe { *const_ptr() };
-   |                      ^^^^^^^^^^^^
-   |                      |
-   |                      cannot move out of data in a `*const` pointer
-   |                      cannot move
-
-error: aborting due to 6 previous errors
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/issues/issue-2590.nll.stderr b/src/test/ui/issues/issue-2590.nll.stderr
index ae726d26d072b..fa2df26498a06 100644
--- a/src/test/ui/issues/issue-2590.nll.stderr
+++ b/src/test/ui/issues/issue-2590.nll.stderr
@@ -4,17 +4,6 @@ error[E0507]: cannot move out of borrowed content
 LL |         self.tokens //~ ERROR cannot move out of borrowed content
    |         ^^^^^^^^^^^ cannot move out of borrowed content
 
-error[E0507]: cannot move out of `self.tokens` which is behind a `&` reference
-  --> $DIR/issue-2590.rs:22:9
-   |
-LL |     fn parse(&self) -> Vec<isize> {
-   |              ----- help: consider changing this to be a mutable reference: `&mut self`
-LL |         self.tokens //~ ERROR cannot move out of borrowed content
-   |         ^^^^^^^^^^^
-   |         |
-   |         cannot move out of `self.tokens` which is behind a `&` reference
-   |         `self` is a `&` reference, so the data it refers to cannot be moved
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/issues/issue-30355.nll.stderr b/src/test/ui/issues/issue-30355.nll.stderr
index 78cf3661bb763..fdf8157dcf833 100644
--- a/src/test/ui/issues/issue-30355.nll.stderr
+++ b/src/test/ui/issues/issue-30355.nll.stderr
@@ -16,16 +16,7 @@ error[E0508]: cannot move out of type `[u8]`, a non-copy slice
 LL |     &X(*Y)
    |        ^^ cannot move out of here
 
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/issue-30355.rs:15:8
-   |
-LL |     &X(*Y)
-   |        ^^
-   |        |
-   |        cannot move out of data in a `&` reference
-   |        cannot move
-
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
 
-Some errors occurred: E0161, E0507, E0508.
+Some errors occurred: E0161, E0508.
 For more information about an error, try `rustc --explain E0161`.
diff --git a/src/test/ui/issues/issue-40402-ref-hints/issue-40402-1.nll.stderr b/src/test/ui/issues/issue-40402-ref-hints/issue-40402-1.nll.stderr
index 22fcffb9527a4..9020d3778c373 100644
--- a/src/test/ui/issues/issue-40402-ref-hints/issue-40402-1.nll.stderr
+++ b/src/test/ui/issues/issue-40402-ref-hints/issue-40402-1.nll.stderr
@@ -7,15 +7,6 @@ LL |     let e = f.v[0]; //~ ERROR cannot move out of indexed content
    |             cannot move out of borrowed content
    |             help: consider borrowing here: `&f.v[0]`
 
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/issue-40402-1.rs:19:13
-   |
-LL |     let e = f.v[0]; //~ ERROR cannot move out of indexed content
-   |             ^^^^^^
-   |             |
-   |             cannot move out of data in a `&` reference
-   |             cannot move
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/issues/issue-40402-ref-hints/issue-40402-2.nll.stderr b/src/test/ui/issues/issue-40402-ref-hints/issue-40402-2.nll.stderr
index cdb547ad10064..a80e9a5fe091f 100644
--- a/src/test/ui/issues/issue-40402-ref-hints/issue-40402-2.nll.stderr
+++ b/src/test/ui/issues/issue-40402-ref-hints/issue-40402-2.nll.stderr
@@ -1,21 +1,3 @@
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/issue-40402-2.rs:15:10
-   |
-LL |     let (a, b) = x[0]; //~ ERROR cannot move out of indexed content
-   |          ^
-   |          |
-   |          cannot move out of data in a `&` reference
-   |          cannot move
-
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/issue-40402-2.rs:15:13
-   |
-LL |     let (a, b) = x[0]; //~ ERROR cannot move out of indexed content
-   |             ^
-   |             |
-   |             cannot move out of data in a `&` reference
-   |             cannot move
-
 error[E0507]: cannot move out of borrowed content
   --> $DIR/issue-40402-2.rs:15:18
    |
@@ -33,6 +15,6 @@ note: move occurs because these variables have types that don't implement the `C
 LL |     let (a, b) = x[0]; //~ ERROR cannot move out of indexed content
    |          ^  ^
 
-error: aborting due to 3 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/issues/issue-4335.nll.stderr b/src/test/ui/issues/issue-4335.nll.stderr
index beb853fc91ae5..d31eddfa5b65d 100644
--- a/src/test/ui/issues/issue-4335.nll.stderr
+++ b/src/test/ui/issues/issue-4335.nll.stderr
@@ -6,15 +6,6 @@ LL | fn f<'r, T>(v: &'r T) -> Box<FnMut() -> T + 'r> {
 LL |     id(Box::new(|| *v))
    |                    ^^ cannot move out of captured variable in an `FnMut` closure
 
-error[E0507]: cannot move out of `*v` which is behind a `&` reference
-  --> $DIR/issue-4335.rs:16:20
-   |
-LL |     id(Box::new(|| *v))
-   |                    ^^
-   |                    |
-   |                    cannot move out of `*v` which is behind a `&` reference
-   |                    cannot move
-
 error[E0373]: closure may outlive the current function, but it borrows `v`, which is owned by the current function
   --> $DIR/issue-4335.rs:16:17
    |
@@ -33,7 +24,7 @@ help: to force the closure to take ownership of `v` (and any other referenced va
 LL |     id(Box::new(move || *v))
    |                 ^^^^^^^
 
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
 Some errors occurred: E0373, E0507.
 For more information about an error, try `rustc --explain E0373`.
diff --git a/src/test/ui/moves/moves-based-on-type-block-bad.nll.stderr b/src/test/ui/moves/moves-based-on-type-block-bad.nll.stderr
index 1ded703fd5a2f..1f22ab1481852 100644
--- a/src/test/ui/moves/moves-based-on-type-block-bad.nll.stderr
+++ b/src/test/ui/moves/moves-based-on-type-block-bad.nll.stderr
@@ -16,18 +16,6 @@ note: move occurs because `x` has type `std::boxed::Box<isize>`, which does not
 LL |                 box E::Bar(x) => println!("{}", x.to_string()),
    |                            ^
 
-error[E0507]: cannot move out of `hellothere.x.0` which is behind a `&` reference
-  --> $DIR/moves-based-on-type-block-bad.rs:37:28
-   |
-LL |         f(&s, |hellothere| {
-   |                ---------- help: consider changing this to be a mutable reference: `&mut S`
-...
-LL |                 box E::Bar(x) => println!("{}", x.to_string()),
-   |                            ^
-   |                            |
-   |                            cannot move out of `hellothere.x.0` which is behind a `&` reference
-   |                            `hellothere` is a `&` reference, so the data it refers to cannot be moved
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.nll.stderr b/src/test/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.nll.stderr
index a34a9efab6bce..13a6fc15ce318 100644
--- a/src/test/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.nll.stderr
+++ b/src/test/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.nll.stderr
@@ -6,21 +6,6 @@ LL |     let i = box 3;
 LL |     let _f = to_fn(|| test(i)); //~ ERROR cannot move out
    |                            ^ cannot move out of captured variable in an `Fn` closure
 
-error[E0507]: cannot move out of `i`, as it is a captured variable in a `Fn` closure
-  --> $DIR/moves-based-on-type-move-out-of-closure-env-issue-1965.rs:21:28
-   |
-LL |     let _f = to_fn(|| test(i)); //~ ERROR cannot move out
-   |                            ^
-   |                            |
-   |                            cannot move out of `i`, as it is a captured variable in a `Fn` closure
-   |                            cannot move
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/moves-based-on-type-move-out-of-closure-env-issue-1965.rs:21:20
-   |
-LL |     let _f = to_fn(|| test(i)); //~ ERROR cannot move out
-   |                    ^^^^^^^^^^
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/nll/cannot-move-block-spans.nll.stderr b/src/test/ui/nll/cannot-move-block-spans.nll.stderr
index 2d54ae0b81ec8..6a4c8f2e8d815 100644
--- a/src/test/ui/nll/cannot-move-block-spans.nll.stderr
+++ b/src/test/ui/nll/cannot-move-block-spans.nll.stderr
@@ -7,17 +7,6 @@ LL |     let x = { *r }; //~ ERROR
    |               cannot move out of borrowed content
    |               help: consider removing the `*`: `r`
 
-error[E0507]: cannot move out of `*r` which is behind a `&` reference
-  --> $DIR/cannot-move-block-spans.rs:15:15
-   |
-LL | pub fn deref(r: &String) {
-   |                 ------- help: consider changing this to be a mutable reference: `&mut std::string::String`
-LL |     let x = { *r }; //~ ERROR
-   |               ^^
-   |               |
-   |               cannot move out of `*r` which is behind a `&` reference
-   |               `r` is a `&` reference, so the data it refers to cannot be moved
-
 error[E0507]: cannot move out of borrowed content
   --> $DIR/cannot-move-block-spans.rs:16:22
    |
@@ -27,18 +16,6 @@ LL |     let y = unsafe { *r }; //~ ERROR
    |                      cannot move out of borrowed content
    |                      help: consider removing the `*`: `r`
 
-error[E0507]: cannot move out of `*r` which is behind a `&` reference
-  --> $DIR/cannot-move-block-spans.rs:16:22
-   |
-LL | pub fn deref(r: &String) {
-   |                 ------- help: consider changing this to be a mutable reference: `&mut std::string::String`
-LL |     let x = { *r }; //~ ERROR
-LL |     let y = unsafe { *r }; //~ ERROR
-   |                      ^^
-   |                      |
-   |                      cannot move out of `*r` which is behind a `&` reference
-   |                      `r` is a `&` reference, so the data it refers to cannot be moved
-
 error[E0507]: cannot move out of borrowed content
   --> $DIR/cannot-move-block-spans.rs:17:26
    |
@@ -48,18 +25,6 @@ LL |     let z = loop { break *r; }; //~ ERROR
    |                          cannot move out of borrowed content
    |                          help: consider removing the `*`: `r`
 
-error[E0507]: cannot move out of `*r` which is behind a `&` reference
-  --> $DIR/cannot-move-block-spans.rs:17:26
-   |
-LL | pub fn deref(r: &String) {
-   |                 ------- help: consider changing this to be a mutable reference: `&mut std::string::String`
-...
-LL |     let z = loop { break *r; }; //~ ERROR
-   |                          ^^
-   |                          |
-   |                          cannot move out of `*r` which is behind a `&` reference
-   |                          `r` is a `&` reference, so the data it refers to cannot be moved
-
 error[E0508]: cannot move out of type `[std::string::String; 2]`, a non-copy array
   --> $DIR/cannot-move-block-spans.rs:21:15
    |
@@ -96,17 +61,6 @@ LL |     let x = { let mut u = 0; u += 1; *r }; //~ ERROR
    |                                      cannot move out of borrowed content
    |                                      help: consider removing the `*`: `r`
 
-error[E0507]: cannot move out of `*r` which is behind a `&` reference
-  --> $DIR/cannot-move-block-spans.rs:27:38
-   |
-LL | pub fn additional_statement_cases(r: &String) {
-   |                                      ------- help: consider changing this to be a mutable reference: `&mut std::string::String`
-LL |     let x = { let mut u = 0; u += 1; *r }; //~ ERROR
-   |                                      ^^
-   |                                      |
-   |                                      cannot move out of `*r` which is behind a `&` reference
-   |                                      `r` is a `&` reference, so the data it refers to cannot be moved
-
 error[E0507]: cannot move out of borrowed content
   --> $DIR/cannot-move-block-spans.rs:28:45
    |
@@ -116,18 +70,6 @@ LL |     let y = unsafe { let mut u = 0; u += 1; *r }; //~ ERROR
    |                                             cannot move out of borrowed content
    |                                             help: consider removing the `*`: `r`
 
-error[E0507]: cannot move out of `*r` which is behind a `&` reference
-  --> $DIR/cannot-move-block-spans.rs:28:45
-   |
-LL | pub fn additional_statement_cases(r: &String) {
-   |                                      ------- help: consider changing this to be a mutable reference: `&mut std::string::String`
-LL |     let x = { let mut u = 0; u += 1; *r }; //~ ERROR
-LL |     let y = unsafe { let mut u = 0; u += 1; *r }; //~ ERROR
-   |                                             ^^
-   |                                             |
-   |                                             cannot move out of `*r` which is behind a `&` reference
-   |                                             `r` is a `&` reference, so the data it refers to cannot be moved
-
 error[E0507]: cannot move out of borrowed content
   --> $DIR/cannot-move-block-spans.rs:29:49
    |
@@ -137,19 +79,7 @@ LL |     let z = loop { let mut u = 0; u += 1; break *r; u += 2; }; //~ ERROR
    |                                                 cannot move out of borrowed content
    |                                                 help: consider removing the `*`: `r`
 
-error[E0507]: cannot move out of `*r` which is behind a `&` reference
-  --> $DIR/cannot-move-block-spans.rs:29:49
-   |
-LL | pub fn additional_statement_cases(r: &String) {
-   |                                      ------- help: consider changing this to be a mutable reference: `&mut std::string::String`
-...
-LL |     let z = loop { let mut u = 0; u += 1; break *r; u += 2; }; //~ ERROR
-   |                                                 ^^
-   |                                                 |
-   |                                                 cannot move out of `*r` which is behind a `&` reference
-   |                                                 `r` is a `&` reference, so the data it refers to cannot be moved
-
-error: aborting due to 15 previous errors
+error: aborting due to 9 previous errors
 
 Some errors occurred: E0507, E0508.
 For more information about an error, try `rustc --explain E0507`.
diff --git a/src/test/ui/nll/match-guards-always-borrow.ast.nll.stderr b/src/test/ui/nll/match-guards-always-borrow.ast.nll.stderr
index 4fe01d472f50e..afb85c69990f9 100644
--- a/src/test/ui/nll/match-guards-always-borrow.ast.nll.stderr
+++ b/src/test/ui/nll/match-guards-always-borrow.ast.nll.stderr
@@ -8,20 +8,6 @@ LL |             (|| { let bar = foo; bar.take() })();
            It represents potential unsoundness in your code.
            This warning will become a hard error in the future.
 
-warning[E0507]: cannot move out of `foo`, as it is immutable for the pattern guard
-  --> $DIR/match-guards-always-borrow.rs:23:13
-   |
-LL |             (|| { let bar = foo; bar.take() })();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |             |
-   |             cannot move out of `foo`, as it is immutable for the pattern guard
-   |             cannot move
-   |
-   = note: variables bound in patterns are immutable until the end of the pattern guard
-   = warning: This error has been downgraded to a warning for backwards compatibility with previous releases.
-           It represents potential unsoundness in your code.
-           This warning will become a hard error in the future.
-
 error: compilation successful
   --> $DIR/match-guards-always-borrow.rs:57:1
    |
diff --git a/src/test/ui/static/static-items-cant-move.nll.stderr b/src/test/ui/static/static-items-cant-move.nll.stderr
deleted file mode 100644
index 35a400b15089f..0000000000000
--- a/src/test/ui/static/static-items-cant-move.nll.stderr
+++ /dev/null
@@ -1,18 +0,0 @@
-error[E0507]: cannot move out of static item
-  --> $DIR/static-items-cant-move.rs:28:10
-   |
-LL |     test(BAR); //~ ERROR cannot move out of static item
-   |          ^^^ cannot move out of static item
-
-error[E0507]: cannot move out of immutable static item `BAR`
-  --> $DIR/static-items-cant-move.rs:28:10
-   |
-LL |     test(BAR); //~ ERROR cannot move out of static item
-   |          ^^^
-   |          |
-   |          cannot move out of immutable static item `BAR`
-   |          cannot move
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/std-uncopyable-atomics.nll.stderr b/src/test/ui/std-uncopyable-atomics.nll.stderr
index d2576a4baf369..e6b612fed8588 100644
--- a/src/test/ui/std-uncopyable-atomics.nll.stderr
+++ b/src/test/ui/std-uncopyable-atomics.nll.stderr
@@ -7,15 +7,6 @@ LL |     let x = *&x; //~ ERROR: cannot move out of borrowed content
    |             cannot move out of borrowed content
    |             help: consider removing the `*`: `&x`
 
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/std-uncopyable-atomics.rs:19:13
-   |
-LL |     let x = *&x; //~ ERROR: cannot move out of borrowed content
-   |             ^^^
-   |             |
-   |             cannot move out of data in a `&` reference
-   |             cannot move
-
 error[E0507]: cannot move out of borrowed content
   --> $DIR/std-uncopyable-atomics.rs:21:13
    |
@@ -25,15 +16,6 @@ LL |     let x = *&x; //~ ERROR: cannot move out of borrowed content
    |             cannot move out of borrowed content
    |             help: consider removing the `*`: `&x`
 
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/std-uncopyable-atomics.rs:21:13
-   |
-LL |     let x = *&x; //~ ERROR: cannot move out of borrowed content
-   |             ^^^
-   |             |
-   |             cannot move out of data in a `&` reference
-   |             cannot move
-
 error[E0507]: cannot move out of borrowed content
   --> $DIR/std-uncopyable-atomics.rs:23:13
    |
@@ -43,15 +25,6 @@ LL |     let x = *&x; //~ ERROR: cannot move out of borrowed content
    |             cannot move out of borrowed content
    |             help: consider removing the `*`: `&x`
 
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/std-uncopyable-atomics.rs:23:13
-   |
-LL |     let x = *&x; //~ ERROR: cannot move out of borrowed content
-   |             ^^^
-   |             |
-   |             cannot move out of data in a `&` reference
-   |             cannot move
-
 error[E0507]: cannot move out of borrowed content
   --> $DIR/std-uncopyable-atomics.rs:25:13
    |
@@ -61,15 +34,6 @@ LL |     let x = *&x; //~ ERROR: cannot move out of borrowed content
    |             cannot move out of borrowed content
    |             help: consider removing the `*`: `&x`
 
-error[E0507]: cannot move out of data in a `&` reference
-  --> $DIR/std-uncopyable-atomics.rs:25:13
-   |
-LL |     let x = *&x; //~ ERROR: cannot move out of borrowed content
-   |             ^^^
-   |             |
-   |             cannot move out of data in a `&` reference
-   |             cannot move
-
-error: aborting due to 8 previous errors
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/trivial-bounds/trivial-bounds-leak-copy.nll.stderr b/src/test/ui/trivial-bounds/trivial-bounds-leak-copy.nll.stderr
deleted file mode 100644
index 9f56bf0538436..0000000000000
--- a/src/test/ui/trivial-bounds/trivial-bounds-leak-copy.nll.stderr
+++ /dev/null
@@ -1,20 +0,0 @@
-error[E0507]: cannot move out of borrowed content
-  --> $DIR/trivial-bounds-leak-copy.rs:19:5
-   |
-LL |     *t //~ ERROR
-   |     ^^ cannot move out of borrowed content
-
-error[E0507]: cannot move out of `*t` which is behind a `&` reference
-  --> $DIR/trivial-bounds-leak-copy.rs:19:5
-   |
-LL | fn move_out_string(t: &String) -> String {
-   |                       ------- help: consider changing this to be a mutable reference: `&mut std::string::String`
-LL |     *t //~ ERROR
-   |     ^^
-   |     |
-   |     cannot move out of `*t` which is behind a `&` reference
-   |     `t` is a `&` reference, so the data it refers to cannot be moved
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-illegal-move.nll.stderr b/src/test/ui/unboxed-closures/unboxed-closure-illegal-move.nll.stderr
index 625084efec2ff..4baa54e34c755 100644
--- a/src/test/ui/unboxed-closures/unboxed-closure-illegal-move.nll.stderr
+++ b/src/test/ui/unboxed-closures/unboxed-closure-illegal-move.nll.stderr
@@ -6,21 +6,6 @@ LL |         let x = Box::new(0);
 LL |         let f = to_fn(|| drop(x)); //~ ERROR cannot move
    |                               ^ cannot move out of captured variable in an `Fn` closure
 
-error[E0507]: cannot move out of `x`, as it is a captured variable in a `Fn` closure
-  --> $DIR/unboxed-closure-illegal-move.rs:25:31
-   |
-LL |         let f = to_fn(|| drop(x)); //~ ERROR cannot move
-   |                               ^
-   |                               |
-   |                               cannot move out of `x`, as it is a captured variable in a `Fn` closure
-   |                               cannot move
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/unboxed-closure-illegal-move.rs:25:23
-   |
-LL |         let f = to_fn(|| drop(x)); //~ ERROR cannot move
-   |                       ^^^^^^^^^^
-
 error[E0507]: cannot move out of captured variable in an `FnMut` closure
   --> $DIR/unboxed-closure-illegal-move.rs:29:35
    |
@@ -37,21 +22,6 @@ LL |         let x = Box::new(0);
 LL |         let f = to_fn(move || drop(x)); //~ ERROR cannot move
    |                                    ^ cannot move out of captured variable in an `Fn` closure
 
-error[E0507]: cannot move out of `x`, as it is a captured variable in a `Fn` closure
-  --> $DIR/unboxed-closure-illegal-move.rs:38:36
-   |
-LL |         let f = to_fn(move || drop(x)); //~ ERROR cannot move
-   |                                    ^
-   |                                    |
-   |                                    cannot move out of `x`, as it is a captured variable in a `Fn` closure
-   |                                    cannot move
-   |
-help: consider changing this to accept closures that implement `FnMut`
-  --> $DIR/unboxed-closure-illegal-move.rs:38:23
-   |
-LL |         let f = to_fn(move || drop(x)); //~ ERROR cannot move
-   |                       ^^^^^^^^^^^^^^^
-
 error[E0507]: cannot move out of captured variable in an `FnMut` closure
   --> $DIR/unboxed-closure-illegal-move.rs:42:40
    |
@@ -60,6 +30,6 @@ LL |         let x = Box::new(0);
 LL |         let f = to_fn_mut(move || drop(x)); //~ ERROR cannot move
    |                                        ^ cannot move out of captured variable in an `FnMut` closure
 
-error: aborting due to 6 previous errors
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/unop-move-semantics.nll.stderr b/src/test/ui/unop-move-semantics.nll.stderr
index 8112ddbe2f16e..111940aab2c32 100644
--- a/src/test/ui/unop-move-semantics.nll.stderr
+++ b/src/test/ui/unop-move-semantics.nll.stderr
@@ -21,19 +21,7 @@ error[E0507]: cannot move out of borrowed content
 LL |     !*n;  //~ ERROR: cannot move out of borrowed content
    |      ^^ cannot move out of borrowed content
 
-error[E0507]: cannot move out of `*n` which is behind a `&` reference
-  --> $DIR/unop-move-semantics.rs:36:6
-   |
-LL |     let n = &y;
-   |             -- help: consider changing this to be a mutable reference: `&mut y`
-...
-LL |     !*n;  //~ ERROR: cannot move out of borrowed content
-   |      ^^
-   |      |
-   |      cannot move out of `*n` which is behind a `&` reference
-   |      `n` is a `&` reference, so the data it refers to cannot be moved
-
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
 
 Some errors occurred: E0382, E0507.
 For more information about an error, try `rustc --explain E0382`.

From 412ad9bf3746bd0682db136c36a433f3205715f1 Mon Sep 17 00:00:00 2001
From: Michael Bradshaw <mjbshaw@gmail.com>
Date: Sun, 21 Oct 2018 20:09:42 -0700
Subject: [PATCH 20/30] Allow extern statics with an extern type

Fixes #55239
---
 src/librustc_typeck/check/wfcheck.rs     | 42 +++++++++++++++---------
 src/test/ui/static/static-extern-type.rs | 37 +++++++++++++++++++++
 2 files changed, 64 insertions(+), 15 deletions(-)
 create mode 100644 src/test/ui/static/static-extern-type.rs

diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs
index ec773e384af38..ec6ca242fa36b 100644
--- a/src/librustc_typeck/check/wfcheck.rs
+++ b/src/librustc_typeck/check/wfcheck.rs
@@ -13,7 +13,7 @@ use constrained_type_params::{identify_constrained_type_params, Parameter};
 
 use hir::def_id::DefId;
 use rustc::traits::{self, ObligationCauseCode};
-use rustc::ty::{self, Lift, Ty, TyCtxt, GenericParamDefKind, TypeFoldable};
+use rustc::ty::{self, Lift, Ty, TyCtxt, TyKind, GenericParamDefKind, TypeFoldable};
 use rustc::ty::subst::{Subst, Substs};
 use rustc::ty::util::ExplicitSelf;
 use rustc::util::nodemap::{FxHashSet, FxHashMap};
@@ -119,14 +119,14 @@ pub fn check_item_well_formed<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: Def
             check_item_fn(tcx, item);
         }
         hir::ItemKind::Static(ref ty, ..) => {
-            check_item_type(tcx, item.id, ty.span);
+            check_item_type(tcx, item.id, ty.span, false);
         }
         hir::ItemKind::Const(ref ty, ..) => {
-            check_item_type(tcx, item.id, ty.span);
+            check_item_type(tcx, item.id, ty.span, false);
         }
         hir::ItemKind::ForeignMod(ref module) => for it in module.items.iter() {
             if let hir::ForeignItemKind::Static(ref ty, ..) = it.node {
-                check_item_type(tcx, it.id, ty.span);
+                check_item_type(tcx, it.id, ty.span, true);
             }
         },
         hir::ItemKind::Struct(ref struct_def, ref ast_generics) => {
@@ -340,23 +340,35 @@ fn check_item_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item: &hir::Item) {
     })
 }
 
-fn check_item_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item_id: ast::NodeId, ty_span: Span) {
+fn check_item_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                             item_id: ast::NodeId,
+                             ty_span: Span,
+                             allow_foreign_ty: bool) {
     debug!("check_item_type: {:?}", item_id);
 
     for_id(tcx, item_id, ty_span).with_fcx(|fcx, _this| {
         let ty = fcx.tcx.type_of(fcx.tcx.hir.local_def_id(item_id));
         let item_ty = fcx.normalize_associated_types_in(ty_span, &ty);
 
-        fcx.register_wf_obligation(item_ty, ty_span, ObligationCauseCode::MiscObligation);
-        fcx.register_bound(
-            item_ty,
-            fcx.tcx.require_lang_item(lang_items::SizedTraitLangItem),
-            traits::ObligationCause::new(
-                ty_span,
-                fcx.body_id,
-                traits::MiscObligation,
-            ),
-        );
+        let mut allow_unsized = false;
+        if allow_foreign_ty {
+            if let TyKind::Foreign(_) = tcx.struct_tail(item_ty).sty {
+                allow_unsized = true;
+            }
+        }
+
+        if !allow_unsized {
+            fcx.register_wf_obligation(item_ty, ty_span, ObligationCauseCode::MiscObligation);
+            fcx.register_bound(
+                item_ty,
+                fcx.tcx.require_lang_item(lang_items::SizedTraitLangItem),
+                traits::ObligationCause::new(
+                    ty_span,
+                    fcx.body_id,
+                    traits::MiscObligation,
+                ),
+            );
+        }
 
         vec![] // no implied bounds in a const etc
     });
diff --git a/src/test/ui/static/static-extern-type.rs b/src/test/ui/static/static-extern-type.rs
new file mode 100644
index 0000000000000..72e2853b9f038
--- /dev/null
+++ b/src/test/ui/static/static-extern-type.rs
@@ -0,0 +1,37 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-pass
+#![feature(extern_types)]
+
+pub mod a {
+    extern "C" {
+        pub type StartFn;
+        pub static start: StartFn;
+    }
+}
+
+pub mod b {
+    #[repr(transparent)]
+    pub struct TransparentType(::a::StartFn);
+    extern "C" {
+        pub static start: TransparentType;
+    }
+}
+
+pub mod c {
+    #[repr(C)]
+    pub struct CType(u32, ::b::TransparentType);
+    extern "C" {
+        pub static start: CType;
+    }
+}
+
+fn main() {}

From 5b84550d7c39821704a48b53c8cbe3916ab100b7 Mon Sep 17 00:00:00 2001
From: Michael Bradshaw <mjbshaw@gmail.com>
Date: Mon, 22 Oct 2018 07:03:53 -0700
Subject: [PATCH 21/30] Keep an obligation for both sized and unsized types

---
 src/librustc_typeck/check/wfcheck.rs | 24 +++++++++++-------------
 1 file changed, 11 insertions(+), 13 deletions(-)

diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs
index ec6ca242fa36b..f21296a2a0772 100644
--- a/src/librustc_typeck/check/wfcheck.rs
+++ b/src/librustc_typeck/check/wfcheck.rs
@@ -340,33 +340,31 @@ fn check_item_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item: &hir::Item) {
     })
 }
 
-fn check_item_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                             item_id: ast::NodeId,
-                             ty_span: Span,
-                             allow_foreign_ty: bool) {
+fn check_item_type<'a, 'tcx>(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    item_id: ast::NodeId,
+    ty_span: Span,
+    allow_foreign_ty: bool,
+) {
     debug!("check_item_type: {:?}", item_id);
 
     for_id(tcx, item_id, ty_span).with_fcx(|fcx, _this| {
         let ty = fcx.tcx.type_of(fcx.tcx.hir.local_def_id(item_id));
         let item_ty = fcx.normalize_associated_types_in(ty_span, &ty);
 
-        let mut allow_unsized = false;
+        let mut forbid_unsized = true;
         if allow_foreign_ty {
             if let TyKind::Foreign(_) = tcx.struct_tail(item_ty).sty {
-                allow_unsized = true;
+                forbid_unsized = false;
             }
         }
 
-        if !allow_unsized {
-            fcx.register_wf_obligation(item_ty, ty_span, ObligationCauseCode::MiscObligation);
+        fcx.register_wf_obligation(item_ty, ty_span, ObligationCauseCode::MiscObligation);
+        if forbid_unsized {
             fcx.register_bound(
                 item_ty,
                 fcx.tcx.require_lang_item(lang_items::SizedTraitLangItem),
-                traits::ObligationCause::new(
-                    ty_span,
-                    fcx.body_id,
-                    traits::MiscObligation,
-                ),
+                traits::ObligationCause::new(ty_span, fcx.body_id, traits::MiscObligation),
             );
         }
 

From b5336c0b9755f635db4eafba6254e192ee451e6a Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote <nnethercote@mozilla.com>
Date: Thu, 18 Oct 2018 09:11:11 +1100
Subject: [PATCH 22/30] Add a cheap mode for `compute_missing_ctors`.

`compute_missing_ctors` is called a lot. It produces a vector, which can
be reasonably large (e.g. 100+ elements), but the vector is almost
always only checked for emptiness.

This commit changes `compute_missing_ctors` so it can be called in a
cheap way that just indicates if the vector would be empty. If
necessary, the function can subsequently be called in an expensive way
to compute the full vector.

This change reduces instruction counts for several benchmarks up to 2%.
---
 src/librustc_mir/hair/pattern/_match.rs | 85 +++++++++++++++++++------
 1 file changed, 66 insertions(+), 19 deletions(-)

diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs
index aa1c6902dce33..7f46e2f384551 100644
--- a/src/librustc_mir/hair/pattern/_match.rs
+++ b/src/librustc_mir/hair/pattern/_match.rs
@@ -931,12 +931,37 @@ impl<'tcx> IntRange<'tcx> {
     }
 }
 
-// Return a set of constructors equivalent to `all_ctors \ used_ctors`.
+// A request for missing constructor data in terms of either:
+// - whether or not there any missing constructors; or
+// - the actual set of missing constructors.
+#[derive(PartialEq)]
+enum MissingCtorsInfo {
+    Emptiness,
+    Ctors,
+}
+
+// Used by `compute_missing_ctors`.
+#[derive(Debug, PartialEq)]
+enum MissingCtors<'tcx> {
+    Empty,
+    NonEmpty,
+
+    // Note that the Vec can be empty.
+    Ctors(Vec<Constructor<'tcx>>),
+}
+
+// When `info` is `MissingCtorsInfo::Ctors`, compute a set of constructors
+// equivalent to `all_ctors \ used_ctors`. When `info` is
+// `MissingCtorsInfo::Emptiness`, just determines if that set is empty or not.
+// (The split logic gives a performance win, because we always need to know if
+// the set is empty, but we rarely need the full set, and it can be expensive
+// to compute the full set.)
 fn compute_missing_ctors<'a, 'tcx: 'a>(
+    info: MissingCtorsInfo,
     tcx: TyCtxt<'a, 'tcx, 'tcx>,
     all_ctors: &Vec<Constructor<'tcx>>,
     used_ctors: &Vec<Constructor<'tcx>>,
-) -> Vec<Constructor<'tcx>> {
+) -> MissingCtors<'tcx> {
     let mut missing_ctors = vec![];
 
     for req_ctor in all_ctors {
@@ -965,10 +990,22 @@ fn compute_missing_ctors<'a, 'tcx: 'a>(
         // We add `refined_ctors` instead of `req_ctor`, because then we can
         // provide more detailed error information about precisely which
         // ranges have been omitted.
-        missing_ctors.extend(refined_ctors);
+        if info == MissingCtorsInfo::Emptiness {
+            if !refined_ctors.is_empty() {
+                // The set is non-empty; return early.
+                return MissingCtors::NonEmpty;
+            }
+        } else {
+            missing_ctors.extend(refined_ctors);
+        }
     }
 
-    missing_ctors
+    if info == MissingCtorsInfo::Emptiness {
+        // If we reached here, the set is empty.
+        MissingCtors::Empty
+    } else {
+        MissingCtors::Ctors(missing_ctors)
+    }
 }
 
 /// Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html
@@ -1081,20 +1118,23 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
         // feature flag is not present, so this is only
         // needed for that case.
 
-        // Find those constructors that are not matched by any non-wildcard patterns in the
-        // current column.
-        let missing_ctors = compute_missing_ctors(cx.tcx, &all_ctors, &used_ctors);
+        // Missing constructors are those that are not matched by any
+        // non-wildcard patterns in the current column. We always determine if
+        // the set is empty, but we only fully construct them on-demand,
+        // because they're rarely used and can be big.
+        let cheap_missing_ctors =
+            compute_missing_ctors(MissingCtorsInfo::Emptiness, cx.tcx, &all_ctors, &used_ctors);
 
         let is_privately_empty = all_ctors.is_empty() && !cx.is_uninhabited(pcx.ty);
         let is_declared_nonexhaustive = cx.is_non_exhaustive_enum(pcx.ty) && !cx.is_local(pcx.ty);
-        debug!("missing_ctors={:#?} is_privately_empty={:#?} is_declared_nonexhaustive={:#?}",
-               missing_ctors, is_privately_empty, is_declared_nonexhaustive);
+        debug!("cheap_missing_ctors={:#?} is_privately_empty={:#?} is_declared_nonexhaustive={:#?}",
+               cheap_missing_ctors, is_privately_empty, is_declared_nonexhaustive);
 
         // For privately empty and non-exhaustive enums, we work as if there were an "extra"
         // `_` constructor for the type, so we can never match over all constructors.
         let is_non_exhaustive = is_privately_empty || is_declared_nonexhaustive;
 
-        if missing_ctors.is_empty() && !is_non_exhaustive {
+        if cheap_missing_ctors == MissingCtors::Empty && !is_non_exhaustive {
             split_grouped_constructors(cx.tcx, all_ctors, matrix, pcx.ty).into_iter().map(|c| {
                 is_useful_specialized(cx, matrix, v, c.clone(), pcx.ty, witness)
             }).find(|result| result.is_useful()).unwrap_or(NotUseful)
@@ -1165,15 +1205,22 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
                             witness
                         }).collect()
                     } else {
-                        pats.into_iter().flat_map(|witness| {
-                            missing_ctors.iter().map(move |ctor| {
-                                // Extends the witness with a "wild" version of this
-                                // constructor, that matches everything that can be built with
-                                // it. For example, if `ctor` is a `Constructor::Variant` for
-                                // `Option::Some`, this pushes the witness for `Some(_)`.
-                                witness.clone().push_wild_constructor(cx, ctor, pcx.ty)
-                            })
-                        }).collect()
+                        let expensive_missing_ctors =
+                            compute_missing_ctors(MissingCtorsInfo::Ctors, cx.tcx, &all_ctors,
+                                                  &used_ctors);
+                        if let MissingCtors::Ctors(missing_ctors) = expensive_missing_ctors {
+                            pats.into_iter().flat_map(|witness| {
+                                missing_ctors.iter().map(move |ctor| {
+                                    // Extends the witness with a "wild" version of this
+                                    // constructor, that matches everything that can be built with
+                                    // it. For example, if `ctor` is a `Constructor::Variant` for
+                                    // `Option::Some`, this pushes the witness for `Some(_)`.
+                                    witness.clone().push_wild_constructor(cx, ctor, pcx.ty)
+                                })
+                            }).collect()
+                        } else {
+                            bug!("cheap missing ctors")
+                        }
                     };
                     UsefulWithWitness(new_witnesses)
                 }

From 8a3bb9a95cc8d99e5f8083ba85e6fa54db86bc72 Mon Sep 17 00:00:00 2001
From: Tom Tromey <tom@tromey.com>
Date: Fri, 12 Oct 2018 07:34:14 -0600
Subject: [PATCH 23/30] Add template parameter debuginfo to generic types

This changes debuginfo generation to add template parameters to
generic types.  With this change the DWARF now has
DW_TAG_template_type_param for types, not just for functions, like:

 <2><40d>: Abbrev Number: 6 (DW_TAG_structure_type)
    <40e>   DW_AT_name        : (indirect string, offset: 0x375): Generic<i32>
    <412>   DW_AT_byte_size   : 4
    <413>   DW_AT_alignment   : 4
...
 <3><41f>: Abbrev Number: 8 (DW_TAG_template_type_param)
    <420>   DW_AT_type        : <0x42a>
    <424>   DW_AT_name        : (indirect string, offset: 0xa65e): T

Closes #9224
---
 .../debuginfo/metadata.rs                     | 62 +++++++++++++++++--
 src/librustc_codegen_llvm/llvm/ffi.rs         |  7 ++-
 src/rustllvm/RustWrapper.cpp                  | 10 +--
 src/test/codegen/generic-debug.rs             | 28 +++++++++
 4 files changed, 96 insertions(+), 11 deletions(-)
 create mode 100644 src/test/codegen/generic-debug.rs

diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs
index 846d505641103..3680810ea79f7 100644
--- a/src/librustc_codegen_llvm/debuginfo/metadata.rs
+++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs
@@ -21,7 +21,7 @@ use abi;
 use value::Value;
 
 use llvm;
-use llvm::debuginfo::{DIType, DIFile, DIScope, DIDescriptor,
+use llvm::debuginfo::{DIArray, DIType, DIFile, DIScope, DIDescriptor,
                       DICompositeType, DILexicalBlock, DIFlags};
 
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
@@ -34,6 +34,7 @@ use rustc::ty::Instance;
 use common::CodegenCx;
 use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt};
 use rustc::ty::layout::{self, Align, LayoutOf, PrimitiveExt, Size, TyLayout};
+use rustc::ty::subst::UnpackedKind;
 use rustc::session::config;
 use rustc::util::nodemap::FxHashMap;
 use rustc_fs_util::path2cstr;
@@ -266,6 +267,7 @@ impl RecursiveTypeDescription<'ll, 'tcx> {
 
                 // ... and attach them to the stub to complete it.
                 set_members_of_composite_type(cx,
+                                              unfinished_type,
                                               metadata_stub,
                                               member_descriptions);
                 return MetadataCreationResult::new(metadata_stub, true);
@@ -1174,6 +1176,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
                     member_description_factory.create_member_descriptions(cx);
 
                 set_members_of_composite_type(cx,
+                                              self.enum_type,
                                               variant_type_metadata,
                                               member_descriptions);
                 vec![
@@ -1204,6 +1207,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
                         .create_member_descriptions(cx);
 
                     set_members_of_composite_type(cx,
+                                                  self.enum_type,
                                                   variant_type_metadata,
                                                   member_descriptions);
                     MemberDescription {
@@ -1231,6 +1235,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> {
                     member_description_factory.create_member_descriptions(cx);
 
                 set_members_of_composite_type(cx,
+                                              self.enum_type,
                                               variant_type_metadata,
                                               variant_member_descriptions);
 
@@ -1534,13 +1539,15 @@ fn composite_type_metadata(
                                                      containing_scope);
     // ... and immediately create and add the member descriptions.
     set_members_of_composite_type(cx,
+                                  composite_type,
                                   composite_type_metadata,
                                   member_descriptions);
 
     composite_type_metadata
 }
 
-fn set_members_of_composite_type(cx: &CodegenCx<'ll, '_>,
+fn set_members_of_composite_type(cx: &CodegenCx<'ll, 'tcx>,
+                                 composite_type: Ty<'tcx>,
                                  composite_type_metadata: &'ll DICompositeType,
                                  member_descriptions: Vec<MemberDescription<'ll>>) {
     // In some rare cases LLVM metadata uniquing would lead to an existing type
@@ -1580,10 +1587,57 @@ fn set_members_of_composite_type(cx: &CodegenCx<'ll, '_>,
         })
         .collect();
 
+    let type_params = compute_type_parameters(cx, composite_type);
     unsafe {
         let type_array = create_DIArray(DIB(cx), &member_metadata[..]);
-        llvm::LLVMRustDICompositeTypeSetTypeArray(
-            DIB(cx), composite_type_metadata, type_array);
+        llvm::LLVMRustDICompositeTypeReplaceArrays(
+            DIB(cx), composite_type_metadata, Some(type_array), type_params);
+    }
+}
+
+// Compute the type parameters for a type, if any, for the given
+// metadata.
+fn compute_type_parameters(cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>) -> Option<&'ll DIArray> {
+    if let ty::Adt(def, substs) = ty.sty {
+        if !substs.types().next().is_none() {
+            let generics = cx.tcx.generics_of(def.did);
+            let names = get_parameter_names(cx, generics);
+            let template_params: Vec<_> = substs.iter().zip(names).filter_map(|(kind, name)| {
+                if let UnpackedKind::Type(ty) = kind.unpack() {
+                    let actual_type = cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty);
+                    let actual_type_metadata =
+                        type_metadata(cx, actual_type, syntax_pos::DUMMY_SP);
+                    let name = SmallCStr::new(&name.as_str());
+                    Some(unsafe {
+
+                        Some(llvm::LLVMRustDIBuilderCreateTemplateTypeParameter(
+                            DIB(cx),
+                            None,
+                            name.as_ptr(),
+                            actual_type_metadata,
+                            unknown_file_metadata(cx),
+                            0,
+                            0,
+                        ))
+                    })
+                } else {
+                    None
+                }
+            }).collect();
+
+            return Some(create_DIArray(DIB(cx), &template_params[..]));
+        }
+    }
+    return None;
+
+    fn get_parameter_names(cx: &CodegenCx,
+                           generics: &ty::Generics)
+                           -> Vec<InternedString> {
+        let mut names = generics.parent.map_or(vec![], |def_id| {
+            get_parameter_names(cx, cx.tcx.generics_of(def_id))
+        });
+        names.extend(generics.params.iter().map(|param| param.name));
+        names
     }
 }
 
diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs
index e2b0142490933..8b38a7d5fc7e6 100644
--- a/src/librustc_codegen_llvm/llvm/ffi.rs
+++ b/src/librustc_codegen_llvm/llvm/ffi.rs
@@ -1421,9 +1421,10 @@ extern "C" {
                                             LineNo: c_uint)
                                             -> &'a DINameSpace;
 
-    pub fn LLVMRustDICompositeTypeSetTypeArray(Builder: &DIBuilder<'a>,
-                                               CompositeType: &'a DIType,
-                                               TypeArray: &'a DIArray);
+    pub fn LLVMRustDICompositeTypeReplaceArrays(Builder: &DIBuilder<'a>,
+                                                CompositeType: &'a DIType,
+                                                Elements: Option<&'a DIArray>,
+                                                Params: Option<&'a DIArray>);
 
 
     pub fn LLVMRustDIBuilderCreateDebugLocation(Context: &'a Context,
diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp
index bf7afa1b6c068..96a34a41dbad3 100644
--- a/src/rustllvm/RustWrapper.cpp
+++ b/src/rustllvm/RustWrapper.cpp
@@ -870,11 +870,13 @@ LLVMRustDIBuilderCreateNameSpace(LLVMRustDIBuilderRef Builder,
 }
 
 extern "C" void
-LLVMRustDICompositeTypeSetTypeArray(LLVMRustDIBuilderRef Builder,
-                                    LLVMMetadataRef CompositeTy,
-                                    LLVMMetadataRef TyArray) {
+LLVMRustDICompositeTypeReplaceArrays(LLVMRustDIBuilderRef Builder,
+                                     LLVMMetadataRef CompositeTy,
+                                     LLVMMetadataRef Elements,
+                                     LLVMMetadataRef Params) {
   DICompositeType *Tmp = unwrapDI<DICompositeType>(CompositeTy);
-  Builder->replaceArrays(Tmp, DINodeArray(unwrap<MDTuple>(TyArray)));
+  Builder->replaceArrays(Tmp, DINodeArray(unwrap<MDTuple>(Elements)),
+                         DINodeArray(unwrap<MDTuple>(Params)));
 }
 
 extern "C" LLVMValueRef
diff --git a/src/test/codegen/generic-debug.rs b/src/test/codegen/generic-debug.rs
new file mode 100644
index 0000000000000..9d5dbf1cc0a09
--- /dev/null
+++ b/src/test/codegen/generic-debug.rs
@@ -0,0 +1,28 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-tidy-linelength
+// ignore-windows
+
+// compile-flags: -g -C no-prepopulate-passes
+
+// CHECK-LABEL: @main
+// CHECK: {{.*}}DICompositeType{{.*}}tag: DW_TAG_structure_type,{{.*}}name: "Generic<i32>",{{.*}}
+// CHECK: {{.*}}DITemplateTypeParameter{{.*}}name: "Type",{{.*}}
+
+#![allow(dead_code)]
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+
+pub struct Generic<Type>(Type);
+
+fn main () {
+    let generic = Generic(10);
+}

From 37e1d2975e1002f0718552554055647392e46f0d Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Sat, 20 Oct 2018 16:18:17 -0400
Subject: [PATCH 24/30] Don't rerun Mir passes when inlining

When inlining a function using the Mir inliner, we shouldn't rerun the
various Mir passes on it because the Mir has already been lowered and
that wil break various early Mir passes.

The issue in #50411 is that we've inlined a function with promotions
whose Mir has already been lowered. The promotions are then copied into
the local function and we begin to run passes on their lowered Mir
which causes the ICE.

Fixes #50411
---
 src/librustc/mir/mod.rs           | 29 +++++++++++++++++++++++++++++
 src/librustc_mir/transform/mod.rs | 25 ++++++++++++++++++++-----
 src/test/ui/issues/issue-50411.rs | 11 +++++++++++
 3 files changed, 60 insertions(+), 5 deletions(-)
 create mode 100644 src/test/ui/issues/issue-50411.rs

diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index 34fc81a495e24..797836f166173 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -69,6 +69,17 @@ impl<'tcx> HasLocalDecls<'tcx> for Mir<'tcx> {
     }
 }
 
+/// The various "big phases" that MIR goes through.
+///
+/// Warning: ordering of variants is significant
+#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub enum MirPhase {
+    Build,
+    Const,
+    Validated,
+    Optimized,
+}
+
 /// Lowered representation of a single function.
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub struct Mir<'tcx> {
@@ -76,6 +87,13 @@ pub struct Mir<'tcx> {
     /// that indexes into this vector.
     basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
 
+    /// Records how far through the "desugaring and optimization" process this particular
+    /// MIR has traversed. This is particularly useful when inlining, since in that context
+    /// we instantiate the promoted constants and add them to our promoted vector -- but those
+    /// promoted items have already been optimized, whereas ours have not. This field allows
+    /// us to see the difference and forego optimization on the inlined promoted items.
+    pub phase: MirPhase,
+
     /// List of source scopes; these are referenced by statements
     /// and used for debuginfo. Indexed by a `SourceScope`.
     pub source_scopes: IndexVec<SourceScope, SourceScopeData>,
@@ -151,6 +169,7 @@ impl<'tcx> Mir<'tcx> {
         );
 
         Mir {
+            phase: MirPhase::Build,
             basic_blocks,
             source_scopes,
             source_scope_local_data,
@@ -368,6 +387,7 @@ pub enum Safety {
 }
 
 impl_stable_hash_for!(struct Mir<'tcx> {
+    phase,
     basic_blocks,
     source_scopes,
     source_scope_local_data,
@@ -616,6 +636,13 @@ impl_stable_hash_for!(enum self::ImplicitSelfKind {
     None
 });
 
+impl_stable_hash_for!(enum self::MirPhase {
+    Build,
+    Const,
+    Validated,
+    Optimized,
+});
+
 mod binding_form_impl {
     use ich::StableHashingContext;
     use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult};
@@ -2777,6 +2804,7 @@ pub enum ClosureOutlivesSubject<'tcx> {
 
 CloneTypeFoldableAndLiftImpls! {
     BlockTailInfo,
+    MirPhase,
     Mutability,
     SourceInfo,
     UpvarDecl,
@@ -2789,6 +2817,7 @@ CloneTypeFoldableAndLiftImpls! {
 
 BraceStructTypeFoldableImpl! {
     impl<'tcx> TypeFoldable<'tcx> for Mir<'tcx> {
+        phase,
         basic_blocks,
         source_scopes,
         source_scope_local_data,
diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs
index d18836999dccf..61e150ea12a22 100644
--- a/src/librustc_mir/transform/mod.rs
+++ b/src/librustc_mir/transform/mod.rs
@@ -11,7 +11,7 @@
 use borrow_check::nll::type_check;
 use build;
 use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
-use rustc::mir::{Mir, Promoted};
+use rustc::mir::{Mir, MirPhase, Promoted};
 use rustc::ty::TyCtxt;
 use rustc::ty::query::Providers;
 use rustc::ty::steal::Steal;
@@ -155,9 +155,22 @@ pub trait MirPass {
                           mir: &mut Mir<'tcx>);
 }
 
-pub macro run_passes($tcx:ident, $mir:ident, $def_id:ident, $suite_index:expr; $($pass:expr,)*) {{
+pub macro run_passes(
+    $tcx:ident,
+    $mir:ident,
+    $def_id:ident,
+    $suite_index:expr,
+    $mir_phase:expr;
+    $($pass:expr,)*
+) {{
     let suite_index: usize = $suite_index;
     let run_passes = |mir: &mut _, promoted| {
+        let mir: &mut Mir<'_> = mir;
+
+        if mir.phase >= $mir_phase {
+            return;
+        }
+
         let source = MirSource {
             def_id: $def_id,
             promoted
@@ -175,6 +188,8 @@ pub macro run_passes($tcx:ident, $mir:ident, $def_id:ident, $suite_index:expr; $
             index += 1;
         };
         $(run_pass(&$pass);)*
+
+        mir.phase = $mir_phase;
     };
 
     run_passes(&mut $mir, None);
@@ -192,7 +207,7 @@ fn mir_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx Stea
     let _ = tcx.unsafety_check_result(def_id);
 
     let mut mir = tcx.mir_built(def_id).steal();
-    run_passes![tcx, mir, def_id, 0;
+    run_passes![tcx, mir, def_id, 0, MirPhase::Const;
         // Remove all `EndRegion` statements that are not involved in borrows.
         cleanup_post_borrowck::CleanEndRegions,
 
@@ -214,7 +229,7 @@ fn mir_validated<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx
     }
 
     let mut mir = tcx.mir_const(def_id).steal();
-    run_passes![tcx, mir, def_id, 1;
+    run_passes![tcx, mir, def_id, 1, MirPhase::Validated;
         // What we need to run borrowck etc.
         qualify_consts::QualifyAndPromoteConstants,
         simplify::SimplifyCfg::new("qualify-consts"),
@@ -232,7 +247,7 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx
     }
 
     let mut mir = tcx.mir_validated(def_id).steal();
-    run_passes![tcx, mir, def_id, 2;
+    run_passes![tcx, mir, def_id, 2, MirPhase::Optimized;
         // Remove all things not needed by analysis
         no_landing_pads::NoLandingPads,
         simplify_branches::SimplifyBranches::new("initial"),
diff --git a/src/test/ui/issues/issue-50411.rs b/src/test/ui/issues/issue-50411.rs
new file mode 100644
index 0000000000000..1ba47d3b932ef
--- /dev/null
+++ b/src/test/ui/issues/issue-50411.rs
@@ -0,0 +1,11 @@
+// Regression test for #50411: the MIR inliner was causing problems
+// here because it would inline promoted code (which had already had
+// elaborate-drops invoked on it) and then try to elaboate drops a
+// second time. Uncool.
+
+// compile-flags:-Zmir-opt-level=3
+// compile-pass
+
+fn main() {
+    let _ = (0 .. 1).filter(|_| [1].iter().all(|_| true)).count();
+}

From 895a4b2d45e6d5c274bcd561391b2cc6a73dd2c5 Mon Sep 17 00:00:00 2001
From: Wesley Wiser <wwiser@gmail.com>
Date: Sun, 21 Oct 2018 10:47:01 -0400
Subject: [PATCH 25/30] Remove the `suite_index` parameter of the
 `run_passes!()` macro

This can be obtained via the `$mir_phase` value.
---
 src/librustc/mir/mod.rs           | 12 ++++++++++++
 src/librustc_mir/transform/mod.rs | 12 ++++++------
 2 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index 797836f166173..e53def65886de 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -80,6 +80,18 @@ pub enum MirPhase {
     Optimized,
 }
 
+impl MirPhase {
+    /// Gets the index of the current MirPhase within the set of all MirPhases.
+    pub fn phase_index(&self) -> usize {
+        match self {
+            MirPhase::Build => 0,
+            MirPhase::Const => 1,
+            MirPhase::Validated => 2,
+            MirPhase::Optimized => 3,
+        }
+    }
+}
+
 /// Lowered representation of a single function.
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub struct Mir<'tcx> {
diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs
index 61e150ea12a22..ff85d780a4ce3 100644
--- a/src/librustc_mir/transform/mod.rs
+++ b/src/librustc_mir/transform/mod.rs
@@ -159,11 +159,11 @@ pub macro run_passes(
     $tcx:ident,
     $mir:ident,
     $def_id:ident,
-    $suite_index:expr,
     $mir_phase:expr;
     $($pass:expr,)*
 ) {{
-    let suite_index: usize = $suite_index;
+    let phase_index = $mir_phase.phase_index();
+
     let run_passes = |mir: &mut _, promoted| {
         let mir: &mut Mir<'_> = mir;
 
@@ -178,7 +178,7 @@ pub macro run_passes(
         let mut index = 0;
         let mut run_pass = |pass: &dyn MirPass| {
             let run_hooks = |mir: &_, index, is_after| {
-                dump_mir::on_mir_pass($tcx, &format_args!("{:03}-{:03}", suite_index, index),
+                dump_mir::on_mir_pass($tcx, &format_args!("{:03}-{:03}", phase_index, index),
                                       &pass.name(), source, mir, is_after);
             };
             run_hooks(mir, index, false);
@@ -207,7 +207,7 @@ fn mir_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx Stea
     let _ = tcx.unsafety_check_result(def_id);
 
     let mut mir = tcx.mir_built(def_id).steal();
-    run_passes![tcx, mir, def_id, 0, MirPhase::Const;
+    run_passes![tcx, mir, def_id, MirPhase::Const;
         // Remove all `EndRegion` statements that are not involved in borrows.
         cleanup_post_borrowck::CleanEndRegions,
 
@@ -229,7 +229,7 @@ fn mir_validated<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx
     }
 
     let mut mir = tcx.mir_const(def_id).steal();
-    run_passes![tcx, mir, def_id, 1, MirPhase::Validated;
+    run_passes![tcx, mir, def_id, MirPhase::Validated;
         // What we need to run borrowck etc.
         qualify_consts::QualifyAndPromoteConstants,
         simplify::SimplifyCfg::new("qualify-consts"),
@@ -247,7 +247,7 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx
     }
 
     let mut mir = tcx.mir_validated(def_id).steal();
-    run_passes![tcx, mir, def_id, 2, MirPhase::Optimized;
+    run_passes![tcx, mir, def_id, MirPhase::Optimized;
         // Remove all things not needed by analysis
         no_landing_pads::NoLandingPads,
         simplify_branches::SimplifyBranches::new("initial"),

From c535147f291fee0c79553eabcd520156707cd0d4 Mon Sep 17 00:00:00 2001
From: Wesley Wiser <wwiser@gmail.com>
Date: Mon, 22 Oct 2018 22:41:21 -0400
Subject: [PATCH 26/30] Replace the `run_passes!` macro with a regular function

As suggested in the feedback for #55244.

When I replaced the macro with a function, rustc started complaining
that there were two unused functions so I also removed those.
---
 src/librustc_mir/dataflow/impls/borrows.rs |   1 -
 src/librustc_mir/dataflow/mod.rs           |  14 ---
 src/librustc_mir/transform/mod.rs          | 134 ++++++++++-----------
 3 files changed, 67 insertions(+), 82 deletions(-)

diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs
index f7043487c51a6..cfccb950e8276 100644
--- a/src/librustc_mir/dataflow/impls/borrows.rs
+++ b/src/librustc_mir/dataflow/impls/borrows.rs
@@ -184,7 +184,6 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
     }
 
     crate fn borrows(&self) -> &IndexVec<BorrowIndex, BorrowData<'tcx>> { &self.borrow_set.borrows }
-    pub fn scope_tree(&self) -> &Lrc<region::ScopeTree> { &self.scope_tree }
 
     pub fn location(&self, idx: BorrowIndex) -> &Location {
         &self.borrow_set.borrows[idx].reserve_location
diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs
index da4bd780eb4fa..c19145636e6da 100644
--- a/src/librustc_mir/dataflow/mod.rs
+++ b/src/librustc_mir/dataflow/mod.rs
@@ -724,20 +724,6 @@ impl<'a, 'tcx, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation
             }
         }
     }
-
-    pub fn new_from_sets(mir: &'a Mir<'tcx>,
-                         dead_unwinds: &'a BitSet<mir::BasicBlock>,
-                         sets: AllSets<D::Idx>,
-                         denotation: D) -> Self {
-        DataflowAnalysis {
-            mir,
-            dead_unwinds,
-            flow_state: DataflowState {
-                sets: sets,
-                operator: denotation,
-            }
-        }
-    }
 }
 
 impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation
diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs
index ff85d780a4ce3..28b7d517da6c9 100644
--- a/src/librustc_mir/transform/mod.rs
+++ b/src/librustc_mir/transform/mod.rs
@@ -155,68 +155,68 @@ pub trait MirPass {
                           mir: &mut Mir<'tcx>);
 }
 
-pub macro run_passes(
-    $tcx:ident,
-    $mir:ident,
-    $def_id:ident,
-    $mir_phase:expr;
-    $($pass:expr,)*
-) {{
-    let phase_index = $mir_phase.phase_index();
-
-    let run_passes = |mir: &mut _, promoted| {
-        let mir: &mut Mir<'_> = mir;
-
-        if mir.phase >= $mir_phase {
+pub fn run_passes(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    mir: &mut Mir<'tcx>,
+    def_id: DefId,
+    mir_phase: MirPhase,
+    passes: &[&dyn MirPass]) {
+    let phase_index = mir_phase.phase_index();
+
+    let run_passes = |mir: &mut Mir<'tcx>, promoted| {
+        if mir.phase >= mir_phase {
             return;
         }
 
         let source = MirSource {
-            def_id: $def_id,
-            promoted
+            def_id,
+            promoted,
         };
         let mut index = 0;
         let mut run_pass = |pass: &dyn MirPass| {
             let run_hooks = |mir: &_, index, is_after| {
-                dump_mir::on_mir_pass($tcx, &format_args!("{:03}-{:03}", phase_index, index),
+                dump_mir::on_mir_pass(tcx, &format_args!("{:03}-{:03}", phase_index, index),
                                       &pass.name(), source, mir, is_after);
             };
             run_hooks(mir, index, false);
-            pass.run_pass($tcx, source, mir);
+            pass.run_pass(tcx, source, mir);
             run_hooks(mir, index, true);
 
             index += 1;
         };
-        $(run_pass(&$pass);)*
 
-        mir.phase = $mir_phase;
+        for pass in passes {
+            run_pass(*pass);
+        }
+
+        mir.phase = mir_phase;
     };
 
-    run_passes(&mut $mir, None);
+    run_passes(mir, None);
 
-    for (index, promoted_mir) in $mir.promoted.iter_enumerated_mut() {
+    for (index, promoted_mir) in mir.promoted.iter_enumerated_mut() {
         run_passes(promoted_mir, Some(index));
 
-        // Let's make sure we don't miss any nested instances
-        assert!(promoted_mir.promoted.is_empty());
+        //Let's make sure we don't miss any nested instances
+        assert!(promoted_mir.promoted.is_empty())
     }
-}}
+}
 
 fn mir_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx Steal<Mir<'tcx>> {
     // Unsafety check uses the raw mir, so make sure it is run
     let _ = tcx.unsafety_check_result(def_id);
 
     let mut mir = tcx.mir_built(def_id).steal();
-    run_passes![tcx, mir, def_id, MirPhase::Const;
+    run_passes(tcx, &mut mir, def_id, MirPhase::Const, &[
         // Remove all `EndRegion` statements that are not involved in borrows.
-        cleanup_post_borrowck::CleanEndRegions,
+        &cleanup_post_borrowck::CleanEndRegions,
 
         // What we need to do constant evaluation.
-        simplify::SimplifyCfg::new("initial"),
-        type_check::TypeckMir,
-        rustc_peek::SanityCheck,
-        uniform_array_move_out::UniformArrayMoveOut,
-    ];
+        &simplify::SimplifyCfg::new("initial"),
+        &type_check::TypeckMir,
+        &rustc_peek::SanityCheck,
+        &uniform_array_move_out::UniformArrayMoveOut,
+    ]);
     tcx.alloc_steal_mir(mir)
 }
 
@@ -229,11 +229,11 @@ fn mir_validated<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx
     }
 
     let mut mir = tcx.mir_const(def_id).steal();
-    run_passes![tcx, mir, def_id, MirPhase::Validated;
+    run_passes(tcx, &mut mir, def_id, MirPhase::Validated, &[
         // What we need to run borrowck etc.
-        qualify_consts::QualifyAndPromoteConstants,
-        simplify::SimplifyCfg::new("qualify-consts"),
-    ];
+        &qualify_consts::QualifyAndPromoteConstants,
+        &simplify::SimplifyCfg::new("qualify-consts"),
+    ]);
     tcx.alloc_steal_mir(mir)
 }
 
@@ -247,59 +247,59 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx
     }
 
     let mut mir = tcx.mir_validated(def_id).steal();
-    run_passes![tcx, mir, def_id, MirPhase::Optimized;
+    run_passes(tcx, &mut mir, def_id, MirPhase::Optimized, &[
         // Remove all things not needed by analysis
-        no_landing_pads::NoLandingPads,
-        simplify_branches::SimplifyBranches::new("initial"),
-        remove_noop_landing_pads::RemoveNoopLandingPads,
+        &no_landing_pads::NoLandingPads,
+        &simplify_branches::SimplifyBranches::new("initial"),
+        &remove_noop_landing_pads::RemoveNoopLandingPads,
         // Remove all `AscribeUserType` statements.
-        cleanup_post_borrowck::CleanAscribeUserType,
+        &cleanup_post_borrowck::CleanAscribeUserType,
         // Remove all `FakeRead` statements and the borrows that are only
         // used for checking matches
-        cleanup_post_borrowck::CleanFakeReadsAndBorrows,
-        simplify::SimplifyCfg::new("early-opt"),
+        &cleanup_post_borrowck::CleanFakeReadsAndBorrows,
+        &simplify::SimplifyCfg::new("early-opt"),
 
         // These next passes must be executed together
-        add_call_guards::CriticalCallEdges,
-        elaborate_drops::ElaborateDrops,
-        no_landing_pads::NoLandingPads,
+        &add_call_guards::CriticalCallEdges,
+        &elaborate_drops::ElaborateDrops,
+        &no_landing_pads::NoLandingPads,
         // AddValidation needs to run after ElaborateDrops and before EraseRegions, and it needs
         // an AllCallEdges pass right before it.
-        add_call_guards::AllCallEdges,
-        add_validation::AddValidation,
+        &add_call_guards::AllCallEdges,
+        &add_validation::AddValidation,
         // AddMovesForPackedDrops needs to run after drop
         // elaboration.
-        add_moves_for_packed_drops::AddMovesForPackedDrops,
+        &add_moves_for_packed_drops::AddMovesForPackedDrops,
 
-        simplify::SimplifyCfg::new("elaborate-drops"),
+        &simplify::SimplifyCfg::new("elaborate-drops"),
 
         // No lifetime analysis based on borrowing can be done from here on out.
 
         // From here on out, regions are gone.
-        erase_regions::EraseRegions,
+        &erase_regions::EraseRegions,
 
-        lower_128bit::Lower128Bit,
+        &lower_128bit::Lower128Bit,
 
 
         // Optimizations begin.
-        uniform_array_move_out::RestoreSubsliceArrayMoveOut,
-        inline::Inline,
+        &uniform_array_move_out::RestoreSubsliceArrayMoveOut,
+        &inline::Inline,
 
         // Lowering generator control-flow and variables
         // has to happen before we do anything else to them.
-        generator::StateTransform,
-
-        instcombine::InstCombine,
-        const_prop::ConstProp,
-        simplify_branches::SimplifyBranches::new("after-const-prop"),
-        deaggregator::Deaggregator,
-        copy_prop::CopyPropagation,
-        remove_noop_landing_pads::RemoveNoopLandingPads,
-        simplify::SimplifyCfg::new("final"),
-        simplify::SimplifyLocals,
-
-        add_call_guards::CriticalCallEdges,
-        dump_mir::Marker("PreCodegen"),
-    ];
+        &generator::StateTransform,
+
+        &instcombine::InstCombine,
+        &const_prop::ConstProp,
+        &simplify_branches::SimplifyBranches::new("after-const-prop"),
+        &deaggregator::Deaggregator,
+        &copy_prop::CopyPropagation,
+        &remove_noop_landing_pads::RemoveNoopLandingPads,
+        &simplify::SimplifyCfg::new("final"),
+        &simplify::SimplifyLocals,
+
+        &add_call_guards::CriticalCallEdges,
+        &dump_mir::Marker("PreCodegen"),
+    ]);
     tcx.alloc_mir(mir)
 }

From 569228a45e73ba994d9dc5fff11381fe700d6cc6 Mon Sep 17 00:00:00 2001
From: Steve Klabnik <steve@steveklabnik.com>
Date: Wed, 24 Oct 2018 15:33:48 -0400
Subject: [PATCH 27/30] Fix link to macros chapter

We're gonna link to nightly as this didn't make it into the corresponding stable.
---
 RELEASES.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/RELEASES.md b/RELEASES.md
index 7ae7dc9935b6d..58e49e68b43e7 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -105,7 +105,7 @@ Misc
 [cargo/5878]: https://github.com/rust-lang/cargo/pull/5878/
 [cargo/5984]: https://github.com/rust-lang/cargo/pull/5984/
 [cargo/5995]: https://github.com/rust-lang/cargo/pull/5995/
-[proc-macros]: https://doc.rust-lang.org/book/2018-edition/ch19-06-macros.html
+[proc-macros]: https://doc.rust-lang.org/stable/book/2018-edition/ch19-06-macros.html
 
 [`Ipv4Addr::BROADCAST`]: https://doc.rust-lang.org/nightly/std/net/struct.Ipv4Addr.html#associatedconstant.BROADCAST
 [`Ipv4Addr::LOCALHOST`]: https://doc.rust-lang.org/nightly/std/net/struct.Ipv4Addr.html#associatedconstant.LOCALHOST

From 4655866a11ddb345c84781cf4e292e99e26ee9fe Mon Sep 17 00:00:00 2001
From: Wesley Wiser <wwiser@gmail.com>
Date: Thu, 25 Oct 2018 08:35:53 -0400
Subject: [PATCH 28/30] Fix CR feedback

---
 src/librustc/mir/mod.rs           | 15 +++++----------
 src/librustc_mir/transform/mod.rs |  3 ++-
 2 files changed, 7 insertions(+), 11 deletions(-)

diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index e53def65886de..f9da40b583899 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -74,21 +74,16 @@ impl<'tcx> HasLocalDecls<'tcx> for Mir<'tcx> {
 /// Warning: ordering of variants is significant
 #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)]
 pub enum MirPhase {
-    Build,
-    Const,
-    Validated,
-    Optimized,
+    Build = 0,
+    Const = 1,
+    Validated = 2,
+    Optimized = 3,
 }
 
 impl MirPhase {
     /// Gets the index of the current MirPhase within the set of all MirPhases.
     pub fn phase_index(&self) -> usize {
-        match self {
-            MirPhase::Build => 0,
-            MirPhase::Const => 1,
-            MirPhase::Validated => 2,
-            MirPhase::Optimized => 3,
-        }
+        *self as usize
     }
 }
 
diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs
index 28b7d517da6c9..46c73c27fe10d 100644
--- a/src/librustc_mir/transform/mod.rs
+++ b/src/librustc_mir/transform/mod.rs
@@ -160,7 +160,8 @@ pub fn run_passes(
     mir: &mut Mir<'tcx>,
     def_id: DefId,
     mir_phase: MirPhase,
-    passes: &[&dyn MirPass]) {
+    passes: &[&dyn MirPass],
+) {
     let phase_index = mir_phase.phase_index();
 
     let run_passes = |mir: &mut Mir<'tcx>, promoted| {

From ee26e8edebd16b1d11e2af686a12143826fa46fb Mon Sep 17 00:00:00 2001
From: Corey Farwell <coreyf@rwell.org>
Date: Thu, 25 Oct 2018 10:47:07 -0400
Subject: [PATCH 29/30] Update RELEASES.md

Co-Authored-By: steveklabnik <steve@steveklabnik.com>
---
 RELEASES.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/RELEASES.md b/RELEASES.md
index 58e49e68b43e7..729947a9189e0 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -105,7 +105,7 @@ Misc
 [cargo/5878]: https://github.com/rust-lang/cargo/pull/5878/
 [cargo/5984]: https://github.com/rust-lang/cargo/pull/5984/
 [cargo/5995]: https://github.com/rust-lang/cargo/pull/5995/
-[proc-macros]: https://doc.rust-lang.org/stable/book/2018-edition/ch19-06-macros.html
+[proc-macros]: https://doc.rust-lang.org/nightly/book/2018-edition/ch19-06-macros.html
 
 [`Ipv4Addr::BROADCAST`]: https://doc.rust-lang.org/nightly/std/net/struct.Ipv4Addr.html#associatedconstant.BROADCAST
 [`Ipv4Addr::LOCALHOST`]: https://doc.rust-lang.org/nightly/std/net/struct.Ipv4Addr.html#associatedconstant.LOCALHOST

From 2cfd79017744aa55abdd470b93bc544685644612 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Esteban=20K=C3=BCber?= <esteban@kuber.com.ar>
Date: Tue, 23 Oct 2018 21:37:32 -0700
Subject: [PATCH 30/30] List allowed tokens after macro fragments

---
 src/libsyntax/ext/tt/macro_rules.rs           | 138 ++++---
 src/test/ui/macros/macro-follow.stderr        | 340 +++++++++++++-----
 .../macros/macro-followed-by-seq-bad.stderr   |   8 +-
 .../macros/macro-input-future-proofing.stderr |  36 +-
 .../unused-macro-with-follow-violation.stderr |   4 +-
 5 files changed, 385 insertions(+), 141 deletions(-)

diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs
index 805aa9bef227d..a28d114bda784 100644
--- a/src/libsyntax/ext/tt/macro_rules.rs
+++ b/src/libsyntax/ext/tt/macro_rules.rs
@@ -792,15 +792,15 @@ fn check_matcher_core(sess: &ParseSess,
             if let TokenTree::MetaVarDecl(_, ref name, ref frag_spec) = *token {
                 for next_token in &suffix_first.tokens {
                     match is_in_follow(next_token, &frag_spec.as_str()) {
-                        Err((msg, help)) => {
+                        IsInFollow::Invalid(msg, help) => {
                             sess.span_diagnostic.struct_span_err(next_token.span(), &msg)
                                 .help(help).emit();
                             // don't bother reporting every source of
                             // conflict for a particular element of `last`.
                             continue 'each_last;
                         }
-                        Ok(true) => {}
-                        Ok(false) => {
+                        IsInFollow::Yes => {}
+                        IsInFollow::No(ref possible) => {
                             let may_be = if last.tokens.len() == 1 &&
                                 suffix_first.tokens.len() == 1
                             {
@@ -809,15 +809,41 @@ fn check_matcher_core(sess: &ParseSess,
                                 "may be"
                             };
 
-                            sess.span_diagnostic.span_err(
-                                next_token.span(),
+                            let sp = next_token.span();
+                            let mut err = sess.span_diagnostic.struct_span_err(
+                                sp,
                                 &format!("`${name}:{frag}` {may_be} followed by `{next}`, which \
                                           is not allowed for `{frag}` fragments",
                                          name=name,
                                          frag=frag_spec,
                                          next=quoted_tt_to_string(next_token),
-                                         may_be=may_be)
+                                         may_be=may_be),
                             );
+                            err.span_label(
+                                sp,
+                                format!("not allowed after `{}` fragments", frag_spec),
+                            );
+                            let msg = "allowed there are: ";
+                            match &possible[..] {
+                                &[] => {}
+                                &[t] => {
+                                    err.note(&format!(
+                                        "only {} is allowed after `{}` fragments",
+                                        t,
+                                        frag_spec,
+                                    ));
+                                }
+                                ts => {
+                                    err.note(&format!(
+                                        "{}{} or {}",
+                                        msg,
+                                        ts[..ts.len() - 1].iter().map(|s| *s)
+                                            .collect::<Vec<_>>().join(", "),
+                                        ts[ts.len() - 1],
+                                    ));
+                                }
+                            }
+                            err.emit();
                         }
                     }
                 }
@@ -860,6 +886,12 @@ fn frag_can_be_followed_by_any(frag: &str) -> bool {
     }
 }
 
+enum IsInFollow {
+    Yes,
+    No(Vec<&'static str>),
+    Invalid(String, &'static str),
+}
+
 /// True if `frag` can legally be followed by the token `tok`. For
 /// fragments that can consume an unbounded number of tokens, `tok`
 /// must be within a well-defined follow set. This is intended to
@@ -868,81 +900,99 @@ fn frag_can_be_followed_by_any(frag: &str) -> bool {
 /// break macros that were relying on that binary operator as a
 /// separator.
 // when changing this do not forget to update doc/book/macros.md!
-fn is_in_follow(tok: &quoted::TokenTree, frag: &str) -> Result<bool, (String, &'static str)> {
+fn is_in_follow(tok: &quoted::TokenTree, frag: &str) -> IsInFollow {
     use self::quoted::TokenTree;
 
     if let TokenTree::Token(_, token::CloseDelim(_)) = *tok {
         // closing a token tree can never be matched by any fragment;
         // iow, we always require that `(` and `)` match, etc.
-        Ok(true)
+        IsInFollow::Yes
     } else {
         match frag {
             "item" => {
                 // since items *must* be followed by either a `;` or a `}`, we can
                 // accept anything after them
-                Ok(true)
+                IsInFollow::Yes
             },
             "block" => {
                 // anything can follow block, the braces provide an easy boundary to
                 // maintain
-                Ok(true)
+                IsInFollow::Yes
             },
-            "stmt" | "expr"  => match *tok {
-                TokenTree::Token(_, ref tok) => match *tok {
-                    FatArrow | Comma | Semi => Ok(true),
-                    _ => Ok(false)
-                },
-                _ => Ok(false),
+            "stmt" | "expr"  => {
+                let tokens = vec!["`=>`", "`,`", "`;`"];
+                match *tok {
+                    TokenTree::Token(_, ref tok) => match *tok {
+                        FatArrow | Comma | Semi => IsInFollow::Yes,
+                        _ => IsInFollow::No(tokens),
+                    },
+                    _ => IsInFollow::No(tokens),
+                }
             },
-            "pat" => match *tok {
-                TokenTree::Token(_, ref tok) => match *tok {
-                    FatArrow | Comma | Eq | BinOp(token::Or) => Ok(true),
-                    Ident(i, false) if i.name == "if" || i.name == "in" => Ok(true),
-                    _ => Ok(false)
-                },
-                _ => Ok(false),
+            "pat" => {
+                let tokens = vec!["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"];
+                match *tok {
+                    TokenTree::Token(_, ref tok) => match *tok {
+                        FatArrow | Comma | Eq | BinOp(token::Or) => IsInFollow::Yes,
+                        Ident(i, false) if i.name == "if" || i.name == "in" => IsInFollow::Yes,
+                        _ => IsInFollow::No(tokens),
+                    },
+                    _ => IsInFollow::No(tokens),
+                }
             },
-            "path" | "ty" => match *tok {
-                TokenTree::Token(_, ref tok) => match *tok {
-                    OpenDelim(token::DelimToken::Brace) | OpenDelim(token::DelimToken::Bracket) |
-                    Comma | FatArrow | Colon | Eq | Gt | BinOp(token::Shr) | Semi |
-                    BinOp(token::Or) => Ok(true),
-                    Ident(i, false) if i.name == "as" || i.name == "where" => Ok(true),
-                    _ => Ok(false)
-                },
-                TokenTree::MetaVarDecl(_, _, frag) if frag.name == "block" => Ok(true),
-                _ => Ok(false),
+            "path" | "ty" => {
+                let tokens = vec![
+                    "`{`", "`[`", "`=>`", "`,`", "`>`","`=`", "`:`", "`;`", "`|`", "`as`",
+                    "`where`",
+                ];
+                match *tok {
+                    TokenTree::Token(_, ref tok) => match *tok {
+                        OpenDelim(token::DelimToken::Brace) |
+                        OpenDelim(token::DelimToken::Bracket) |
+                        Comma | FatArrow | Colon | Eq | Gt | BinOp(token::Shr) | Semi |
+                        BinOp(token::Or) => IsInFollow::Yes,
+                        Ident(i, false) if i.name == "as" || i.name == "where" => IsInFollow::Yes,
+                        _ => IsInFollow::No(tokens),
+                    },
+                    TokenTree::MetaVarDecl(_, _, frag) if frag.name == "block" => IsInFollow::Yes,
+                    _ => IsInFollow::No(tokens),
+                }
             },
             "ident" | "lifetime" => {
                 // being a single token, idents and lifetimes are harmless
-                Ok(true)
+                IsInFollow::Yes
             },
             "literal" => {
                 // literals may be of a single token, or two tokens (negative numbers)
-                Ok(true)
+                IsInFollow::Yes
             },
             "meta" | "tt" => {
                 // being either a single token or a delimited sequence, tt is
                 // harmless
-                Ok(true)
+                IsInFollow::Yes
             },
             "vis" => {
                 // Explicitly disallow `priv`, on the off chance it comes back.
+                let tokens = vec!["`,`", "an ident", "a type"];
                 match *tok {
                     TokenTree::Token(_, ref tok) => match *tok {
-                        Comma => Ok(true),
-                        Ident(i, is_raw) if is_raw || i.name != "priv" => Ok(true),
-                        ref tok => Ok(tok.can_begin_type())
+                        Comma => IsInFollow::Yes,
+                        Ident(i, is_raw) if is_raw || i.name != "priv" => IsInFollow::Yes,
+                        ref tok => if tok.can_begin_type() {
+                            IsInFollow::Yes
+                        } else {
+                            IsInFollow::No(tokens)
+                        }
                     },
                     TokenTree::MetaVarDecl(_, _, frag) if frag.name == "ident"
                                                        || frag.name == "ty"
-                                                       || frag.name == "path" => Ok(true),
-                    _ => Ok(false)
+                                                       || frag.name == "path" => IsInFollow::Yes,
+                    _ => IsInFollow::No(tokens),
                 }
             },
-            "" => Ok(true), // keywords::Invalid
-            _ => Err((format!("invalid fragment specifier `{}`", frag),
-                     VALID_FRAGMENT_NAMES_MSG))
+            "" => IsInFollow::Yes, // keywords::Invalid
+            _ => IsInFollow::Invalid(format!("invalid fragment specifier `{}`", frag),
+                                     VALID_FRAGMENT_NAMES_MSG),
         }
     }
 }
diff --git a/src/test/ui/macros/macro-follow.stderr b/src/test/ui/macros/macro-follow.stderr
index ccd658af89fbb..8760f6eb572e3 100644
--- a/src/test/ui/macros/macro-follow.stderr
+++ b/src/test/ui/macros/macro-follow.stderr
@@ -2,511 +2,681 @@ error: `$p:pat` is followed by `(`, which is not allowed for `pat` fragments
   --> $DIR/macro-follow.rs:17:14
    |
 LL |     ($p:pat ()) => {};       //~ERROR  `$p:pat` is followed by `(`
-   |              ^
+   |              ^ not allowed after `pat` fragments
+   |
+   = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in`
 
 error: `$p:pat` is followed by `[`, which is not allowed for `pat` fragments
   --> $DIR/macro-follow.rs:18:14
    |
 LL |     ($p:pat []) => {};       //~ERROR  `$p:pat` is followed by `[`
-   |              ^
+   |              ^ not allowed after `pat` fragments
+   |
+   = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in`
 
 error: `$p:pat` is followed by `{`, which is not allowed for `pat` fragments
   --> $DIR/macro-follow.rs:19:14
    |
 LL |     ($p:pat {}) => {};       //~ERROR  `$p:pat` is followed by `{`
-   |              ^
+   |              ^ not allowed after `pat` fragments
+   |
+   = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in`
 
 error: `$p:pat` is followed by `:`, which is not allowed for `pat` fragments
   --> $DIR/macro-follow.rs:20:13
    |
 LL |     ($p:pat :) => {};        //~ERROR `$p:pat` is followed by `:`
-   |             ^
+   |             ^ not allowed after `pat` fragments
+   |
+   = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in`
 
 error: `$p:pat` is followed by `>`, which is not allowed for `pat` fragments
   --> $DIR/macro-follow.rs:21:13
    |
 LL |     ($p:pat >) => {};        //~ERROR `$p:pat` is followed by `>`
-   |             ^
+   |             ^ not allowed after `pat` fragments
+   |
+   = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in`
 
 error: `$p:pat` is followed by `+`, which is not allowed for `pat` fragments
   --> $DIR/macro-follow.rs:22:13
    |
 LL |     ($p:pat +) => {};        //~ERROR `$p:pat` is followed by `+`
-   |             ^
+   |             ^ not allowed after `pat` fragments
+   |
+   = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in`
 
 error: `$p:pat` is followed by `ident`, which is not allowed for `pat` fragments
   --> $DIR/macro-follow.rs:23:13
    |
 LL |     ($p:pat ident) => {};    //~ERROR `$p:pat` is followed by `ident`
-   |             ^^^^^
+   |             ^^^^^ not allowed after `pat` fragments
+   |
+   = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in`
 
 error: `$p:pat` is followed by `$p:pat`, which is not allowed for `pat` fragments
   --> $DIR/macro-follow.rs:24:13
    |
 LL |     ($p:pat $p:pat) => {};   //~ERROR `$p:pat` is followed by `$p:pat`
-   |             ^^^^^^
+   |             ^^^^^^ not allowed after `pat` fragments
+   |
+   = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in`
 
 error: `$p:pat` is followed by `$e:expr`, which is not allowed for `pat` fragments
   --> $DIR/macro-follow.rs:25:13
    |
 LL |     ($p:pat $e:expr) => {};  //~ERROR `$p:pat` is followed by `$e:expr`
-   |             ^^^^^^^
+   |             ^^^^^^^ not allowed after `pat` fragments
+   |
+   = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in`
 
 error: `$p:pat` is followed by `$t:ty`, which is not allowed for `pat` fragments
   --> $DIR/macro-follow.rs:26:13
    |
 LL |     ($p:pat $t:ty) => {};    //~ERROR `$p:pat` is followed by `$t:ty`
-   |             ^^^^^
+   |             ^^^^^ not allowed after `pat` fragments
+   |
+   = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in`
 
 error: `$p:pat` is followed by `$s:stmt`, which is not allowed for `pat` fragments
   --> $DIR/macro-follow.rs:27:13
    |
 LL |     ($p:pat $s:stmt) => {};  //~ERROR `$p:pat` is followed by `$s:stmt`
-   |             ^^^^^^^
+   |             ^^^^^^^ not allowed after `pat` fragments
+   |
+   = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in`
 
 error: `$p:pat` is followed by `$p:path`, which is not allowed for `pat` fragments
   --> $DIR/macro-follow.rs:28:13
    |
 LL |     ($p:pat $p:path) => {};  //~ERROR `$p:pat` is followed by `$p:path`
-   |             ^^^^^^^
+   |             ^^^^^^^ not allowed after `pat` fragments
+   |
+   = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in`
 
 error: `$p:pat` is followed by `$b:block`, which is not allowed for `pat` fragments
   --> $DIR/macro-follow.rs:29:13
    |
 LL |     ($p:pat $b:block) => {}; //~ERROR `$p:pat` is followed by `$b:block`
-   |             ^^^^^^^^
+   |             ^^^^^^^^ not allowed after `pat` fragments
+   |
+   = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in`
 
 error: `$p:pat` is followed by `$i:ident`, which is not allowed for `pat` fragments
   --> $DIR/macro-follow.rs:30:13
    |
 LL |     ($p:pat $i:ident) => {}; //~ERROR `$p:pat` is followed by `$i:ident`
-   |             ^^^^^^^^
+   |             ^^^^^^^^ not allowed after `pat` fragments
+   |
+   = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in`
 
 error: `$p:pat` is followed by `$t:tt`, which is not allowed for `pat` fragments
   --> $DIR/macro-follow.rs:31:13
    |
 LL |     ($p:pat $t:tt) => {};    //~ERROR `$p:pat` is followed by `$t:tt`
-   |             ^^^^^
+   |             ^^^^^ not allowed after `pat` fragments
+   |
+   = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in`
 
 error: `$p:pat` is followed by `$i:item`, which is not allowed for `pat` fragments
   --> $DIR/macro-follow.rs:32:13
    |
 LL |     ($p:pat $i:item) => {};  //~ERROR `$p:pat` is followed by `$i:item`
-   |             ^^^^^^^
+   |             ^^^^^^^ not allowed after `pat` fragments
+   |
+   = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in`
 
 error: `$p:pat` is followed by `$m:meta`, which is not allowed for `pat` fragments
   --> $DIR/macro-follow.rs:33:13
    |
 LL |     ($p:pat $m:meta) => {};  //~ERROR `$p:pat` is followed by `$m:meta`
-   |             ^^^^^^^
+   |             ^^^^^^^ not allowed after `pat` fragments
+   |
+   = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in`
 
 error: `$e:expr` is followed by `(`, which is not allowed for `expr` fragments
   --> $DIR/macro-follow.rs:37:15
    |
 LL |     ($e:expr ()) => {};       //~ERROR  `$e:expr` is followed by `(`
-   |               ^
+   |               ^ not allowed after `expr` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$e:expr` is followed by `[`, which is not allowed for `expr` fragments
   --> $DIR/macro-follow.rs:38:15
    |
 LL |     ($e:expr []) => {};       //~ERROR  `$e:expr` is followed by `[`
-   |               ^
+   |               ^ not allowed after `expr` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$e:expr` is followed by `{`, which is not allowed for `expr` fragments
   --> $DIR/macro-follow.rs:39:15
    |
 LL |     ($e:expr {}) => {};       //~ERROR  `$e:expr` is followed by `{`
-   |               ^
+   |               ^ not allowed after `expr` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$e:expr` is followed by `=`, which is not allowed for `expr` fragments
   --> $DIR/macro-follow.rs:40:14
    |
 LL |     ($e:expr =) => {};        //~ERROR `$e:expr` is followed by `=`
-   |              ^
+   |              ^ not allowed after `expr` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$e:expr` is followed by `|`, which is not allowed for `expr` fragments
   --> $DIR/macro-follow.rs:41:14
    |
 LL |     ($e:expr |) => {};        //~ERROR `$e:expr` is followed by `|`
-   |              ^
+   |              ^ not allowed after `expr` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$e:expr` is followed by `:`, which is not allowed for `expr` fragments
   --> $DIR/macro-follow.rs:42:14
    |
 LL |     ($e:expr :) => {};        //~ERROR `$e:expr` is followed by `:`
-   |              ^
+   |              ^ not allowed after `expr` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$e:expr` is followed by `>`, which is not allowed for `expr` fragments
   --> $DIR/macro-follow.rs:43:14
    |
 LL |     ($e:expr >) => {};        //~ERROR `$e:expr` is followed by `>`
-   |              ^
+   |              ^ not allowed after `expr` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$e:expr` is followed by `+`, which is not allowed for `expr` fragments
   --> $DIR/macro-follow.rs:44:14
    |
 LL |     ($e:expr +) => {};        //~ERROR `$e:expr` is followed by `+`
-   |              ^
+   |              ^ not allowed after `expr` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$e:expr` is followed by `ident`, which is not allowed for `expr` fragments
   --> $DIR/macro-follow.rs:45:14
    |
 LL |     ($e:expr ident) => {};    //~ERROR `$e:expr` is followed by `ident`
-   |              ^^^^^
+   |              ^^^^^ not allowed after `expr` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$e:expr` is followed by `if`, which is not allowed for `expr` fragments
   --> $DIR/macro-follow.rs:46:14
    |
 LL |     ($e:expr if) => {};       //~ERROR `$e:expr` is followed by `if`
-   |              ^^
+   |              ^^ not allowed after `expr` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$e:expr` is followed by `in`, which is not allowed for `expr` fragments
   --> $DIR/macro-follow.rs:47:14
    |
 LL |     ($e:expr in) => {};       //~ERROR `$e:expr` is followed by `in`
-   |              ^^
+   |              ^^ not allowed after `expr` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$e:expr` is followed by `$p:pat`, which is not allowed for `expr` fragments
   --> $DIR/macro-follow.rs:48:14
    |
 LL |     ($e:expr $p:pat) => {};   //~ERROR `$e:expr` is followed by `$p:pat`
-   |              ^^^^^^
+   |              ^^^^^^ not allowed after `expr` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$e:expr` is followed by `$e:expr`, which is not allowed for `expr` fragments
   --> $DIR/macro-follow.rs:49:14
    |
 LL |     ($e:expr $e:expr) => {};  //~ERROR `$e:expr` is followed by `$e:expr`
-   |              ^^^^^^^
+   |              ^^^^^^^ not allowed after `expr` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$e:expr` is followed by `$t:ty`, which is not allowed for `expr` fragments
   --> $DIR/macro-follow.rs:50:14
    |
 LL |     ($e:expr $t:ty) => {};    //~ERROR `$e:expr` is followed by `$t:ty`
-   |              ^^^^^
+   |              ^^^^^ not allowed after `expr` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$e:expr` is followed by `$s:stmt`, which is not allowed for `expr` fragments
   --> $DIR/macro-follow.rs:51:14
    |
 LL |     ($e:expr $s:stmt) => {};  //~ERROR `$e:expr` is followed by `$s:stmt`
-   |              ^^^^^^^
+   |              ^^^^^^^ not allowed after `expr` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$e:expr` is followed by `$p:path`, which is not allowed for `expr` fragments
   --> $DIR/macro-follow.rs:52:14
    |
 LL |     ($e:expr $p:path) => {};  //~ERROR `$e:expr` is followed by `$p:path`
-   |              ^^^^^^^
+   |              ^^^^^^^ not allowed after `expr` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$e:expr` is followed by `$b:block`, which is not allowed for `expr` fragments
   --> $DIR/macro-follow.rs:53:14
    |
 LL |     ($e:expr $b:block) => {}; //~ERROR `$e:expr` is followed by `$b:block`
-   |              ^^^^^^^^
+   |              ^^^^^^^^ not allowed after `expr` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$e:expr` is followed by `$i:ident`, which is not allowed for `expr` fragments
   --> $DIR/macro-follow.rs:54:14
    |
 LL |     ($e:expr $i:ident) => {}; //~ERROR `$e:expr` is followed by `$i:ident`
-   |              ^^^^^^^^
+   |              ^^^^^^^^ not allowed after `expr` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$e:expr` is followed by `$t:tt`, which is not allowed for `expr` fragments
   --> $DIR/macro-follow.rs:55:14
    |
 LL |     ($e:expr $t:tt) => {};    //~ERROR `$e:expr` is followed by `$t:tt`
-   |              ^^^^^
+   |              ^^^^^ not allowed after `expr` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$e:expr` is followed by `$i:item`, which is not allowed for `expr` fragments
   --> $DIR/macro-follow.rs:56:14
    |
 LL |     ($e:expr $i:item) => {};  //~ERROR `$e:expr` is followed by `$i:item`
-   |              ^^^^^^^
+   |              ^^^^^^^ not allowed after `expr` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$e:expr` is followed by `$m:meta`, which is not allowed for `expr` fragments
   --> $DIR/macro-follow.rs:57:14
    |
 LL |     ($e:expr $m:meta) => {};  //~ERROR `$e:expr` is followed by `$m:meta`
-   |              ^^^^^^^
+   |              ^^^^^^^ not allowed after `expr` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$t:ty` is followed by `(`, which is not allowed for `ty` fragments
   --> $DIR/macro-follow.rs:62:13
    |
 LL |     ($t:ty ()) => {};       //~ERROR  `$t:ty` is followed by `(`
-   |             ^
+   |             ^ not allowed after `ty` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$t:ty` is followed by `+`, which is not allowed for `ty` fragments
   --> $DIR/macro-follow.rs:64:12
    |
 LL |     ($t:ty +) => {};        //~ERROR `$t:ty` is followed by `+`
-   |            ^
+   |            ^ not allowed after `ty` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$t:ty` is followed by `ident`, which is not allowed for `ty` fragments
   --> $DIR/macro-follow.rs:65:12
    |
 LL |     ($t:ty ident) => {};    //~ERROR `$t:ty` is followed by `ident`
-   |            ^^^^^
+   |            ^^^^^ not allowed after `ty` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$t:ty` is followed by `if`, which is not allowed for `ty` fragments
   --> $DIR/macro-follow.rs:66:12
    |
 LL |     ($t:ty if) => {};       //~ERROR `$t:ty` is followed by `if`
-   |            ^^
+   |            ^^ not allowed after `ty` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$t:ty` is followed by `$p:pat`, which is not allowed for `ty` fragments
   --> $DIR/macro-follow.rs:67:12
    |
 LL |     ($t:ty $p:pat) => {};   //~ERROR `$t:ty` is followed by `$p:pat`
-   |            ^^^^^^
+   |            ^^^^^^ not allowed after `ty` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$t:ty` is followed by `$e:expr`, which is not allowed for `ty` fragments
   --> $DIR/macro-follow.rs:68:12
    |
 LL |     ($t:ty $e:expr) => {};  //~ERROR `$t:ty` is followed by `$e:expr`
-   |            ^^^^^^^
+   |            ^^^^^^^ not allowed after `ty` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$t:ty` is followed by `$t:ty`, which is not allowed for `ty` fragments
   --> $DIR/macro-follow.rs:69:12
    |
 LL |     ($t:ty $t:ty) => {};    //~ERROR `$t:ty` is followed by `$t:ty`
-   |            ^^^^^
+   |            ^^^^^ not allowed after `ty` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$t:ty` is followed by `$s:stmt`, which is not allowed for `ty` fragments
   --> $DIR/macro-follow.rs:70:12
    |
 LL |     ($t:ty $s:stmt) => {};  //~ERROR `$t:ty` is followed by `$s:stmt`
-   |            ^^^^^^^
+   |            ^^^^^^^ not allowed after `ty` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$t:ty` is followed by `$p:path`, which is not allowed for `ty` fragments
   --> $DIR/macro-follow.rs:71:12
    |
 LL |     ($t:ty $p:path) => {};  //~ERROR `$t:ty` is followed by `$p:path`
-   |            ^^^^^^^
+   |            ^^^^^^^ not allowed after `ty` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$t:ty` is followed by `$i:ident`, which is not allowed for `ty` fragments
   --> $DIR/macro-follow.rs:73:12
    |
 LL |     ($t:ty $i:ident) => {}; //~ERROR `$t:ty` is followed by `$i:ident`
-   |            ^^^^^^^^
+   |            ^^^^^^^^ not allowed after `ty` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$t:ty` is followed by `$t:tt`, which is not allowed for `ty` fragments
   --> $DIR/macro-follow.rs:74:12
    |
 LL |     ($t:ty $t:tt) => {};    //~ERROR `$t:ty` is followed by `$t:tt`
-   |            ^^^^^
+   |            ^^^^^ not allowed after `ty` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$t:ty` is followed by `$i:item`, which is not allowed for `ty` fragments
   --> $DIR/macro-follow.rs:75:12
    |
 LL |     ($t:ty $i:item) => {};  //~ERROR `$t:ty` is followed by `$i:item`
-   |            ^^^^^^^
+   |            ^^^^^^^ not allowed after `ty` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$t:ty` is followed by `$m:meta`, which is not allowed for `ty` fragments
   --> $DIR/macro-follow.rs:76:12
    |
 LL |     ($t:ty $m:meta) => {};  //~ERROR `$t:ty` is followed by `$m:meta`
-   |            ^^^^^^^
+   |            ^^^^^^^ not allowed after `ty` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$s:stmt` is followed by `(`, which is not allowed for `stmt` fragments
   --> $DIR/macro-follow.rs:80:15
    |
 LL |     ($s:stmt ()) => {};       //~ERROR  `$s:stmt` is followed by `(`
-   |               ^
+   |               ^ not allowed after `stmt` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$s:stmt` is followed by `[`, which is not allowed for `stmt` fragments
   --> $DIR/macro-follow.rs:81:15
    |
 LL |     ($s:stmt []) => {};       //~ERROR  `$s:stmt` is followed by `[`
-   |               ^
+   |               ^ not allowed after `stmt` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$s:stmt` is followed by `{`, which is not allowed for `stmt` fragments
   --> $DIR/macro-follow.rs:82:15
    |
 LL |     ($s:stmt {}) => {};       //~ERROR  `$s:stmt` is followed by `{`
-   |               ^
+   |               ^ not allowed after `stmt` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$s:stmt` is followed by `=`, which is not allowed for `stmt` fragments
   --> $DIR/macro-follow.rs:83:14
    |
 LL |     ($s:stmt =) => {};        //~ERROR `$s:stmt` is followed by `=`
-   |              ^
+   |              ^ not allowed after `stmt` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$s:stmt` is followed by `|`, which is not allowed for `stmt` fragments
   --> $DIR/macro-follow.rs:84:14
    |
 LL |     ($s:stmt |) => {};        //~ERROR `$s:stmt` is followed by `|`
-   |              ^
+   |              ^ not allowed after `stmt` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$s:stmt` is followed by `:`, which is not allowed for `stmt` fragments
   --> $DIR/macro-follow.rs:85:14
    |
 LL |     ($s:stmt :) => {};        //~ERROR `$s:stmt` is followed by `:`
-   |              ^
+   |              ^ not allowed after `stmt` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$s:stmt` is followed by `>`, which is not allowed for `stmt` fragments
   --> $DIR/macro-follow.rs:86:14
    |
 LL |     ($s:stmt >) => {};        //~ERROR `$s:stmt` is followed by `>`
-   |              ^
+   |              ^ not allowed after `stmt` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$s:stmt` is followed by `+`, which is not allowed for `stmt` fragments
   --> $DIR/macro-follow.rs:87:14
    |
 LL |     ($s:stmt +) => {};        //~ERROR `$s:stmt` is followed by `+`
-   |              ^
+   |              ^ not allowed after `stmt` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$s:stmt` is followed by `ident`, which is not allowed for `stmt` fragments
   --> $DIR/macro-follow.rs:88:14
    |
 LL |     ($s:stmt ident) => {};    //~ERROR `$s:stmt` is followed by `ident`
-   |              ^^^^^
+   |              ^^^^^ not allowed after `stmt` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$s:stmt` is followed by `if`, which is not allowed for `stmt` fragments
   --> $DIR/macro-follow.rs:89:14
    |
 LL |     ($s:stmt if) => {};       //~ERROR `$s:stmt` is followed by `if`
-   |              ^^
+   |              ^^ not allowed after `stmt` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$s:stmt` is followed by `in`, which is not allowed for `stmt` fragments
   --> $DIR/macro-follow.rs:90:14
    |
 LL |     ($s:stmt in) => {};       //~ERROR `$s:stmt` is followed by `in`
-   |              ^^
+   |              ^^ not allowed after `stmt` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$s:stmt` is followed by `$p:pat`, which is not allowed for `stmt` fragments
   --> $DIR/macro-follow.rs:91:14
    |
 LL |     ($s:stmt $p:pat) => {};   //~ERROR `$s:stmt` is followed by `$p:pat`
-   |              ^^^^^^
+   |              ^^^^^^ not allowed after `stmt` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$s:stmt` is followed by `$e:expr`, which is not allowed for `stmt` fragments
   --> $DIR/macro-follow.rs:92:14
    |
 LL |     ($s:stmt $e:expr) => {};  //~ERROR `$s:stmt` is followed by `$e:expr`
-   |              ^^^^^^^
+   |              ^^^^^^^ not allowed after `stmt` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$s:stmt` is followed by `$t:ty`, which is not allowed for `stmt` fragments
   --> $DIR/macro-follow.rs:93:14
    |
 LL |     ($s:stmt $t:ty) => {};    //~ERROR `$s:stmt` is followed by `$t:ty`
-   |              ^^^^^
+   |              ^^^^^ not allowed after `stmt` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$s:stmt` is followed by `$s:stmt`, which is not allowed for `stmt` fragments
   --> $DIR/macro-follow.rs:94:14
    |
 LL |     ($s:stmt $s:stmt) => {};  //~ERROR `$s:stmt` is followed by `$s:stmt`
-   |              ^^^^^^^
+   |              ^^^^^^^ not allowed after `stmt` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$s:stmt` is followed by `$p:path`, which is not allowed for `stmt` fragments
   --> $DIR/macro-follow.rs:95:14
    |
 LL |     ($s:stmt $p:path) => {};  //~ERROR `$s:stmt` is followed by `$p:path`
-   |              ^^^^^^^
+   |              ^^^^^^^ not allowed after `stmt` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$s:stmt` is followed by `$b:block`, which is not allowed for `stmt` fragments
   --> $DIR/macro-follow.rs:96:14
    |
 LL |     ($s:stmt $b:block) => {}; //~ERROR `$s:stmt` is followed by `$b:block`
-   |              ^^^^^^^^
+   |              ^^^^^^^^ not allowed after `stmt` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$s:stmt` is followed by `$i:ident`, which is not allowed for `stmt` fragments
   --> $DIR/macro-follow.rs:97:14
    |
 LL |     ($s:stmt $i:ident) => {}; //~ERROR `$s:stmt` is followed by `$i:ident`
-   |              ^^^^^^^^
+   |              ^^^^^^^^ not allowed after `stmt` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$s:stmt` is followed by `$t:tt`, which is not allowed for `stmt` fragments
   --> $DIR/macro-follow.rs:98:14
    |
 LL |     ($s:stmt $t:tt) => {};    //~ERROR `$s:stmt` is followed by `$t:tt`
-   |              ^^^^^
+   |              ^^^^^ not allowed after `stmt` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$s:stmt` is followed by `$i:item`, which is not allowed for `stmt` fragments
   --> $DIR/macro-follow.rs:99:14
    |
 LL |     ($s:stmt $i:item) => {};  //~ERROR `$s:stmt` is followed by `$i:item`
-   |              ^^^^^^^
+   |              ^^^^^^^ not allowed after `stmt` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$s:stmt` is followed by `$m:meta`, which is not allowed for `stmt` fragments
   --> $DIR/macro-follow.rs:100:14
    |
 LL |     ($s:stmt $m:meta) => {};  //~ERROR `$s:stmt` is followed by `$m:meta`
-   |              ^^^^^^^
+   |              ^^^^^^^ not allowed after `stmt` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$p:path` is followed by `(`, which is not allowed for `path` fragments
   --> $DIR/macro-follow.rs:104:15
    |
 LL |     ($p:path ()) => {};       //~ERROR  `$p:path` is followed by `(`
-   |               ^
+   |               ^ not allowed after `path` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$p:path` is followed by `+`, which is not allowed for `path` fragments
   --> $DIR/macro-follow.rs:106:14
    |
 LL |     ($p:path +) => {};        //~ERROR `$p:path` is followed by `+`
-   |              ^
+   |              ^ not allowed after `path` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$p:path` is followed by `ident`, which is not allowed for `path` fragments
   --> $DIR/macro-follow.rs:107:14
    |
 LL |     ($p:path ident) => {};    //~ERROR `$p:path` is followed by `ident`
-   |              ^^^^^
+   |              ^^^^^ not allowed after `path` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$p:path` is followed by `if`, which is not allowed for `path` fragments
   --> $DIR/macro-follow.rs:108:14
    |
 LL |     ($p:path if) => {};       //~ERROR `$p:path` is followed by `if`
-   |              ^^
+   |              ^^ not allowed after `path` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$p:path` is followed by `$p:pat`, which is not allowed for `path` fragments
   --> $DIR/macro-follow.rs:109:14
    |
 LL |     ($p:path $p:pat) => {};   //~ERROR `$p:path` is followed by `$p:pat`
-   |              ^^^^^^
+   |              ^^^^^^ not allowed after `path` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$p:path` is followed by `$e:expr`, which is not allowed for `path` fragments
   --> $DIR/macro-follow.rs:110:14
    |
 LL |     ($p:path $e:expr) => {};  //~ERROR `$p:path` is followed by `$e:expr`
-   |              ^^^^^^^
+   |              ^^^^^^^ not allowed after `path` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$p:path` is followed by `$t:ty`, which is not allowed for `path` fragments
   --> $DIR/macro-follow.rs:111:14
    |
 LL |     ($p:path $t:ty) => {};    //~ERROR `$p:path` is followed by `$t:ty`
-   |              ^^^^^
+   |              ^^^^^ not allowed after `path` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$p:path` is followed by `$s:stmt`, which is not allowed for `path` fragments
   --> $DIR/macro-follow.rs:112:14
    |
 LL |     ($p:path $s:stmt) => {};  //~ERROR `$p:path` is followed by `$s:stmt`
-   |              ^^^^^^^
+   |              ^^^^^^^ not allowed after `path` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$p:path` is followed by `$p:path`, which is not allowed for `path` fragments
   --> $DIR/macro-follow.rs:113:14
    |
 LL |     ($p:path $p:path) => {};  //~ERROR `$p:path` is followed by `$p:path`
-   |              ^^^^^^^
+   |              ^^^^^^^ not allowed after `path` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$p:path` is followed by `$i:ident`, which is not allowed for `path` fragments
   --> $DIR/macro-follow.rs:115:14
    |
 LL |     ($p:path $i:ident) => {}; //~ERROR `$p:path` is followed by `$i:ident`
-   |              ^^^^^^^^
+   |              ^^^^^^^^ not allowed after `path` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$p:path` is followed by `$t:tt`, which is not allowed for `path` fragments
   --> $DIR/macro-follow.rs:116:14
    |
 LL |     ($p:path $t:tt) => {};    //~ERROR `$p:path` is followed by `$t:tt`
-   |              ^^^^^
+   |              ^^^^^ not allowed after `path` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$p:path` is followed by `$i:item`, which is not allowed for `path` fragments
   --> $DIR/macro-follow.rs:117:14
    |
 LL |     ($p:path $i:item) => {};  //~ERROR `$p:path` is followed by `$i:item`
-   |              ^^^^^^^
+   |              ^^^^^^^ not allowed after `path` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$p:path` is followed by `$m:meta`, which is not allowed for `path` fragments
   --> $DIR/macro-follow.rs:118:14
    |
 LL |     ($p:path $m:meta) => {};  //~ERROR `$p:path` is followed by `$m:meta`
-   |              ^^^^^^^
+   |              ^^^^^^^ not allowed after `path` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: aborting due to 85 previous errors
 
diff --git a/src/test/ui/macros/macro-followed-by-seq-bad.stderr b/src/test/ui/macros/macro-followed-by-seq-bad.stderr
index bb070334d36e9..2ad8990e1156f 100644
--- a/src/test/ui/macros/macro-followed-by-seq-bad.stderr
+++ b/src/test/ui/macros/macro-followed-by-seq-bad.stderr
@@ -2,13 +2,17 @@ error: `$a:expr` is followed by `$b:tt`, which is not allowed for `expr` fragmen
   --> $DIR/macro-followed-by-seq-bad.rs:17:15
    |
 LL |   ( $a:expr $($b:tt)* ) => { }; //~ ERROR not allowed for `expr` fragments
-   |               ^^^^^
+   |               ^^^^^ not allowed after `expr` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: `$a:ty` is followed by `$b:tt`, which is not allowed for `ty` fragments
   --> $DIR/macro-followed-by-seq-bad.rs:18:13
    |
 LL |   ( $a:ty $($b:tt)* ) => { };   //~ ERROR not allowed for `ty` fragments
-   |             ^^^^^
+   |             ^^^^^ not allowed after `ty` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/macros/macro-input-future-proofing.stderr b/src/test/ui/macros/macro-input-future-proofing.stderr
index aed7a8a119ced..4bb46e39562cb 100644
--- a/src/test/ui/macros/macro-input-future-proofing.stderr
+++ b/src/test/ui/macros/macro-input-future-proofing.stderr
@@ -2,55 +2,73 @@ error: `$ty:ty` is followed by `<`, which is not allowed for `ty` fragments
   --> $DIR/macro-input-future-proofing.rs:14:13
    |
 LL |     ($ty:ty <) => (); //~ ERROR `$ty:ty` is followed by `<`, which is not allowed for `ty`
-   |             ^
+   |             ^ not allowed after `ty` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$ty:ty` is followed by `<`, which is not allowed for `ty` fragments
   --> $DIR/macro-input-future-proofing.rs:15:13
    |
 LL |     ($ty:ty < foo ,) => (); //~ ERROR `$ty:ty` is followed by `<`, which is not allowed for `ty`
-   |             ^
+   |             ^ not allowed after `ty` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$pa:pat` is followed by `>`, which is not allowed for `pat` fragments
   --> $DIR/macro-input-future-proofing.rs:21:14
    |
 LL |     ($pa:pat >) => (); //~ ERROR `$pa:pat` is followed by `>`, which is not allowed for `pat`
-   |              ^
+   |              ^ not allowed after `pat` fragments
+   |
+   = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in`
 
 error: `$pa:pat` is followed by `$pb:pat`, which is not allowed for `pat` fragments
   --> $DIR/macro-input-future-proofing.rs:23:14
    |
 LL |     ($pa:pat $pb:pat $ty:ty ,) => ();
-   |              ^^^^^^^
+   |              ^^^^^^^ not allowed after `pat` fragments
+   |
+   = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in`
 
 error: `$pb:pat` is followed by `$ty:ty`, which is not allowed for `pat` fragments
   --> $DIR/macro-input-future-proofing.rs:23:22
    |
 LL |     ($pa:pat $pb:pat $ty:ty ,) => ();
-   |                      ^^^^^^
+   |                      ^^^^^^ not allowed after `pat` fragments
+   |
+   = note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in`
 
 error: `$ty:ty` is followed by `-`, which is not allowed for `ty` fragments
   --> $DIR/macro-input-future-proofing.rs:26:17
    |
 LL |     ($($ty:ty)* -) => (); //~ ERROR `$ty:ty` is followed by `-`
-   |                 ^
+   |                 ^ not allowed after `ty` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$b:ty` is followed by `-`, which is not allowed for `ty` fragments
   --> $DIR/macro-input-future-proofing.rs:27:23
    |
 LL |     ($($a:ty, $b:ty)* -) => (); //~ ERROR `$b:ty` is followed by `-`
-   |                       ^
+   |                       ^ not allowed after `ty` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$ty:ty` is followed by `-`, which is not allowed for `ty` fragments
   --> $DIR/macro-input-future-proofing.rs:28:7
    |
 LL |     ($($ty:ty)-+) => (); //~ ERROR `$ty:ty` is followed by `-`, which is not allowed for `ty`
-   |       ^^^^^^^^
+   |       ^^^^^^^^ not allowed after `ty` fragments
+   |
+   = note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
 
 error: `$a:expr` is followed by `$b:tt`, which is not allowed for `expr` fragments
   --> $DIR/macro-input-future-proofing.rs:29:21
    |
 LL |     ( $($a:expr)* $($b:tt)* ) => { };
-   |                     ^^^^^
+   |                     ^^^^^ not allowed after `expr` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: aborting due to 9 previous errors
 
diff --git a/src/test/ui/unused/unused-macro-with-follow-violation.stderr b/src/test/ui/unused/unused-macro-with-follow-violation.stderr
index 8efb191c7c645..78362fcb05a8c 100644
--- a/src/test/ui/unused/unused-macro-with-follow-violation.stderr
+++ b/src/test/ui/unused/unused-macro-with-follow-violation.stderr
@@ -2,7 +2,9 @@ error: `$e:expr` is followed by `+`, which is not allowed for `expr` fragments
   --> $DIR/unused-macro-with-follow-violation.rs:14:14
    |
 LL |     ($e:expr +) => () //~ ERROR not allowed for `expr` fragments
-   |              ^
+   |              ^ not allowed after `expr` fragments
+   |
+   = note: allowed there are: `=>`, `,` or `;`
 
 error: aborting due to previous error