about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-05-03 15:10:16 +0000
committerbors <bors@rust-lang.org>2019-05-03 15:10:16 +0000
commit3af1bdc4bcc502d576b1e836f99bae1eb29dbafd (patch)
tree928e4e1504008643140afc74d2c998e62e0947e9
parentef9a876f8297e26859469b004047e0946ed10565 (diff)
parent6f7a1eabdf2770d1a83ad6f1968162f64b36a13d (diff)
downloadrust-3af1bdc4bcc502d576b1e836f99bae1eb29dbafd.tar.gz
rust-3af1bdc4bcc502d576b1e836f99bae1eb29dbafd.zip
Auto merge of #60510 - Centril:rollup-gsndjbp, r=Centril
Rollup of 12 pull requests

Successful merges:

 - #59928 (Make deprecation lint `ambiguous_associated_items` deny-by-default)
 - #60220 (report fatal errors during doctest parsing)
 - #60373 (Tidy: ensure lang features are sorted by since)
 - #60388 (Disallow non-explicit elided lifetimes in async fn)
 - #60393 ( Do not suggest incorrect syntax on pattern type error due to borrow)
 - #60401 (Rename `RUST_LOG` to `RUSTC_LOG`)
 - #60409 (Require a trait in the bounds of existential types)
 - #60455 (Resolve match arm ty when arms diverge)
 - #60457 (Const prop refactoring)
 - #60467 (Avoid repeated interning of static strings.)
 - #60478 (minor compiler doc tweaks)
 - #60501 (Propagate mutability from arguments to local bindings in async fn)

Failed merges:

r? @ghost
-rw-r--r--Cargo.lock1
-rw-r--r--src/bootstrap/native.rs2
-rw-r--r--src/librustc/error_codes.rs5
-rw-r--r--src/librustc/hir/lowering.rs68
-rw-r--r--src/librustc/hir/mod.rs3
-rw-r--r--src/librustc/infer/error_reporting/mod.rs2
-rw-r--r--src/librustc/infer/mod.rs2
-rw-r--r--src/librustc/lint/builtin.rs82
-rw-r--r--src/librustc/lint/context.rs8
-rw-r--r--src/librustc_codegen_llvm/debuginfo/metadata.rs22
-rw-r--r--src/librustc_codegen_llvm/debuginfo/mod.rs2
-rw-r--r--src/librustc_driver/lib.rs2
-rw-r--r--src/librustc_mir/transform/const_prop.rs124
-rw-r--r--src/librustc_passes/ast_validation.rs10
-rw-r--r--src/librustc_typeck/check/_match.rs57
-rw-r--r--src/librustdoc/test.rs23
-rw-r--r--src/libstd/sys/redox/ext/net.rs82
-rw-r--r--src/libsyntax/feature_gate.rs33
-rw-r--r--src/libsyntax/parse/parser.rs10
-rw-r--r--src/test/run-make-fulldeps/libs-through-symlinks/Makefile2
-rw-r--r--src/test/run-pass/existential_type.rs4
-rw-r--r--src/test/run-pass/issues/issue-18075.rs2
-rw-r--r--src/test/run-pass/logging-only-prints-once.rs2
-rw-r--r--src/test/run-pass/logging_before_rt_started.rs2
-rw-r--r--src/test/run-pass/rustc-rust-log.rs2
-rw-r--r--src/test/run-pass/threads-sendsync/spawning-with-debug.rs2
-rw-r--r--src/test/rustdoc-ui/failed-doctest-output.stdout4
-rw-r--r--src/test/rustdoc-ui/unparseable-doc-test.rs10
-rw-r--r--src/test/rustdoc-ui/unparseable-doc-test.stdout24
-rw-r--r--src/test/ui/async-await/mutable-arguments.rs10
-rw-r--r--src/test/ui/async-fn-path-elision.rs16
-rw-r--r--src/test/ui/async-fn-path-elision.stderr8
-rw-r--r--src/test/ui/consts/const-err.rs1
-rw-r--r--src/test/ui/consts/const-err.stderr12
-rw-r--r--src/test/ui/destructure-trait-ref.stderr12
-rw-r--r--src/test/ui/existential_types/existential-types-with-no-traits.rs14
-rw-r--r--src/test/ui/existential_types/existential-types-with-no-traits.stderr14
-rw-r--r--src/test/ui/existential_types/generic_nondefining_use.rs2
-rw-r--r--src/test/ui/existential_types/generic_nondefining_use.stderr10
-rw-r--r--src/test/ui/existential_types/generic_not_used.rs1
-rw-r--r--src/test/ui/existential_types/generic_not_used.stderr10
-rw-r--r--src/test/ui/existential_types/generic_type_does_not_live_long_enough.rs1
-rw-r--r--src/test/ui/existential_types/generic_type_does_not_live_long_enough.stderr8
-rw-r--r--src/test/ui/existential_types/generic_underconstrained.rs1
-rw-r--r--src/test/ui/existential_types/generic_underconstrained.stderr8
-rw-r--r--src/test/ui/existential_types/generic_underconstrained2.rs2
-rw-r--r--src/test/ui/existential_types/generic_underconstrained2.stderr16
-rw-r--r--src/test/ui/existential_types/unused_generic_param.rs5
-rw-r--r--src/test/ui/existential_types/unused_generic_param.stderr14
-rw-r--r--src/test/ui/impl-header-lifetime-elision/path-elided.rs2
-rw-r--r--src/test/ui/impl-header-lifetime-elision/path-elided.stderr5
-rw-r--r--src/test/ui/impl-header-lifetime-elision/trait-elided.rs2
-rw-r--r--src/test/ui/impl-header-lifetime-elision/trait-elided.stderr5
-rw-r--r--src/test/ui/issues/issue-10412.rs3
-rw-r--r--src/test/ui/issues/issue-10412.stderr21
-rw-r--r--src/test/ui/match/match-type-err-first-arm.rs24
-rw-r--r--src/test/ui/match/match-type-err-first-arm.stderr35
-rw-r--r--src/test/ui/mismatched_types/issue-38371.stderr6
-rw-r--r--src/test/ui/suggestions/match-ergonomics.rs41
-rw-r--r--src/test/ui/suggestions/match-ergonomics.stderr52
-rw-r--r--src/test/ui/type-alias-enum-variants-priority.rs1
-rw-r--r--src/test/ui/type-alias-enum-variants-priority.stderr12
-rw-r--r--src/tools/tidy/Cargo.toml1
-rw-r--r--src/tools/tidy/src/features.rs128
-rw-r--r--src/tools/tidy/src/features/version.rs92
-rw-r--r--src/tools/tidy/src/lib.rs1
66 files changed, 891 insertions, 297 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 16d17cf8583..3d7f00e6410 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3556,6 +3556,7 @@ dependencies = [
 name = "tidy"
 version = "0.1.0"
 dependencies = [
+ "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.81 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs
index 96aed729345..285f9458c44 100644
--- a/src/bootstrap/native.rs
+++ b/src/bootstrap/native.rs
@@ -436,7 +436,7 @@ fn configure_cmake(builder: &Builder<'_>,
     }
 
     if env::var_os("SCCACHE_ERROR_LOG").is_some() {
-        cfg.env("RUST_LOG", "sccache=warn");
+        cfg.env("RUSTC_LOG", "sccache=warn");
     }
 }
 
diff --git a/src/librustc/error_codes.rs b/src/librustc/error_codes.rs
index 7f68b35d014..fd089fc688e 100644
--- a/src/librustc/error_codes.rs
+++ b/src/librustc/error_codes.rs
@@ -362,10 +362,6 @@ struct Foo1 { x: &bool }
               // ^ expected lifetime parameter
 struct Foo2<'a> { x: &'a bool } // correct
 
-impl Foo2 {}
-  // ^^^^ expected lifetime parameter
-impl<'a> Foo2<'a> {} // correct
-
 struct Bar1 { x: Foo2 }
               // ^^^^ expected lifetime parameter
 struct Bar2<'a> { x: Foo2<'a> } // correct
@@ -2208,4 +2204,5 @@ register_diagnostics! {
     E0710, // an unknown tool name found in scoped lint
     E0711, // a feature has been declared with conflicting stability attributes
 //  E0702, // replaced with a generic attribute input check
+    E0726, // non-explicit (not `'_`) elided lifetime in unsupported position
 }
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 6d57882976e..ad26e05bf80 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -2110,15 +2110,49 @@ impl<'a> LoweringContext<'a> {
                         .expect("already checked that type args or bindings exist");
                     (false, first_generic_span.shrink_to_lo(), format!("{}, ", anon_lt_suggestion))
                 };
-                self.sess.buffer_lint_with_diagnostic(
-                    ELIDED_LIFETIMES_IN_PATHS,
-                    CRATE_NODE_ID,
-                    path_span,
-                    "hidden lifetime parameters in types are deprecated",
-                    builtin::BuiltinLintDiagnostics::ElidedLifetimesInPaths(
-                        expected_lifetimes, path_span, incl_angl_brckt, insertion_span, suggestion
-                    )
-                );
+                match self.anonymous_lifetime_mode {
+                    // In create-parameter mode we error here because we don't want to support
+                    // deprecated impl elision in new features like impl elision and `async fn`,
+                    // both of which work using the `CreateParameter` mode:
+                    //
+                    //     impl Foo for std::cell::Ref<u32> // note lack of '_
+                    //     async fn foo(_: std::cell::Ref<u32>) { ... }
+                    AnonymousLifetimeMode::CreateParameter => {
+                        let mut err = struct_span_err!(
+                            self.sess,
+                            path_span,
+                            E0726,
+                            "implicit elided lifetime not allowed here"
+                        );
+                        crate::lint::builtin::add_elided_lifetime_in_path_suggestion(
+                            &self.sess,
+                            &mut err,
+                            expected_lifetimes,
+                            path_span,
+                            incl_angl_brckt,
+                            insertion_span,
+                            suggestion,
+                        );
+                        err.emit();
+                    }
+                    AnonymousLifetimeMode::PassThrough |
+                    AnonymousLifetimeMode::ReportError |
+                    AnonymousLifetimeMode::Replace(_) => {
+                        self.sess.buffer_lint_with_diagnostic(
+                            ELIDED_LIFETIMES_IN_PATHS,
+                            CRATE_NODE_ID,
+                            path_span,
+                            "hidden lifetime parameters in types are deprecated",
+                            builtin::BuiltinLintDiagnostics::ElidedLifetimesInPaths(
+                                expected_lifetimes,
+                                path_span,
+                                incl_angl_brckt,
+                                insertion_span,
+                                suggestion,
+                            )
+                        );
+                    }
+                }
             }
         }
 
@@ -5335,13 +5369,15 @@ impl<'a> LoweringContext<'a> {
 
     fn elided_path_lifetime(&mut self, span: Span) -> hir::Lifetime {
         match self.anonymous_lifetime_mode {
-            // N.B., We intentionally ignore the create-parameter mode here
-            // and instead "pass through" to resolve-lifetimes, which will then
-            // report an error. This is because we don't want to support
-            // impl elision for deprecated forms like
-            //
-            //     impl Foo for std::cell::Ref<u32> // note lack of '_
-            AnonymousLifetimeMode::CreateParameter |
+            AnonymousLifetimeMode::CreateParameter => {
+                // We should have emitted E0726 when processing this path above
+                self.sess.delay_span_bug(
+                    span,
+                    "expected 'implicit elided lifetime not allowed' error",
+                );
+                let id = self.sess.next_node_id();
+                self.new_named_lifetime(id, span, hir::LifetimeName::Error)
+            }
             // This is the normal case.
             AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span),
 
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index 5a2807ac93d..4293019aed5 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -1927,6 +1927,9 @@ pub enum ArgSource {
 /// Represents the header (not the body) of a function declaration.
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable)]
 pub struct FnDecl {
+    /// The types of the function's arguments.
+    ///
+    /// Additional argument data is stored in the function's [body](Body::arguments).
     pub inputs: HirVec<Ty>,
     pub output: FunctionRetTy,
     pub c_variadic: bool,
diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs
index c20a08fc5ae..95b566d4a1b 100644
--- a/src/librustc/infer/error_reporting/mod.rs
+++ b/src/librustc/infer/error_reporting/mod.rs
@@ -644,7 +644,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                         for sp in prior_arms {
                             err.span_label(*sp, format!(
                                 "this is found to be of type `{}`",
-                                last_ty,
+                                self.resolve_type_vars_if_possible(&last_ty),
                             ));
                         }
                     } else if let Some(sp) = prior_arms.last() {
diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs
index 65bc2b3e945..a8f39e44130 100644
--- a/src/librustc/infer/mod.rs
+++ b/src/librustc/infer/mod.rs
@@ -704,7 +704,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     /// potentially leaving "dangling type variables" behind.
     /// In such cases, an assertion will fail when attempting to
     /// register obligations, within a snapshot. Very useful, much
-    /// better than grovelling through megabytes of `RUST_LOG` output.
+    /// better than grovelling through megabytes of `RUSTC_LOG` output.
     ///
     /// HOWEVER, in some cases the flag is unhelpful. In particular, we
     /// sometimes create a "mini-fulfilment-cx" in which we enroll
diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs
index 28a2a1eaf6b..993e1aacb4e 100644
--- a/src/librustc/lint/builtin.rs
+++ b/src/librustc/lint/builtin.rs
@@ -376,7 +376,7 @@ declare_lint! {
 
 declare_lint! {
     pub AMBIGUOUS_ASSOCIATED_ITEMS,
-    Warn,
+    Deny,
     "ambiguous associated items"
 }
 
@@ -477,6 +477,48 @@ pub enum BuiltinLintDiagnostics {
     RedundantImport(Vec<(Span, bool)>, ast::Ident),
 }
 
+pub(crate) fn add_elided_lifetime_in_path_suggestion(
+    sess: &Session,
+    db: &mut DiagnosticBuilder<'_>,
+    n: usize,
+    path_span: Span,
+    incl_angl_brckt: bool,
+    insertion_span: Span,
+    anon_lts: String,
+) {
+    let (replace_span, suggestion) = if incl_angl_brckt {
+        (insertion_span, anon_lts)
+    } else {
+        // When possible, prefer a suggestion that replaces the whole
+        // `Path<T>` expression with `Path<'_, T>`, rather than inserting `'_, `
+        // at a point (which makes for an ugly/confusing label)
+        if let Ok(snippet) = sess.source_map().span_to_snippet(path_span) {
+            // But our spans can get out of whack due to macros; if the place we think
+            // we want to insert `'_` isn't even within the path expression's span, we
+            // should bail out of making any suggestion rather than panicking on a
+            // subtract-with-overflow or string-slice-out-out-bounds (!)
+            // FIXME: can we do better?
+            if insertion_span.lo().0 < path_span.lo().0 {
+                return;
+            }
+            let insertion_index = (insertion_span.lo().0 - path_span.lo().0) as usize;
+            if insertion_index > snippet.len() {
+                return;
+            }
+            let (before, after) = snippet.split_at(insertion_index);
+            (path_span, format!("{}{}{}", before, anon_lts, after))
+        } else {
+            (insertion_span, anon_lts)
+        }
+    };
+    db.span_suggestion(
+        replace_span,
+        &format!("indicate the anonymous lifetime{}", if n >= 2 { "s" } else { "" }),
+        suggestion,
+        Applicability::MachineApplicable
+    );
+}
+
 impl BuiltinLintDiagnostics {
     pub fn run(self, sess: &Session, db: &mut DiagnosticBuilder<'_>) {
         match self {
@@ -521,36 +563,14 @@ impl BuiltinLintDiagnostics {
             BuiltinLintDiagnostics::ElidedLifetimesInPaths(
                 n, path_span, incl_angl_brckt, insertion_span, anon_lts
             ) => {
-                let (replace_span, suggestion) = if incl_angl_brckt {
-                    (insertion_span, anon_lts)
-                } else {
-                    // When possible, prefer a suggestion that replaces the whole
-                    // `Path<T>` expression with `Path<'_, T>`, rather than inserting `'_, `
-                    // at a point (which makes for an ugly/confusing label)
-                    if let Ok(snippet) = sess.source_map().span_to_snippet(path_span) {
-                        // But our spans can get out of whack due to macros; if the place we think
-                        // we want to insert `'_` isn't even within the path expression's span, we
-                        // should bail out of making any suggestion rather than panicking on a
-                        // subtract-with-overflow or string-slice-out-out-bounds (!)
-                        // FIXME: can we do better?
-                        if insertion_span.lo().0 < path_span.lo().0 {
-                            return;
-                        }
-                        let insertion_index = (insertion_span.lo().0 - path_span.lo().0) as usize;
-                        if insertion_index > snippet.len() {
-                            return;
-                        }
-                        let (before, after) = snippet.split_at(insertion_index);
-                        (path_span, format!("{}{}{}", before, anon_lts, after))
-                    } else {
-                        (insertion_span, anon_lts)
-                    }
-                };
-                db.span_suggestion(
-                    replace_span,
-                    &format!("indicate the anonymous lifetime{}", if n >= 2 { "s" } else { "" }),
-                    suggestion,
-                    Applicability::MachineApplicable
+                add_elided_lifetime_in_path_suggestion(
+                    sess,
+                    db,
+                    n,
+                    path_span,
+                    incl_angl_brckt,
+                    insertion_span,
+                    anon_lts,
                 );
             }
             BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => {
diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs
index 8d5c1798e0f..e713cf8d805 100644
--- a/src/librustc/lint/context.rs
+++ b/src/librustc/lint/context.rs
@@ -757,12 +757,12 @@ impl<'a, 'tcx> LateContext<'a, 'tcx> {
     /// Check if a `DefId`'s path matches the given absolute type path usage.
     ///
     /// # Examples
-    /// ```rust,ignore (no `cx` or `def_id` available)
+    ///
+    /// ```rust,ignore (no context or def id available)
     /// if cx.match_def_path(def_id, &["core", "option", "Option"]) {
     ///     // The given `def_id` is that of an `Option` type
     /// }
     /// ```
-    // Uplifted from rust-lang/rust-clippy
     pub fn match_def_path(&self, def_id: DefId, path: &[&str]) -> bool {
         let names = self.get_def_path(def_id);
 
@@ -772,13 +772,13 @@ impl<'a, 'tcx> LateContext<'a, 'tcx> {
     /// Gets the absolute path of `def_id` as a vector of `&str`.
     ///
     /// # Examples
-    /// ```rust,ignore (no `cx` or `def_id` available)
+    ///
+    /// ```rust,ignore (no context or def id available)
     /// let def_path = cx.get_def_path(def_id);
     /// if let &["core", "option", "Option"] = &def_path[..] {
     ///     // The given `def_id` is that of an `Option` type
     /// }
     /// ```
-    // Uplifted from rust-lang/rust-clippy
     pub fn get_def_path(&self, def_id: DefId) -> Vec<LocalInternedString> {
         pub struct AbsolutePathPrinter<'a, 'tcx> {
             pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs
index 765992fd2b7..0d67865d717 100644
--- a/src/librustc_codegen_llvm/debuginfo/metadata.rs
+++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs
@@ -784,26 +784,30 @@ pub fn file_metadata(cx: &CodegenCx<'ll, '_>,
            file_name,
            defining_crate);
 
-    let directory = if defining_crate == LOCAL_CRATE {
-        &cx.sess().working_dir.0
+    let file_name = &file_name.to_string();
+    let file_name_symbol = Symbol::intern(file_name);
+    if defining_crate == LOCAL_CRATE {
+        let directory = &cx.sess().working_dir.0.to_string_lossy();
+        file_metadata_raw(cx, file_name, Some(file_name_symbol),
+                          directory, Some(Symbol::intern(directory)))
     } else {
         // If the path comes from an upstream crate we assume it has been made
         // independent of the compiler's working directory one way or another.
-        Path::new("")
-    };
-
-    file_metadata_raw(cx, &file_name.to_string(), &directory.to_string_lossy())
+        file_metadata_raw(cx, file_name, Some(file_name_symbol), "", None)
+    }
 }
 
 pub fn unknown_file_metadata(cx: &CodegenCx<'ll, '_>) -> &'ll DIFile {
-    file_metadata_raw(cx, "<unknown>", "")
+    file_metadata_raw(cx, "<unknown>", None, "", None)
 }
 
 fn file_metadata_raw(cx: &CodegenCx<'ll, '_>,
                      file_name: &str,
-                     directory: &str)
+                     file_name_symbol: Option<Symbol>,
+                     directory: &str,
+                     directory_symbol: Option<Symbol>)
                      -> &'ll DIFile {
-    let key = (Symbol::intern(file_name), Symbol::intern(directory));
+    let key = (file_name_symbol, directory_symbol);
 
     if let Some(file_metadata) = debug_context(cx).created_files.borrow().get(&key) {
         return *file_metadata;
diff --git a/src/librustc_codegen_llvm/debuginfo/mod.rs b/src/librustc_codegen_llvm/debuginfo/mod.rs
index 2154ac9b1d2..f3070a03b4e 100644
--- a/src/librustc_codegen_llvm/debuginfo/mod.rs
+++ b/src/librustc_codegen_llvm/debuginfo/mod.rs
@@ -63,7 +63,7 @@ pub struct CrateDebugContext<'a, 'tcx> {
     llcontext: &'a llvm::Context,
     llmod: &'a llvm::Module,
     builder: &'a mut DIBuilder<'a>,
-    created_files: RefCell<FxHashMap<(Symbol, Symbol), &'a DIFile>>,
+    created_files: RefCell<FxHashMap<(Option<Symbol>, Option<Symbol>), &'a DIFile>>,
     created_enum_disr_types: RefCell<FxHashMap<(DefId, layout::Primitive), &'a DIType>>,
 
     type_map: RefCell<TypeMap<'a, 'tcx>>,
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index 6a5accc8d7f..5b42b049b5b 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -1163,7 +1163,7 @@ pub fn report_ices_to_stderr_if_any<F: FnOnce() -> R, R>(f: F) -> Result<R, Erro
 /// This allows tools to enable rust logging without having to magically match rustc's
 /// log crate version
 pub fn init_rustc_env_logger() {
-    env_logger::init();
+    env_logger::init_from_env("RUSTC_LOG");
 }
 
 pub fn main() {
diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs
index 2465adf46cd..2d89f73a95f 100644
--- a/src/librustc_mir/transform/const_prop.rs
+++ b/src/librustc_mir/transform/const_prop.rs
@@ -1,18 +1,19 @@
 //! Propagates constants for early reporting of statically known
 //! assertion failures
 
-
 use rustc::hir::def::Def;
-use rustc::mir::{Constant, Location, Place, PlaceBase, Mir, Operand, Rvalue, Local};
-use rustc::mir::{NullOp, UnOp, StatementKind, Statement, LocalKind, Static, StaticKind};
-use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp, ProjectionElem};
+use rustc::mir::{
+    Constant, Location, Place, PlaceBase, Mir, Operand, Rvalue, Local,
+    NullOp, UnOp, StatementKind, Statement, LocalKind, Static, StaticKind,
+    TerminatorKind, Terminator,  ClearCrossCrate, SourceInfo, BinOp, ProjectionElem,
+    SourceScope, SourceScopeLocalData, LocalDecl, Promoted,
+};
 use rustc::mir::visit::{Visitor, PlaceContext, MutatingUseContext, NonMutatingUseContext};
 use rustc::mir::interpret::{InterpError, Scalar, GlobalId, EvalResult};
-use rustc::ty::{self, Instance, Ty, TyCtxt};
-use syntax::source_map::{Span, DUMMY_SP};
+use rustc::ty::{self, Instance, ParamEnv, Ty, TyCtxt};
+use syntax::source_map::DUMMY_SP;
 use rustc::ty::subst::InternalSubsts;
 use rustc_data_structures::indexed_vec::IndexVec;
-use rustc::ty::ParamEnv;
 use rustc::ty::layout::{
     LayoutOf, TyLayout, LayoutError,
     HasTyCtxt, TargetDataLayout, HasDataLayout,
@@ -62,21 +63,33 @@ impl MirPass for ConstProp {
         let mut optimization_finder = ConstPropagator::new(mir, tcx, source);
         optimization_finder.visit_mir(mir);
 
+        // put back the data we stole from `mir`
+        std::mem::replace(
+            &mut mir.source_scope_local_data,
+            optimization_finder.source_scope_local_data
+        );
+        std::mem::replace(
+            &mut mir.promoted,
+            optimization_finder.promoted
+        );
+
         trace!("ConstProp done for {:?}", source.def_id());
     }
 }
 
-type Const<'tcx> = (OpTy<'tcx>, Span);
+type Const<'tcx> = OpTy<'tcx>;
 
 /// Finds optimization opportunities on the MIR.
 struct ConstPropagator<'a, 'mir, 'tcx:'a+'mir> {
     ecx: InterpretCx<'a, 'mir, 'tcx, CompileTimeInterpreter<'a, 'mir, 'tcx>>,
-    mir: &'mir Mir<'tcx>,
     tcx: TyCtxt<'a, 'tcx, 'tcx>,
     source: MirSource<'tcx>,
     places: IndexVec<Local, Option<Const<'tcx>>>,
     can_const_prop: IndexVec<Local, bool>,
     param_env: ParamEnv<'tcx>,
+    source_scope_local_data: ClearCrossCrate<IndexVec<SourceScope, SourceScopeLocalData>>,
+    local_decls: IndexVec<Local, LocalDecl<'tcx>>,
+    promoted: IndexVec<Promoted, Mir<'tcx>>,
 }
 
 impl<'a, 'b, 'tcx> LayoutOf for ConstPropagator<'a, 'b, 'tcx> {
@@ -104,20 +117,33 @@ impl<'a, 'b, 'tcx> HasTyCtxt<'tcx> for ConstPropagator<'a, 'b, 'tcx> {
 
 impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
     fn new(
-        mir: &'mir Mir<'tcx>,
+        mir: &mut Mir<'tcx>,
         tcx: TyCtxt<'a, 'tcx, 'tcx>,
         source: MirSource<'tcx>,
     ) -> ConstPropagator<'a, 'mir, 'tcx> {
         let param_env = tcx.param_env(source.def_id());
         let ecx = mk_eval_cx(tcx, tcx.def_span(source.def_id()), param_env);
+        let can_const_prop = CanConstProp::check(mir);
+        let source_scope_local_data = std::mem::replace(
+            &mut mir.source_scope_local_data,
+            ClearCrossCrate::Clear
+        );
+        let promoted = std::mem::replace(
+            &mut mir.promoted,
+            IndexVec::new()
+        );
+
         ConstPropagator {
             ecx,
-            mir,
             tcx,
             source,
             param_env,
-            can_const_prop: CanConstProp::check(mir),
+            can_const_prop,
             places: IndexVec::from_elem(None, &mir.local_decls),
+            source_scope_local_data,
+            //FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_mir()` needs it
+            local_decls: mir.local_decls.clone(),
+            promoted,
         }
     }
 
@@ -130,7 +156,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
         F: FnOnce(&mut Self) -> EvalResult<'tcx, T>,
     {
         self.ecx.tcx.span = source_info.span;
-        let lint_root = match self.mir.source_scope_local_data {
+        let lint_root = match self.source_scope_local_data {
             ClearCrossCrate::Set(ref ivs) => {
                 //FIXME(#51314): remove this check
                 if source_info.scope.index() >= ivs.len() {
@@ -252,12 +278,11 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
     fn eval_constant(
         &mut self,
         c: &Constant<'tcx>,
-        source_info: SourceInfo,
     ) -> Option<Const<'tcx>> {
-        self.ecx.tcx.span = source_info.span;
+        self.ecx.tcx.span = c.span;
         match self.ecx.eval_const_to_op(*c.literal, None) {
             Ok(op) => {
-                Some((op, c.span))
+                Some(op)
             },
             Err(error) => {
                 let err = error_to_const_error(&self.ecx, error);
@@ -273,11 +298,11 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
             Place::Projection(ref proj) => match proj.elem {
                 ProjectionElem::Field(field, _) => {
                     trace!("field proj on {:?}", proj.base);
-                    let (base, span) = self.eval_place(&proj.base, source_info)?;
+                    let base = self.eval_place(&proj.base, source_info)?;
                     let res = self.use_ecx(source_info, |this| {
                         this.ecx.operand_field(base, field.index() as u64)
                     })?;
-                    Some((res, span))
+                    Some(res)
                 },
                 // We could get more projections by using e.g., `operand_projection`,
                 // but we do not even have the stack frame set up properly so
@@ -301,11 +326,11 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
                 // cannot use `const_eval` here, because that would require having the MIR
                 // for the current function available, but we're producing said MIR right now
                 let res = self.use_ecx(source_info, |this| {
-                    let mir = &this.mir.promoted[promoted];
+                    let mir = &this.promoted[promoted];
                     eval_promoted(this.tcx, cid, mir, this.param_env)
                 })?;
                 trace!("evaluated promoted {:?} to {:?}", promoted, res);
-                Some((res.into(), source_info.span))
+                Some(res.into())
             },
             _ => None,
         }
@@ -313,7 +338,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
 
     fn eval_operand(&mut self, op: &Operand<'tcx>, source_info: SourceInfo) -> Option<Const<'tcx>> {
         match *op {
-            Operand::Constant(ref c) => self.eval_constant(c, source_info),
+            Operand::Constant(ref c) => self.eval_constant(c),
             | Operand::Move(ref place)
             | Operand::Copy(ref place) => self.eval_place(place, source_info),
         }
@@ -337,18 +362,18 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
             Rvalue::Discriminant(..) => None,
 
             Rvalue::Cast(kind, ref operand, _) => {
-                let (op, span) = self.eval_operand(operand, source_info)?;
+                let op = self.eval_operand(operand, source_info)?;
                 self.use_ecx(source_info, |this| {
                     let dest = this.ecx.allocate(place_layout, MemoryKind::Stack);
                     this.ecx.cast(op, kind, dest.into())?;
-                    Ok((dest.into(), span))
+                    Ok(dest.into())
                 })
             }
 
             // FIXME(oli-obk): evaluate static/constant slice lengths
             Rvalue::Len(_) => None,
             Rvalue::NullaryOp(NullOp::SizeOf, ty) => {
-                type_size_of(self.tcx, self.param_env, ty).and_then(|n| Some((
+                type_size_of(self.tcx, self.param_env, ty).and_then(|n| Some(
                     ImmTy {
                         imm: Immediate::Scalar(
                             Scalar::Bits {
@@ -357,9 +382,8 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
                             }.into()
                         ),
                         layout: self.tcx.layout_of(self.param_env.and(self.tcx.types.usize)).ok()?,
-                    }.into(),
-                    span,
-                )))
+                    }.into()
+                ))
             }
             Rvalue::UnaryOp(op, ref arg) => {
                 let def_id = if self.tcx.is_closure(self.source.def_id()) {
@@ -373,7 +397,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
                     return None;
                 }
 
-                let (arg, _) = self.eval_operand(arg, source_info)?;
+                let arg = self.eval_operand(arg, source_info)?;
                 let val = self.use_ecx(source_info, |this| {
                     let prim = this.ecx.read_immediate(arg)?;
                     match op {
@@ -395,7 +419,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
                     imm: Immediate::Scalar(val.into()),
                     layout: place_layout,
                 };
-                Some((res.into(), span))
+                Some(res.into())
             }
             Rvalue::CheckedBinaryOp(op, ref left, ref right) |
             Rvalue::BinaryOp(op, ref left, ref right) => {
@@ -413,20 +437,20 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
                 }
 
                 let r = self.use_ecx(source_info, |this| {
-                    this.ecx.read_immediate(right.0)
+                    this.ecx.read_immediate(right)
                 })?;
                 if op == BinOp::Shr || op == BinOp::Shl {
-                    let left_ty = left.ty(self.mir, self.tcx);
+                    let left_ty = left.ty(&self.local_decls, self.tcx);
                     let left_bits = self
                         .tcx
                         .layout_of(self.param_env.and(left_ty))
                         .unwrap()
                         .size
                         .bits();
-                    let right_size = right.0.layout.size;
+                    let right_size = right.layout.size;
                     let r_bits = r.to_scalar().and_then(|r| r.to_bits(right_size));
                     if r_bits.ok().map_or(false, |b| b >= left_bits as u128) {
-                        let source_scope_local_data = match self.mir.source_scope_local_data {
+                        let source_scope_local_data = match self.source_scope_local_data {
                             ClearCrossCrate::Set(ref data) => data,
                             ClearCrossCrate::Clear => return None,
                         };
@@ -446,7 +470,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
                 }
                 let left = self.eval_operand(left, source_info)?;
                 let l = self.use_ecx(source_info, |this| {
-                    this.ecx.read_immediate(left.0)
+                    this.ecx.read_immediate(left)
                 })?;
                 trace!("const evaluating {:?} for {:?} and {:?}", op, left, right);
                 let (val, overflow) = self.use_ecx(source_info, |this| {
@@ -469,7 +493,7 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
                     imm: val,
                     layout: place_layout,
                 };
-                Some((res.into(), span))
+                Some(res.into())
             },
         }
     }
@@ -544,8 +568,7 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> {
     ) {
         trace!("visit_constant: {:?}", constant);
         self.super_constant(constant, location);
-        let source_info = *self.mir.source_info(location);
-        self.eval_constant(constant, source_info);
+        self.eval_constant(constant);
     }
 
     fn visit_statement(
@@ -556,7 +579,7 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> {
         trace!("visit_statement: {:?}", statement);
         if let StatementKind::Assign(ref place, ref rval) = statement.kind {
             let place_ty: Ty<'tcx> = place
-                .ty(&self.mir.local_decls, self.tcx)
+                .ty(&self.local_decls, self.tcx)
                 .ty;
             if let Ok(place_layout) = self.tcx.layout_of(self.param_env.and(place_ty)) {
                 if let Some(value) = self.const_prop(rval, place_layout, statement.source_info) {
@@ -574,18 +597,18 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> {
         self.super_statement(statement, location);
     }
 
-    fn visit_terminator_kind(
+    fn visit_terminator(
         &mut self,
-        kind: &TerminatorKind<'tcx>,
+        terminator: &Terminator<'tcx>,
         location: Location,
     ) {
-        self.super_terminator_kind(kind, location);
-        let source_info = *self.mir.source_info(location);
-        if let TerminatorKind::Assert { expected, msg, cond, .. } = kind {
-            if let Some(value) = self.eval_operand(cond, source_info) {
+        self.super_terminator(terminator, location);
+        let source_info = terminator.source_info;;
+        if let TerminatorKind::Assert { expected, msg, cond, .. } = &terminator.kind {
+            if let Some(value) = self.eval_operand(&cond, source_info) {
                 trace!("assertion on {:?} should be {:?}", value, expected);
                 let expected = ScalarMaybeUndef::from(Scalar::from_bool(*expected));
-                if expected != self.ecx.read_scalar(value.0).unwrap() {
+                if expected != self.ecx.read_scalar(value).unwrap() {
                     // poison all places this operand references so that further code
                     // doesn't use the invalid value
                     match cond {
@@ -600,12 +623,7 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> {
                         },
                         Operand::Constant(_) => {}
                     }
-                    let span = self.mir[location.block]
-                        .terminator
-                        .as_ref()
-                        .unwrap()
-                        .source_info
-                        .span;
+                    let span = terminator.source_info.span;
                     let hir_id = self
                         .tcx
                         .hir()
@@ -621,7 +639,7 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> {
                             let len = self
                                 .eval_operand(len, source_info)
                                 .expect("len must be const");
-                            let len = match self.ecx.read_scalar(len.0) {
+                            let len = match self.ecx.read_scalar(len) {
                                 Ok(ScalarMaybeUndef::Scalar(Scalar::Bits {
                                     bits, ..
                                 })) => bits,
@@ -630,7 +648,7 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> {
                             let index = self
                                 .eval_operand(index, source_info)
                                 .expect("index must be const");
-                            let index = match self.ecx.read_scalar(index.0) {
+                            let index = match self.ecx.read_scalar(index) {
                                 Ok(ScalarMaybeUndef::Scalar(Scalar::Bits {
                                     bits, ..
                                 })) => bits,
diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs
index 9dd8a7050fd..1f5a6d79141 100644
--- a/src/librustc_passes/ast_validation.rs
+++ b/src/librustc_passes/ast_validation.rs
@@ -20,7 +20,7 @@ use syntax::ptr::P;
 use syntax::visit::{self, Visitor};
 use syntax::{span_err, struct_span_err, walk_list};
 use syntax_ext::proc_macro_decls::is_proc_macro_attr;
-use syntax_pos::Span;
+use syntax_pos::{Span, MultiSpan};
 use errors::Applicability;
 use log::debug;
 
@@ -679,6 +679,14 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                                                 "unions cannot have zero fields");
                 }
             }
+            ItemKind::Existential(ref bounds, _) => {
+                if !bounds.iter()
+                          .any(|b| if let GenericBound::Trait(..) = *b { true } else { false }) {
+                    let msp = MultiSpan::from_spans(bounds.iter()
+                        .map(|bound| bound.span()).collect());
+                    self.err_handler().span_err(msp, "at least one trait must be specified");
+                }
+            }
             _ => {}
         }
 
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index 8cdfbf5f55c..6cc7818d712 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -1,8 +1,8 @@
 use crate::check::{FnCtxt, Expectation, Diverges, Needs};
 use crate::check::coercion::CoerceMany;
 use crate::util::nodemap::FxHashMap;
-use errors::Applicability;
-use rustc::hir::{self, PatKind};
+use errors::{Applicability, DiagnosticBuilder};
+use rustc::hir::{self, PatKind, Pat};
 use rustc::hir::def::{Def, CtorKind};
 use rustc::hir::pat_util::EnumerateAndAdjustIterator;
 use rustc::infer;
@@ -377,15 +377,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                             // Look for a case like `fn foo(&foo: u32)` and suggest
                             // `fn foo(foo: &u32)`
                             if let Some(mut err) = err {
-                                if let PatKind::Binding(..) = inner.node {
-                                    if let Ok(snippet) = tcx.sess.source_map()
-                                                                    .span_to_snippet(pat.span)
-                                    {
-                                        err.help(&format!("did you mean `{}: &{}`?",
-                                                            &snippet[1..],
-                                                            expected));
-                                    }
-                                }
+                                self.borrow_pat_suggestion(&mut err, &pat, &inner, &expected);
                                 err.emit();
                             }
                             (rptr_ty, inner_ty)
@@ -517,6 +509,49 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // subtyping.
     }
 
+    fn borrow_pat_suggestion(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        pat: &Pat,
+        inner: &Pat,
+        expected: Ty<'tcx>,
+    ) {
+        let tcx = self.tcx;
+        if let PatKind::Binding(..) = inner.node {
+            let parent_id = tcx.hir().get_parent_node_by_hir_id(pat.hir_id);
+            let parent = tcx.hir().get_by_hir_id(parent_id);
+            debug!("inner {:?} pat {:?} parent {:?}", inner, pat, parent);
+            match parent {
+                hir::Node::Item(hir::Item { node: hir::ItemKind::Fn(..), .. }) |
+                hir::Node::ForeignItem(hir::ForeignItem {
+                    node: hir::ForeignItemKind::Fn(..), ..
+                }) |
+                hir::Node::TraitItem(hir::TraitItem { node: hir::TraitItemKind::Method(..), .. }) |
+                hir::Node::ImplItem(hir::ImplItem { node: hir::ImplItemKind::Method(..), .. }) => {
+                    // this pat is likely an argument
+                    if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(inner.span) {
+                        // FIXME: turn into structured suggestion, will need a span that also
+                        // includes the the arg's type.
+                        err.help(&format!("did you mean `{}: &{}`?", snippet, expected));
+                    }
+                }
+                hir::Node::Expr(hir::Expr { node: hir::ExprKind::Match(..), .. }) |
+                hir::Node::Pat(_) => {
+                    // rely on match ergonomics or it might be nested `&&pat`
+                    if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(inner.span) {
+                        err.span_suggestion(
+                            pat.span,
+                            "you can probably remove the explicit borrow",
+                            snippet,
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
+                _ => {} // don't provide suggestions in other cases #55175
+            }
+        }
+    }
+
     pub fn check_dereferencable(&self, span: Span, expected: Ty<'tcx>, inner: &hir::Pat) -> bool {
         if let PatKind::Binding(..) = inner.node {
             if let Some(mt) = self.shallow_resolve(expected).builtin_deref(true) {
diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs
index 849298698af..5c0a4da1cd7 100644
--- a/src/librustdoc/test.rs
+++ b/src/librustdoc/test.rs
@@ -166,9 +166,18 @@ fn run_test(test: &str, cratename: &str, filename: &FileName, line: usize,
             compile_fail: bool, mut error_codes: Vec<String>, opts: &TestOptions,
             maybe_sysroot: Option<PathBuf>, linker: Option<PathBuf>, edition: Edition,
             persist_doctests: Option<PathBuf>) {
-    // The test harness wants its own `main` and top-level functions, so
-    // never wrap the test in `fn main() { ... }`.
-    let (test, line_offset) = make_test(test, Some(cratename), as_test_harness, opts);
+    let (test, line_offset) = match panic::catch_unwind(|| {
+        make_test(test, Some(cratename), as_test_harness, opts)
+    }) {
+        Ok((test, line_offset)) => (test, line_offset),
+        Err(cause) if cause.is::<errors::FatalErrorMarker>() => {
+            // If the parser used by `make_test` panicked due to a fatal error, pass the test code
+            // through unchanged. The error will be reported during compilation.
+            (test.to_owned(), 0)
+        },
+        Err(cause) => panic::resume_unwind(cause),
+    };
+
     // FIXME(#44940): if doctests ever support path remapping, then this filename
     // needs to be the result of `SourceMap::span_to_unmapped_path`.
     let path = match filename {
@@ -337,7 +346,13 @@ fn run_test(test: &str, cratename: &str, filename: &FileName, line: usize,
     }
 }
 
-/// Makes the test file. Also returns the number of lines before the code begins
+/// Transforms a test into code that can be compiled into a Rust binary, and returns the number of
+/// lines before the test code begins.
+///
+/// # Panics
+///
+/// This function uses the compiler's parser internally. The parser will panic if it encounters a
+/// fatal error while parsing the test.
 pub fn make_test(s: &str,
                  cratename: Option<&str>,
                  dont_insert_main: bool,
diff --git a/src/libstd/sys/redox/ext/net.rs b/src/libstd/sys/redox/ext/net.rs
index 096d0681959..b3ef5f3064c 100644
--- a/src/libstd/sys/redox/ext/net.rs
+++ b/src/libstd/sys/redox/ext/net.rs
@@ -1,4 +1,4 @@
-#![stable(feature = "unix_socket_redox", since = "1.29")]
+#![stable(feature = "unix_socket_redox", since = "1.29.0")]
 
 //! Unix-specific networking functionality
 
@@ -27,7 +27,7 @@ use crate::sys::{cvt, fd::FileDesc, syscall};
 /// let addr = socket.local_addr().expect("Couldn't get local address");
 /// ```
 #[derive(Clone)]
-#[stable(feature = "unix_socket_redox", since = "1.29")]
+#[stable(feature = "unix_socket_redox", since = "1.29.0")]
 pub struct SocketAddr(());
 
 impl SocketAddr {
@@ -55,7 +55,7 @@ impl SocketAddr {
     /// let addr = socket.local_addr().expect("Couldn't get local address");
     /// assert_eq!(addr.as_pathname(), None);
     /// ```
-    #[stable(feature = "unix_socket_redox", since = "1.29")]
+    #[stable(feature = "unix_socket_redox", since = "1.29.0")]
     pub fn as_pathname(&self) -> Option<&Path> {
         None
     }
@@ -83,12 +83,12 @@ impl SocketAddr {
     /// let addr = socket.local_addr().expect("Couldn't get local address");
     /// assert_eq!(addr.is_unnamed(), true);
     /// ```
-    #[stable(feature = "unix_socket_redox", since = "1.29")]
+    #[stable(feature = "unix_socket_redox", since = "1.29.0")]
     pub fn is_unnamed(&self) -> bool {
         false
     }
 }
-#[stable(feature = "unix_socket_redox", since = "1.29")]
+#[stable(feature = "unix_socket_redox", since = "1.29.0")]
 impl fmt::Debug for SocketAddr {
     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(fmt, "SocketAddr")
@@ -109,10 +109,10 @@ impl fmt::Debug for SocketAddr {
 /// stream.read_to_string(&mut response).unwrap();
 /// println!("{}", response);
 /// ```
-#[stable(feature = "unix_socket_redox", since = "1.29")]
+#[stable(feature = "unix_socket_redox", since = "1.29.0")]
 pub struct UnixStream(FileDesc);
 
-#[stable(feature = "unix_socket_redox", since = "1.29")]
+#[stable(feature = "unix_socket_redox", since = "1.29.0")]
 impl fmt::Debug for UnixStream {
     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
         let mut builder = fmt.debug_struct("UnixStream");
@@ -143,7 +143,7 @@ impl UnixStream {
     ///     }
     /// };
     /// ```
-    #[stable(feature = "unix_socket_redox", since = "1.29")]
+    #[stable(feature = "unix_socket_redox", since = "1.29.0")]
     pub fn connect<P: AsRef<Path>>(path: P) -> io::Result<UnixStream> {
         if let Some(s) = path.as_ref().to_str() {
             cvt(syscall::open(format!("chan:{}", s), syscall::O_CLOEXEC))
@@ -174,7 +174,7 @@ impl UnixStream {
     ///     }
     /// };
     /// ```
-    #[stable(feature = "unix_socket_redox", since = "1.29")]
+    #[stable(feature = "unix_socket_redox", since = "1.29.0")]
     pub fn pair() -> io::Result<(UnixStream, UnixStream)> {
         let server = cvt(syscall::open("chan:", syscall::O_CREAT | syscall::O_CLOEXEC))
             .map(FileDesc::new)?;
@@ -198,7 +198,7 @@ impl UnixStream {
     /// let socket = UnixStream::connect("/tmp/sock").unwrap();
     /// let sock_copy = socket.try_clone().expect("Couldn't clone socket");
     /// ```
-    #[stable(feature = "unix_socket_redox", since = "1.29")]
+    #[stable(feature = "unix_socket_redox", since = "1.29.0")]
     pub fn try_clone(&self) -> io::Result<UnixStream> {
         self.0.duplicate().map(UnixStream)
     }
@@ -213,7 +213,7 @@ impl UnixStream {
     /// let socket = UnixStream::connect("/tmp/sock").unwrap();
     /// let addr = socket.local_addr().expect("Couldn't get local address");
     /// ```
-    #[stable(feature = "unix_socket_redox", since = "1.29")]
+    #[stable(feature = "unix_socket_redox", since = "1.29.0")]
     pub fn local_addr(&self) -> io::Result<SocketAddr> {
         Err(Error::new(ErrorKind::Other, "UnixStream::local_addr unimplemented on redox"))
     }
@@ -228,7 +228,7 @@ impl UnixStream {
     /// let socket = UnixStream::connect("/tmp/sock").unwrap();
     /// let addr = socket.peer_addr().expect("Couldn't get peer address");
     /// ```
-    #[stable(feature = "unix_socket_redox", since = "1.29")]
+    #[stable(feature = "unix_socket_redox", since = "1.29.0")]
     pub fn peer_addr(&self) -> io::Result<SocketAddr> {
         Err(Error::new(ErrorKind::Other, "UnixStream::peer_addr unimplemented on redox"))
     }
@@ -267,7 +267,7 @@ impl UnixStream {
     /// let err = result.unwrap_err();
     /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput)
     /// ```
-    #[stable(feature = "unix_socket_redox", since = "1.29")]
+    #[stable(feature = "unix_socket_redox", since = "1.29.0")]
     pub fn set_read_timeout(&self, _timeout: Option<Duration>) -> io::Result<()> {
         Err(Error::new(ErrorKind::Other, "UnixStream::set_read_timeout unimplemented on redox"))
     }
@@ -306,7 +306,7 @@ impl UnixStream {
     /// let err = result.unwrap_err();
     /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput)
     /// ```
-    #[stable(feature = "unix_socket_redox", since = "1.29")]
+    #[stable(feature = "unix_socket_redox", since = "1.29.0")]
     pub fn set_write_timeout(&self, _timeout: Option<Duration>) -> io::Result<()> {
         Err(Error::new(ErrorKind::Other, "UnixStream::set_write_timeout unimplemented on redox"))
     }
@@ -323,7 +323,7 @@ impl UnixStream {
     /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout");
     /// assert_eq!(socket.read_timeout().unwrap(), Some(Duration::new(1, 0)));
     /// ```
-    #[stable(feature = "unix_socket_redox", since = "1.29")]
+    #[stable(feature = "unix_socket_redox", since = "1.29.0")]
     pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
         Err(Error::new(ErrorKind::Other, "UnixStream::read_timeout unimplemented on redox"))
     }
@@ -340,7 +340,7 @@ impl UnixStream {
     /// socket.set_write_timeout(Some(Duration::new(1, 0))).expect("Couldn't set write timeout");
     /// assert_eq!(socket.write_timeout().unwrap(), Some(Duration::new(1, 0)));
     /// ```
-    #[stable(feature = "unix_socket_redox", since = "1.29")]
+    #[stable(feature = "unix_socket_redox", since = "1.29.0")]
     pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
         Err(Error::new(ErrorKind::Other, "UnixStream::write_timeout unimplemented on redox"))
     }
@@ -355,7 +355,7 @@ impl UnixStream {
     /// let socket = UnixStream::connect("/tmp/sock").unwrap();
     /// socket.set_nonblocking(true).expect("Couldn't set nonblocking");
     /// ```
-    #[stable(feature = "unix_socket_redox", since = "1.29")]
+    #[stable(feature = "unix_socket_redox", since = "1.29.0")]
     pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
         self.0.set_nonblocking(nonblocking)
     }
@@ -375,7 +375,7 @@ impl UnixStream {
     ///
     /// # Platform specific
     /// On Redox this always returns `None`.
-    #[stable(feature = "unix_socket_redox", since = "1.29")]
+    #[stable(feature = "unix_socket_redox", since = "1.29.0")]
     pub fn take_error(&self) -> io::Result<Option<io::Error>> {
         Ok(None)
     }
@@ -397,13 +397,13 @@ impl UnixStream {
     /// let socket = UnixStream::connect("/tmp/sock").unwrap();
     /// socket.shutdown(Shutdown::Both).expect("shutdown function failed");
     /// ```
-    #[stable(feature = "unix_socket_redox", since = "1.29")]
+    #[stable(feature = "unix_socket_redox", since = "1.29.0")]
     pub fn shutdown(&self, _how: Shutdown) -> io::Result<()> {
         Err(Error::new(ErrorKind::Other, "UnixStream::shutdown unimplemented on redox"))
     }
 }
 
-#[stable(feature = "unix_socket_redox", since = "1.29")]
+#[stable(feature = "unix_socket_redox", since = "1.29.0")]
 impl io::Read for UnixStream {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
         io::Read::read(&mut &*self, buf)
@@ -415,7 +415,7 @@ impl io::Read for UnixStream {
     }
 }
 
-#[stable(feature = "unix_socket_redox", since = "1.29")]
+#[stable(feature = "unix_socket_redox", since = "1.29.0")]
 impl<'a> io::Read for &'a UnixStream {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
         self.0.read(buf)
@@ -427,7 +427,7 @@ impl<'a> io::Read for &'a UnixStream {
     }
 }
 
-#[stable(feature = "unix_socket_redox", since = "1.29")]
+#[stable(feature = "unix_socket_redox", since = "1.29.0")]
 impl io::Write for UnixStream {
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
         io::Write::write(&mut &*self, buf)
@@ -438,7 +438,7 @@ impl io::Write for UnixStream {
     }
 }
 
-#[stable(feature = "unix_socket_redox", since = "1.29")]
+#[stable(feature = "unix_socket_redox", since = "1.29.0")]
 impl<'a> io::Write for &'a UnixStream {
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
         self.0.write(buf)
@@ -449,21 +449,21 @@ impl<'a> io::Write for &'a UnixStream {
     }
 }
 
-#[stable(feature = "unix_socket_redox", since = "1.29")]
+#[stable(feature = "unix_socket_redox", since = "1.29.0")]
 impl AsRawFd for UnixStream {
     fn as_raw_fd(&self) -> RawFd {
         self.0.raw()
     }
 }
 
-#[stable(feature = "unix_socket_redox", since = "1.29")]
+#[stable(feature = "unix_socket_redox", since = "1.29.0")]
 impl FromRawFd for UnixStream {
     unsafe fn from_raw_fd(fd: RawFd) -> UnixStream {
         UnixStream(FileDesc::new(fd))
     }
 }
 
-#[stable(feature = "unix_socket_redox", since = "1.29")]
+#[stable(feature = "unix_socket_redox", since = "1.29.0")]
 impl IntoRawFd for UnixStream {
     fn into_raw_fd(self) -> RawFd {
         self.0.into_raw()
@@ -498,10 +498,10 @@ impl IntoRawFd for UnixStream {
 ///     }
 /// }
 /// ```
-#[stable(feature = "unix_socket_redox", since = "1.29")]
+#[stable(feature = "unix_socket_redox", since = "1.29.0")]
 pub struct UnixListener(FileDesc);
 
-#[stable(feature = "unix_socket_redox", since = "1.29")]
+#[stable(feature = "unix_socket_redox", since = "1.29.0")]
 impl fmt::Debug for UnixListener {
     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
         let mut builder = fmt.debug_struct("UnixListener");
@@ -529,7 +529,7 @@ impl UnixListener {
     ///     }
     /// };
     /// ```
-    #[stable(feature = "unix_socket_redox", since = "1.29")]
+    #[stable(feature = "unix_socket_redox", since = "1.29.0")]
     pub fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixListener> {
         if let Some(s) = path.as_ref().to_str() {
             cvt(syscall::open(format!("chan:{}", s), syscall::O_CREAT | syscall::O_CLOEXEC))
@@ -563,7 +563,7 @@ impl UnixListener {
     ///     Err(e) => println!("accept function failed: {:?}", e),
     /// }
     /// ```
-    #[stable(feature = "unix_socket_redox", since = "1.29")]
+    #[stable(feature = "unix_socket_redox", since = "1.29.0")]
     pub fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> {
         self.0.duplicate_path(b"listen").map(|fd| (UnixStream(fd), SocketAddr(())))
     }
@@ -583,7 +583,7 @@ impl UnixListener {
     ///
     /// let listener_copy = listener.try_clone().expect("try_clone failed");
     /// ```
-    #[stable(feature = "unix_socket_redox", since = "1.29")]
+    #[stable(feature = "unix_socket_redox", since = "1.29.0")]
     pub fn try_clone(&self) -> io::Result<UnixListener> {
         self.0.duplicate().map(UnixListener)
     }
@@ -599,7 +599,7 @@ impl UnixListener {
     ///
     /// let addr = listener.local_addr().expect("Couldn't get local address");
     /// ```
-    #[stable(feature = "unix_socket_redox", since = "1.29")]
+    #[stable(feature = "unix_socket_redox", since = "1.29.0")]
     pub fn local_addr(&self) -> io::Result<SocketAddr> {
         Err(Error::new(ErrorKind::Other, "UnixListener::local_addr unimplemented on redox"))
     }
@@ -615,7 +615,7 @@ impl UnixListener {
     ///
     /// listener.set_nonblocking(true).expect("Couldn't set non blocking");
     /// ```
-    #[stable(feature = "unix_socket_redox", since = "1.29")]
+    #[stable(feature = "unix_socket_redox", since = "1.29.0")]
     pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
         self.0.set_nonblocking(nonblocking)
     }
@@ -636,7 +636,7 @@ impl UnixListener {
     ///
     /// # Platform specific
     /// On Redox this always returns `None`.
-    #[stable(feature = "unix_socket_redox", since = "1.29")]
+    #[stable(feature = "unix_socket_redox", since = "1.29.0")]
     pub fn take_error(&self) -> io::Result<Option<io::Error>> {
         Ok(None)
     }
@@ -672,34 +672,34 @@ impl UnixListener {
     ///     }
     /// }
     /// ```
-    #[stable(feature = "unix_socket_redox", since = "1.29")]
+    #[stable(feature = "unix_socket_redox", since = "1.29.0")]
     pub fn incoming<'a>(&'a self) -> Incoming<'a> {
         Incoming { listener: self }
     }
 }
 
-#[stable(feature = "unix_socket_redox", since = "1.29")]
+#[stable(feature = "unix_socket_redox", since = "1.29.0")]
 impl AsRawFd for UnixListener {
     fn as_raw_fd(&self) -> RawFd {
         self.0.raw()
     }
 }
 
-#[stable(feature = "unix_socket_redox", since = "1.29")]
+#[stable(feature = "unix_socket_redox", since = "1.29.0")]
 impl FromRawFd for UnixListener {
     unsafe fn from_raw_fd(fd: RawFd) -> UnixListener {
         UnixListener(FileDesc::new(fd))
     }
 }
 
-#[stable(feature = "unix_socket_redox", since = "1.29")]
+#[stable(feature = "unix_socket_redox", since = "1.29.0")]
 impl IntoRawFd for UnixListener {
     fn into_raw_fd(self) -> RawFd {
         self.0.into_raw()
     }
 }
 
-#[stable(feature = "unix_socket_redox", since = "1.29")]
+#[stable(feature = "unix_socket_redox", since = "1.29.0")]
 impl<'a> IntoIterator for &'a UnixListener {
     type Item = io::Result<UnixStream>;
     type IntoIter = Incoming<'a>;
@@ -740,12 +740,12 @@ impl<'a> IntoIterator for &'a UnixListener {
 /// }
 /// ```
 #[derive(Debug)]
-#[stable(feature = "unix_socket_redox", since = "1.29")]
+#[stable(feature = "unix_socket_redox", since = "1.29.0")]
 pub struct Incoming<'a> {
     listener: &'a UnixListener,
 }
 
-#[stable(feature = "unix_socket_redox", since = "1.29")]
+#[stable(feature = "unix_socket_redox", since = "1.29.0")]
 impl<'a> Iterator for Incoming<'a> {
     type Item = io::Result<UnixStream>;
 
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 012fcbdd8c8..2a1f3c48014 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -109,15 +109,14 @@ macro_rules! declare_features {
 // stable (active).
 //
 // Note that the features should be grouped into internal/user-facing
-// and then sorted by version inside those groups.
-// FIXME(60361): Enforce ^-- with tidy.
+// and then sorted by version inside those groups. This is inforced with tidy.
 //
 // N.B., `tools/tidy/src/features.rs` parses this information directly out of the
 // source, so take care when modifying it.
 
 declare_features! (
     // -------------------------------------------------------------------------
-    // Internal feature gates.
+    // feature-group-start: internal feature gates
     // -------------------------------------------------------------------------
 
     // no tracking issue START
@@ -211,12 +210,12 @@ declare_features! (
 
     // no tracking issue END
 
-    // Allows using the `may_dangle` attribute (RFC 1327).
-    (active, dropck_eyepatch, "1.10.0", Some(34761), None),
-
     // Allows using `#[structural_match]` which indicates that a type is structurally matchable.
     (active, structural_match, "1.8.0", Some(31434), None),
 
+    // Allows using the `may_dangle` attribute (RFC 1327).
+    (active, dropck_eyepatch, "1.10.0", Some(34761), None),
+
     // Allows using the `#![panic_runtime]` attribute.
     (active, panic_runtime, "1.10.0", Some(32837), None),
 
@@ -252,7 +251,11 @@ declare_features! (
     (active, test_2018_feature, "1.31.0", Some(0), Some(Edition::Edition2018)),
 
     // -------------------------------------------------------------------------
-    // Actual feature gates (target features).
+    // feature-group-end: internal feature gates
+    // -------------------------------------------------------------------------
+
+    // -------------------------------------------------------------------------
+    // feature-group-start: actual feature gates (target features)
     // -------------------------------------------------------------------------
 
     // FIXME: Document these and merge with the list below.
@@ -275,7 +278,11 @@ declare_features! (
     (active, f16c_target_feature, "1.36.0", Some(44839), None),
 
     // -------------------------------------------------------------------------
-    // Actual feature gates.
+    // feature-group-end: actual feature gates (target features)
+    // -------------------------------------------------------------------------
+
+    // -------------------------------------------------------------------------
+    // feature-group-start: actual feature gates
     // -------------------------------------------------------------------------
 
     // Allows using `asm!` macro with which inline assembly can be embedded.
@@ -340,9 +347,6 @@ declare_features! (
     // Permits specifying whether a function should permit unwinding or abort on unwind.
     (active, unwind_attributes, "1.4.0", Some(58760), None),
 
-    // Allows using `#[naked]` on functions.
-    (active, naked_functions, "1.9.0", Some(32408), None),
-
     // Allows `#[no_debug]`.
     (active, no_debug, "1.5.0", Some(29721), None),
 
@@ -358,6 +362,9 @@ declare_features! (
     // Allows specialization of implementations (RFC 1210).
     (active, specialization, "1.7.0", Some(31844), None),
 
+    // Allows using `#[naked]` on functions.
+    (active, naked_functions, "1.9.0", Some(32408), None),
+
     // Allows `cfg(target_has_atomic = "...")`.
     (active, cfg_target_has_atomic, "1.9.0", Some(32976), None),
 
@@ -545,6 +552,10 @@ declare_features! (
 
     // Allows using C-variadics.
     (active, c_variadic, "1.34.0", Some(44930), None),
+
+    // -------------------------------------------------------------------------
+    // feature-group-end: actual feature gates
+    // -------------------------------------------------------------------------
 );
 
 // Some features are known to be incomplete and using them is likely to have
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index f70acb3e7da..c5173aa5569 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -8725,9 +8725,9 @@ impl<'a> Parser<'a> {
                 // Check if this is a ident pattern, if so, we can optimize and avoid adding a
                 // `let <pat> = __argN;` statement, instead just adding a `let <pat> = <pat>;`
                 // statement.
-                let (ident, is_simple_pattern) = match input.pat.node {
-                    PatKind::Ident(_, ident, _) => (ident, true),
-                    _ => (ident, false),
+                let (binding_mode, ident, is_simple_pattern) = match input.pat.node {
+                    PatKind::Ident(binding_mode, ident, _) => (binding_mode, ident, true),
+                    _ => (BindingMode::ByValue(Mutability::Immutable), ident, false),
                 };
 
                 // Construct an argument representing `__argN: <ty>` to replace the argument of the
@@ -8755,9 +8755,7 @@ impl<'a> Parser<'a> {
                 let move_local = Local {
                     pat: P(Pat {
                         id,
-                        node: PatKind::Ident(
-                            BindingMode::ByValue(Mutability::Immutable), ident, None,
-                        ),
+                        node: PatKind::Ident(binding_mode, ident, None),
                         span,
                     }),
                     // We explicitly do not specify the type for this statement. When the user's
diff --git a/src/test/run-make-fulldeps/libs-through-symlinks/Makefile b/src/test/run-make-fulldeps/libs-through-symlinks/Makefile
index 7c5ea7e402e..8be2e234fea 100644
--- a/src/test/run-make-fulldeps/libs-through-symlinks/Makefile
+++ b/src/test/run-make-fulldeps/libs-through-symlinks/Makefile
@@ -8,4 +8,4 @@ all:
 	mkdir -p $(TMPDIR)/outdir
 	$(RUSTC) foo.rs -o $(TMPDIR)/outdir/$(NAME)
 	ln -nsf outdir/$(NAME) $(TMPDIR)
-	RUST_LOG=rustc_metadata::loader $(RUSTC) bar.rs
+	RUSTC_LOG=rustc_metadata::loader $(RUSTC) bar.rs
diff --git a/src/test/run-pass/existential_type.rs b/src/test/run-pass/existential_type.rs
index dfb195ec830..b36435cf113 100644
--- a/src/test/run-pass/existential_type.rs
+++ b/src/test/run-pass/existential_type.rs
@@ -68,14 +68,14 @@ fn my_other_iter<U>(u: U) -> MyOtherIter<U> {
 }
 
 trait Trait {}
-existential type GenericBound<'a, T: Trait>: 'a;
+existential type GenericBound<'a, T: Trait>: Sized + 'a;
 
 fn generic_bound<'a, T: Trait + 'a>(t: T) -> GenericBound<'a, T> {
     t
 }
 
 mod pass_through {
-    pub existential type Passthrough<T>: 'static;
+    pub existential type Passthrough<T>: Sized + 'static;
 
     fn define_passthrough<T: 'static>(t: T) -> Passthrough<T> {
         t
diff --git a/src/test/run-pass/issues/issue-18075.rs b/src/test/run-pass/issues/issue-18075.rs
index dcd67d8d15a..ee6845c1278 100644
--- a/src/test/run-pass/issues/issue-18075.rs
+++ b/src/test/run-pass/issues/issue-18075.rs
@@ -1,5 +1,5 @@
 // run-pass
-// exec-env:RUST_LOG=rustc::middle=debug
+// exec-env:RUSTC_LOG=rustc::middle=debug
 
 fn main() {
     let b = 1isize;
diff --git a/src/test/run-pass/logging-only-prints-once.rs b/src/test/run-pass/logging-only-prints-once.rs
index 7b105600225..1a4c4d89e7d 100644
--- a/src/test/run-pass/logging-only-prints-once.rs
+++ b/src/test/run-pass/logging-only-prints-once.rs
@@ -1,6 +1,6 @@
 // ignore-windows
 // ignore-emscripten no threads support
-// exec-env:RUST_LOG=debug
+// exec-env:RUSTC_LOG=debug
 
 use std::cell::Cell;
 use std::fmt;
diff --git a/src/test/run-pass/logging_before_rt_started.rs b/src/test/run-pass/logging_before_rt_started.rs
index 0cba1080de2..69cfc54c465 100644
--- a/src/test/run-pass/logging_before_rt_started.rs
+++ b/src/test/run-pass/logging_before_rt_started.rs
@@ -1,4 +1,4 @@
-// exec-env:RUST_LOG=std::ptr
+// exec-env:RUSTC_LOG=std::ptr
 
 // In issue #9487, it was realized that std::ptr was invoking the logging
 // infrastructure, and when std::ptr was used during runtime initialization,
diff --git a/src/test/run-pass/rustc-rust-log.rs b/src/test/run-pass/rustc-rust-log.rs
index 4360a154015..b6642572418 100644
--- a/src/test/run-pass/rustc-rust-log.rs
+++ b/src/test/run-pass/rustc-rust-log.rs
@@ -8,6 +8,6 @@
 // dont-check-compiler-stderr
 // compile-flags: --error-format human
 
-// rustc-env:RUST_LOG=debug
+// rustc-env:RUSTC_LOG=debug
 
 fn main() {}
diff --git a/src/test/run-pass/threads-sendsync/spawning-with-debug.rs b/src/test/run-pass/threads-sendsync/spawning-with-debug.rs
index c78ae78dd1b..388d62aa710 100644
--- a/src/test/run-pass/threads-sendsync/spawning-with-debug.rs
+++ b/src/test/run-pass/threads-sendsync/spawning-with-debug.rs
@@ -2,7 +2,7 @@
 #![allow(unused_must_use)]
 #![allow(unused_mut)]
 // ignore-windows
-// exec-env:RUST_LOG=debug
+// exec-env:RUSTC_LOG=debug
 // ignore-emscripten no threads support
 
 // regression test for issue #10405, make sure we don't call println! too soon.
diff --git a/src/test/rustdoc-ui/failed-doctest-output.stdout b/src/test/rustdoc-ui/failed-doctest-output.stdout
index c9f59405ce0..7b1cd702731 100644
--- a/src/test/rustdoc-ui/failed-doctest-output.stdout
+++ b/src/test/rustdoc-ui/failed-doctest-output.stdout
@@ -15,7 +15,7 @@ error[E0425]: cannot find value `no` in this scope
 error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0425`.
-thread '$DIR/failed-doctest-output.rs - OtherStruct (line 17)' panicked at 'couldn't compile the test', src/librustdoc/test.rs:310:13
+thread '$DIR/failed-doctest-output.rs - OtherStruct (line 17)' panicked at 'couldn't compile the test', src/librustdoc/test.rs:319:13
 note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
 
 ---- $DIR/failed-doctest-output.rs - SomeStruct (line 11) stdout ----
@@ -24,7 +24,7 @@ thread '$DIR/failed-doctest-output.rs - SomeStruct (line 11)' panicked at 'test
 thread 'main' panicked at 'oh no', $DIR/failed-doctest-output.rs:3:1
 note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
 
-', src/librustdoc/test.rs:332:17
+', src/librustdoc/test.rs:341:17
 
 
 failures:
diff --git a/src/test/rustdoc-ui/unparseable-doc-test.rs b/src/test/rustdoc-ui/unparseable-doc-test.rs
new file mode 100644
index 00000000000..18d6b32bf40
--- /dev/null
+++ b/src/test/rustdoc-ui/unparseable-doc-test.rs
@@ -0,0 +1,10 @@
+// compile-flags: --test
+// normalize-stdout-test: "src/test/rustdoc-ui" -> "$$DIR"
+// failure-status: 101
+// rustc-env: RUST_BACKTRACE=0
+
+/// ```rust
+/// let x = 7;
+/// "unterminated
+/// ```
+pub fn foo() {}
diff --git a/src/test/rustdoc-ui/unparseable-doc-test.stdout b/src/test/rustdoc-ui/unparseable-doc-test.stdout
new file mode 100644
index 00000000000..7048ef2c589
--- /dev/null
+++ b/src/test/rustdoc-ui/unparseable-doc-test.stdout
@@ -0,0 +1,24 @@
+
+running 1 test
+test $DIR/unparseable-doc-test.rs - foo (line 6) ... FAILED
+
+failures:
+
+---- $DIR/unparseable-doc-test.rs - foo (line 6) stdout ----
+error: unterminated double quote string
+ --> $DIR/unparseable-doc-test.rs:8:1
+  |
+2 | "unterminated
+  | ^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+thread '$DIR/unparseable-doc-test.rs - foo (line 6)' panicked at 'couldn't compile the test', src/librustdoc/test.rs:319:13
+note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
+
+
+failures:
+    $DIR/unparseable-doc-test.rs - foo (line 6)
+
+test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
+
diff --git a/src/test/ui/async-await/mutable-arguments.rs b/src/test/ui/async-await/mutable-arguments.rs
new file mode 100644
index 00000000000..4d6dba74097
--- /dev/null
+++ b/src/test/ui/async-await/mutable-arguments.rs
@@ -0,0 +1,10 @@
+// edition:2018
+// run-pass
+
+#![feature(async_await)]
+
+async fn foo(n: u32, mut vec: Vec<u32>) {
+    vec.push(n);
+}
+
+fn main() {}
diff --git a/src/test/ui/async-fn-path-elision.rs b/src/test/ui/async-fn-path-elision.rs
new file mode 100644
index 00000000000..8db7631ef41
--- /dev/null
+++ b/src/test/ui/async-fn-path-elision.rs
@@ -0,0 +1,16 @@
+// edition:2018
+
+#![feature(async_await, await_macro)]
+#![allow(dead_code)]
+
+struct HasLifetime<'a>(&'a bool);
+
+async fn error(lt: HasLifetime) { //~ ERROR implicit elided lifetime not allowed here
+    if *lt.0 {}
+}
+
+fn no_error(lt: HasLifetime) {
+    if *lt.0 {}
+}
+
+fn main() {}
diff --git a/src/test/ui/async-fn-path-elision.stderr b/src/test/ui/async-fn-path-elision.stderr
new file mode 100644
index 00000000000..3b311baba01
--- /dev/null
+++ b/src/test/ui/async-fn-path-elision.stderr
@@ -0,0 +1,8 @@
+error[E0726]: implicit elided lifetime not allowed here
+  --> $DIR/async-fn-path-elision.rs:8:20
+   |
+LL | async fn error(lt: HasLifetime) {
+   |                    ^^^^^^^^^^^- help: indicate the anonymous lifetime: `<'_>`
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/consts/const-err.rs b/src/test/ui/consts/const-err.rs
index 8cc3dc7f587..7dfcda69058 100644
--- a/src/test/ui/consts/const-err.rs
+++ b/src/test/ui/consts/const-err.rs
@@ -13,4 +13,5 @@ const FOO: u8 = [5u8][1];
 fn main() {
     black_box((FOO, FOO));
     //~^ ERROR erroneous constant used
+    //~| ERROR erroneous constant
 }
diff --git a/src/test/ui/consts/const-err.stderr b/src/test/ui/consts/const-err.stderr
index 0ee9ecdef45..429e2ae7600 100644
--- a/src/test/ui/consts/const-err.stderr
+++ b/src/test/ui/consts/const-err.stderr
@@ -13,11 +13,17 @@ LL | #![warn(const_err)]
    |         ^^^^^^^^^
 
 error[E0080]: erroneous constant used
-  --> $DIR/const-err.rs:14:15
+  --> $DIR/const-err.rs:14:16
    |
 LL |     black_box((FOO, FOO));
-   |               ^^^^^^^^^^ referenced constant has errors
+   |                ^^^ referenced constant has errors
 
-error: aborting due to previous error
+error[E0080]: erroneous constant used
+  --> $DIR/const-err.rs:14:21
+   |
+LL |     black_box((FOO, FOO));
+   |                     ^^^ referenced constant has errors
+
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/destructure-trait-ref.stderr b/src/test/ui/destructure-trait-ref.stderr
index 34dd213e2b3..bc3013b78b3 100644
--- a/src/test/ui/destructure-trait-ref.stderr
+++ b/src/test/ui/destructure-trait-ref.stderr
@@ -20,21 +20,25 @@ error[E0308]: mismatched types
   --> $DIR/destructure-trait-ref.rs:31:10
    |
 LL |     let &&x = &1isize as &T;
-   |          ^^ expected trait T, found reference
+   |          ^^
+   |          |
+   |          expected trait T, found reference
+   |          help: you can probably remove the explicit borrow: `x`
    |
    = note: expected type `dyn T`
               found type `&_`
-   = help: did you mean `x: &dyn T`?
 
 error[E0308]: mismatched types
   --> $DIR/destructure-trait-ref.rs:36:11
    |
 LL |     let &&&x = &(&1isize as &T);
-   |           ^^ expected trait T, found reference
+   |           ^^
+   |           |
+   |           expected trait T, found reference
+   |           help: you can probably remove the explicit borrow: `x`
    |
    = note: expected type `dyn T`
               found type `&_`
-   = help: did you mean `x: &dyn T`?
 
 error[E0308]: mismatched types
   --> $DIR/destructure-trait-ref.rs:41:13
diff --git a/src/test/ui/existential_types/existential-types-with-no-traits.rs b/src/test/ui/existential_types/existential-types-with-no-traits.rs
new file mode 100644
index 00000000000..46339c73b1f
--- /dev/null
+++ b/src/test/ui/existential_types/existential-types-with-no-traits.rs
@@ -0,0 +1,14 @@
+#![feature(existential_type)]
+
+existential type Foo: 'static;
+//~^ ERROR: at least one trait must be specified
+
+fn foo() -> Foo {
+    "foo"
+}
+
+fn bar() -> impl 'static { //~ ERROR: at least one trait must be specified
+    "foo"
+}
+
+fn main() {}
diff --git a/src/test/ui/existential_types/existential-types-with-no-traits.stderr b/src/test/ui/existential_types/existential-types-with-no-traits.stderr
new file mode 100644
index 00000000000..4b2fbc79d3b
--- /dev/null
+++ b/src/test/ui/existential_types/existential-types-with-no-traits.stderr
@@ -0,0 +1,14 @@
+error: at least one trait must be specified
+  --> $DIR/existential-types-with-no-traits.rs:3:23
+   |
+LL | existential type Foo: 'static;
+   |                       ^^^^^^^
+
+error: at least one trait must be specified
+  --> $DIR/existential-types-with-no-traits.rs:10:13
+   |
+LL | fn bar() -> impl 'static {
+   |             ^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/existential_types/generic_nondefining_use.rs b/src/test/ui/existential_types/generic_nondefining_use.rs
index 75af5d9570f..ffc965aca47 100644
--- a/src/test/ui/existential_types/generic_nondefining_use.rs
+++ b/src/test/ui/existential_types/generic_nondefining_use.rs
@@ -4,6 +4,8 @@ fn main() {}
 
 existential type Cmp<T>: 'static;
 //~^ ERROR could not find defining uses
+//~^^ ERROR: at least one trait must be specified
+
 
 // not a defining use, because it doesn't define *all* possible generics
 fn cmp() -> Cmp<u32> { //~ ERROR defining existential type use does not fully define
diff --git a/src/test/ui/existential_types/generic_nondefining_use.stderr b/src/test/ui/existential_types/generic_nondefining_use.stderr
index ef579260f06..d205d44c68c 100644
--- a/src/test/ui/existential_types/generic_nondefining_use.stderr
+++ b/src/test/ui/existential_types/generic_nondefining_use.stderr
@@ -1,5 +1,11 @@
+error: at least one trait must be specified
+  --> $DIR/generic_nondefining_use.rs:5:26
+   |
+LL | existential type Cmp<T>: 'static;
+   |                          ^^^^^^^
+
 error: defining existential type use does not fully define existential type
-  --> $DIR/generic_nondefining_use.rs:9:1
+  --> $DIR/generic_nondefining_use.rs:11:1
    |
 LL | / fn cmp() -> Cmp<u32> {
 LL | |     5u32
@@ -12,5 +18,5 @@ error: could not find defining uses
 LL | existential type Cmp<T>: 'static;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/existential_types/generic_not_used.rs b/src/test/ui/existential_types/generic_not_used.rs
index bfe7b8c4a1d..054e6f5f2ad 100644
--- a/src/test/ui/existential_types/generic_not_used.rs
+++ b/src/test/ui/existential_types/generic_not_used.rs
@@ -3,6 +3,7 @@
 fn main() {}
 
 existential type WrongGeneric<T: 'static>: 'static;
+//~^ ERROR: at least one trait must be specified
 
 fn wrong_generic<U: 'static, V: 'static>(_: U, v: V) -> WrongGeneric<U> {
 //~^ ERROR type parameter `V` is part of concrete type but not used in parameter list
diff --git a/src/test/ui/existential_types/generic_not_used.stderr b/src/test/ui/existential_types/generic_not_used.stderr
index 1ae4ab65929..d243233992b 100644
--- a/src/test/ui/existential_types/generic_not_used.stderr
+++ b/src/test/ui/existential_types/generic_not_used.stderr
@@ -1,5 +1,11 @@
+error: at least one trait must be specified
+  --> $DIR/generic_not_used.rs:5:44
+   |
+LL | existential type WrongGeneric<T: 'static>: 'static;
+   |                                            ^^^^^^^
+
 error: type parameter `V` is part of concrete type but not used in parameter list for existential type
-  --> $DIR/generic_not_used.rs:7:73
+  --> $DIR/generic_not_used.rs:8:73
    |
 LL |   fn wrong_generic<U: 'static, V: 'static>(_: U, v: V) -> WrongGeneric<U> {
    |  _________________________________________________________________________^
@@ -8,5 +14,5 @@ LL | |     v
 LL | | }
    | |_^
 
-error: aborting due to previous error
+error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/existential_types/generic_type_does_not_live_long_enough.rs b/src/test/ui/existential_types/generic_type_does_not_live_long_enough.rs
index 02bb151ccb6..d9eedd6dca7 100644
--- a/src/test/ui/existential_types/generic_type_does_not_live_long_enough.rs
+++ b/src/test/ui/existential_types/generic_type_does_not_live_long_enough.rs
@@ -8,6 +8,7 @@ fn main() {
 
 existential type WrongGeneric<T>: 'static;
 //~^ ERROR the parameter type `T` may not live long enough
+//~^^ ERROR: at least one trait must be specified
 
 fn wrong_generic<T>(t: T) -> WrongGeneric<T> {
     t
diff --git a/src/test/ui/existential_types/generic_type_does_not_live_long_enough.stderr b/src/test/ui/existential_types/generic_type_does_not_live_long_enough.stderr
index 002acc41553..2f76eea4460 100644
--- a/src/test/ui/existential_types/generic_type_does_not_live_long_enough.stderr
+++ b/src/test/ui/existential_types/generic_type_does_not_live_long_enough.stderr
@@ -1,3 +1,9 @@
+error: at least one trait must be specified
+  --> $DIR/generic_type_does_not_live_long_enough.rs:9:35
+   |
+LL | existential type WrongGeneric<T>: 'static;
+   |                                   ^^^^^^^
+
 error[E0308]: mismatched types
   --> $DIR/generic_type_does_not_live_long_enough.rs:6:18
    |
@@ -22,7 +28,7 @@ note: ...so that the type `T` will meet its required lifetime bounds
 LL | existential type WrongGeneric<T>: 'static;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
 Some errors have detailed explanations: E0308, E0310.
 For more information about an error, try `rustc --explain E0308`.
diff --git a/src/test/ui/existential_types/generic_underconstrained.rs b/src/test/ui/existential_types/generic_underconstrained.rs
index 967faca067c..cc0db893c6a 100644
--- a/src/test/ui/existential_types/generic_underconstrained.rs
+++ b/src/test/ui/existential_types/generic_underconstrained.rs
@@ -4,6 +4,7 @@ fn main() {}
 
 trait Trait {}
 existential type Underconstrained<T: Trait>: 'static; //~ ERROR the trait bound `T: Trait`
+//~^ ERROR: at least one trait must be specified
 
 // no `Trait` bound
 fn underconstrain<T>(_: T) -> Underconstrained<T> {
diff --git a/src/test/ui/existential_types/generic_underconstrained.stderr b/src/test/ui/existential_types/generic_underconstrained.stderr
index 8551a939e8e..35083a53eb3 100644
--- a/src/test/ui/existential_types/generic_underconstrained.stderr
+++ b/src/test/ui/existential_types/generic_underconstrained.stderr
@@ -1,3 +1,9 @@
+error: at least one trait must be specified
+  --> $DIR/generic_underconstrained.rs:6:46
+   |
+LL | existential type Underconstrained<T: Trait>: 'static;
+   |                                              ^^^^^^^
+
 error[E0277]: the trait bound `T: Trait` is not satisfied
   --> $DIR/generic_underconstrained.rs:6:1
    |
@@ -7,6 +13,6 @@ LL | existential type Underconstrained<T: Trait>: 'static;
    = help: consider adding a `where T: Trait` bound
    = note: the return type of a function must have a statically known size
 
-error: aborting due to previous error
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/existential_types/generic_underconstrained2.rs b/src/test/ui/existential_types/generic_underconstrained2.rs
index 98d9da832cf..c6263eacd53 100644
--- a/src/test/ui/existential_types/generic_underconstrained2.rs
+++ b/src/test/ui/existential_types/generic_underconstrained2.rs
@@ -4,6 +4,7 @@ fn main() {}
 
 existential type Underconstrained<T: std::fmt::Debug>: 'static;
 //~^ ERROR `U` doesn't implement `std::fmt::Debug`
+//~^^ ERROR: at least one trait must be specified
 
 // not a defining use, because it doesn't define *all* possible generics
 fn underconstrained<U>(_: U) -> Underconstrained<U> {
@@ -12,6 +13,7 @@ fn underconstrained<U>(_: U) -> Underconstrained<U> {
 
 existential type Underconstrained2<T: std::fmt::Debug>: 'static;
 //~^ ERROR `V` doesn't implement `std::fmt::Debug`
+//~^^ ERROR: at least one trait must be specified
 
 // not a defining use, because it doesn't define *all* possible generics
 fn underconstrained2<U, V>(_: U, _: V) -> Underconstrained2<V> {
diff --git a/src/test/ui/existential_types/generic_underconstrained2.stderr b/src/test/ui/existential_types/generic_underconstrained2.stderr
index c7b6d6ade55..6ff783f33b9 100644
--- a/src/test/ui/existential_types/generic_underconstrained2.stderr
+++ b/src/test/ui/existential_types/generic_underconstrained2.stderr
@@ -1,3 +1,15 @@
+error: at least one trait must be specified
+  --> $DIR/generic_underconstrained2.rs:5:56
+   |
+LL | existential type Underconstrained<T: std::fmt::Debug>: 'static;
+   |                                                        ^^^^^^^
+
+error: at least one trait must be specified
+  --> $DIR/generic_underconstrained2.rs:14:57
+   |
+LL | existential type Underconstrained2<T: std::fmt::Debug>: 'static;
+   |                                                         ^^^^^^^
+
 error[E0277]: `U` doesn't implement `std::fmt::Debug`
   --> $DIR/generic_underconstrained2.rs:5:1
    |
@@ -9,7 +21,7 @@ LL | existential type Underconstrained<T: std::fmt::Debug>: 'static;
    = note: the return type of a function must have a statically known size
 
 error[E0277]: `V` doesn't implement `std::fmt::Debug`
-  --> $DIR/generic_underconstrained2.rs:13:1
+  --> $DIR/generic_underconstrained2.rs:14:1
    |
 LL | existential type Underconstrained2<T: std::fmt::Debug>: 'static;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `V` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug`
@@ -18,6 +30,6 @@ LL | existential type Underconstrained2<T: std::fmt::Debug>: 'static;
    = help: consider adding a `where V: std::fmt::Debug` bound
    = note: the return type of a function must have a statically known size
 
-error: aborting due to 2 previous errors
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/existential_types/unused_generic_param.rs b/src/test/ui/existential_types/unused_generic_param.rs
index 7af65087881..5455b39f4cb 100644
--- a/src/test/ui/existential_types/unused_generic_param.rs
+++ b/src/test/ui/existential_types/unused_generic_param.rs
@@ -1,18 +1,17 @@
-// compile-pass
 #![feature(existential_type)]
 
 fn main() {
 }
 
-// test that unused generic parameters are ok
 existential type PartiallyDefined<T>: 'static;
+//~^ ERROR: at least one trait must be specified
 
 fn partially_defined<T: std::fmt::Debug>(_: T) -> PartiallyDefined<T> {
     4u32
 }
 
-// test that unused generic parameters are ok
 existential type PartiallyDefined2<T>: 'static;
+//~^ ERROR: at least one trait must be specified
 
 fn partially_defined2<T: std::fmt::Debug>(_: T) -> PartiallyDefined2<T> {
     4u32
diff --git a/src/test/ui/existential_types/unused_generic_param.stderr b/src/test/ui/existential_types/unused_generic_param.stderr
new file mode 100644
index 00000000000..9d628d069d3
--- /dev/null
+++ b/src/test/ui/existential_types/unused_generic_param.stderr
@@ -0,0 +1,14 @@
+error: at least one trait must be specified
+  --> $DIR/unused_generic_param.rs:6:39
+   |
+LL | existential type PartiallyDefined<T>: 'static;
+   |                                       ^^^^^^^
+
+error: at least one trait must be specified
+  --> $DIR/unused_generic_param.rs:13:40
+   |
+LL | existential type PartiallyDefined2<T>: 'static;
+   |                                        ^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/impl-header-lifetime-elision/path-elided.rs b/src/test/ui/impl-header-lifetime-elision/path-elided.rs
index 6532b0aebe7..40a52efc7f9 100644
--- a/src/test/ui/impl-header-lifetime-elision/path-elided.rs
+++ b/src/test/ui/impl-header-lifetime-elision/path-elided.rs
@@ -5,7 +5,7 @@ trait MyTrait { }
 struct Foo<'a> { x: &'a u32 }
 
 impl MyTrait for Foo {
-    //~^ ERROR missing lifetime specifier
+    //~^ ERROR implicit elided lifetime not allowed here
 }
 
 fn main() {}
diff --git a/src/test/ui/impl-header-lifetime-elision/path-elided.stderr b/src/test/ui/impl-header-lifetime-elision/path-elided.stderr
index c9287ffba96..6500a2a55f6 100644
--- a/src/test/ui/impl-header-lifetime-elision/path-elided.stderr
+++ b/src/test/ui/impl-header-lifetime-elision/path-elided.stderr
@@ -1,9 +1,8 @@
-error[E0106]: missing lifetime specifier
+error[E0726]: implicit elided lifetime not allowed here
   --> $DIR/path-elided.rs:7:18
    |
 LL | impl MyTrait for Foo {
-   |                  ^^^ expected lifetime parameter
+   |                  ^^^- help: indicate the anonymous lifetime: `<'_>`
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0106`.
diff --git a/src/test/ui/impl-header-lifetime-elision/trait-elided.rs b/src/test/ui/impl-header-lifetime-elision/trait-elided.rs
index 3979eda740a..102d259b0c8 100644
--- a/src/test/ui/impl-header-lifetime-elision/trait-elided.rs
+++ b/src/test/ui/impl-header-lifetime-elision/trait-elided.rs
@@ -3,7 +3,7 @@
 trait MyTrait<'a> { }
 
 impl MyTrait for u32 {
-    //~^ ERROR missing lifetime specifier
+    //~^ ERROR implicit elided lifetime not allowed here
 }
 
 fn main() {}
diff --git a/src/test/ui/impl-header-lifetime-elision/trait-elided.stderr b/src/test/ui/impl-header-lifetime-elision/trait-elided.stderr
index b742db1c75c..ad97cb0abd6 100644
--- a/src/test/ui/impl-header-lifetime-elision/trait-elided.stderr
+++ b/src/test/ui/impl-header-lifetime-elision/trait-elided.stderr
@@ -1,9 +1,8 @@
-error[E0106]: missing lifetime specifier
+error[E0726]: implicit elided lifetime not allowed here
   --> $DIR/trait-elided.rs:5:6
    |
 LL | impl MyTrait for u32 {
-   |      ^^^^^^^ expected lifetime parameter
+   |      ^^^^^^^- help: indicate the anonymous lifetime: `<'_>`
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0106`.
diff --git a/src/test/ui/issues/issue-10412.rs b/src/test/ui/issues/issue-10412.rs
index a0bc2fc2f3c..02058513685 100644
--- a/src/test/ui/issues/issue-10412.rs
+++ b/src/test/ui/issues/issue-10412.rs
@@ -5,7 +5,8 @@ trait Serializable<'self, T> { //~ ERROR lifetimes cannot use keyword names
 
 impl<'self> Serializable<str> for &'self str { //~ ERROR lifetimes cannot use keyword names
     //~^ ERROR lifetimes cannot use keyword names
-    //~| ERROR missing lifetime specifier
+    //~| ERROR implicit elided lifetime not allowed here
+    //~| ERROR the size for values of type `str` cannot be known at compilation time
     fn serialize(val : &'self str) -> Vec<u8> { //~ ERROR lifetimes cannot use keyword names
         vec![1]
     }
diff --git a/src/test/ui/issues/issue-10412.stderr b/src/test/ui/issues/issue-10412.stderr
index 48920813ae1..0793dd99b4d 100644
--- a/src/test/ui/issues/issue-10412.stderr
+++ b/src/test/ui/issues/issue-10412.stderr
@@ -29,23 +29,32 @@ LL | impl<'self> Serializable<str> for &'self str {
    |                                    ^^^^^
 
 error: lifetimes cannot use keyword names
-  --> $DIR/issue-10412.rs:9:25
+  --> $DIR/issue-10412.rs:10:25
    |
 LL |     fn serialize(val : &'self str) -> Vec<u8> {
    |                         ^^^^^
 
 error: lifetimes cannot use keyword names
-  --> $DIR/issue-10412.rs:12:37
+  --> $DIR/issue-10412.rs:13:37
    |
 LL |     fn deserialize(repr: &[u8]) -> &'self str {
    |                                     ^^^^^
 
-error[E0106]: missing lifetime specifier
+error[E0726]: implicit elided lifetime not allowed here
   --> $DIR/issue-10412.rs:6:13
    |
 LL | impl<'self> Serializable<str> for &'self str {
-   |             ^^^^^^^^^^^^^^^^^ expected lifetime parameter
+   |             ^^^^^^^^^^^^^^^^^ help: indicate the anonymous lifetime: `Serializable<'_, str>`
 
-error: aborting due to 8 previous errors
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/issue-10412.rs:6:13
+   |
+LL | impl<'self> Serializable<str> for &'self str {
+   |             ^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `std::marker::Sized` is not implemented for `str`
+   = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
+
+error: aborting due to 9 previous errors
 
-For more information about this error, try `rustc --explain E0106`.
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/match/match-type-err-first-arm.rs b/src/test/ui/match/match-type-err-first-arm.rs
index b4b84ef8f1c..8dfbf1019e9 100644
--- a/src/test/ui/match/match-type-err-first-arm.rs
+++ b/src/test/ui/match/match-type-err-first-arm.rs
@@ -3,8 +3,7 @@ fn main() {
     let _ = test_func2(1);
 }
 
-fn test_func1(n: i32) -> i32 {
-    //~^ NOTE expected `i32` because of return type
+fn test_func1(n: i32) -> i32 { //~ NOTE expected `i32` because of return type
     match n {
         12 => 'b',
         //~^ ERROR mismatched types
@@ -14,10 +13,8 @@ fn test_func1(n: i32) -> i32 {
 }
 
 fn test_func2(n: i32) -> i32 {
-    let x = match n {
-    //~^ NOTE `match` arms have incompatible types
-        12 => 'b',
-        //~^ NOTE this is found to be of type `char`
+    let x = match n { //~ NOTE `match` arms have incompatible types
+        12 => 'b', //~ NOTE this is found to be of type `char`
         _ => 42,
         //~^ ERROR match arms have incompatible types
         //~| NOTE expected char, found integer
@@ -27,8 +24,7 @@ fn test_func2(n: i32) -> i32 {
 }
 
 fn test_func3(n: i32) -> i32 {
-    let x = match n {
-    //~^ NOTE `match` arms have incompatible types
+    let x = match n { //~ NOTE `match` arms have incompatible types
         1 => 'b',
         2 => 'b',
         3 => 'b',
@@ -43,3 +39,15 @@ fn test_func3(n: i32) -> i32 {
     };
     x
 }
+
+fn test_func4() {
+    match Some(0u32) { //~ NOTE `match` arms have incompatible types
+        Some(x) => {
+            x //~ NOTE this is found to be of type `u32`
+        },
+        None => {}
+        //~^ ERROR match arms have incompatible types
+        //~| NOTE expected u32, found ()
+        //~| NOTE expected type `u32`
+    };
+}
diff --git a/src/test/ui/match/match-type-err-first-arm.stderr b/src/test/ui/match/match-type-err-first-arm.stderr
index a318e6cffb9..e0553fca683 100644
--- a/src/test/ui/match/match-type-err-first-arm.stderr
+++ b/src/test/ui/match/match-type-err-first-arm.stderr
@@ -1,24 +1,23 @@
 error[E0308]: mismatched types
-  --> $DIR/match-type-err-first-arm.rs:9:15
+  --> $DIR/match-type-err-first-arm.rs:8:15
    |
 LL | fn test_func1(n: i32) -> i32 {
    |                          --- expected `i32` because of return type
-...
+LL |     match n {
 LL |         12 => 'b',
    |               ^^^ expected i32, found char
 
 error[E0308]: match arms have incompatible types
-  --> $DIR/match-type-err-first-arm.rs:21:14
+  --> $DIR/match-type-err-first-arm.rs:18:14
    |
 LL |       let x = match n {
    |  _____________-
-LL | |
 LL | |         12 => 'b',
    | |               --- this is found to be of type `char`
-LL | |
 LL | |         _ => 42,
    | |              ^^ expected char, found integer
-...  |
+LL | |
+LL | |
 LL | |
 LL | |     };
    | |_____- `match` arms have incompatible types
@@ -27,13 +26,13 @@ LL | |     };
               found type `{integer}`
 
 error[E0308]: match arms have incompatible types
-  --> $DIR/match-type-err-first-arm.rs:39:14
+  --> $DIR/match-type-err-first-arm.rs:35:14
    |
 LL |       let x = match n {
    |  _____________-
-LL | |
 LL | |         1 => 'b',
 LL | |         2 => 'b',
+LL | |         3 => 'b',
 ...  |
 LL | |         6 => 'b',
    | |              --- this and all prior arms are found to be of type `char`
@@ -48,6 +47,24 @@ LL | |     };
    = note: expected type `char`
               found type `{integer}`
 
-error: aborting due to 3 previous errors
+error[E0308]: match arms have incompatible types
+  --> $DIR/match-type-err-first-arm.rs:48:17
+   |
+LL | /     match Some(0u32) {
+LL | |         Some(x) => {
+LL | |             x
+   | |             - this is found to be of type `u32`
+LL | |         },
+LL | |         None => {}
+   | |                 ^^ expected u32, found ()
+...  |
+LL | |
+LL | |     };
+   | |_____- `match` arms have incompatible types
+   |
+   = note: expected type `u32`
+              found type `()`
+
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/mismatched_types/issue-38371.stderr b/src/test/ui/mismatched_types/issue-38371.stderr
index 236f742db3f..a9347926bda 100644
--- a/src/test/ui/mismatched_types/issue-38371.stderr
+++ b/src/test/ui/mismatched_types/issue-38371.stderr
@@ -12,11 +12,13 @@ error[E0308]: mismatched types
   --> $DIR/issue-38371.rs:18:9
    |
 LL | fn agh(&&bar: &u32) {
-   |         ^^^^ expected u32, found reference
+   |         ^^^^
+   |         |
+   |         expected u32, found reference
+   |         help: you can probably remove the explicit borrow: `bar`
    |
    = note: expected type `u32`
               found type `&_`
-   = help: did you mean `bar: &u32`?
 
 error[E0308]: mismatched types
   --> $DIR/issue-38371.rs:21:8
diff --git a/src/test/ui/suggestions/match-ergonomics.rs b/src/test/ui/suggestions/match-ergonomics.rs
new file mode 100644
index 00000000000..c4fc01469bf
--- /dev/null
+++ b/src/test/ui/suggestions/match-ergonomics.rs
@@ -0,0 +1,41 @@
+fn main() {
+    let x = vec![1i32];
+    match &x[..] {
+        [&v] => {}, //~ ERROR mismatched types
+        _ => {},
+    }
+    match x {
+        [&v] => {}, //~ ERROR expected an array or slice
+        _ => {},
+    }
+    match &x[..] {
+        [v] => {},
+        _ => {},
+    }
+    match &x[..] {
+        &[v] => {},
+        _ => {},
+    }
+    match x {
+        [v] => {}, //~ ERROR expected an array or slice
+        _ => {},
+    }
+    let y = 1i32;
+    match &y {
+        &v => {},
+        _ => {},
+    }
+    match y {
+        &v => {}, //~ ERROR mismatched types
+        _ => {},
+    }
+    match &y {
+        v => {},
+        _ => {},
+    }
+    match y {
+        v => {},
+        _ => {},
+    }
+    if let [&v] = &x[..] {} //~ ERROR mismatched types
+}
diff --git a/src/test/ui/suggestions/match-ergonomics.stderr b/src/test/ui/suggestions/match-ergonomics.stderr
new file mode 100644
index 00000000000..b7497be6ceb
--- /dev/null
+++ b/src/test/ui/suggestions/match-ergonomics.stderr
@@ -0,0 +1,52 @@
+error[E0308]: mismatched types
+  --> $DIR/match-ergonomics.rs:4:10
+   |
+LL |         [&v] => {},
+   |          ^^
+   |          |
+   |          expected i32, found reference
+   |          help: you can probably remove the explicit borrow: `v`
+   |
+   = note: expected type `i32`
+              found type `&_`
+
+error[E0529]: expected an array or slice, found `std::vec::Vec<i32>`
+  --> $DIR/match-ergonomics.rs:8:9
+   |
+LL |         [&v] => {},
+   |         ^^^^ pattern cannot match with input type `std::vec::Vec<i32>`
+
+error[E0529]: expected an array or slice, found `std::vec::Vec<i32>`
+  --> $DIR/match-ergonomics.rs:20:9
+   |
+LL |         [v] => {},
+   |         ^^^ pattern cannot match with input type `std::vec::Vec<i32>`
+
+error[E0308]: mismatched types
+  --> $DIR/match-ergonomics.rs:29:9
+   |
+LL |         &v => {},
+   |         ^^
+   |         |
+   |         expected i32, found reference
+   |         help: you can probably remove the explicit borrow: `v`
+   |
+   = note: expected type `i32`
+              found type `&_`
+
+error[E0308]: mismatched types
+  --> $DIR/match-ergonomics.rs:40:13
+   |
+LL |     if let [&v] = &x[..] {}
+   |             ^^
+   |             |
+   |             expected i32, found reference
+   |             help: you can probably remove the explicit borrow: `v`
+   |
+   = note: expected type `i32`
+              found type `&_`
+
+error: aborting due to 5 previous errors
+
+Some errors have detailed explanations: E0308, E0529.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/src/test/ui/type-alias-enum-variants-priority.rs b/src/test/ui/type-alias-enum-variants-priority.rs
index db1da2b12e2..82cd21b09d3 100644
--- a/src/test/ui/type-alias-enum-variants-priority.rs
+++ b/src/test/ui/type-alias-enum-variants-priority.rs
@@ -1,5 +1,4 @@
 #![feature(type_alias_enum_variants)]
-#![deny(ambiguous_associated_items)]
 
 enum E {
     V
diff --git a/src/test/ui/type-alias-enum-variants-priority.stderr b/src/test/ui/type-alias-enum-variants-priority.stderr
index dcf7dc77ed5..b8271807b83 100644
--- a/src/test/ui/type-alias-enum-variants-priority.stderr
+++ b/src/test/ui/type-alias-enum-variants-priority.stderr
@@ -1,23 +1,19 @@
 error: ambiguous associated item
-  --> $DIR/type-alias-enum-variants-priority.rs:15:15
+  --> $DIR/type-alias-enum-variants-priority.rs:14:15
    |
 LL |     fn f() -> Self::V { 0 }
    |               ^^^^^^^ help: use fully-qualified syntax: `<E as Trait>::V`
    |
-note: lint level defined here
-  --> $DIR/type-alias-enum-variants-priority.rs:2:9
-   |
-LL | #![deny(ambiguous_associated_items)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: #[deny(ambiguous_associated_items)] on by default
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #57644 <https://github.com/rust-lang/rust/issues/57644>
 note: `V` could refer to variant defined here
-  --> $DIR/type-alias-enum-variants-priority.rs:5:5
+  --> $DIR/type-alias-enum-variants-priority.rs:4:5
    |
 LL |     V
    |     ^
 note: `V` could also refer to associated type defined here
-  --> $DIR/type-alias-enum-variants-priority.rs:9:5
+  --> $DIR/type-alias-enum-variants-priority.rs:8:5
    |
 LL |     type V;
    |     ^^^^^^^
diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml
index f7b491823f8..f5db2487618 100644
--- a/src/tools/tidy/Cargo.toml
+++ b/src/tools/tidy/Cargo.toml
@@ -4,6 +4,7 @@ version = "0.1.0"
 authors = ["Alex Crichton <alex@alexcrichton.com>"]
 
 [dependencies]
+regex = "1"
 serde = "1.0.8"
 serde_derive = "1.0.8"
 serde_json = "1.0.2"
diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs
index 8239fd9dce0..3144df6dd4c 100644
--- a/src/tools/tidy/src/features.rs
+++ b/src/tools/tidy/src/features.rs
@@ -7,6 +7,7 @@
 //! * Library features have at most one stability level.
 //! * Library features have at most one `since` value.
 //! * All unstable lang features have tests to ensure they are actually unstable.
+//! * Language features in a group are sorted by `since` value.
 
 use std::collections::HashMap;
 use std::fmt;
@@ -14,6 +15,14 @@ use std::fs::{self, File};
 use std::io::prelude::*;
 use std::path::Path;
 
+use regex::{Regex, escape};
+
+mod version;
+use self::version::Version;
+
+const FEATURE_GROUP_START_PREFIX: &str = "// feature-group-start";
+const FEATURE_GROUP_END_PREFIX: &str = "// feature-group-end";
+
 #[derive(Debug, PartialEq, Clone)]
 pub enum Status {
     Stable,
@@ -35,7 +44,7 @@ impl fmt::Display for Status {
 #[derive(Debug, Clone)]
 pub struct Feature {
     pub level: Status,
-    pub since: String,
+    pub since: Option<Version>,
     pub has_gate_test: bool,
     pub tracking_issue: Option<u32>,
 }
@@ -129,20 +138,8 @@ pub fn check(path: &Path, bad: &mut bool, quiet: bool) {
     }
 
     let mut lines = Vec::new();
-    for (name, feature) in features.iter() {
-        lines.push(format!("{:<32} {:<8} {:<12} {:<8}",
-                           name,
-                           "lang",
-                           feature.level,
-                           feature.since));
-    }
-    for (name, feature) in lib_features {
-        lines.push(format!("{:<32} {:<8} {:<12} {:<8}",
-                           name,
-                           "lib",
-                           feature.level,
-                           feature.since));
-    }
+    lines.extend(format_features(&features, "lang"));
+    lines.extend(format_features(&lib_features, "lib"));
 
     lines.sort();
     for line in lines {
@@ -150,11 +147,31 @@ pub fn check(path: &Path, bad: &mut bool, quiet: bool) {
     }
 }
 
+fn format_features<'a>(features: &'a Features, family: &'a str) -> impl Iterator<Item = String> + 'a {
+    features.iter().map(move |(name, feature)| {
+        format!("{:<32} {:<8} {:<12} {:<8}",
+                name,
+                family,
+                feature.level,
+                feature.since.map_or("None".to_owned(),
+                                     |since| since.to_string()))
+    })
+}
+
 fn find_attr_val<'a>(line: &'a str, attr: &str) -> Option<&'a str> {
-    line.find(attr)
-        .and_then(|i| line[i..].find('"').map(|j| i + j + 1))
-        .and_then(|i| line[i..].find('"').map(|j| (i, i + j)))
-        .map(|(i, j)| &line[i..j])
+    let r = Regex::new(&format!(r#"{}\s*=\s*"([^"]*)""#, escape(attr)))
+        .expect("malformed regex for find_attr_val");
+    r.captures(line)
+        .and_then(|c| c.get(1))
+        .map(|m| m.as_str())
+}
+
+#[test]
+fn test_find_attr_val() {
+    let s = r#"#[unstable(feature = "checked_duration_since", issue = "58402")]"#;
+    assert_eq!(find_attr_val(s, "feature"), Some("checked_duration_since"));
+    assert_eq!(find_attr_val(s, "issue"), Some("58402"));
+    assert_eq!(find_attr_val(s, "since"), None);
 }
 
 fn test_filen_gate(filen_underscore: &str, features: &mut Features) -> bool {
@@ -177,6 +194,9 @@ pub fn collect_lang_features(base_src_path: &Path, bad: &mut bool) -> Features {
     // without one inside `// no tracking issue START` and `// no tracking issue END`.
     let mut next_feature_omits_tracking_issue = false;
 
+    let mut in_feature_group = false;
+    let mut prev_since = None;
+
     contents.lines().zip(1..)
         .filter_map(|(line, line_number)| {
             let line = line.trim();
@@ -194,6 +214,25 @@ pub fn collect_lang_features(base_src_path: &Path, bad: &mut bool) -> Features {
                 _ => {}
             }
 
+            if line.starts_with(FEATURE_GROUP_START_PREFIX) {
+                if in_feature_group {
+                    tidy_error!(
+                        bad,
+                        // ignore-tidy-linelength
+                        "libsyntax/feature_gate.rs:{}: new feature group is started without ending the previous one",
+                        line_number,
+                    );
+                }
+
+                in_feature_group = true;
+                prev_since = None;
+                return None;
+            } else if line.starts_with(FEATURE_GROUP_END_PREFIX) {
+                in_feature_group = false;
+                prev_since = None;
+                return None;
+            }
+
             let mut parts = line.split(',');
             let level = match parts.next().map(|l| l.trim().trim_start_matches('(')) {
                 Some("active") => Status::Unstable,
@@ -202,7 +241,33 @@ pub fn collect_lang_features(base_src_path: &Path, bad: &mut bool) -> Features {
                 _ => return None,
             };
             let name = parts.next().unwrap().trim();
-            let since = parts.next().unwrap().trim().trim_matches('"');
+
+            let since_str = parts.next().unwrap().trim().trim_matches('"');
+            let since = match since_str.parse() {
+                Ok(since) => Some(since),
+                Err(err) => {
+                    tidy_error!(
+                        bad,
+                        "libsyntax/feature_gate.rs:{}: failed to parse since: {} ({:?})",
+                        line_number,
+                        since_str,
+                        err,
+                    );
+                    None
+                }
+            };
+            if in_feature_group {
+                if prev_since > since {
+                    tidy_error!(
+                        bad,
+                        "libsyntax/feature_gate.rs:{}: feature {} is not sorted by since",
+                        line_number,
+                        name,
+                    );
+                }
+                prev_since = since;
+            }
+
             let issue_str = parts.next().unwrap().trim();
             let tracking_issue = if issue_str.starts_with("None") {
                 if level == Status::Unstable && !next_feature_omits_tracking_issue {
@@ -222,7 +287,7 @@ pub fn collect_lang_features(base_src_path: &Path, bad: &mut bool) -> Features {
             Some((name.to_owned(),
                 Feature {
                     level,
-                    since: since.to_owned(),
+                    since,
                     has_gate_test: false,
                     tracking_issue,
                 }))
@@ -239,7 +304,7 @@ pub fn collect_lib_features(base_src_path: &Path) -> Features {
     // add it to the set of known library features so we can still generate docs.
     lib_features.insert("compiler_builtins_lib".to_owned(), Feature {
         level: Status::Unstable,
-        since: String::new(),
+        since: None,
         has_gate_test: false,
         tracking_issue: None,
     });
@@ -336,11 +401,11 @@ fn map_lib_features(base_src_path: &Path,
                 // `const fn` features are handled specially.
                 let feature_name = match find_attr_val(line, "feature") {
                     Some(name) => name,
-                    None => err!("malformed stability attribute"),
+                    None => err!("malformed stability attribute: missing `feature` key"),
                 };
                 let feature = Feature {
                     level: Status::Unstable,
-                    since: "None".to_owned(),
+                    since: None,
                     has_gate_test: false,
                     // FIXME(#57563): #57563 is now used as a common tracking issue,
                     // although we would like to have specific tracking issues for each
@@ -359,20 +424,23 @@ fn map_lib_features(base_src_path: &Path,
             };
             let feature_name = match find_attr_val(line, "feature") {
                 Some(name) => name,
-                None => err!("malformed stability attribute"),
+                None => err!("malformed stability attribute: missing `feature` key"),
             };
-            let since = match find_attr_val(line, "since") {
-                Some(name) => name,
+            let since = match find_attr_val(line, "since").map(|x| x.parse()) {
+                Some(Ok(since)) => Some(since),
+                Some(Err(_err)) => {
+                    err!("malformed stability attribute: can't parse `since` key");
+                },
                 None if level == Status::Stable => {
-                    err!("malformed stability attribute");
+                    err!("malformed stability attribute: missing the `since` key");
                 }
-                None => "None",
+                None => None,
             };
             let tracking_issue = find_attr_val(line, "issue").map(|s| s.parse().unwrap());
 
             let feature = Feature {
                 level,
-                since: since.to_owned(),
+                since,
                 has_gate_test: false,
                 tracking_issue,
             };
diff --git a/src/tools/tidy/src/features/version.rs b/src/tools/tidy/src/features/version.rs
new file mode 100644
index 00000000000..6027e7d35e2
--- /dev/null
+++ b/src/tools/tidy/src/features/version.rs
@@ -0,0 +1,92 @@
+use std::str::FromStr;
+use std::num::ParseIntError;
+use std::fmt;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Version {
+    parts: [u32; 3],
+}
+
+impl fmt::Display for Version {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.pad(&format!("{}.{}.{}", self.parts[0], self.parts[1], self.parts[2]))
+    }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum ParseVersionError {
+    ParseIntError(ParseIntError),
+    WrongNumberOfParts,
+}
+
+impl From<ParseIntError> for ParseVersionError {
+    fn from(err: ParseIntError) -> Self {
+        ParseVersionError::ParseIntError(err)
+    }
+}
+
+impl FromStr for Version {
+    type Err = ParseVersionError;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let mut iter = s.split('.').map(|part| Ok(part.parse()?));
+
+        let parts = {
+            let mut part = || {
+                iter.next()
+                    .unwrap_or(Err(ParseVersionError::WrongNumberOfParts))
+            };
+
+            [part()?, part()?, part()?]
+        };
+
+        if let Some(_) = iter.next() {
+            // Ensure we don't have more than 3 parts.
+            return Err(ParseVersionError::WrongNumberOfParts);
+        }
+
+        Ok(Self { parts })
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::Version;
+
+    #[test]
+    fn test_try_from_invalid_version() {
+        assert!("".parse::<Version>().is_err());
+        assert!("hello".parse::<Version>().is_err());
+        assert!("1.32.hi".parse::<Version>().is_err());
+        assert!("1.32..1".parse::<Version>().is_err());
+        assert!("1.32".parse::<Version>().is_err());
+        assert!("1.32.0.1".parse::<Version>().is_err());
+    }
+
+    #[test]
+    fn test_try_from_single() {
+        assert_eq!("1.32.0".parse(), Ok(Version { parts: [1, 32, 0] }));
+        assert_eq!("1.0.0".parse(), Ok(Version { parts: [1, 0, 0] }));
+    }
+
+    #[test]
+    fn test_compare() {
+        let v_1_0_0 = "1.0.0".parse::<Version>().unwrap();
+        let v_1_32_0 = "1.32.0".parse::<Version>().unwrap();
+        let v_1_32_1 = "1.32.1".parse::<Version>().unwrap();
+        assert!(v_1_0_0 < v_1_32_1);
+        assert!(v_1_0_0 < v_1_32_0);
+        assert!(v_1_32_0 < v_1_32_1);
+    }
+
+    #[test]
+    fn test_to_string() {
+        let v_1_0_0 = "1.0.0".parse::<Version>().unwrap();
+        let v_1_32_1 = "1.32.1".parse::<Version>().unwrap();
+
+        assert_eq!(v_1_0_0.to_string(), "1.0.0");
+        assert_eq!(v_1_32_1.to_string(), "1.32.1");
+        assert_eq!(format!("{:<8}", v_1_32_1), "1.32.1  ");
+        assert_eq!(format!("{:>8}", v_1_32_1), "  1.32.1");
+    }
+}
diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs
index c4a1246ffdf..30080452edc 100644
--- a/src/tools/tidy/src/lib.rs
+++ b/src/tools/tidy/src/lib.rs
@@ -5,6 +5,7 @@
 
 #![deny(rust_2018_idioms)]
 
+extern crate regex;
 extern crate serde_json;
 #[macro_use]
 extern crate serde_derive;