about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_codegen_cranelift/src/abi/mod.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/intrinsic.rs15
-rw-r--r--compiler/rustc_expand/src/mbe/macro_parser.rs33
-rw-r--r--compiler/rustc_infer/src/infer/canonical/canonicalizer.rs3
-rw-r--r--compiler/rustc_infer/src/infer/canonical/substitute.rs2
-rw-r--r--compiler/rustc_infer/src/infer/combine.rs14
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs6
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs8
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs8
-rw-r--r--compiler/rustc_infer/src/infer/higher_ranked/mod.rs4
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs5
-rw-r--r--compiler/rustc_infer/src/infer/nll_relate/mod.rs2
-rw-r--r--compiler/rustc_middle/src/hir/place.rs20
-rw-r--r--compiler/rustc_middle/src/ich/impls_ty.rs6
-rw-r--r--compiler/rustc_middle/src/infer/canonical.rs7
-rw-r--r--compiler/rustc_middle/src/ty/context.rs6
-rw-r--r--compiler/rustc_middle/src/ty/error.rs8
-rw-r--r--compiler/rustc_middle/src/ty/fold.rs57
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs3
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs6
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs18
-rw-r--r--compiler/rustc_middle/src/ty/relate.rs11
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs8
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs40
-rw-r--r--compiler/rustc_middle/src/ty/util.rs3
-rw-r--r--compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs4
-rw-r--r--compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs2
-rw-r--r--compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs6
-rw-r--r--compiler/rustc_mir/src/borrow_check/universal_regions.rs4
-rw-r--r--compiler/rustc_mir/src/transform/check_consts/validation.rs11
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs2
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/usefulness.rs297
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs8
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs1
-rw-r--r--compiler/rustc_parse/src/parser/nonterminal.rs23
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs17
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs4
-rw-r--r--compiler/rustc_symbol_mangling/src/v0.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs18
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs6
-rw-r--r--compiler/rustc_traits/src/chalk/db.rs12
-rw-r--r--compiler/rustc_traits/src/chalk/lowering.rs66
-rw-r--r--compiler/rustc_traits/src/chalk/mod.rs2
-rw-r--r--compiler/rustc_typeck/src/astconv/mod.rs10
-rw-r--r--compiler/rustc_typeck/src/check/_match.rs2
-rw-r--r--compiler/rustc_typeck/src/check/generator_interior.rs3
-rw-r--r--compiler/rustc_typeck/src/check/intrinsic.rs19
-rw-r--r--compiler/rustc_typeck/src/check/op.rs10
-rw-r--r--compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs175
-rw-r--r--compiler/rustc_typeck/src/collect.rs2
-rw-r--r--config.toml.example8
-rw-r--r--library/core/src/num/f32.rs35
-rw-r--r--library/core/src/num/f64.rs35
-rw-r--r--library/core/src/sync/atomic.rs10
-rw-r--r--library/core/tests/mem.rs16
-rw-r--r--library/std/src/f32.rs35
-rw-r--r--library/std/src/f64.rs35
-rw-r--r--library/std/src/primitive_docs.rs4
-rw-r--r--library/std/src/sys/unix/fd.rs9
-rw-r--r--library/std/src/sys/unix/fd/tests.rs2
-rw-r--r--src/bootstrap/builder.rs13
-rw-r--r--src/bootstrap/config.rs3
-rw-r--r--src/bootstrap/install.rs14
-rw-r--r--src/librustdoc/clean/mod.rs4
-rw-r--r--src/librustdoc/clean/utils.rs4
-rw-r--r--src/librustdoc/html/markdown.rs135
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs62
-rw-r--r--src/test/ui/binop/issue-77910-1.rs11
-rw-r--r--src/test/ui/binop/issue-77910-1.stderr26
-rw-r--r--src/test/ui/binop/issue-77910-2.rs9
-rw-r--r--src/test/ui/binop/issue-77910-2.stderr11
-rw-r--r--src/test/ui/const-generics/occurs-check/unused-substs-5.rs20
-rw-r--r--src/test/ui/const-generics/occurs-check/unused-substs-5.stderr12
-rw-r--r--src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr5
-rw-r--r--src/test/ui/error-codes/E0396.rs11
-rw-r--r--src/test/ui/error-codes/E0396.stderr20
-rw-r--r--src/test/ui/inherent-impls-overlap-check/auxiliary/repeat.rs54
-rw-r--r--src/test/ui/inherent-impls-overlap-check/no-overlap.rs34
-rw-r--r--src/test/ui/inherent-impls-overlap-check/overlap.rs71
-rw-r--r--src/test/ui/inherent-impls-overlap-check/overlap.stderr47
-rw-r--r--src/test/ui/macros/macro-pat-follow-2018.rs15
-rw-r--r--src/test/ui/macros/macro-pat-follow.rs16
-rw-r--r--src/test/ui/nll/closure-requirements/escape-argument-callee.stderr2
-rw-r--r--src/test/ui/nll/closure-requirements/escape-argument.stderr2
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr2
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr2
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr4
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr2
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr2
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr2
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr2
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr2
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr2
-rw-r--r--src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr2
-rw-r--r--src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr4
-rw-r--r--src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs29
-rw-r--r--src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr22
-rw-r--r--src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.rs15
-rw-r--r--src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.stderr20
-rw-r--r--src/test/ui/or-patterns/or-patterns-syntactic-fail.rs10
-rw-r--r--src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr48
-rw-r--r--src/test/ui/or-patterns/or-patterns-syntactic-pass-2021.rs14
-rw-r--r--src/test/ui/pattern/or-pattern-macro-pat.rs44
-rw-r--r--src/test/ui/pattern/usefulness/issue-15129.rs8
-rw-r--r--src/test/ui/pattern/usefulness/issue-15129.stderr4
-rw-r--r--src/test/ui/pattern/usefulness/issue-2111.rs13
-rw-r--r--src/test/ui/pattern/usefulness/issue-2111.stderr8
-rw-r--r--src/test/ui/pattern/usefulness/issue-56379.rs14
-rw-r--r--src/test/ui/pattern/usefulness/issue-56379.stderr22
-rw-r--r--src/test/ui/pattern/usefulness/non-exhaustive-match.rs2
-rw-r--r--src/test/ui/pattern/usefulness/non-exhaustive-match.stderr4
-rw-r--r--src/test/ui/print_type_sizes/niche-filling.rs16
-rw-r--r--src/test/ui/print_type_sizes/niche-filling.stdout6
-rw-r--r--src/test/ui/suggestions/impl-trait-with-missing-bounds.rs8
-rw-r--r--src/test/ui/suggestions/impl-trait-with-missing-bounds.stderr17
-rw-r--r--src/test/ui/suggestions/issue-79843-impl-trait-with-missing-bounds-on-async-fn.rs32
-rw-r--r--src/test/ui/suggestions/issue-79843-impl-trait-with-missing-bounds-on-async-fn.stderr33
-rw-r--r--src/test/ui/traits/impl-evaluation-order.rs39
-rw-r--r--src/tools/clippy/.github/workflows/clippy.yml34
-rw-r--r--src/tools/clippy/.github/workflows/clippy_bors.yml97
-rw-r--r--src/tools/clippy/.github/workflows/clippy_dev.yml10
-rw-r--r--src/tools/clippy/CHANGELOG.md3
-rw-r--r--src/tools/clippy/CONTRIBUTING.md61
-rw-r--r--src/tools/clippy/Cargo.toml1
-rw-r--r--src/tools/clippy/README.md17
-rw-r--r--src/tools/clippy/clippy_dev/src/bless.rs74
-rw-r--r--src/tools/clippy/clippy_dev/src/fmt.rs21
-rw-r--r--src/tools/clippy/clippy_dev/src/lib.rs1
-rw-r--r--src/tools/clippy/clippy_dev/src/main.rs102
-rw-r--r--src/tools/clippy/clippy_dev/src/ra_setup.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/await_holding_invalid.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/checked_conversions.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/create_dir.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/doc.rs139
-rw-r--r--src/tools/clippy/clippy_lints/src/functions.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/len_zero.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs30
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_ok_or.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/matches.rs27
-rw-r--r--src/tools/clippy/clippy_lints/src/mem_replace.rs27
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs153
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs31
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_doc.rs64
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_borrow.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_update.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/non_expressive_names.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs68
-rw-r--r--src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/ranges.rs36
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_clone.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_else.rs135
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_field_names.rs25
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/ref_option_ref.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/strings.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/types.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/use_self.rs25
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/ast_utils.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/conf.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints.rs78
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/mod.rs35
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/paths.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/usage.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/write.rs107
-rw-r--r--src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs82
-rw-r--r--src/tools/clippy/clippy_workspace_tests/path_dep/Cargo.toml3
-rw-r--r--src/tools/clippy/clippy_workspace_tests/path_dep/src/lib.rs6
-rw-r--r--src/tools/clippy/clippy_workspace_tests/subcrate/Cargo.toml3
-rw-r--r--src/tools/clippy/doc/adding_lints.md70
-rw-r--r--src/tools/clippy/doc/basics.md34
-rw-r--r--src/tools/clippy/rust-toolchain4
-rwxr-xr-xsrc/tools/clippy/setup-toolchain.sh36
-rw-r--r--src/tools/clippy/src/driver.rs144
-rw-r--r--src/tools/clippy/src/main.rs106
-rw-r--r--src/tools/clippy/tests/dogfood.rs73
-rw-r--r--src/tools/clippy/tests/integration.rs2
-rwxr-xr-xsrc/tools/clippy/tests/ui-cargo/update-all-references.sh17
-rwxr-xr-xsrc/tools/clippy/tests/ui-cargo/update-references.sh46
-rw-r--r--src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed33
-rw-r--r--src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs33
-rw-r--r--src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr27
-rwxr-xr-xsrc/tools/clippy/tests/ui-toml/update-all-references.sh17
-rwxr-xr-xsrc/tools/clippy/tests/ui-toml/update-references.sh46
-rw-r--r--src/tools/clippy/tests/ui/clone_on_copy.stderr10
-rw-r--r--src/tools/clippy/tests/ui/eprint_with_newline.rs49
-rw-r--r--src/tools/clippy/tests/ui/eprint_with_newline.stderr121
-rw-r--r--src/tools/clippy/tests/ui/eta.stderr2
-rw-r--r--src/tools/clippy/tests/ui/formatting.rs87
-rw-r--r--src/tools/clippy/tests/ui/formatting.stderr89
-rw-r--r--src/tools/clippy/tests/ui/match_single_binding.fixed28
-rw-r--r--src/tools/clippy/tests/ui/match_single_binding.rs33
-rw-r--r--src/tools/clippy/tests/ui/match_single_binding.stderr13
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_attr.rs84
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_attr.stderr8
-rw-r--r--src/tools/clippy/tests/ui/missing-doc-crate-missing.stderr2
-rw-r--r--src/tools/clippy/tests/ui/missing-doc-impl.stderr12
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow.stderr4
-rw-r--r--src/tools/clippy/tests/ui/needless_doc_main.rs10
-rw-r--r--src/tools/clippy/tests/ui/needless_update.rs11
-rw-r--r--src/tools/clippy/tests/ui/needless_update.stderr2
-rw-r--r--src/tools/clippy/tests/ui/panic_in_result_fn.stderr24
-rw-r--r--src/tools/clippy/tests/ui/panic_in_result_fn_assertions.rs48
-rw-r--r--src/tools/clippy/tests/ui/panic_in_result_fn_assertions.stderr57
-rw-r--r--src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.rs48
-rw-r--r--src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.stderr57
-rw-r--r--src/tools/clippy/tests/ui/print_stderr.rs8
-rw-r--r--src/tools/clippy/tests/ui/print_stderr.stderr16
-rw-r--r--src/tools/clippy/tests/ui/println_empty_string.fixed7
-rw-r--r--src/tools/clippy/tests/ui/println_empty_string.rs7
-rw-r--r--src/tools/clippy/tests/ui/println_empty_string.stderr14
-rw-r--r--src/tools/clippy/tests/ui/range_contains.fixed5
-rw-r--r--src/tools/clippy/tests/ui/range_contains.rs5
-rw-r--r--src/tools/clippy/tests/ui/redundant_else.rs154
-rw-r--r--src/tools/clippy/tests/ui/redundant_else.stderr80
-rw-r--r--src/tools/clippy/tests/ui/suspicious_else_formatting.rs79
-rw-r--r--src/tools/clippy/tests/ui/suspicious_else_formatting.stderr77
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_clone.stderr12
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs4
-rwxr-xr-xsrc/tools/clippy/tests/ui/update-all-references.sh20
-rwxr-xr-xsrc/tools/clippy/tests/ui/update-references.sh56
-rw-r--r--src/tools/clippy/tests/ui/use_self.fixed1
-rw-r--r--src/tools/clippy/tests/ui/use_self.rs1
-rw-r--r--src/tools/clippy/tests/ui/use_self.stderr38
-rw-r--r--src/tools/clippy/tests/ui/wrong_self_convention.rs49
-rw-r--r--src/tools/clippy/tests/ui/wrong_self_convention.stderr62
-rw-r--r--src/tools/clippy/tests/ui/zero_sized_btreemap_values.rs68
-rw-r--r--src/tools/clippy/tests/ui/zero_sized_btreemap_values.stderr107
-rw-r--r--src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs68
-rw-r--r--src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr107
-rw-r--r--src/tools/clippy/triagebot.toml2
-rw-r--r--src/tools/clippy/util/gh-pages/index.html42
234 files changed, 4800 insertions, 1695 deletions
diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
index aee274ab4a8..76e1987459f 100644
--- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
@@ -64,7 +64,7 @@ pub(crate) fn fn_sig_for_fn_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx
         ty::Generator(_, substs, _) => {
             let sig = substs.as_generator().poly_sig();
 
-            let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv);
+            let env_region = ty::ReLateBound(ty::INNERMOST, ty::BoundRegion { kind: ty::BrEnv });
             let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty);
 
             let pin_did = tcx.require_lang_item(rustc_hir::LangItem::Pin, None);
diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
index 34022643101..80e3ed75b85 100644
--- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
@@ -524,8 +524,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         };
 
                         let ty = substs.type_at(0);
-                        if int_type_width_signed(ty, bx.tcx()).is_some() {
-                            bx.atomic_rmw(atom_op, args[0].immediate(), args[1].immediate(), order)
+                        if int_type_width_signed(ty, bx.tcx()).is_some()
+                            || (ty.is_unsafe_ptr() && op == "xchg")
+                        {
+                            let mut ptr = args[0].immediate();
+                            let mut val = args[1].immediate();
+                            if ty.is_unsafe_ptr() {
+                                // Some platforms do not support atomic operations on pointers,
+                                // so we cast to integer first.
+                                let ptr_llty = bx.type_ptr_to(bx.type_isize());
+                                ptr = bx.pointercast(ptr, ptr_llty);
+                                val = bx.ptrtoint(val, bx.type_isize());
+                            }
+                            bx.atomic_rmw(atom_op, ptr, val, order)
                         } else {
                             return invalid_monomorphization(ty);
                         }
diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs
index c37f9125675..3cf2d8f8ac1 100644
--- a/compiler/rustc_expand/src/mbe/macro_parser.rs
+++ b/compiler/rustc_expand/src/mbe/macro_parser.rs
@@ -77,9 +77,9 @@ use TokenTreeOrTokenTreeSlice::*;
 use crate::mbe::{self, TokenTree};
 
 use rustc_ast::token::{self, DocComment, Nonterminal, Token};
-use rustc_parse::parser::Parser;
+use rustc_parse::parser::{OrPatNonterminalMode, Parser};
 use rustc_session::parse::ParseSess;
-use rustc_span::symbol::MacroRulesNormalizedIdent;
+use rustc_span::{edition::Edition, symbol::MacroRulesNormalizedIdent};
 
 use smallvec::{smallvec, SmallVec};
 
@@ -414,6 +414,18 @@ fn token_name_eq(t1: &Token, t2: &Token) -> bool {
     }
 }
 
+/// In edition 2015/18, `:pat` can only match `pat<no_top_alt>` because otherwise, we have
+/// breakage. As of edition 2021, `:pat` matches `top_pat`.
+///
+/// See <https://github.com/rust-lang/rust/issues/54883> for more info.
+fn or_pat_mode(edition: Edition) -> OrPatNonterminalMode {
+    match edition {
+        Edition::Edition2015 | Edition::Edition2018 => OrPatNonterminalMode::NoTopAlt,
+        // FIXME(mark-i-m): uncomment this when edition 2021 machinery is added.
+        // Edition::Edition2021 =>  OrPatNonterminalMode::TopPat,
+    }
+}
+
 /// Process the matcher positions of `cur_items` until it is empty. In the process, this will
 /// produce more items in `next_items`, `eof_items`, and `bb_items`.
 ///
@@ -553,10 +565,14 @@ fn inner_parse_loop<'root, 'tt>(
 
                 // We need to match a metavar with a valid ident... call out to the black-box
                 // parser by adding an item to `bb_items`.
-                TokenTree::MetaVarDecl(_, _, kind) => {
-                    // Built-in nonterminals never start with these tokens,
-                    // so we can eliminate them from consideration.
-                    if Parser::nonterminal_may_begin_with(kind, token) {
+                TokenTree::MetaVarDecl(span, _, kind) => {
+                    // Built-in nonterminals never start with these tokens, so we can eliminate
+                    // them from consideration.
+                    //
+                    // We use the span of the metavariable declaration to determine any
+                    // edition-specific matching behavior for non-terminals.
+                    if Parser::nonterminal_may_begin_with(kind, token, or_pat_mode(span.edition()))
+                    {
                         bb_items.push(item);
                     }
                 }
@@ -717,7 +733,10 @@ pub(super) fn parse_tt(parser: &mut Cow<'_, Parser<'_>>, ms: &[TokenTree]) -> Na
             let mut item = bb_items.pop().unwrap();
             if let TokenTree::MetaVarDecl(span, _, kind) = item.top_elts.get_tt(item.idx) {
                 let match_cur = item.match_cur;
-                let nt = match parser.to_mut().parse_nonterminal(kind) {
+                // We use the span of the metavariable declaration to determine any
+                // edition-specific matching behavior for non-terminals.
+                let nt = match parser.to_mut().parse_nonterminal(kind, or_pat_mode(span.edition()))
+                {
                     Err(mut err) => {
                         err.span_label(
                             span,
diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
index 8a60b196e5e..9002d251f12 100644
--- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
+++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
@@ -625,7 +625,8 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
         r: ty::Region<'tcx>,
     ) -> ty::Region<'tcx> {
         let var = self.canonical_var(info, r.into());
-        let region = ty::ReLateBound(self.binder_index, ty::BoundRegion::BrAnon(var.as_u32()));
+        let br = ty::BoundRegion { kind: ty::BrAnon(var.as_u32()) };
+        let region = ty::ReLateBound(self.binder_index, br);
         self.tcx().mk_region(region)
     }
 
diff --git a/compiler/rustc_infer/src/infer/canonical/substitute.rs b/compiler/rustc_infer/src/infer/canonical/substitute.rs
index cd4f1fa3bc3..387f480814a 100644
--- a/compiler/rustc_infer/src/infer/canonical/substitute.rs
+++ b/compiler/rustc_infer/src/infer/canonical/substitute.rs
@@ -87,6 +87,6 @@ where
             c => bug!("{:?} is a const but value is {:?}", bound_ct, c),
         };
 
-        tcx.replace_escaping_bound_vars(value, fld_r, fld_t, fld_c).0
+        tcx.replace_escaping_bound_vars(value, fld_r, fld_t, fld_c)
     }
 }
diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs
index 7770f2bd911..e38eebe23b1 100644
--- a/compiler/rustc_infer/src/infer/combine.rs
+++ b/compiler/rustc_infer/src/infer/combine.rs
@@ -543,6 +543,10 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
         true
     }
 
+    fn visit_ct_substs(&self) -> bool {
+        true
+    }
+
     fn binders<T>(
         &mut self,
         a: ty::Binder<T>,
@@ -716,7 +720,10 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
                 let variable_table = &mut inner.const_unification_table();
                 let var_value = variable_table.probe_value(vid);
                 match var_value.val {
-                    ConstVariableValue::Known { value: u } => self.relate(u, u),
+                    ConstVariableValue::Known { value: u } => {
+                        drop(inner);
+                        self.relate(u, u)
+                    }
                     ConstVariableValue::Unknown { universe } => {
                         if self.for_universe.can_name(universe) {
                             Ok(c)
@@ -815,6 +822,10 @@ impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> {
         true
     }
 
+    fn visit_ct_substs(&self) -> bool {
+        true
+    }
+
     fn relate_with_variance<T: Relate<'tcx>>(
         &mut self,
         _variance: ty::Variance,
@@ -870,6 +881,7 @@ impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> {
                     }
                 }
             }
+            ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => Ok(t),
             _ => relate::super_relate_tys(self, t, t),
         }
     }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 18e1465c0e6..6b7edde9a67 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -165,7 +165,9 @@ fn msg_span_from_early_bound_and_free_regions(
             }
             (format!("the lifetime `{}` as defined on", br.name), sp)
         }
-        ty::ReFree(ty::FreeRegion { bound_region: ty::BoundRegion::BrNamed(_, name), .. }) => {
+        ty::ReFree(ty::FreeRegion {
+            bound_region: ty::BoundRegionKind::BrNamed(_, name), ..
+        }) => {
             let mut sp = sm.guess_head_span(tcx.hir().span(node));
             if let Some(param) =
                 tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(name))
@@ -2279,7 +2281,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         &self,
         var_origin: RegionVariableOrigin,
     ) -> DiagnosticBuilder<'tcx> {
-        let br_string = |br: ty::BoundRegion| {
+        let br_string = |br: ty::BoundRegionKind| {
             let mut s = match br {
                 ty::BrNamed(_, name) => name.to_string(),
                 _ => String::new(),
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs
index eb1521f0565..b014b9832e7 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs
@@ -25,7 +25,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
     pub(super) fn find_anon_type(
         &self,
         region: Region<'tcx>,
-        br: &ty::BoundRegion,
+        br: &ty::BoundRegionKind,
     ) -> Option<(&hir::Ty<'tcx>, &hir::FnDecl<'tcx>)> {
         if let Some(anon_reg) = self.tcx().is_suitable_region(region) {
             let hir_id = self.tcx().hir().local_def_id_to_hir_id(anon_reg.def_id);
@@ -56,7 +56,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
     fn find_component_for_bound_region(
         &self,
         arg: &'tcx hir::Ty<'tcx>,
-        br: &ty::BoundRegion,
+        br: &ty::BoundRegionKind,
     ) -> Option<&'tcx hir::Ty<'tcx>> {
         let mut nested_visitor = FindNestedTypeVisitor {
             tcx: self.tcx(),
@@ -80,7 +80,7 @@ struct FindNestedTypeVisitor<'tcx> {
     tcx: TyCtxt<'tcx>,
     // The bound_region corresponding to the Refree(freeregion)
     // associated with the anonymous region we are looking for.
-    bound_region: ty::BoundRegion,
+    bound_region: ty::BoundRegionKind,
     // The type where the anonymous lifetime appears
     // for e.g., Vec<`&u8`> and <`&u8`>
     found_type: Option<&'tcx hir::Ty<'tcx>>,
@@ -207,7 +207,7 @@ impl Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
 struct TyPathVisitor<'tcx> {
     tcx: TyCtxt<'tcx>,
     found_it: bool,
-    bound_region: ty::BoundRegion,
+    bound_region: ty::BoundRegionKind,
     current_index: ty::DebruijnIndex,
 }
 
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
index 61fad8863e7..17a56046a5c 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
@@ -14,8 +14,8 @@ pub(super) struct AnonymousParamInfo<'tcx> {
     pub param: &'tcx hir::Param<'tcx>,
     /// The type corresponding to the anonymous region parameter.
     pub param_ty: Ty<'tcx>,
-    /// The ty::BoundRegion corresponding to the anonymous region.
-    pub bound_region: ty::BoundRegion,
+    /// The ty::BoundRegionKind corresponding to the anonymous region.
+    pub bound_region: ty::BoundRegionKind,
     /// The `Span` of the parameter type.
     pub param_ty_span: Span,
     /// Signals that the argument is the first parameter in the declaration.
@@ -43,7 +43,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
             ty::ReFree(ref free_region) => (free_region.scope, free_region.bound_region),
             ty::ReEarlyBound(ebr) => (
                 self.tcx().parent(ebr.def_id).unwrap(),
-                ty::BoundRegion::BrNamed(ebr.def_id, ebr.name),
+                ty::BoundRegionKind::BrNamed(ebr.def_id, ebr.name),
             ),
             _ => return None, // not a free region
         };
@@ -145,7 +145,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
     pub(super) fn is_return_type_anon(
         &self,
         scope_def_id: LocalDefId,
-        br: ty::BoundRegion,
+        br: ty::BoundRegionKind,
         decl: &hir::FnDecl<'_>,
     ) -> Option<Span> {
         let ret_ty = self.tcx().type_of(scope_def_id);
diff --git a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs
index 39043980dc4..e794903fca3 100644
--- a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs
+++ b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs
@@ -77,10 +77,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         // (i.e., if there are no placeholders).
         let next_universe = self.universe().next_universe();
 
-        let fld_r = |br| {
+        let fld_r = |br: ty::BoundRegion| {
             self.tcx.mk_region(ty::RePlaceholder(ty::PlaceholderRegion {
                 universe: next_universe,
-                name: br,
+                name: br.kind,
             }))
         };
 
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 6affe0e5463..069f708856e 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -450,7 +450,7 @@ pub enum RegionVariableOrigin {
 
     /// Region variables created for bound regions
     /// in a function or method that is called
-    LateBoundRegion(Span, ty::BoundRegion, LateBoundRegionConversionTime),
+    LateBoundRegion(Span, ty::BoundRegionKind, LateBoundRegionConversionTime),
 
     UpvarRegion(ty::UpvarId, Span),
 
@@ -1421,7 +1421,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     where
         T: TypeFoldable<'tcx>,
     {
-        let fld_r = |br| self.next_region_var(LateBoundRegion(span, br, lbrct));
+        let fld_r =
+            |br: ty::BoundRegion| self.next_region_var(LateBoundRegion(span, br.kind, lbrct));
         let fld_t = |_| {
             self.next_ty_var(TypeVariableOrigin {
                 kind: TypeVariableOriginKind::MiscVariable,
diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs
index b9bd66e4b92..97ef685cf6f 100644
--- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs
+++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs
@@ -176,7 +176,7 @@ where
                         universe
                     });
 
-                    let placeholder = ty::PlaceholderRegion { universe, name: br };
+                    let placeholder = ty::PlaceholderRegion { universe, name: br.kind };
                     delegate.next_placeholder_region(placeholder)
                 } else {
                     delegate.next_existential_region_var(true)
diff --git a/compiler/rustc_middle/src/hir/place.rs b/compiler/rustc_middle/src/hir/place.rs
index 143b3867d9f..1e2e9df88c5 100644
--- a/compiler/rustc_middle/src/hir/place.rs
+++ b/compiler/rustc_middle/src/hir/place.rs
@@ -17,13 +17,13 @@ use rustc_target::abi::VariantIdx;
     HashStable
 )]
 pub enum PlaceBase {
-    /// A temporary variable
+    /// A temporary variable.
     Rvalue,
-    /// A named `static` item
+    /// A named `static` item.
     StaticItem,
-    /// A named local variable
+    /// A named local variable.
     Local(HirId),
-    /// An upvar referenced by closure env
+    /// An upvar referenced by closure env.
     Upvar(ty::UpvarId),
 }
 
@@ -40,7 +40,7 @@ pub enum PlaceBase {
     HashStable
 )]
 pub enum ProjectionKind {
-    /// A dereference of a pointer, reference or `Box<T>` of the given type
+    /// A dereference of a pointer, reference or `Box<T>` of the given type.
     Deref,
 
     /// `B.F` where `B` is the base expression and `F` is
@@ -71,16 +71,16 @@ pub enum ProjectionKind {
     HashStable
 )]
 pub struct Projection<'tcx> {
-    /// Type after the projection is being applied.
+    /// Type after the projection is applied.
     pub ty: Ty<'tcx>,
 
-    /// Defines the type of access
+    /// Defines the kind of access made by the projection.
     pub kind: ProjectionKind,
 }
 
 /// A `Place` represents how a value is located in memory.
 ///
-/// This is an HIR version of `mir::Place`
+/// This is an HIR version of [`rustc_middle::mir::Place`].
 #[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeFoldable, HashStable)]
 pub struct Place<'tcx> {
     /// The type of the `PlaceBase`
@@ -93,13 +93,13 @@ pub struct Place<'tcx> {
 
 /// A `PlaceWithHirId` represents how a value is located in memory.
 ///
-/// This is an HIR version of `mir::Place`
+/// This is an HIR version of [`rustc_middle::mir::Place`].
 #[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, TypeFoldable, HashStable)]
 pub struct PlaceWithHirId<'tcx> {
     /// `HirId` of the expression or pattern producing this value.
     pub hir_id: HirId,
 
-    /// Information about the `Place`
+    /// Information about the `Place`.
     pub place: Place<'tcx>,
 }
 
diff --git a/compiler/rustc_middle/src/ich/impls_ty.rs b/compiler/rustc_middle/src/ich/impls_ty.rs
index 69bb4e23c4c..573b514e844 100644
--- a/compiler/rustc_middle/src/ich/impls_ty.rs
+++ b/compiler/rustc_middle/src/ich/impls_ty.rs
@@ -70,16 +70,16 @@ impl<'a> HashStable<StableHashingContext<'a>> for ty::RegionKind {
             ty::ReEmpty(universe) => {
                 universe.hash_stable(hcx, hasher);
             }
-            ty::ReLateBound(db, ty::BrAnon(i)) => {
+            ty::ReLateBound(db, ty::BoundRegion { kind: ty::BrAnon(i) }) => {
                 db.hash_stable(hcx, hasher);
                 i.hash_stable(hcx, hasher);
             }
-            ty::ReLateBound(db, ty::BrNamed(def_id, name)) => {
+            ty::ReLateBound(db, ty::BoundRegion { kind: ty::BrNamed(def_id, name) }) => {
                 db.hash_stable(hcx, hasher);
                 def_id.hash_stable(hcx, hasher);
                 name.hash_stable(hcx, hasher);
             }
-            ty::ReLateBound(db, ty::BrEnv) => {
+            ty::ReLateBound(db, ty::BoundRegion { kind: ty::BrEnv }) => {
                 db.hash_stable(hcx, hasher);
             }
             ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, index, name }) => {
diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs
index 6e5f95c4527..e106db38b2c 100644
--- a/compiler/rustc_middle/src/infer/canonical.rs
+++ b/compiler/rustc_middle/src/infer/canonical.rs
@@ -323,9 +323,10 @@ impl<'tcx> CanonicalVarValues<'tcx> {
                     GenericArgKind::Type(..) => {
                         tcx.mk_ty(ty::Bound(ty::INNERMOST, ty::BoundVar::from_u32(i).into())).into()
                     }
-                    GenericArgKind::Lifetime(..) => tcx
-                        .mk_region(ty::ReLateBound(ty::INNERMOST, ty::BoundRegion::BrAnon(i)))
-                        .into(),
+                    GenericArgKind::Lifetime(..) => {
+                        let br = ty::BoundRegion { kind: ty::BrAnon(i) };
+                        tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)).into()
+                    }
                     GenericArgKind::Const(ct) => tcx
                         .mk_const(ty::Const {
                             ty: ct.ty,
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 615972ae45c..4205e2ca5aa 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -890,7 +890,7 @@ pub struct FreeRegionInfo {
     // `LocalDefId` corresponding to FreeRegion
     pub def_id: LocalDefId,
     // the bound region corresponding to FreeRegion
-    pub boundregion: ty::BoundRegion,
+    pub boundregion: ty::BoundRegionKind,
     // checks if bound region is in Impl Item
     pub is_impl_item: bool,
 }
@@ -1412,7 +1412,7 @@ impl<'tcx> TyCtxt<'tcx> {
         })
     }
 
-    // Returns the `DefId` and the `BoundRegion` corresponding to the given region.
+    // Returns the `DefId` and the `BoundRegionKind` corresponding to the given region.
     pub fn is_suitable_region(self, region: Region<'tcx>) -> Option<FreeRegionInfo> {
         let (suitable_region_binding_scope, bound_region) = match *region {
             ty::ReFree(ref free_region) => {
@@ -1420,7 +1420,7 @@ impl<'tcx> TyCtxt<'tcx> {
             }
             ty::ReEarlyBound(ref ebr) => (
                 self.parent(ebr.def_id).unwrap().expect_local(),
-                ty::BoundRegion::BrNamed(ebr.def_id, ebr.name),
+                ty::BoundRegionKind::BrNamed(ebr.def_id, ebr.name),
             ),
             _ => return None, // not a free region
         };
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index 97af927dfcb..fc02e78b2fa 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -1,6 +1,6 @@
 use crate::traits::{ObligationCause, ObligationCauseCode};
 use crate::ty::diagnostics::suggest_constraining_type_param;
-use crate::ty::{self, BoundRegion, Region, Ty, TyCtxt};
+use crate::ty::{self, BoundRegionKind, Region, Ty, TyCtxt};
 use rustc_ast as ast;
 use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect};
 use rustc_errors::{pluralize, DiagnosticBuilder};
@@ -42,8 +42,8 @@ pub enum TypeError<'tcx> {
     ArgCount,
 
     RegionsDoesNotOutlive(Region<'tcx>, Region<'tcx>),
-    RegionsInsufficientlyPolymorphic(BoundRegion, Region<'tcx>),
-    RegionsOverlyPolymorphic(BoundRegion, Region<'tcx>),
+    RegionsInsufficientlyPolymorphic(BoundRegionKind, Region<'tcx>),
+    RegionsOverlyPolymorphic(BoundRegionKind, Region<'tcx>),
     RegionsPlaceholderMismatch,
 
     Sorts(ExpectedFound<Ty<'tcx>>),
@@ -94,7 +94,7 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
             }
         }
 
-        let br_string = |br: ty::BoundRegion| match br {
+        let br_string = |br: ty::BoundRegionKind| match br {
             ty::BrNamed(_, name) => format!(" {}", name),
             _ => String::new(),
         };
diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs
index 13c8d6b2bcc..382f3708c3d 100644
--- a/compiler/rustc_middle/src/ty/fold.rs
+++ b/compiler/rustc_middle/src/ty/fold.rs
@@ -534,8 +534,8 @@ impl<'tcx> TyCtxt<'tcx> {
     /// results returned by the closure; the closure is expected to
     /// return a free region (relative to this binder), and hence the
     /// binder is removed in the return type. The closure is invoked
-    /// once for each unique `BoundRegion`; multiple references to the
-    /// same `BoundRegion` will reuse the previous result. A map is
+    /// once for each unique `BoundRegionKind`; multiple references to the
+    /// same `BoundRegionKind` will reuse the previous result. A map is
     /// returned at the end with each bound region and the free region
     /// that replaced it.
     ///
@@ -544,7 +544,7 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn replace_late_bound_regions<T, F>(
         self,
         value: Binder<T>,
-        fld_r: F,
+        mut fld_r: F,
     ) -> (T, BTreeMap<ty::BoundRegion, ty::Region<'tcx>>)
     where
         F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
@@ -555,7 +555,10 @@ impl<'tcx> TyCtxt<'tcx> {
         let fld_c = |bound_ct, ty| {
             self.mk_const(ty::Const { val: ty::ConstKind::Bound(ty::INNERMOST, bound_ct), ty })
         };
-        self.replace_escaping_bound_vars(value.skip_binder(), fld_r, fld_t, fld_c)
+        let mut region_map = BTreeMap::new();
+        let real_fld_r = |br: ty::BoundRegion| *region_map.entry(br).or_insert_with(|| fld_r(br));
+        let value = self.replace_escaping_bound_vars(value.skip_binder(), real_fld_r, fld_t, fld_c);
+        (value, region_map)
     }
 
     /// Replaces all escaping bound vars. The `fld_r` closure replaces escaping
@@ -567,34 +570,18 @@ impl<'tcx> TyCtxt<'tcx> {
         mut fld_r: F,
         mut fld_t: G,
         mut fld_c: H,
-    ) -> (T, BTreeMap<ty::BoundRegion, ty::Region<'tcx>>)
+    ) -> T
     where
         F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
         G: FnMut(ty::BoundTy) -> Ty<'tcx>,
         H: FnMut(ty::BoundVar, Ty<'tcx>) -> &'tcx ty::Const<'tcx>,
         T: TypeFoldable<'tcx>,
     {
-        use rustc_data_structures::fx::FxHashMap;
-
-        let mut region_map = BTreeMap::new();
-        let mut type_map = FxHashMap::default();
-        let mut const_map = FxHashMap::default();
-
         if !value.has_escaping_bound_vars() {
-            (value, region_map)
+            value
         } else {
-            let mut real_fld_r = |br| *region_map.entry(br).or_insert_with(|| fld_r(br));
-
-            let mut real_fld_t =
-                |bound_ty| *type_map.entry(bound_ty).or_insert_with(|| fld_t(bound_ty));
-
-            let mut real_fld_c =
-                |bound_ct, ty| *const_map.entry(bound_ct).or_insert_with(|| fld_c(bound_ct, ty));
-
-            let mut replacer =
-                BoundVarReplacer::new(self, &mut real_fld_r, &mut real_fld_t, &mut real_fld_c);
-            let result = value.fold_with(&mut replacer);
-            (result, region_map)
+            let mut replacer = BoundVarReplacer::new(self, &mut fld_r, &mut fld_t, &mut fld_c);
+            value.fold_with(&mut replacer)
         }
     }
 
@@ -604,7 +591,7 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn replace_bound_vars<T, F, G, H>(
         self,
         value: Binder<T>,
-        fld_r: F,
+        mut fld_r: F,
         fld_t: G,
         fld_c: H,
     ) -> (T, BTreeMap<ty::BoundRegion, ty::Region<'tcx>>)
@@ -614,7 +601,10 @@ impl<'tcx> TyCtxt<'tcx> {
         H: FnMut(ty::BoundVar, Ty<'tcx>) -> &'tcx ty::Const<'tcx>,
         T: TypeFoldable<'tcx>,
     {
-        self.replace_escaping_bound_vars(value.skip_binder(), fld_r, fld_t, fld_c)
+        let mut region_map = BTreeMap::new();
+        let real_fld_r = |br: ty::BoundRegion| *region_map.entry(br).or_insert_with(|| fld_r(br));
+        let value = self.replace_escaping_bound_vars(value.skip_binder(), real_fld_r, fld_t, fld_c);
+        (value, region_map)
     }
 
     /// Replaces any late-bound regions bound in `value` with
@@ -626,7 +616,7 @@ impl<'tcx> TyCtxt<'tcx> {
         self.replace_late_bound_regions(value, |br| {
             self.mk_region(ty::ReFree(ty::FreeRegion {
                 scope: all_outlive_scope,
-                bound_region: br,
+                bound_region: br.kind,
             }))
         })
         .0
@@ -639,7 +629,7 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn collect_constrained_late_bound_regions<T>(
         self,
         value: &Binder<T>,
-    ) -> FxHashSet<ty::BoundRegion>
+    ) -> FxHashSet<ty::BoundRegionKind>
     where
         T: TypeFoldable<'tcx>,
     {
@@ -650,7 +640,7 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn collect_referenced_late_bound_regions<T>(
         self,
         value: &Binder<T>,
-    ) -> FxHashSet<ty::BoundRegion>
+    ) -> FxHashSet<ty::BoundRegionKind>
     where
         T: TypeFoldable<'tcx>,
     {
@@ -661,7 +651,7 @@ impl<'tcx> TyCtxt<'tcx> {
         self,
         value: &Binder<T>,
         just_constraint: bool,
-    ) -> FxHashSet<ty::BoundRegion>
+    ) -> FxHashSet<ty::BoundRegionKind>
     where
         T: TypeFoldable<'tcx>,
     {
@@ -695,7 +685,8 @@ impl<'tcx> TyCtxt<'tcx> {
         let mut counter = 0;
         Binder::bind(
             self.replace_late_bound_regions(sig, |_| {
-                let r = self.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(counter)));
+                let br = ty::BoundRegion { kind: ty::BrAnon(counter) };
+                let r = self.mk_region(ty::ReLateBound(ty::INNERMOST, br));
                 counter += 1;
                 r
             })
@@ -955,7 +946,7 @@ impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor {
 /// into a hash set.
 struct LateBoundRegionsCollector {
     current_index: ty::DebruijnIndex,
-    regions: FxHashSet<ty::BoundRegion>,
+    regions: FxHashSet<ty::BoundRegionKind>,
 
     /// `true` if we only want regions that are known to be
     /// "constrained" when you equate this type with another type. In
@@ -1014,7 +1005,7 @@ impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector {
     fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
         if let ty::ReLateBound(debruijn, br) = *r {
             if debruijn == self.current_index {
-                self.regions.insert(br);
+                self.regions.insert(br.kind);
             }
         }
         ControlFlow::CONTINUE
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index d6b3afb3be3..b545b92c925 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -2455,7 +2455,8 @@ impl<'tcx> ty::Instance<'tcx> {
             ty::Generator(_, substs, _) => {
                 let sig = substs.as_generator().poly_sig();
 
-                let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv);
+                let br = ty::BoundRegion { kind: ty::BrEnv };
+                let env_region = ty::ReLateBound(ty::INNERMOST, br);
                 let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty);
 
                 let pin_did = tcx.require_lang_item(LangItem::Pin, None);
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 98602d6a459..c163a37c5a1 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -51,13 +51,13 @@ use std::ops::{ControlFlow, Range};
 use std::ptr;
 use std::str;
 
-pub use self::sty::BoundRegion::*;
+pub use self::sty::BoundRegionKind::*;
 pub use self::sty::InferTy::*;
 pub use self::sty::RegionKind;
 pub use self::sty::RegionKind::*;
 pub use self::sty::TyKind::*;
 pub use self::sty::{Binder, BoundTy, BoundTyKind, BoundVar};
-pub use self::sty::{BoundRegion, EarlyBoundRegion, FreeRegion, Region};
+pub use self::sty::{BoundRegion, BoundRegionKind, EarlyBoundRegion, FreeRegion, Region};
 pub use self::sty::{CanonicalPolyFnSig, FnSig, GenSig, PolyFnSig, PolyGenSig};
 pub use self::sty::{ClosureSubsts, GeneratorSubsts, TypeAndMut, UpvarSubsts};
 pub use self::sty::{ClosureSubstsParts, GeneratorSubstsParts};
@@ -1607,7 +1607,7 @@ where
     }
 }
 
-pub type PlaceholderRegion = Placeholder<BoundRegion>;
+pub type PlaceholderRegion = Placeholder<BoundRegionKind>;
 
 pub type PlaceholderType = Placeholder<BoundVar>;
 
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 09ef69e9690..9b178d9d2bd 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -125,13 +125,13 @@ pub struct RegionHighlightMode {
     highlight_regions: [Option<(ty::RegionKind, usize)>; 3],
 
     /// If enabled, when printing a "free region" that originated from
-    /// the given `ty::BoundRegion`, print it as "`'1`". Free regions that would ordinarily
+    /// the given `ty::BoundRegionKind`, print it as "`'1`". Free regions that would ordinarily
     /// have names print as normal.
     ///
     /// This is used when you have a signature like `fn foo(x: &u32,
     /// y: &'a u32)` and we want to give a name to the region of the
     /// reference `x`.
-    highlight_bound_region: Option<(ty::BoundRegion, usize)>,
+    highlight_bound_region: Option<(ty::BoundRegionKind, usize)>,
 }
 
 impl RegionHighlightMode {
@@ -175,7 +175,7 @@ impl RegionHighlightMode {
     /// Highlight the given bound region.
     /// We can only highlight one bound region at a time. See
     /// the field `highlight_bound_region` for more detailed notes.
-    pub fn highlighting_bound_region(&mut self, br: ty::BoundRegion, number: usize) {
+    pub fn highlighting_bound_region(&mut self, br: ty::BoundRegionKind, number: usize) {
         assert!(self.highlight_bound_region.is_none());
         self.highlight_bound_region = Some((br, number));
     }
@@ -1611,7 +1611,7 @@ impl<F: fmt::Write> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx, F> {
                 data.name != kw::Invalid && data.name != kw::UnderscoreLifetime
             }
 
-            ty::ReLateBound(_, br)
+            ty::ReLateBound(_, ty::BoundRegion { kind: br })
             | ty::ReFree(ty::FreeRegion { bound_region: br, .. })
             | ty::RePlaceholder(ty::Placeholder { name: br, .. }) => {
                 if let ty::BrNamed(_, name) = br {
@@ -1690,7 +1690,7 @@ impl<F: fmt::Write> FmtPrinter<'_, '_, F> {
                     return Ok(self);
                 }
             }
-            ty::ReLateBound(_, br)
+            ty::ReLateBound(_, ty::BoundRegion { kind: br })
             | ty::ReFree(ty::FreeRegion { bound_region: br, .. })
             | ty::RePlaceholder(ty::Placeholder { name: br, .. }) => {
                 if let ty::BrNamed(_, name) = br {
@@ -1779,10 +1779,10 @@ impl<F: fmt::Write> FmtPrinter<'_, 'tcx, F> {
         let mut region_index = self.region_index;
         let new_value = self.tcx.replace_late_bound_regions(value.clone(), |br| {
             let _ = start_or_continue(&mut self, "for<", ", ");
-            let br = match br {
+            let kind = match br.kind {
                 ty::BrNamed(_, name) => {
                     let _ = write!(self, "{}", name);
-                    br
+                    br.kind
                 }
                 ty::BrAnon(_) | ty::BrEnv => {
                     let name = loop {
@@ -1796,7 +1796,7 @@ impl<F: fmt::Write> FmtPrinter<'_, 'tcx, F> {
                     ty::BrNamed(DefId::local(CRATE_DEF_INDEX), name)
                 }
             };
-            self.tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br))
+            self.tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BoundRegion { kind }))
         });
         start_or_continue(&mut self, "", "> ")?;
 
@@ -1840,7 +1840,7 @@ impl<F: fmt::Write> FmtPrinter<'_, 'tcx, F> {
         struct LateBoundRegionNameCollector<'a>(&'a mut FxHashSet<Symbol>);
         impl<'tcx> ty::fold::TypeVisitor<'tcx> for LateBoundRegionNameCollector<'_> {
             fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
-                if let ty::ReLateBound(_, ty::BrNamed(_, name)) = *r {
+                if let ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrNamed(_, name) }) = *r {
                     self.0.insert(name);
                 }
                 r.super_visit_with(self)
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index af7fc429719..293b3c6b047 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -33,6 +33,15 @@ pub trait TypeRelation<'tcx>: Sized {
     /// relation. Just affects error messages.
     fn a_is_expected(&self) -> bool;
 
+    /// Whether we should look into the substs of unevaluated constants
+    /// even if `feature(const_evaluatable_checked)` is active.
+    ///
+    /// This is needed in `combine` to prevent accidentially creating
+    /// infinite types as we abuse `TypeRelation` to walk a type there.
+    fn visit_ct_substs(&self) -> bool {
+        false
+    }
+
     fn with_cause<F, R>(&mut self, _cause: Cause, f: F) -> R
     where
         F: FnOnce(&mut Self) -> R,
@@ -579,7 +588,7 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>(
         (
             ty::ConstKind::Unevaluated(a_def, a_substs, None),
             ty::ConstKind::Unevaluated(b_def, b_substs, None),
-        ) if tcx.features().const_evaluatable_checked => {
+        ) if tcx.features().const_evaluatable_checked && !relation.visit_ct_substs() => {
             if tcx.try_unify_abstract_consts(((a_def, a_substs), (b_def, b_substs))) {
                 Ok(a.val)
             } else {
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index 8af5792b3fb..7a1ca6a6c2b 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -65,7 +65,7 @@ impl fmt::Debug for ty::adjustment::Adjustment<'tcx> {
     }
 }
 
-impl fmt::Debug for ty::BoundRegion {
+impl fmt::Debug for ty::BoundRegionKind {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match *self {
             ty::BrAnon(n) => write!(f, "BrAnon({:?})", n),
@@ -308,13 +308,13 @@ TrivialTypeFoldableAndLiftImpls! {
     crate::traits::Reveal,
     crate::ty::adjustment::AutoBorrowMutability,
     crate::ty::AdtKind,
-    // Including `BoundRegion` is a *bit* dubious, but direct
+    // Including `BoundRegionKind` is a *bit* dubious, but direct
     // references to bound region appear in `ty::Error`, and aren't
     // really meant to be folded. In general, we can only fold a fully
     // general `Region`.
-    crate::ty::BoundRegion,
+    crate::ty::BoundRegionKind,
     crate::ty::AssocItem,
-    crate::ty::Placeholder<crate::ty::BoundRegion>,
+    crate::ty::Placeholder<crate::ty::BoundRegionKind>,
     crate::ty::ClosureKind,
     crate::ty::FreeRegion,
     crate::ty::InferTy,
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 62d1dda37d6..dc72a713a7d 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -40,12 +40,12 @@ pub struct TypeAndMut<'tcx> {
 /// at least as big as the scope `fr.scope`".
 pub struct FreeRegion {
     pub scope: DefId,
-    pub bound_region: BoundRegion,
+    pub bound_region: BoundRegionKind,
 }
 
 #[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, TyEncodable, TyDecodable, Copy)]
 #[derive(HashStable)]
-pub enum BoundRegion {
+pub enum BoundRegionKind {
     /// An anonymous region parameter for a given fn (&T)
     BrAnon(u32),
 
@@ -60,26 +60,36 @@ pub enum BoundRegion {
     BrEnv,
 }
 
-impl BoundRegion {
-    pub fn is_named(&self) -> bool {
-        match *self {
-            BoundRegion::BrNamed(_, name) => name != kw::UnderscoreLifetime,
-            _ => false,
-        }
-    }
+#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug, PartialOrd, Ord)]
+#[derive(HashStable)]
+pub struct BoundRegion {
+    pub kind: BoundRegionKind,
+}
 
+impl BoundRegion {
     /// When canonicalizing, we replace unbound inference variables and free
     /// regions with anonymous late bound regions. This method asserts that
     /// we have an anonymous late bound region, which hence may refer to
     /// a canonical variable.
     pub fn assert_bound_var(&self) -> BoundVar {
-        match *self {
-            BoundRegion::BrAnon(var) => BoundVar::from_u32(var),
+        match self.kind {
+            BoundRegionKind::BrAnon(var) => BoundVar::from_u32(var),
             _ => bug!("bound region is not anonymous"),
         }
     }
 }
 
+impl BoundRegionKind {
+    pub fn is_named(&self) -> bool {
+        match *self {
+            BoundRegionKind::BrNamed(_, name) => name != kw::UnderscoreLifetime,
+            _ => false,
+        }
+    }
+}
+
+/// Defines the kinds of types.
+///
 /// N.B., if you change this, you'll probably want to change the corresponding
 /// AST structure in `librustc_ast/ast.rs` as well.
 #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable, Debug)]
@@ -102,7 +112,7 @@ pub enum TyKind<'tcx> {
     /// A primitive floating-point type. For example, `f64`.
     Float(ast::FloatTy),
 
-    /// Structures, enumerations and unions.
+    /// Algebraic data types (ADT). For example: structures, enumerations and unions.
     ///
     /// InternalSubsts here, possibly against intuition, *may* contain `Param`s.
     /// That is, even after substitution it is possible that there are type
@@ -162,11 +172,11 @@ pub enum TyKind<'tcx> {
     /// `|a| yield a`.
     Generator(DefId, SubstsRef<'tcx>, hir::Movability),
 
-    /// A type representin the types stored inside a generator.
+    /// A type representing the types stored inside a generator.
     /// This should only appear in GeneratorInteriors.
     GeneratorWitness(Binder<&'tcx List<Ty<'tcx>>>),
 
-    /// The never type `!`
+    /// The never type `!`.
     Never,
 
     /// A tuple type. For example, `(i32, bool)`.
@@ -1551,7 +1561,7 @@ impl RegionKind {
     pub fn has_name(&self) -> bool {
         match *self {
             RegionKind::ReEarlyBound(ebr) => ebr.has_name(),
-            RegionKind::ReLateBound(_, br) => br.is_named(),
+            RegionKind::ReLateBound(_, br) => br.kind.is_named(),
             RegionKind::ReFree(fr) => fr.bound_region.is_named(),
             RegionKind::ReStatic => true,
             RegionKind::ReVar(..) => false,
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 25787f005aa..a64580336ad 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -503,7 +503,8 @@ impl<'tcx> TyCtxt<'tcx> {
         closure_substs: SubstsRef<'tcx>,
     ) -> Option<ty::Binder<Ty<'tcx>>> {
         let closure_ty = self.mk_closure(closure_def_id, closure_substs);
-        let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv);
+        let br = ty::BoundRegion { kind: ty::BrEnv };
+        let env_region = ty::ReLateBound(ty::INNERMOST, br);
         let closure_kind_ty = closure_substs.as_closure().kind_ty();
         let closure_kind = closure_kind_ty.to_opt_closure_kind()?;
         let env_ty = match closure_kind {
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
index 41f3edaa413..81571fd7300 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
@@ -496,7 +496,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         // lifetimes without names with the value `'0`.
         match ty.kind() {
             ty::Ref(
-                ty::RegionKind::ReLateBound(_, br)
+                ty::RegionKind::ReLateBound(_, ty::BoundRegion { kind: br })
                 | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }),
                 _,
                 _,
@@ -517,7 +517,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         let region = match ty.kind() {
             ty::Ref(region, _, _) => {
                 match region {
-                    ty::RegionKind::ReLateBound(_, br)
+                    ty::RegionKind::ReLateBound(_, ty::BoundRegion { kind: br })
                     | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }) => {
                         printer.region_highlight_mode.highlighting_bound_region(*br, counter)
                     }
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs
index e22dab01517..78da43c31c0 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs
@@ -138,7 +138,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
     /// Returns `true` if a closure is inferred to be an `FnMut` closure.
     fn is_closure_fn_mut(&self, fr: RegionVid) -> bool {
         if let Some(ty::ReFree(free_region)) = self.to_error_region(fr) {
-            if let ty::BoundRegion::BrEnv = free_region.bound_region {
+            if let ty::BoundRegionKind::BrEnv = free_region.bound_region {
                 if let DefiningTy::Closure(_, substs) =
                     self.regioncx.universal_regions().defining_ty
                 {
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs
index 6211cf8a9da..cbca012824f 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs
@@ -281,7 +281,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
             }
 
             ty::ReFree(free_region) => match free_region.bound_region {
-                ty::BoundRegion::BrNamed(region_def_id, name) => {
+                ty::BoundRegionKind::BrNamed(region_def_id, name) => {
                     // Get the span to point to, even if we don't use the name.
                     let span = tcx.hir().span_if_local(region_def_id).unwrap_or(DUMMY_SP);
                     debug!(
@@ -307,7 +307,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
                     }
                 }
 
-                ty::BoundRegion::BrEnv => {
+                ty::BoundRegionKind::BrEnv => {
                     let def_ty = self.regioncx.universal_regions().defining_ty;
 
                     if let DefiningTy::Closure(_, substs) = def_ty {
@@ -349,7 +349,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
                     }
                 }
 
-                ty::BoundRegion::BrAnon(_) => None,
+                ty::BoundRegionKind::BrAnon(_) => None,
             },
 
             ty::ReLateBound(..)
diff --git a/compiler/rustc_mir/src/borrow_check/universal_regions.rs b/compiler/rustc_mir/src/borrow_check/universal_regions.rs
index 7ad38a1f82c..c1a0d9856b7 100644
--- a/compiler/rustc_mir/src/borrow_check/universal_regions.rs
+++ b/compiler/rustc_mir/src/borrow_check/universal_regions.rs
@@ -700,7 +700,7 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> {
             debug!("replace_bound_regions_with_nll_infer_vars: br={:?}", br);
             let liberated_region = self.tcx.mk_region(ty::ReFree(ty::FreeRegion {
                 scope: all_outlive_scope.to_def_id(),
-                bound_region: br,
+                bound_region: br.kind,
             }));
             let region_vid = self.next_nll_region_var(origin);
             indices.insert_late_bound_region(liberated_region, region_vid.to_region_vid());
@@ -795,7 +795,7 @@ fn for_each_late_bound_region_defined_on<'tcx>(
             let region_def_id = tcx.hir().local_def_id(hir_id);
             let liberated_region = tcx.mk_region(ty::ReFree(ty::FreeRegion {
                 scope: fn_def_id,
-                bound_region: ty::BoundRegion::BrNamed(region_def_id.to_def_id(), name),
+                bound_region: ty::BoundRegionKind::BrNamed(region_def_id.to_def_id(), name),
             }));
             f(liberated_region);
         }
diff --git a/compiler/rustc_mir/src/transform/check_consts/validation.rs b/compiler/rustc_mir/src/transform/check_consts/validation.rs
index d00038f345c..90688ebbd0a 100644
--- a/compiler/rustc_mir/src/transform/check_consts/validation.rs
+++ b/compiler/rustc_mir/src/transform/check_consts/validation.rs
@@ -722,17 +722,16 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
     fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
         trace!("visit_statement: statement={:?} location={:?}", statement, location);
 
-        match statement.kind {
-            StatementKind::Assign(..) | StatementKind::SetDiscriminant { .. } => {
-                self.super_statement(statement, location);
-            }
+        self.super_statement(statement, location);
 
+        match statement.kind {
             StatementKind::LlvmInlineAsm { .. } => {
-                self.super_statement(statement, location);
                 self.check_op(ops::InlineAsm);
             }
 
-            StatementKind::FakeRead(..)
+            StatementKind::Assign(..)
+            | StatementKind::SetDiscriminant { .. }
+            | StatementKind::FakeRead(..)
             | StatementKind::StorageLive(_)
             | StatementKind::StorageDead(_)
             | StatementKind::Retag { .. }
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index 29b7e176b0e..db817b378f9 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -433,7 +433,7 @@ fn report_arm_reachability<'p, 'tcx>(
             Useful(unreachables) if unreachables.is_empty() => {}
             // The arm is reachable, but contains unreachable subpatterns (from or-patterns).
             Useful(unreachables) => {
-                let mut unreachables: Vec<_> = unreachables.iter().flatten().copied().collect();
+                let mut unreachables: Vec<_> = unreachables.iter().collect();
                 // Emit lints in the order in which they occur in the file.
                 unreachables.sort_unstable();
                 for span in unreachables {
diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
index f3e1507b37a..0ecc034ac0b 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
@@ -311,7 +311,6 @@ use super::{Pat, PatKind};
 use super::{PatternFoldable, PatternFolder};
 
 use rustc_data_structures::captures::Captures;
-use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::sync::OnceCell;
 
 use rustc_arena::TypedArena;
@@ -626,11 +625,82 @@ impl<'p, 'tcx> FromIterator<PatStack<'p, 'tcx>> for Matrix<'p, 'tcx> {
     }
 }
 
+/// Represents a set of `Span`s closed under the containment relation. That is, if a `Span` is
+/// contained in the set then all `Span`s contained in it are also implicitly contained in the set.
+/// In particular this means that when intersecting two sets, taking the intersection of some span
+/// and one of its subspans returns the subspan, whereas a simple `HashSet` would have returned an
+/// empty intersection.
+/// It is assumed that two spans don't overlap without one being contained in the other; in other
+/// words, that the inclusion structure forms a tree and not a DAG.
+/// Intersection is not very efficient. It compares everything pairwise. If needed it could be made
+/// faster by sorting the `Span`s and merging cleverly.
+#[derive(Debug, Clone, Default)]
+pub(crate) struct SpanSet {
+    /// The minimal set of `Span`s required to represent the whole set. If A and B are `Span`s in
+    /// the `SpanSet`, and A is a descendant of B, then only B will be in `root_spans`.
+    /// Invariant: the spans are disjoint.
+    root_spans: Vec<Span>,
+}
+
+impl SpanSet {
+    /// Creates an empty set.
+    fn new() -> Self {
+        Self::default()
+    }
+
+    /// Tests whether the set is empty.
+    pub(crate) fn is_empty(&self) -> bool {
+        self.root_spans.is_empty()
+    }
+
+    /// Iterate over the disjoint list of spans at the roots of this set.
+    pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item = Span> + Captures<'a> {
+        self.root_spans.iter().copied()
+    }
+
+    /// Tests whether the set contains a given Span.
+    fn contains(&self, span: Span) -> bool {
+        self.iter().any(|root_span| root_span.contains(span))
+    }
+
+    /// Add a span to the set if we know the span has no intersection in this set.
+    fn push_nonintersecting(&mut self, new_span: Span) {
+        self.root_spans.push(new_span);
+    }
+
+    fn intersection_mut(&mut self, other: &Self) {
+        if self.is_empty() || other.is_empty() {
+            *self = Self::new();
+            return;
+        }
+        // Those that were in `self` but not contained in `other`
+        let mut leftover = SpanSet::new();
+        // We keep the elements in `self` that are also in `other`.
+        self.root_spans.retain(|span| {
+            let retain = other.contains(*span);
+            if !retain {
+                leftover.root_spans.push(*span);
+            }
+            retain
+        });
+        // We keep the elements in `other` that are also in the original `self`. You might think
+        // this is not needed because `self` already contains the intersection. But those aren't
+        // just sets of things. If `self = [a]`, `other = [b]` and `a` contains `b`, then `b`
+        // belongs in the intersection but we didn't catch it in the filtering above. We look at
+        // `leftover` instead of the full original `self` to avoid duplicates.
+        for span in other.iter() {
+            if leftover.contains(span) {
+                self.root_spans.push(span);
+            }
+        }
+    }
+}
+
 #[derive(Clone, Debug)]
 crate enum Usefulness<'tcx> {
-    /// Carries, for each column in the matrix, a set of sub-branches that have been found to be
-    /// unreachable. Used only in the presence of or-patterns, otherwise it stays empty.
-    Useful(Vec<FxHashSet<Span>>),
+    /// Pontentially carries a set of sub-branches that have been found to be unreachable. Used
+    /// only in the presence of or-patterns, otherwise it stays empty.
+    Useful(SpanSet),
     /// Carries a list of witnesses of non-exhaustiveness.
     UsefulWithWitness(Vec<Witness<'tcx>>),
     NotUseful,
@@ -640,14 +710,97 @@ impl<'tcx> Usefulness<'tcx> {
     fn new_useful(preference: WitnessPreference) -> Self {
         match preference {
             ConstructWitness => UsefulWithWitness(vec![Witness(vec![])]),
-            LeaveOutWitness => Useful(vec![]),
+            LeaveOutWitness => Useful(Default::default()),
         }
     }
 
-    fn is_useful(&self) -> bool {
-        !matches!(*self, NotUseful)
+    /// When trying several branches and each returns a `Usefulness`, we need to combine the
+    /// results together.
+    fn merge(usefulnesses: impl Iterator<Item = Self>) -> Self {
+        // If we have detected some unreachable sub-branches, we only want to keep them when they
+        // were unreachable in _all_ branches. Eg. in the following, the last `true` is unreachable
+        // in the second branch of the first or-pattern, but not otherwise. Therefore we don't want
+        // to lint that it is unreachable.
+        // ```
+        // match (true, true) {
+        //     (true, true) => {}
+        //     (false | true, false | true) => {}
+        // }
+        // ```
+        // Here however we _do_ want to lint that the last `false` is unreachable. So we don't want
+        // to intersect the spans that come directly from the or-pattern, since each branch of the
+        // or-pattern brings a new disjoint pattern.
+        // ```
+        // match None {
+        //     Some(false) => {}
+        //     None | Some(true | false) => {}
+        // }
+        // ```
+
+        // Is `None` when no branch was useful. Will often be `Some(Spanset::new())` because the
+        // sets are only non-empty in the presence of or-patterns.
+        let mut unreachables: Option<SpanSet> = None;
+        // Witnesses of usefulness, if any.
+        let mut witnesses = Vec::new();
+
+        for u in usefulnesses {
+            match u {
+                Useful(spans) if spans.is_empty() => {
+                    // Once we reach the empty set, more intersections won't change the result.
+                    return Useful(SpanSet::new());
+                }
+                Useful(spans) => {
+                    if let Some(unreachables) = &mut unreachables {
+                        if !unreachables.is_empty() {
+                            unreachables.intersection_mut(&spans);
+                        }
+                        if unreachables.is_empty() {
+                            return Useful(SpanSet::new());
+                        }
+                    } else {
+                        unreachables = Some(spans);
+                    }
+                }
+                NotUseful => {}
+                UsefulWithWitness(wits) => {
+                    witnesses.extend(wits);
+                }
+            }
+        }
+
+        if !witnesses.is_empty() {
+            UsefulWithWitness(witnesses)
+        } else if let Some(unreachables) = unreachables {
+            Useful(unreachables)
+        } else {
+            NotUseful
+        }
     }
 
+    /// After calculating the usefulness for a branch of an or-pattern, call this to make this
+    /// usefulness mergeable with those from the other branches.
+    fn unsplit_or_pat(self, this_span: Span, or_pat_spans: &[Span]) -> Self {
+        match self {
+            Useful(mut spans) => {
+                // We register the spans of the other branches of this or-pattern as being
+                // unreachable from this one. This ensures that intersecting together the sets of
+                // spans returns what we want.
+                // Until we optimize `SpanSet` however, intersecting this entails a number of
+                // comparisons quadratic in the number of branches.
+                for &span in or_pat_spans {
+                    if span != this_span {
+                        spans.push_nonintersecting(span);
+                    }
+                }
+                Useful(spans)
+            }
+            x => x,
+        }
+    }
+
+    /// After calculating usefulness after a specialization, call this to recontruct a usefulness
+    /// that makes sense for the matrix pre-specialization. This new usefulness can then be merged
+    /// with the results of specializing with the other constructors.
     fn apply_constructor<'p>(
         self,
         pcx: PatCtxt<'_, 'p, 'tcx>,
@@ -677,23 +830,6 @@ impl<'tcx> Usefulness<'tcx> {
                 };
                 UsefulWithWitness(new_witnesses)
             }
-            Useful(mut unreachables) => {
-                if !unreachables.is_empty() {
-                    // When we apply a constructor, there are `arity` columns of the matrix that
-                    // corresponded to its arguments. All the unreachables found in these columns
-                    // will, after `apply`, come from the first column. So we take the union of all
-                    // the corresponding sets and put them in the first column.
-                    // Note that `arity` may be 0, in which case we just push a new empty set.
-                    let len = unreachables.len();
-                    let arity = ctor_wild_subpatterns.len();
-                    let mut unioned = FxHashSet::default();
-                    for set in unreachables.drain((len - arity)..) {
-                        unioned.extend(set)
-                    }
-                    unreachables.push(unioned);
-                }
-                Useful(unreachables)
-            }
             x => x,
         }
     }
@@ -829,102 +965,37 @@ fn is_useful<'p, 'tcx>(
 
     assert!(rows.iter().all(|r| r.len() == v.len()));
 
+    // FIXME(Nadrieril): Hack to work around type normalization issues (see #72476).
+    let ty = matrix.heads().next().map(|r| r.ty).unwrap_or(v.head().ty);
+    let pcx = PatCtxt { cx, matrix, ty, span: v.head().span, is_top_level };
+
+    debug!("is_useful_expand_first_col: ty={:#?}, expanding {:#?}", pcx.ty, v.head());
+
     // If the first pattern is an or-pattern, expand it.
-    if let Some(vs) = v.expand_or_pat() {
+    let ret = if let Some(vs) = v.expand_or_pat() {
+        let subspans: Vec<_> = vs.iter().map(|v| v.head().span).collect();
         // We expand the or pattern, trying each of its branches in turn and keeping careful track
         // of possible unreachable sub-branches.
-        //
-        // If two branches have detected some unreachable sub-branches, we need to be careful. If
-        // they were detected in columns that are not the current one, we want to keep only the
-        // sub-branches that were unreachable in _all_ branches. Eg. in the following, the last
-        // `true` is unreachable in the second branch of the first or-pattern, but not otherwise.
-        // Therefore we don't want to lint that it is unreachable.
-        //
-        // ```
-        // match (true, true) {
-        //     (true, true) => {}
-        //     (false | true, false | true) => {}
-        // }
-        // ```
-        // If however the sub-branches come from the current column, they come from the inside of
-        // the current or-pattern, and we want to keep them all. Eg. in the following, we _do_ want
-        // to lint that the last `false` is unreachable.
-        // ```
-        // match None {
-        //     Some(false) => {}
-        //     None | Some(true | false) => {}
-        // }
-        // ```
-
         let mut matrix = matrix.clone();
-        // We keep track of sub-branches separately depending on whether they come from this column
-        // or from others.
-        let mut unreachables_this_column: FxHashSet<Span> = FxHashSet::default();
-        let mut unreachables_other_columns: Vec<FxHashSet<Span>> = Vec::default();
-        // Whether at least one branch is reachable.
-        let mut any_is_useful = false;
-
-        for v in vs {
-            let res = is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false);
-            match res {
-                Useful(unreachables) => {
-                    if let Some((this_column, other_columns)) = unreachables.split_last() {
-                        // We keep the union of unreachables found in the first column.
-                        unreachables_this_column.extend(this_column);
-                        // We keep the intersection of unreachables found in other columns.
-                        if unreachables_other_columns.is_empty() {
-                            unreachables_other_columns = other_columns.to_vec();
-                        } else {
-                            unreachables_other_columns = unreachables_other_columns
-                                .into_iter()
-                                .zip(other_columns)
-                                .map(|(x, y)| x.intersection(&y).copied().collect())
-                                .collect();
-                        }
-                    }
-                    any_is_useful = true;
-                }
-                NotUseful => {
-                    unreachables_this_column.insert(v.head().span);
-                }
-                UsefulWithWitness(_) => bug!(
-                    "encountered or-pat in the expansion of `_` during exhaustiveness checking"
-                ),
-            }
-
+        let usefulnesses = vs.into_iter().map(|v| {
+            let v_span = v.head().span;
+            let usefulness =
+                is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false);
             // If pattern has a guard don't add it to the matrix.
             if !is_under_guard {
                 // We push the already-seen patterns into the matrix in order to detect redundant
                 // branches like `Some(_) | Some(0)`.
                 matrix.push(v);
             }
-        }
-
-        return if any_is_useful {
-            let mut unreachables = if unreachables_other_columns.is_empty() {
-                let n_columns = v.len();
-                (0..n_columns - 1).map(|_| FxHashSet::default()).collect()
-            } else {
-                unreachables_other_columns
-            };
-            unreachables.push(unreachables_this_column);
-            Useful(unreachables)
-        } else {
-            NotUseful
-        };
-    }
-
-    // FIXME(Nadrieril): Hack to work around type normalization issues (see #72476).
-    let ty = matrix.heads().next().map(|r| r.ty).unwrap_or(v.head().ty);
-    let pcx = PatCtxt { cx, matrix, ty, span: v.head().span, is_top_level };
-
-    debug!("is_useful_expand_first_col: ty={:#?}, expanding {:#?}", pcx.ty, v.head());
-
-    let ret = v
-        .head_ctor(cx)
-        .split(pcx, Some(hir_id))
-        .into_iter()
-        .map(|ctor| {
+            usefulness.unsplit_or_pat(v_span, &subspans)
+        });
+        Usefulness::merge(usefulnesses)
+    } else {
+        // We split the head constructor of `v`.
+        let ctors = v.head_ctor(cx).split(pcx, Some(hir_id));
+        // For each constructor, we compute whether there's a value that starts with it that would
+        // witness the usefulness of `v`.
+        let usefulnesses = ctors.into_iter().map(|ctor| {
             // We cache the result of `Fields::wildcards` because it is used a lot.
             let ctor_wild_subpatterns = Fields::wildcards(pcx, &ctor);
             let matrix = pcx.matrix.specialize_constructor(pcx, &ctor, &ctor_wild_subpatterns);
@@ -932,9 +1003,9 @@ fn is_useful<'p, 'tcx>(
             let usefulness =
                 is_useful(pcx.cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false);
             usefulness.apply_constructor(pcx, &ctor, &ctor_wild_subpatterns)
-        })
-        .find(|result| result.is_useful())
-        .unwrap_or(NotUseful);
+        });
+        Usefulness::merge(usefulnesses)
+    };
     debug!("is_useful::returns({:#?}, {:#?}) = {:?}", matrix, v, ret);
     ret
 }
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 4d2167442be..eed3e9947b2 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -1,4 +1,4 @@
-use super::pat::{GateOr, PARAM_EXPECTED};
+use super::pat::{GateOr, RecoverComma, PARAM_EXPECTED};
 use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
 use super::{BlockMode, Parser, PathStyle, Restrictions, TokenType};
 use super::{SemiColonMode, SeqSep, TokenExpectType};
@@ -1729,7 +1729,7 @@ impl<'a> Parser<'a> {
     /// The `let` token has already been eaten.
     fn parse_let_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
         let lo = self.prev_token.span;
-        let pat = self.parse_top_pat(GateOr::No)?;
+        let pat = self.parse_top_pat(GateOr::No, RecoverComma::Yes)?;
         self.expect(&token::Eq)?;
         let expr = self.with_res(self.restrictions | Restrictions::NO_STRUCT_LITERAL, |this| {
             this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into())
@@ -1792,7 +1792,7 @@ impl<'a> Parser<'a> {
             _ => None,
         };
 
-        let pat = self.parse_top_pat(GateOr::Yes)?;
+        let pat = self.parse_top_pat(GateOr::Yes, RecoverComma::Yes)?;
         if !self.eat_keyword(kw::In) {
             self.error_missing_in_for_loop();
         }
@@ -1902,7 +1902,7 @@ impl<'a> Parser<'a> {
     pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> {
         let attrs = self.parse_outer_attributes()?;
         let lo = self.token.span;
-        let pat = self.parse_top_pat(GateOr::No)?;
+        let pat = self.parse_top_pat(GateOr::No, RecoverComma::Yes)?;
         let guard = if self.eat_keyword(kw::If) {
             let if_span = self.prev_token.span;
             let cond = self.parse_expr()?;
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index d51a0fcbf09..e19ebb8fd2f 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -12,6 +12,7 @@ mod ty;
 use crate::lexer::UnmatchedBrace;
 pub use diagnostics::AttemptLocalParseRecovery;
 use diagnostics::Error;
+pub use pat::OrPatNonterminalMode;
 pub use path::PathStyle;
 
 use rustc_ast::ptr::P;
diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs
index 76ad5acd530..a6b9ac1014e 100644
--- a/compiler/rustc_parse/src/parser/nonterminal.rs
+++ b/compiler/rustc_parse/src/parser/nonterminal.rs
@@ -4,6 +4,7 @@ use rustc_ast_pretty::pprust;
 use rustc_errors::PResult;
 use rustc_span::symbol::{kw, Ident};
 
+use crate::parser::pat::{GateOr, OrPatNonterminalMode, RecoverComma};
 use crate::parser::{FollowedByType, Parser, PathStyle};
 
 impl<'a> Parser<'a> {
@@ -11,7 +12,11 @@ impl<'a> Parser<'a> {
     ///
     /// Returning `false` is a *stability guarantee* that such a matcher will *never* begin with that
     /// token. Be conservative (return true) if not sure.
-    pub fn nonterminal_may_begin_with(kind: NonterminalKind, token: &Token) -> bool {
+    pub fn nonterminal_may_begin_with(
+        kind: NonterminalKind,
+        token: &Token,
+        or_pat_mode: OrPatNonterminalMode,
+    ) -> bool {
         /// Checks whether the non-terminal may contain a single (non-keyword) identifier.
         fn may_be_ident(nt: &token::Nonterminal) -> bool {
             match *nt {
@@ -70,6 +75,8 @@ impl<'a> Parser<'a> {
                 token::ModSep |                     // path
                 token::Lt |                         // path (UFCS constant)
                 token::BinOp(token::Shl) => true,   // path (double UFCS)
+                // leading vert `|` or-pattern
+                token::BinOp(token::Or) =>  matches!(or_pat_mode, OrPatNonterminalMode::TopPat),
                 token::Interpolated(ref nt) => may_be_ident(nt),
                 _ => false,
             },
@@ -86,7 +93,12 @@ impl<'a> Parser<'a> {
         }
     }
 
-    pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, Nonterminal> {
+    /// Parse a non-terminal (e.g. MBE `:pat` or `:ident`).
+    pub fn parse_nonterminal(
+        &mut self,
+        kind: NonterminalKind,
+        or_pat_mode: OrPatNonterminalMode,
+    ) -> PResult<'a, Nonterminal> {
         // Any `Nonterminal` which stores its tokens (currently `NtItem` and `NtExpr`)
         // needs to have them force-captured here.
         // A `macro_rules!` invocation may pass a captured item/expr to a proc-macro,
@@ -130,7 +142,12 @@ impl<'a> Parser<'a> {
                 }
             }
             NonterminalKind::Pat => {
-                let (mut pat, tokens) = self.collect_tokens(|this| this.parse_pat(None))?;
+                let (mut pat, tokens) = self.collect_tokens(|this| match or_pat_mode {
+                    OrPatNonterminalMode::TopPat => {
+                        this.parse_top_pat(GateOr::Yes, RecoverComma::No)
+                    }
+                    OrPatNonterminalMode::NoTopAlt => this.parse_pat(None),
+                })?;
                 // We have have eaten an NtPat, which could already have tokens
                 if pat.tokens.is_none() {
                     pat.tokens = tokens;
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index b62c7373800..1da371e0b72 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -26,11 +26,18 @@ pub(super) enum GateOr {
 
 /// Whether or not to recover a `,` when parsing or-patterns.
 #[derive(PartialEq, Copy, Clone)]
-enum RecoverComma {
+pub(super) enum RecoverComma {
     Yes,
     No,
 }
 
+/// Used when parsing a non-terminal (see `parse_nonterminal`) to determine if `:pat` should match
+/// `top_pat` or `pat<no_top_alt>`. See issue <https://github.com/rust-lang/rust/pull/78935>.
+pub enum OrPatNonterminalMode {
+    TopPat,
+    NoTopAlt,
+}
+
 impl<'a> Parser<'a> {
     /// Parses a pattern.
     ///
@@ -43,13 +50,17 @@ impl<'a> Parser<'a> {
 
     /// Entry point to the main pattern parser.
     /// Corresponds to `top_pat` in RFC 2535 and allows or-pattern at the top level.
-    pub(super) fn parse_top_pat(&mut self, gate_or: GateOr) -> PResult<'a, P<Pat>> {
+    pub(super) fn parse_top_pat(
+        &mut self,
+        gate_or: GateOr,
+        rc: RecoverComma,
+    ) -> PResult<'a, P<Pat>> {
         // Allow a '|' before the pats (RFCs 1925, 2530, and 2535).
         let gated_leading_vert = self.eat_or_separator(None) && gate_or == GateOr::Yes;
         let leading_vert_span = self.prev_token.span;
 
         // Parse the possibly-or-pattern.
-        let pat = self.parse_pat_with_or(None, gate_or, RecoverComma::Yes)?;
+        let pat = self.parse_pat_with_or(None, gate_or, rc)?;
 
         // If we parsed a leading `|` which should be gated,
         // and no other gated or-pattern has been parsed thus far,
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index e974556f43a..2942747991a 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -1,7 +1,7 @@
 use super::attr::DEFAULT_INNER_ATTR_FORBIDDEN;
 use super::diagnostics::{AttemptLocalParseRecovery, Error};
 use super::expr::LhsExpr;
-use super::pat::GateOr;
+use super::pat::{GateOr, RecoverComma};
 use super::path::PathStyle;
 use super::{BlockMode, Parser, Restrictions, SemiColonMode};
 use crate::maybe_whole;
@@ -185,7 +185,7 @@ impl<'a> Parser<'a> {
     /// Parses a local variable declaration.
     fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P<Local>> {
         let lo = self.prev_token.span;
-        let pat = self.parse_top_pat(GateOr::Yes)?;
+        let pat = self.parse_top_pat(GateOr::Yes, RecoverComma::Yes)?;
 
         let (err, ty) = if self.eat(&token::Colon) {
             // Save the state of the parser before parsing type normally, in case there is a `:`
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index 0294fb23c56..7b6e6ad0696 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -319,7 +319,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> {
 
             // Late-bound lifetimes use indices starting at 1,
             // see `BinderLevel` for more details.
-            ty::ReLateBound(debruijn, ty::BrAnon(i)) => {
+            ty::ReLateBound(debruijn, ty::BoundRegion { kind: ty::BrAnon(i) }) => {
                 let binder = &self.binders[self.binders.len() - 1 - debruijn.index()];
                 let depth = binder.lifetime_depths.start + i;
 
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index b4b71a48ce9..79fea83a667 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -254,27 +254,21 @@ fn suggest_restriction(
         let pred = trait_ref.without_const().to_predicate(tcx).to_string();
         let pred = pred.replace(&impl_trait_str, &type_param_name);
         let mut sugg = vec![
+            // Find the last of the generic parameters contained within the span of
+            // the generics
             match generics
                 .params
                 .iter()
-                .filter(|p| match p.kind {
-                    hir::GenericParamKind::Type {
-                        synthetic: Some(hir::SyntheticTyParamKind::ImplTrait),
-                        ..
-                    } => false,
-                    _ => true,
-                })
-                .last()
+                .map(|p| p.bounds_span().unwrap_or(p.span))
+                .filter(|&span| generics.span.contains(span) && span.desugaring_kind().is_none())
+                .max_by_key(|span| span.hi())
             {
                 // `fn foo(t: impl Trait)`
                 //        ^ suggest `<T: Trait>` here
                 None => (generics.span, format!("<{}>", type_param)),
                 // `fn foo<A>(t: impl Trait)`
                 //        ^^^ suggest `<A, T: Trait>` here
-                Some(param) => (
-                    param.bounds_span().unwrap_or(param.span).shrink_to_hi(),
-                    format!(", {}", type_param),
-                ),
+                Some(span) => (span.shrink_to_hi(), format!(", {}", type_param)),
             },
             // `fn foo(t: impl Trait)`
             //                       ^ suggest `where <T as Trait>::A: Bound`
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 81de6dc71f4..030c29171a1 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -334,7 +334,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     fn vtable_impl(
         &mut self,
         impl_def_id: DefId,
-        mut substs: Normalized<'tcx, SubstsRef<'tcx>>,
+        substs: Normalized<'tcx, SubstsRef<'tcx>>,
         cause: ObligationCause<'tcx>,
         recursion_depth: usize,
         param_env: ty::ParamEnv<'tcx>,
@@ -356,9 +356,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // relying on projections in the impl-trait-ref.
         //
         // e.g., `impl<U: Tr, V: Iterator<Item=U>> Foo<<U as Tr>::T> for V`
-        substs.obligations.append(&mut impl_obligations);
+        impl_obligations.extend(substs.obligations);
 
-        ImplSourceUserDefinedData { impl_def_id, substs: substs.value, nested: substs.obligations }
+        ImplSourceUserDefinedData { impl_def_id, substs: substs.value, nested: impl_obligations }
     }
 
     fn confirm_object_candidate(
diff --git a/compiler/rustc_traits/src/chalk/db.rs b/compiler/rustc_traits/src/chalk/db.rs
index b1b9ef343d5..1893d74335a 100644
--- a/compiler/rustc_traits/src/chalk/db.rs
+++ b/compiler/rustc_traits/src/chalk/db.rs
@@ -648,7 +648,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
 
 /// Creates a `InternalSubsts` that maps each generic parameter to a higher-ranked
 /// var bound at index `0`. For types, we use a `BoundVar` index equal to
-/// the type parameter index. For regions, we use the `BoundRegion::BrNamed`
+/// the type parameter index. For regions, we use the `BoundRegionKind::BrNamed`
 /// variant (which has a `DefId`).
 fn bound_vars_for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> SubstsRef<'tcx> {
     InternalSubsts::for_item(tcx, def_id, |param, substs| match param.kind {
@@ -662,12 +662,10 @@ fn bound_vars_for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> SubstsRef<'tcx> {
             ))
             .into(),
 
-        ty::GenericParamDefKind::Lifetime => tcx
-            .mk_region(ty::RegionKind::ReLateBound(
-                ty::INNERMOST,
-                ty::BoundRegion::BrAnon(substs.len() as u32),
-            ))
-            .into(),
+        ty::GenericParamDefKind::Lifetime => {
+            let br = ty::BoundRegion { kind: ty::BrAnon(substs.len() as u32) };
+            tcx.mk_region(ty::RegionKind::ReLateBound(ty::INNERMOST, br)).into()
+        }
 
         ty::GenericParamDefKind::Const => tcx
             .mk_const(ty::Const {
diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs
index 3a747b09cd4..8aa68e533a2 100644
--- a/compiler/rustc_traits/src/chalk/lowering.rs
+++ b/compiler/rustc_traits/src/chalk/lowering.rs
@@ -35,9 +35,7 @@ use rustc_ast::ast;
 use rustc_middle::traits::{ChalkEnvironmentAndGoal, ChalkRustInterner as RustInterner};
 use rustc_middle::ty::fold::TypeFolder;
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
-use rustc_middle::ty::{
-    self, Binder, BoundRegion, Region, RegionKind, Ty, TyCtxt, TypeFoldable, TypeVisitor,
-};
+use rustc_middle::ty::{self, Binder, Region, RegionKind, Ty, TyCtxt, TypeFoldable, TypeVisitor};
 use rustc_span::def_id::DefId;
 
 use chalk_ir::{FnSig, ForeignDefId};
@@ -444,15 +442,15 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Lifetime<RustInterner<'tcx>>> for Region<'t
             ReEarlyBound(_) => {
                 panic!("Should have already been substituted.");
             }
-            ReLateBound(db, br) => match br {
-                ty::BoundRegion::BrAnon(var) => {
+            ReLateBound(db, br) => match br.kind {
+                ty::BoundRegionKind::BrAnon(var) => {
                     chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new(
                         chalk_ir::DebruijnIndex::new(db.as_u32()),
-                        *var as usize,
+                        var as usize,
                     ))
                     .intern(interner)
                 }
-                ty::BoundRegion::BrNamed(_def_id, _name) => unimplemented!(),
+                ty::BoundRegionKind::BrNamed(_def_id, _name) => unimplemented!(),
                 ty::BrEnv => unimplemented!(),
             },
             ReFree(_) => unimplemented!(),
@@ -477,13 +475,13 @@ impl<'tcx> LowerInto<'tcx, Region<'tcx>> for &chalk_ir::Lifetime<RustInterner<'t
         let kind = match self.data(interner) {
             chalk_ir::LifetimeData::BoundVar(var) => ty::RegionKind::ReLateBound(
                 ty::DebruijnIndex::from_u32(var.debruijn.depth()),
-                ty::BoundRegion::BrAnon(var.index as u32),
+                ty::BoundRegion { kind: ty::BrAnon(var.index as u32) },
             ),
             chalk_ir::LifetimeData::InferenceVar(_var) => unimplemented!(),
             chalk_ir::LifetimeData::Placeholder(p) => {
                 ty::RegionKind::RePlaceholder(ty::Placeholder {
                     universe: ty::UniverseIndex::from_usize(p.ui.counter),
-                    name: ty::BoundRegion::BrAnon(p.idx as u32),
+                    name: ty::BoundRegionKind::BrAnon(p.idx as u32),
                 })
             }
             chalk_ir::LifetimeData::Static => ty::RegionKind::ReStatic,
@@ -805,7 +803,7 @@ impl<'tcx> LowerInto<'tcx, chalk_solve::rust_ir::AliasEqBound<RustInterner<'tcx>
 }
 
 /// To collect bound vars, we have to do two passes. In the first pass, we
-/// collect all `BoundRegion`s and `ty::Bound`s. In the second pass, we then
+/// collect all `BoundRegionKind`s and `ty::Bound`s. In the second pass, we then
 /// replace `BrNamed` into `BrAnon`. The two separate passes are important,
 /// since we can only replace `BrNamed` with `BrAnon`s with indices *after* all
 /// "real" `BrAnon`s.
@@ -893,14 +891,14 @@ impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> {
 
     fn visit_region(&mut self, r: Region<'tcx>) -> ControlFlow<Self::BreakTy> {
         match r {
-            ty::ReLateBound(index, br) if *index == self.binder_index => match br {
-                ty::BoundRegion::BrNamed(def_id, _name) => {
-                    if self.named_parameters.iter().find(|d| *d == def_id).is_none() {
-                        self.named_parameters.push(*def_id);
+            ty::ReLateBound(index, br) if *index == self.binder_index => match br.kind {
+                ty::BoundRegionKind::BrNamed(def_id, _name) => {
+                    if self.named_parameters.iter().find(|d| **d == def_id).is_none() {
+                        self.named_parameters.push(def_id);
                     }
                 }
 
-                ty::BoundRegion::BrAnon(var) => match self.parameters.entry(*var) {
+                ty::BoundRegionKind::BrAnon(var) => match self.parameters.entry(var) {
                     Entry::Vacant(entry) => {
                         entry.insert(chalk_ir::VariableKind::Lifetime);
                     }
@@ -926,7 +924,7 @@ impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> {
     }
 }
 
-/// This is used to replace `BoundRegion::BrNamed` with `BoundRegion::BrAnon`.
+/// This is used to replace `BoundRegionKind::BrNamed` with `BoundRegionKind::BrAnon`.
 /// Note: we assume that we will always have room for more bound vars. (i.e. we
 /// won't ever hit the `u32` limit in `BrAnon`s).
 struct NamedBoundVarSubstitutor<'a, 'tcx> {
@@ -955,20 +953,16 @@ impl<'a, 'tcx> TypeFolder<'tcx> for NamedBoundVarSubstitutor<'a, 'tcx> {
 
     fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> {
         match r {
-            ty::ReLateBound(index, br) if *index == self.binder_index => match br {
-                ty::BoundRegion::BrNamed(def_id, _name) => {
-                    match self.named_parameters.get(def_id) {
-                        Some(idx) => {
-                            return self.tcx.mk_region(RegionKind::ReLateBound(
-                                *index,
-                                BoundRegion::BrAnon(*idx),
-                            ));
-                        }
-                        None => panic!("Missing `BrNamed`."),
+            ty::ReLateBound(index, br) if *index == self.binder_index => match br.kind {
+                ty::BrNamed(def_id, _name) => match self.named_parameters.get(&def_id) {
+                    Some(idx) => {
+                        let new_br = ty::BoundRegion { kind: ty::BrAnon(*idx) };
+                        return self.tcx.mk_region(RegionKind::ReLateBound(*index, new_br));
                     }
-                }
+                    None => panic!("Missing `BrNamed`."),
+                },
                 ty::BrEnv => unimplemented!(),
-                ty::BoundRegion::BrAnon(_) => {}
+                ty::BrAnon(_) => {}
             },
             _ => (),
         };
@@ -1044,17 +1038,15 @@ impl<'tcx> TypeFolder<'tcx> for ParamsSubstitutor<'tcx> {
             // FIXME(chalk) - jackh726 - this currently isn't hit in any tests.
             // This covers any region variables in a goal, right?
             ty::ReEarlyBound(_re) => match self.named_regions.get(&_re.def_id) {
-                Some(idx) => self.tcx.mk_region(RegionKind::ReLateBound(
-                    self.binder_index,
-                    BoundRegion::BrAnon(*idx),
-                )),
+                Some(idx) => {
+                    let br = ty::BoundRegion { kind: ty::BrAnon(*idx) };
+                    self.tcx.mk_region(RegionKind::ReLateBound(self.binder_index, br))
+                }
                 None => {
                     let idx = self.named_regions.len() as u32;
+                    let br = ty::BoundRegion { kind: ty::BrAnon(idx) };
                     self.named_regions.insert(_re.def_id, idx);
-                    self.tcx.mk_region(RegionKind::ReLateBound(
-                        self.binder_index,
-                        BoundRegion::BrAnon(idx),
-                    ))
+                    self.tcx.mk_region(RegionKind::ReLateBound(self.binder_index, br))
                 }
             },
 
@@ -1096,7 +1088,7 @@ impl<'tcx> TypeVisitor<'tcx> for PlaceholdersCollector {
     fn visit_region(&mut self, r: Region<'tcx>) -> ControlFlow<Self::BreakTy> {
         match r {
             ty::RePlaceholder(p) if p.universe == self.universe_index => {
-                if let ty::BoundRegion::BrAnon(anon) = p.name {
+                if let ty::BoundRegionKind::BrAnon(anon) = p.name {
                     self.next_anon_region_placeholder = self.next_anon_region_placeholder.max(anon);
                 }
             }
diff --git a/compiler/rustc_traits/src/chalk/mod.rs b/compiler/rustc_traits/src/chalk/mod.rs
index b117e28875e..f3a55fec9e4 100644
--- a/compiler/rustc_traits/src/chalk/mod.rs
+++ b/compiler/rustc_traits/src/chalk/mod.rs
@@ -44,7 +44,7 @@ crate fn evaluate_goal<'tcx>(
 
     let reempty_placeholder = tcx.mk_region(ty::RegionKind::RePlaceholder(ty::Placeholder {
         universe: ty::UniverseIndex::ROOT,
-        name: ty::BoundRegion::BrAnon(placeholders_collector.next_anon_region_placeholder + 1),
+        name: ty::BoundRegionKind::BrAnon(placeholders_collector.next_anon_region_placeholder + 1),
     }));
 
     let mut params_substitutor =
diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs
index c470659e18a..38d33e55866 100644
--- a/compiler/rustc_typeck/src/astconv/mod.rs
+++ b/compiler/rustc_typeck/src/astconv/mod.rs
@@ -196,11 +196,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
 
             Some(rl::Region::LateBound(debruijn, id, _)) => {
                 let name = lifetime_name(id.expect_local());
-                tcx.mk_region(ty::ReLateBound(debruijn, ty::BrNamed(id, name)))
+                let br = ty::BoundRegion { kind: ty::BrNamed(id, name) };
+                tcx.mk_region(ty::ReLateBound(debruijn, br))
             }
 
             Some(rl::Region::LateBoundAnon(debruijn, index)) => {
-                tcx.mk_region(ty::ReLateBound(debruijn, ty::BrAnon(index)))
+                let br = ty::BoundRegion { kind: ty::BrAnon(index) };
+                tcx.mk_region(ty::ReLateBound(debruijn, br))
             }
 
             Some(rl::Region::EarlyBound(index, id, _)) => {
@@ -2295,8 +2297,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
 
     fn validate_late_bound_regions(
         &self,
-        constrained_regions: FxHashSet<ty::BoundRegion>,
-        referenced_regions: FxHashSet<ty::BoundRegion>,
+        constrained_regions: FxHashSet<ty::BoundRegionKind>,
+        referenced_regions: FxHashSet<ty::BoundRegionKind>,
         generate_err: impl Fn(&str) -> rustc_errors::DiagnosticBuilder<'tcx>,
     ) {
         for br in referenced_regions.difference(&constrained_regions) {
diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs
index 3106f19cf86..6467e044079 100644
--- a/compiler/rustc_typeck/src/check/_match.rs
+++ b/compiler/rustc_typeck/src/check/_match.rs
@@ -31,7 +31,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             _ => (false, false, false),
         };
 
-        // Type check the descriminant and get its type.
+        // Type check the discriminant and get its type.
         let scrutinee_ty = if force_scrutinee_bool {
             // Here we want to ensure:
             //
diff --git a/compiler/rustc_typeck/src/check/generator_interior.rs b/compiler/rustc_typeck/src/check/generator_interior.rs
index 54710e12a74..91708465b3f 100644
--- a/compiler/rustc_typeck/src/check/generator_interior.rs
+++ b/compiler/rustc_typeck/src/check/generator_interior.rs
@@ -186,7 +186,8 @@ pub fn resolve_interior<'a, 'tcx>(
                 // which means that none of the regions inside relate to any other, even if
                 // typeck had previously found constraints that would cause them to be related.
                 let folded = fcx.tcx.fold_regions(erased, &mut false, |_, current_depth| {
-                    let r = fcx.tcx.mk_region(ty::ReLateBound(current_depth, ty::BrAnon(counter)));
+                    let br = ty::BoundRegion { kind: ty::BrAnon(counter) };
+                    let r = fcx.tcx.mk_region(ty::ReLateBound(current_depth, br));
                     counter += 1;
                     r
                 });
diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs
index dd629980ab4..c3b0fc60b97 100644
--- a/compiler/rustc_typeck/src/check/intrinsic.rs
+++ b/compiler/rustc_typeck/src/check/intrinsic.rs
@@ -107,13 +107,12 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
 
     let mk_va_list_ty = |mutbl| {
         tcx.lang_items().va_list().map(|did| {
-            let region = tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(0)));
-            let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv);
+            let region = tcx
+                .mk_region(ty::ReLateBound(ty::INNERMOST, ty::BoundRegion { kind: ty::BrAnon(0) }));
+            let env_region =
+                tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BoundRegion { kind: ty::BrEnv }));
             let va_list_ty = tcx.type_of(did).subst(tcx, &[region.into()]);
-            (
-                tcx.mk_ref(tcx.mk_region(env_region), ty::TypeAndMut { ty: va_list_ty, mutbl }),
-                va_list_ty,
-            )
+            (tcx.mk_ref(env_region, ty::TypeAndMut { ty: va_list_ty, mutbl }), va_list_ty)
         })
     };
 
@@ -311,12 +310,12 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
                     tcx.associated_items(tcx.lang_items().discriminant_kind_trait().unwrap());
                 let discriminant_def_id = assoc_items.in_definition_order().next().unwrap().def_id;
 
+                let br = ty::BoundRegion { kind: ty::BrAnon(0) };
                 (
                     1,
-                    vec![tcx.mk_imm_ref(
-                        tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(0))),
-                        param(0),
-                    )],
+                    vec![
+                        tcx.mk_imm_ref(tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)), param(0)),
+                    ],
                     tcx.mk_projection(discriminant_def_id, tcx.mk_substs([param(0).into()].iter())),
                 )
             }
diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs
index 854bc70108f..9ab056c0d74 100644
--- a/compiler/rustc_typeck/src/check/op.rs
+++ b/compiler/rustc_typeck/src/check/op.rs
@@ -503,8 +503,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             if !self.tcx.has_typeck_results(def_id) {
                 return false;
             }
-            // We're emitting a suggestion, so we can just ignore regions
-            let fn_sig = self.tcx.fn_sig(def_id).skip_binder();
+            // FIXME: Instead of exiting early when encountering bound vars in
+            // the function signature, consider keeping the binder here and
+            // propagating it downwards.
+            let fn_sig = if let Some(fn_sig) = self.tcx.fn_sig(def_id).no_bound_vars() {
+                fn_sig
+            } else {
+                return false;
+            };
 
             let other_ty = if let FnDef(def_id, _) = *other_ty.kind() {
                 if !self.tcx.has_typeck_results(def_id) {
diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs
index dd90724e93f..50d88674328 100644
--- a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs
+++ b/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs
@@ -1,10 +1,13 @@
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
 use rustc_hir::itemlikevisit::ItemLikeVisitor;
 use rustc_middle::ty::{self, TyCtxt};
+use rustc_span::Symbol;
 use rustc_trait_selection::traits::{self, SkipLeakCheck};
 use smallvec::SmallVec;
+use std::collections::hash_map::Entry;
 
 pub fn crate_inherent_impls_overlap_check(tcx: TyCtxt<'_>, crate_num: CrateNum) {
     assert_eq!(crate_num, LOCAL_CRATE);
@@ -33,12 +36,9 @@ impl InherentOverlapChecker<'tcx> {
         }
 
         for item1 in impl_items1.in_definition_order() {
-            let collision = impl_items2.filter_by_name_unhygienic(item1.ident.name).any(|item2| {
-                // Symbols and namespace match, compare hygienically.
-                item1.kind.namespace() == item2.kind.namespace()
-                    && item1.ident.normalize_to_macros_2_0()
-                        == item2.ident.normalize_to_macros_2_0()
-            });
+            let collision = impl_items2
+                .filter_by_name_unhygienic(item1.ident.name)
+                .any(|item2| self.compare_hygienically(item1, item2));
 
             if collision {
                 return true;
@@ -48,6 +48,12 @@ impl InherentOverlapChecker<'tcx> {
         false
     }
 
+    fn compare_hygienically(&self, item1: &ty::AssocItem, item2: &ty::AssocItem) -> bool {
+        // Symbols and namespace match, compare hygienically.
+        item1.kind.namespace() == item2.kind.namespace()
+            && item1.ident.normalize_to_macros_2_0() == item2.ident.normalize_to_macros_2_0()
+    }
+
     fn check_for_common_items_in_impls(
         &self,
         impl1: DefId,
@@ -58,12 +64,9 @@ impl InherentOverlapChecker<'tcx> {
         let impl_items2 = self.tcx.associated_items(impl2);
 
         for item1 in impl_items1.in_definition_order() {
-            let collision = impl_items2.filter_by_name_unhygienic(item1.ident.name).find(|item2| {
-                // Symbols and namespace match, compare hygienically.
-                item1.kind.namespace() == item2.kind.namespace()
-                    && item1.ident.normalize_to_macros_2_0()
-                        == item2.ident.normalize_to_macros_2_0()
-            });
+            let collision = impl_items2
+                .filter_by_name_unhygienic(item1.ident.name)
+                .find(|item2| self.compare_hygienically(item1, item2));
 
             if let Some(item2) = collision {
                 let name = item1.ident.normalize_to_macros_2_0();
@@ -134,10 +137,150 @@ impl ItemLikeVisitor<'v> for InherentOverlapChecker<'tcx> {
                     .map(|impl_def_id| (impl_def_id, self.tcx.associated_items(*impl_def_id)))
                     .collect::<SmallVec<[_; 8]>>();
 
-                for (i, &(&impl1_def_id, impl_items1)) in impls_items.iter().enumerate() {
-                    for &(&impl2_def_id, impl_items2) in &impls_items[(i + 1)..] {
-                        if self.impls_have_common_items(impl_items1, impl_items2) {
-                            self.check_for_overlapping_inherent_impls(impl1_def_id, impl2_def_id);
+                // Perform a O(n^2) algorithm for small n,
+                // otherwise switch to an allocating algorithm with
+                // faster asymptotic runtime.
+                const ALLOCATING_ALGO_THRESHOLD: usize = 500;
+                if impls.len() < ALLOCATING_ALGO_THRESHOLD {
+                    for (i, &(&impl1_def_id, impl_items1)) in impls_items.iter().enumerate() {
+                        for &(&impl2_def_id, impl_items2) in &impls_items[(i + 1)..] {
+                            if self.impls_have_common_items(impl_items1, impl_items2) {
+                                self.check_for_overlapping_inherent_impls(
+                                    impl1_def_id,
+                                    impl2_def_id,
+                                );
+                            }
+                        }
+                    }
+                } else {
+                    // Build a set of connected regions of impl blocks.
+                    // Two impl blocks are regarded as connected if they share
+                    // an item with the same unhygienic identifier.
+                    // After we have assembled the connected regions,
+                    // run the O(n^2) algorithm on each connected region.
+                    // This is advantageous to running the algorithm over the
+                    // entire graph when there are many connected regions.
+
+                    struct ConnectedRegion {
+                        idents: SmallVec<[Symbol; 8]>,
+                        impl_blocks: FxHashSet<usize>,
+                    }
+                    // Highest connected region id
+                    let mut highest_region_id = 0;
+                    let mut connected_region_ids = FxHashMap::default();
+                    let mut connected_regions = FxHashMap::default();
+
+                    for (i, &(&_impl_def_id, impl_items)) in impls_items.iter().enumerate() {
+                        if impl_items.len() == 0 {
+                            continue;
+                        }
+                        // First obtain a list of existing connected region ids
+                        let mut idents_to_add = SmallVec::<[Symbol; 8]>::new();
+                        let ids = impl_items
+                            .in_definition_order()
+                            .filter_map(|item| {
+                                let entry = connected_region_ids.entry(item.ident.name);
+                                if let Entry::Occupied(e) = &entry {
+                                    Some(*e.get())
+                                } else {
+                                    idents_to_add.push(item.ident.name);
+                                    None
+                                }
+                            })
+                            .collect::<FxHashSet<usize>>();
+                        match ids.len() {
+                            0 | 1 => {
+                                let id_to_set = if ids.len() == 0 {
+                                    // Create a new connected region
+                                    let region = ConnectedRegion {
+                                        idents: idents_to_add,
+                                        impl_blocks: std::iter::once(i).collect(),
+                                    };
+                                    connected_regions.insert(highest_region_id, region);
+                                    (highest_region_id, highest_region_id += 1).0
+                                } else {
+                                    // Take the only id inside the list
+                                    let id_to_set = *ids.iter().next().unwrap();
+                                    let region = connected_regions.get_mut(&id_to_set).unwrap();
+                                    region.impl_blocks.insert(i);
+                                    region.idents.extend_from_slice(&idents_to_add);
+                                    id_to_set
+                                };
+                                let (_id, region) = connected_regions.iter().next().unwrap();
+                                // Update the connected region ids
+                                for ident in region.idents.iter() {
+                                    connected_region_ids.insert(*ident, id_to_set);
+                                }
+                            }
+                            _ => {
+                                // We have multiple connected regions to merge.
+                                // In the worst case this might add impl blocks
+                                // one by one and can thus be O(n^2) in the size
+                                // of the resulting final connected region, but
+                                // this is no issue as the final step to check
+                                // for overlaps runs in O(n^2) as well.
+
+                                // Take the smallest id from the list
+                                let id_to_set = *ids.iter().min().unwrap();
+
+                                // Sort the id list so that the algorithm is deterministic
+                                let mut ids = ids.into_iter().collect::<SmallVec<[_; 8]>>();
+                                ids.sort();
+
+                                let mut region = connected_regions.remove(&id_to_set).unwrap();
+                                region.idents.extend_from_slice(&idents_to_add);
+                                region.impl_blocks.insert(i);
+
+                                for &id in ids.iter() {
+                                    if id == id_to_set {
+                                        continue;
+                                    }
+                                    let r = connected_regions.remove(&id).unwrap();
+                                    // Update the connected region ids
+                                    for ident in r.idents.iter() {
+                                        connected_region_ids.insert(*ident, id_to_set);
+                                    }
+                                    region.idents.extend_from_slice(&r.idents);
+                                    region.impl_blocks.extend(r.impl_blocks);
+                                }
+                                connected_regions.insert(id_to_set, region);
+                            }
+                        }
+                    }
+
+                    debug!(
+                        "churning through {} components (sum={}, avg={}, var={}, max={})",
+                        connected_regions.len(),
+                        impls.len(),
+                        impls.len() / connected_regions.len(),
+                        {
+                            let avg = impls.len() / connected_regions.len();
+                            let s = connected_regions
+                                .iter()
+                                .map(|r| r.1.impl_blocks.len() as isize - avg as isize)
+                                .map(|v| v.abs() as usize)
+                                .sum::<usize>();
+                            s / connected_regions.len()
+                        },
+                        connected_regions.iter().map(|r| r.1.impl_blocks.len()).max().unwrap()
+                    );
+                    // List of connected regions is built. Now, run the overlap check
+                    // for each pair of impl blocks in the same connected region.
+                    for (_id, region) in connected_regions.into_iter() {
+                        let mut impl_blocks =
+                            region.impl_blocks.into_iter().collect::<SmallVec<[_; 8]>>();
+                        impl_blocks.sort();
+                        for (i, &impl1_items_idx) in impl_blocks.iter().enumerate() {
+                            let &(&impl1_def_id, impl_items1) = &impls_items[impl1_items_idx];
+                            for &impl2_items_idx in impl_blocks[(i + 1)..].iter() {
+                                let &(&impl2_def_id, impl_items2) = &impls_items[impl2_items_idx];
+                                if self.impls_have_common_items(impl_items1, impl_items2) {
+                                    self.check_for_overlapping_inherent_impls(
+                                        impl1_def_id,
+                                        impl2_def_id,
+                                    );
+                                }
+                            }
                         }
                     }
                 }
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index 543e39af669..d5643fcca87 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -461,7 +461,7 @@ fn get_new_lifetime_name<'tcx>(
         .collect_referenced_late_bound_regions(&poly_trait_ref)
         .into_iter()
         .filter_map(|lt| {
-            if let ty::BoundRegion::BrNamed(_, name) = lt {
+            if let ty::BoundRegionKind::BrNamed(_, name) = lt {
                 Some(name.as_str().to_string())
             } else {
                 None
diff --git a/config.toml.example b/config.toml.example
index 5b045d4e32d..b1fb8904ca9 100644
--- a/config.toml.example
+++ b/config.toml.example
@@ -426,6 +426,14 @@ changelog-seen = 2
 # FIXME(#61117): Some tests fail when this option is enabled.
 #debuginfo-level-tests = 0
 
+# Whether to run `dsymutil` on Apple platforms to gather debug info into .dSYM
+# bundles. `dsymutil` adds time to builds for no clear benefit, and also makes
+# it more difficult for debuggers to find debug info. The compiler currently
+# defaults to running `dsymutil` to preserve its historical default, but when
+# compiling the compiler itself, we skip it by default since we know it's safe
+# to do so in that case.
+#run-dsymutil = false
+
 # Whether or not `panic!`s generate backtraces (RUST_BACKTRACE)
 #backtrace = true
 
diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs
index 33df175bfc5..4d876fd8c33 100644
--- a/library/core/src/num/f32.rs
+++ b/library/core/src/num/f32.rs
@@ -961,4 +961,39 @@ impl f32 {
 
         left.cmp(&right)
     }
+
+    /// Restrict a value to a certain interval unless it is NaN.
+    ///
+    /// Returns `max` if `self` is greater than `max`, and `min` if `self` is
+    /// less than `min`. Otherwise this returns `self`.
+    ///
+    /// Note that this function returns NaN if the initial value was NaN as
+    /// well.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `min > max`, `min` is NaN, or `max` is NaN.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// assert!((-3.0f32).clamp(-2.0, 1.0) == -2.0);
+    /// assert!((0.0f32).clamp(-2.0, 1.0) == 0.0);
+    /// assert!((2.0f32).clamp(-2.0, 1.0) == 1.0);
+    /// assert!((f32::NAN).clamp(-2.0, 1.0).is_nan());
+    /// ```
+    #[must_use = "method returns a new number and does not mutate the original value"]
+    #[stable(feature = "clamp", since = "1.50.0")]
+    #[inline]
+    pub fn clamp(self, min: f32, max: f32) -> f32 {
+        assert!(min <= max);
+        let mut x = self;
+        if x < min {
+            x = min;
+        }
+        if x > max {
+            x = max;
+        }
+        x
+    }
 }
diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs
index b85e8deb6d2..3323b7d6774 100644
--- a/library/core/src/num/f64.rs
+++ b/library/core/src/num/f64.rs
@@ -975,4 +975,39 @@ impl f64 {
 
         left.cmp(&right)
     }
+
+    /// Restrict a value to a certain interval unless it is NaN.
+    ///
+    /// Returns `max` if `self` is greater than `max`, and `min` if `self` is
+    /// less than `min`. Otherwise this returns `self`.
+    ///
+    /// Note that this function returns NaN if the initial value was NaN as
+    /// well.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `min > max`, `min` is NaN, or `max` is NaN.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// assert!((-3.0f64).clamp(-2.0, 1.0) == -2.0);
+    /// assert!((0.0f64).clamp(-2.0, 1.0) == 0.0);
+    /// assert!((2.0f64).clamp(-2.0, 1.0) == 1.0);
+    /// assert!((f64::NAN).clamp(-2.0, 1.0).is_nan());
+    /// ```
+    #[must_use = "method returns a new number and does not mutate the original value"]
+    #[stable(feature = "clamp", since = "1.50.0")]
+    #[inline]
+    pub fn clamp(self, min: f64, max: f64) -> f64 {
+        assert!(min <= max);
+        let mut x = self;
+        if x < min {
+            x = min;
+        }
+        if x > max {
+            x = max;
+        }
+        x
+    }
 }
diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs
index be6c86b5176..a96da9aa6dc 100644
--- a/library/core/src/sync/atomic.rs
+++ b/library/core/src/sync/atomic.rs
@@ -1040,8 +1040,16 @@ impl<T> AtomicPtr<T> {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[cfg(target_has_atomic = "ptr")]
     pub fn swap(&self, ptr: *mut T, order: Ordering) -> *mut T {
+        #[cfg(bootstrap)]
         // SAFETY: data races are prevented by atomic intrinsics.
-        unsafe { atomic_swap(self.p.get() as *mut usize, ptr as usize, order) as *mut T }
+        unsafe {
+            atomic_swap(self.p.get() as *mut usize, ptr as usize, order) as *mut T
+        }
+        #[cfg(not(bootstrap))]
+        // SAFETY: data races are prevented by atomic intrinsics.
+        unsafe {
+            atomic_swap(self.p.get(), ptr, order)
+        }
     }
 
     /// Stores a value into the pointer if the current value is the same as the `current` value.
diff --git a/library/core/tests/mem.rs b/library/core/tests/mem.rs
index 5e24fa690ef..5d0fedd4d9c 100644
--- a/library/core/tests/mem.rs
+++ b/library/core/tests/mem.rs
@@ -1,5 +1,6 @@
 use core::mem::*;
 
+#[cfg(panic = "unwind")]
 use std::rc::Rc;
 
 #[test]
@@ -250,14 +251,19 @@ fn uninit_write_slice_cloned_mid_panic() {
 
 #[test]
 fn uninit_write_slice_cloned_no_drop() {
-    let rc = Rc::new(());
+    #[derive(Clone)]
+    struct Bomb;
+
+    impl Drop for Bomb {
+        fn drop(&mut self) {
+            panic!("dropped a bomb! kaboom")
+        }
+    }
 
     let mut dst = [MaybeUninit::uninit()];
-    let src = [rc.clone()];
+    let src = [Bomb];
 
     MaybeUninit::write_slice_cloned(&mut dst, &src);
 
-    drop(src);
-
-    assert_eq!(Rc::strong_count(&rc), 2);
+    forget(src);
 }
diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs
index 48c77bd13e0..c30458c0545 100644
--- a/library/std/src/f32.rs
+++ b/library/std/src/f32.rs
@@ -879,39 +879,4 @@ impl f32 {
     pub fn atanh(self) -> f32 {
         0.5 * ((2.0 * self) / (1.0 - self)).ln_1p()
     }
-
-    /// Restrict a value to a certain interval unless it is NaN.
-    ///
-    /// Returns `max` if `self` is greater than `max`, and `min` if `self` is
-    /// less than `min`. Otherwise this returns `self`.
-    ///
-    /// Note that this function returns NaN if the initial value was NaN as
-    /// well.
-    ///
-    /// # Panics
-    ///
-    /// Panics if `min > max`, `min` is NaN, or `max` is NaN.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// assert!((-3.0f32).clamp(-2.0, 1.0) == -2.0);
-    /// assert!((0.0f32).clamp(-2.0, 1.0) == 0.0);
-    /// assert!((2.0f32).clamp(-2.0, 1.0) == 1.0);
-    /// assert!((f32::NAN).clamp(-2.0, 1.0).is_nan());
-    /// ```
-    #[must_use = "method returns a new number and does not mutate the original value"]
-    #[stable(feature = "clamp", since = "1.50.0")]
-    #[inline]
-    pub fn clamp(self, min: f32, max: f32) -> f32 {
-        assert!(min <= max);
-        let mut x = self;
-        if x < min {
-            x = min;
-        }
-        if x > max {
-            x = max;
-        }
-        x
-    }
 }
diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs
index e823ac43fdd..f4cc53979d1 100644
--- a/library/std/src/f64.rs
+++ b/library/std/src/f64.rs
@@ -882,41 +882,6 @@ impl f64 {
         0.5 * ((2.0 * self) / (1.0 - self)).ln_1p()
     }
 
-    /// Restrict a value to a certain interval unless it is NaN.
-    ///
-    /// Returns `max` if `self` is greater than `max`, and `min` if `self` is
-    /// less than `min`. Otherwise this returns `self`.
-    ///
-    /// Note that this function returns NaN if the initial value was NaN as
-    /// well.
-    ///
-    /// # Panics
-    ///
-    /// Panics if `min > max`, `min` is NaN, or `max` is NaN.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// assert!((-3.0f64).clamp(-2.0, 1.0) == -2.0);
-    /// assert!((0.0f64).clamp(-2.0, 1.0) == 0.0);
-    /// assert!((2.0f64).clamp(-2.0, 1.0) == 1.0);
-    /// assert!((f64::NAN).clamp(-2.0, 1.0).is_nan());
-    /// ```
-    #[must_use = "method returns a new number and does not mutate the original value"]
-    #[stable(feature = "clamp", since = "1.50.0")]
-    #[inline]
-    pub fn clamp(self, min: f64, max: f64) -> f64 {
-        assert!(min <= max);
-        let mut x = self;
-        if x < min {
-            x = min;
-        }
-        if x > max {
-            x = max;
-        }
-        x
-    }
-
     // Solaris/Illumos requires a wrapper around log, log2, and log10 functions
     // because of their non-standard behavior (e.g., log(-n) returns -Inf instead
     // of expected NaN).
diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs
index c077512f80a..ec12e9f09d8 100644
--- a/library/std/src/primitive_docs.rs
+++ b/library/std/src/primitive_docs.rs
@@ -478,8 +478,10 @@ mod prim_unit {}
 #[stable(feature = "rust1", since = "1.0.0")]
 mod prim_pointer {}
 
+#[doc(alias = "[]")]
+#[doc(alias = "[T;N]")] // unfortunately, rustdoc doesn't have fuzzy search for aliases
+#[doc(alias = "[T; N]")]
 #[doc(primitive = "array")]
-//
 /// A fixed-size array, denoted `[T; N]`, for the element type, `T`, and the
 /// non-negative compile-time constant size, `N`.
 ///
diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs
index d3a279a2355..821851a6c65 100644
--- a/library/std/src/sys/unix/fd.rs
+++ b/library/std/src/sys/unix/fd.rs
@@ -12,6 +12,11 @@ use crate::sys_common::AsInner;
 use libc::{c_int, c_void};
 
 #[derive(Debug)]
+#[rustc_layout_scalar_valid_range_start(0)]
+// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a
+// 32-bit c_int. Below is -2, in two's complement, but that only works out
+// because c_int is 32 bits.
+#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
 pub struct FileDesc {
     fd: c_int,
 }
@@ -63,7 +68,9 @@ const fn max_iov() -> usize {
 
 impl FileDesc {
     pub fn new(fd: c_int) -> FileDesc {
-        FileDesc { fd }
+        assert_ne!(fd, -1i32);
+        // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned)
+        unsafe { FileDesc { fd } }
     }
 
     pub fn raw(&self) -> c_int {
diff --git a/library/std/src/sys/unix/fd/tests.rs b/library/std/src/sys/unix/fd/tests.rs
index a932043cbc6..c9520485c3c 100644
--- a/library/std/src/sys/unix/fd/tests.rs
+++ b/library/std/src/sys/unix/fd/tests.rs
@@ -3,7 +3,7 @@ use core::mem::ManuallyDrop;
 
 #[test]
 fn limit_vector_count() {
-    let stdout = ManuallyDrop::new(FileDesc { fd: 1 });
+    let stdout = ManuallyDrop::new(unsafe { FileDesc { fd: 1 } });
     let bufs = (0..1500).map(|_| IoSlice::new(&[])).collect::<Vec<_>>();
     assert!(stdout.write_vectored(&bufs).is_ok());
 }
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 9af79e20630..ab0c4a5c31b 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -1126,6 +1126,19 @@ impl<'a> Builder<'a> {
             },
         );
 
+        // `dsymutil` adds time to builds on Apple platforms for no clear benefit, and also makes
+        // it more difficult for debuggers to find debug info. The compiler currently defaults to
+        // running `dsymutil` to preserve its historical default, but when compiling the compiler
+        // itself, we skip it by default since we know it's safe to do so in that case.
+        // See https://github.com/rust-lang/rust/issues/79361 for more info on this flag.
+        if target.contains("apple") {
+            if self.config.rust_run_dsymutil {
+                rustflags.arg("-Zrun-dsymutil=yes");
+            } else {
+                rustflags.arg("-Zrun-dsymutil=no");
+            }
+        }
+
         if self.config.cmd.bless() {
             // Bless `expect!` tests.
             cargo.env("UPDATE_EXPECT", "1");
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index fb2c6d1f92a..ece8a7494b5 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -123,6 +123,7 @@ pub struct Config {
     pub rust_debuginfo_level_std: u32,
     pub rust_debuginfo_level_tools: u32,
     pub rust_debuginfo_level_tests: u32,
+    pub rust_run_dsymutil: bool,
     pub rust_rpath: bool,
     pub rustc_parallel: bool,
     pub rustc_default_linker: Option<String>,
@@ -466,6 +467,7 @@ struct Rust {
     debuginfo_level_std: Option<u32>,
     debuginfo_level_tools: Option<u32>,
     debuginfo_level_tests: Option<u32>,
+    run_dsymutil: Option<bool>,
     backtrace: Option<bool>,
     incremental: Option<bool>,
     parallel_compiler: Option<bool>,
@@ -830,6 +832,7 @@ impl Config {
             debuginfo_level_std = rust.debuginfo_level_std;
             debuginfo_level_tools = rust.debuginfo_level_tools;
             debuginfo_level_tests = rust.debuginfo_level_tests;
+            config.rust_run_dsymutil = rust.run_dsymutil.unwrap_or(false);
             optimize = rust.optimize;
             ignore_git = rust.ignore_git;
             set(&mut config.rust_new_symbol_mangling, rust.new_symbol_mangling);
diff --git a/src/bootstrap/install.rs b/src/bootstrap/install.rs
index 074f5cd73f3..8f2b128b368 100644
--- a/src/bootstrap/install.rs
+++ b/src/bootstrap/install.rs
@@ -73,12 +73,7 @@ fn install_sh(
     let docdir_default = datadir_default.join("doc/rust");
     let libdir_default = PathBuf::from("lib");
     let mandir_default = datadir_default.join("man");
-    let prefix = builder.config.prefix.as_ref().map_or(prefix_default, |p| {
-        fs::create_dir_all(p)
-            .unwrap_or_else(|err| panic!("could not create {}: {}", p.display(), err));
-        fs::canonicalize(p)
-            .unwrap_or_else(|err| panic!("could not canonicalize {}: {}", p.display(), err))
-    });
+    let prefix = builder.config.prefix.as_ref().unwrap_or(&prefix_default);
     let sysconfdir = builder.config.sysconfdir.as_ref().unwrap_or(&sysconfdir_default);
     let datadir = builder.config.datadir.as_ref().unwrap_or(&datadir_default);
     let docdir = builder.config.docdir.as_ref().unwrap_or(&docdir_default);
@@ -103,6 +98,13 @@ fn install_sh(
     let libdir = add_destdir(&libdir, &destdir);
     let mandir = add_destdir(&mandir, &destdir);
 
+    let prefix = {
+        fs::create_dir_all(&prefix)
+            .unwrap_or_else(|err| panic!("could not create {}: {}", prefix.display(), err));
+        fs::canonicalize(&prefix)
+            .unwrap_or_else(|err| panic!("could not canonicalize {}: {}", prefix.display(), err))
+    };
+
     let empty_dir = builder.out.join("tmp/empty_dir");
 
     t!(fs::create_dir_all(&empty_dir));
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 1c9b19e6b87..cc5f68bc297 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -431,7 +431,9 @@ impl Clean<Option<Lifetime>> for ty::RegionKind {
     fn clean(&self, _cx: &DocContext<'_>) -> Option<Lifetime> {
         match *self {
             ty::ReStatic => Some(Lifetime::statik()),
-            ty::ReLateBound(_, ty::BrNamed(_, name)) => Some(Lifetime(name)),
+            ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrNamed(_, name) }) => {
+                Some(Lifetime(name))
+            }
             ty::ReEarlyBound(ref data) => Some(Lifetime(data.name)),
 
             ty::ReLateBound(..)
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index 1ae2e5de82c..4d525d62c52 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -104,7 +104,9 @@ fn external_generic_args(
         .iter()
         .filter_map(|kind| match kind.unpack() {
             GenericArgKind::Lifetime(lt) => match lt {
-                ty::ReLateBound(_, ty::BrAnon(_)) => Some(GenericArg::Lifetime(Lifetime::elided())),
+                ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrAnon(_) }) => {
+                    Some(GenericArg::Lifetime(Lifetime::elided()))
+                }
                 _ => lt.clean(cx).map(GenericArg::Lifetime),
             },
             GenericArgKind::Type(_) if skip_self => {
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 22096203d4c..0e87dd72ef1 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -447,21 +447,23 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
 }
 
 /// Make headings links with anchor IDs and build up TOC.
-struct HeadingLinks<'a, 'b, 'ids, I: Iterator<Item = Event<'a>>> {
+struct HeadingLinks<'a, 'b, 'ids, I> {
     inner: I,
     toc: Option<&'b mut TocBuilder>,
-    buf: VecDeque<Event<'a>>,
+    buf: VecDeque<(Event<'a>, Range<usize>)>,
     id_map: &'ids mut IdMap,
 }
 
-impl<'a, 'b, 'ids, I: Iterator<Item = Event<'a>>> HeadingLinks<'a, 'b, 'ids, I> {
+impl<'a, 'b, 'ids, I> HeadingLinks<'a, 'b, 'ids, I> {
     fn new(iter: I, toc: Option<&'b mut TocBuilder>, ids: &'ids mut IdMap) -> Self {
         HeadingLinks { inner: iter, toc, buf: VecDeque::new(), id_map: ids }
     }
 }
 
-impl<'a, 'b, 'ids, I: Iterator<Item = Event<'a>>> Iterator for HeadingLinks<'a, 'b, 'ids, I> {
-    type Item = Event<'a>;
+impl<'a, 'b, 'ids, I: Iterator<Item = (Event<'a>, Range<usize>)>> Iterator
+    for HeadingLinks<'a, 'b, 'ids, I>
+{
+    type Item = (Event<'a>, Range<usize>);
 
     fn next(&mut self) -> Option<Self::Item> {
         if let Some(e) = self.buf.pop_front() {
@@ -469,31 +471,29 @@ impl<'a, 'b, 'ids, I: Iterator<Item = Event<'a>>> Iterator for HeadingLinks<'a,
         }
 
         let event = self.inner.next();
-        if let Some(Event::Start(Tag::Heading(level))) = event {
+        if let Some((Event::Start(Tag::Heading(level)), _)) = event {
             let mut id = String::new();
             for event in &mut self.inner {
-                match &event {
+                match &event.0 {
                     Event::End(Tag::Heading(..)) => break,
+                    Event::Start(Tag::Link(_, _, _)) | Event::End(Tag::Link(..)) => {}
                     Event::Text(text) | Event::Code(text) => {
                         id.extend(text.chars().filter_map(slugify));
+                        self.buf.push_back(event);
                     }
-                    _ => {}
-                }
-                match event {
-                    Event::Start(Tag::Link(_, _, _)) | Event::End(Tag::Link(..)) => {}
-                    event => self.buf.push_back(event),
+                    _ => self.buf.push_back(event),
                 }
             }
             let id = self.id_map.derive(id);
 
             if let Some(ref mut builder) = self.toc {
                 let mut html_header = String::new();
-                html::push_html(&mut html_header, self.buf.iter().cloned());
+                html::push_html(&mut html_header, self.buf.iter().map(|(ev, _)| ev.clone()));
                 let sec = builder.push(level as u32, html_header, id.clone());
-                self.buf.push_front(Event::Html(format!("{} ", sec).into()));
+                self.buf.push_front((Event::Html(format!("{} ", sec).into()), 0..0));
             }
 
-            self.buf.push_back(Event::Html(format!("</a></h{}>", level).into()));
+            self.buf.push_back((Event::Html(format!("</a></h{}>", level).into()), 0..0));
 
             let start_tags = format!(
                 "<h{level} id=\"{id}\" class=\"section-header\">\
@@ -501,7 +501,7 @@ impl<'a, 'b, 'ids, I: Iterator<Item = Event<'a>>> Iterator for HeadingLinks<'a,
                 id = id,
                 level = level
             );
-            return Some(Event::Html(start_tags.into()));
+            return Some((Event::Html(start_tags.into()), 0..0));
         }
         event
     }
@@ -575,15 +575,16 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for SummaryLine<'a, I> {
 
 /// Moves all footnote definitions to the end and add back links to the
 /// references.
-struct Footnotes<'a, I: Iterator<Item = Event<'a>>> {
+struct Footnotes<'a, I> {
     inner: I,
     footnotes: FxHashMap<String, (Vec<Event<'a>>, u16)>,
 }
 
-impl<'a, I: Iterator<Item = Event<'a>>> Footnotes<'a, I> {
+impl<'a, I> Footnotes<'a, I> {
     fn new(iter: I) -> Self {
         Footnotes { inner: iter, footnotes: FxHashMap::default() }
     }
+
     fn get_entry(&mut self, key: &str) -> &mut (Vec<Event<'a>>, u16) {
         let new_id = self.footnotes.keys().count() + 1;
         let key = key.to_owned();
@@ -591,23 +592,23 @@ impl<'a, I: Iterator<Item = Event<'a>>> Footnotes<'a, I> {
     }
 }
 
-impl<'a, I: Iterator<Item = Event<'a>>> Iterator for Footnotes<'a, I> {
-    type Item = Event<'a>;
+impl<'a, I: Iterator<Item = (Event<'a>, Range<usize>)>> Iterator for Footnotes<'a, I> {
+    type Item = (Event<'a>, Range<usize>);
 
     fn next(&mut self) -> Option<Self::Item> {
         loop {
             match self.inner.next() {
-                Some(Event::FootnoteReference(ref reference)) => {
+                Some((Event::FootnoteReference(ref reference), range)) => {
                     let entry = self.get_entry(&reference);
                     let reference = format!(
                         "<sup id=\"fnref{0}\"><a href=\"#fn{0}\">{0}</a></sup>",
                         (*entry).1
                     );
-                    return Some(Event::Html(reference.into()));
+                    return Some((Event::Html(reference.into()), range));
                 }
-                Some(Event::Start(Tag::FootnoteDefinition(def))) => {
+                Some((Event::Start(Tag::FootnoteDefinition(def)), _)) => {
                     let mut content = Vec::new();
-                    for event in &mut self.inner {
+                    for (event, _) in &mut self.inner {
                         if let Event::End(Tag::FootnoteDefinition(..)) = event {
                             break;
                         }
@@ -638,7 +639,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for Footnotes<'a, I> {
                             ret.push_str("</li>");
                         }
                         ret.push_str("</ol></div>");
-                        return Some(Event::Html(ret.into()));
+                        return Some((Event::Html(ret.into()), 0..0));
                     } else {
                         return None;
                     }
@@ -946,13 +947,14 @@ impl Markdown<'_> {
         };
 
         let p = Parser::new_with_broken_link_callback(md, opts(), Some(&mut replacer));
+        let p = p.into_offset_iter();
 
         let mut s = String::with_capacity(md.len() * 3 / 2);
 
         let p = HeadingLinks::new(p, None, &mut ids);
-        let p = LinkReplacer::new(p, links);
-        let p = CodeBlocks::new(p, codes, edition, playground);
         let p = Footnotes::new(p);
+        let p = LinkReplacer::new(p.map(|(ev, _)| ev), links);
+        let p = CodeBlocks::new(p, codes, edition, playground);
         html::push_html(&mut s, p);
 
         s
@@ -963,7 +965,7 @@ impl MarkdownWithToc<'_> {
     crate fn into_string(self) -> String {
         let MarkdownWithToc(md, mut ids, codes, edition, playground) = self;
 
-        let p = Parser::new_ext(md, opts());
+        let p = Parser::new_ext(md, opts()).into_offset_iter();
 
         let mut s = String::with_capacity(md.len() * 3 / 2);
 
@@ -971,8 +973,8 @@ impl MarkdownWithToc<'_> {
 
         {
             let p = HeadingLinks::new(p, Some(&mut toc), &mut ids);
-            let p = CodeBlocks::new(p, codes, edition, playground);
             let p = Footnotes::new(p);
+            let p = CodeBlocks::new(p.map(|(ev, _)| ev), codes, edition, playground);
             html::push_html(&mut s, p);
         }
 
@@ -988,19 +990,19 @@ impl MarkdownHtml<'_> {
         if md.is_empty() {
             return String::new();
         }
-        let p = Parser::new_ext(md, opts());
+        let p = Parser::new_ext(md, opts()).into_offset_iter();
 
         // Treat inline HTML as plain text.
-        let p = p.map(|event| match event {
-            Event::Html(text) => Event::Text(text),
+        let p = p.map(|event| match event.0 {
+            Event::Html(text) => (Event::Text(text), event.1),
             _ => event,
         });
 
         let mut s = String::with_capacity(md.len() * 3 / 2);
 
         let p = HeadingLinks::new(p, None, &mut ids);
-        let p = CodeBlocks::new(p, codes, edition, playground);
         let p = Footnotes::new(p);
+        let p = CodeBlocks::new(p.map(|(ev, _)| ev), codes, edition, playground);
         html::push_html(&mut s, p);
 
         s
@@ -1153,50 +1155,45 @@ crate fn plain_text_summary(md: &str) -> String {
     s
 }
 
-crate fn markdown_links(md: &str) -> Vec<(String, Option<Range<usize>>)> {
+crate fn markdown_links(md: &str) -> Vec<(String, Range<usize>)> {
     if md.is_empty() {
         return vec![];
     }
 
     let mut links = vec![];
+    // Used to avoid mutable borrow issues in the `push` closure
+    // Probably it would be more efficient to use a `RefCell` but it doesn't seem worth the churn.
     let mut shortcut_links = vec![];
 
-    {
-        let locate = |s: &str| unsafe {
-            let s_start = s.as_ptr();
-            let s_end = s_start.add(s.len());
-            let md_start = md.as_ptr();
-            let md_end = md_start.add(md.len());
-            if md_start <= s_start && s_end <= md_end {
-                let start = s_start.offset_from(md_start) as usize;
-                let end = s_end.offset_from(md_start) as usize;
-                Some(start..end)
-            } else {
-                None
-            }
-        };
-
-        let mut push = |link: BrokenLink<'_>| {
-            // FIXME: use `link.span` instead of `locate`
-            // (doing it now includes the `[]` as well as the text)
-            shortcut_links.push((link.reference.to_owned(), locate(link.reference)));
-            None
-        };
-        let p = Parser::new_with_broken_link_callback(md, opts(), Some(&mut push));
-
-        // There's no need to thread an IdMap through to here because
-        // the IDs generated aren't going to be emitted anywhere.
-        let mut ids = IdMap::new();
-        let iter = Footnotes::new(HeadingLinks::new(p, None, &mut ids));
-
-        for ev in iter {
-            if let Event::Start(Tag::Link(_, dest, _)) = ev {
-                debug!("found link: {}", dest);
-                links.push(match dest {
-                    CowStr::Borrowed(s) => (s.to_owned(), locate(s)),
-                    s @ (CowStr::Boxed(..) | CowStr::Inlined(..)) => (s.into_string(), None),
-                });
+    let span_for_link = |link: &str, span: Range<usize>| {
+        // Pulldown includes the `[]` as well as the URL. Only highlight the relevant span.
+        // NOTE: uses `rfind` in case the title and url are the same: `[Ok][Ok]`
+        match md[span.clone()].rfind(link) {
+            Some(start) => {
+                let start = span.start + start;
+                start..start + link.len()
             }
+            // This can happen for things other than intra-doc links, like `#1` expanded to `https://github.com/rust-lang/rust/issues/1`.
+            None => span,
+        }
+    };
+    let mut push = |link: BrokenLink<'_>| {
+        let span = span_for_link(link.reference, link.span);
+        shortcut_links.push((link.reference.to_owned(), span));
+        None
+    };
+    let p = Parser::new_with_broken_link_callback(md, opts(), Some(&mut push));
+
+    // There's no need to thread an IdMap through to here because
+    // the IDs generated aren't going to be emitted anywhere.
+    let mut ids = IdMap::new();
+    let iter = Footnotes::new(HeadingLinks::new(p.into_offset_iter(), None, &mut ids));
+
+    for ev in iter {
+        if let Event::Start(Tag::Link(_, dest, _)) = ev.0 {
+            debug!("found link: {}", dest);
+            let span = span_for_link(&dest, ev.1);
+            links.push((dest.into_string(), span));
         }
     }
 
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index ea5bf94689b..a8adfe08b25 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -180,7 +180,7 @@ struct DiagnosticInfo<'a> {
     item: &'a Item,
     dox: &'a str,
     ori_link: &'a str,
-    link_range: Option<Range<usize>>,
+    link_range: Range<usize>,
 }
 
 #[derive(Clone, Debug, Hash)]
@@ -920,7 +920,7 @@ impl LinkCollector<'_, '_> {
         parent_node: Option<DefId>,
         krate: CrateNum,
         ori_link: String,
-        link_range: Option<Range<usize>>,
+        link_range: Range<usize>,
     ) -> Option<ItemLink> {
         trace!("considering link '{}'", ori_link);
 
@@ -1566,7 +1566,7 @@ fn report_diagnostic(
     msg: &str,
     item: &Item,
     dox: &str,
-    link_range: &Option<Range<usize>>,
+    link_range: &Range<usize>,
     decorate: impl FnOnce(&mut DiagnosticBuilder<'_>, Option<rustc_span::Span>),
 ) {
     let hir_id = match cx.as_local_hir_id(item.def_id) {
@@ -1584,31 +1584,26 @@ fn report_diagnostic(
     cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |lint| {
         let mut diag = lint.build(msg);
 
-        let span = link_range
-            .as_ref()
-            .and_then(|range| super::source_span_for_markdown_range(cx, dox, range, attrs));
-
-        if let Some(link_range) = link_range {
-            if let Some(sp) = span {
-                diag.set_span(sp);
-            } else {
-                // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
-                //                       ^     ~~~~
-                //                       |     link_range
-                //                       last_new_line_offset
-                let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
-                let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
-
-                // Print the line containing the `link_range` and manually mark it with '^'s.
-                diag.note(&format!(
-                    "the link appears in this line:\n\n{line}\n\
-                     {indicator: <before$}{indicator:^<found$}",
-                    line = line,
-                    indicator = "",
-                    before = link_range.start - last_new_line_offset,
-                    found = link_range.len(),
-                ));
-            }
+        let span = super::source_span_for_markdown_range(cx, dox, link_range, attrs);
+        if let Some(sp) = span {
+            diag.set_span(sp);
+        } else {
+            // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
+            //                       ^     ~~~~
+            //                       |     link_range
+            //                       last_new_line_offset
+            let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
+            let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
+
+            // Print the line containing the `link_range` and manually mark it with '^'s.
+            diag.note(&format!(
+                "the link appears in this line:\n\n{line}\n\
+                    {indicator: <before$}{indicator:^<found$}",
+                line = line,
+                indicator = "",
+                before = link_range.start - last_new_line_offset,
+                found = link_range.len(),
+            ));
         }
 
         decorate(&mut diag, span);
@@ -1628,7 +1623,7 @@ fn resolution_failure(
     path_str: &str,
     disambiguator: Option<Disambiguator>,
     dox: &str,
-    link_range: Option<Range<usize>>,
+    link_range: Range<usize>,
     kinds: SmallVec<[ResolutionFailure<'_>; 3]>,
 ) {
     report_diagnostic(
@@ -1862,7 +1857,7 @@ fn anchor_failure(
     item: &Item,
     path_str: &str,
     dox: &str,
-    link_range: Option<Range<usize>>,
+    link_range: Range<usize>,
     failure: AnchorFailure,
 ) {
     let msg = match failure {
@@ -1887,7 +1882,7 @@ fn ambiguity_error(
     item: &Item,
     path_str: &str,
     dox: &str,
-    link_range: Option<Range<usize>>,
+    link_range: Range<usize>,
     candidates: Vec<Res>,
 ) {
     let mut msg = format!("`{}` is ", path_str);
@@ -1936,13 +1931,12 @@ fn suggest_disambiguator(
     path_str: &str,
     dox: &str,
     sp: Option<rustc_span::Span>,
-    link_range: &Option<Range<usize>>,
+    link_range: &Range<usize>,
 ) {
     let suggestion = disambiguator.suggestion();
     let help = format!("to link to the {}, {}", disambiguator.descr(), suggestion.descr());
 
     if let Some(sp) = sp {
-        let link_range = link_range.as_ref().expect("must have a link range if we have a span");
         let msg = if dox.bytes().nth(link_range.start) == Some(b'`') {
             format!("`{}`", suggestion.as_help(path_str))
         } else {
@@ -1961,7 +1955,7 @@ fn privacy_error(
     item: &Item,
     path_str: &str,
     dox: &str,
-    link_range: Option<Range<usize>>,
+    link_range: Range<usize>,
 ) {
     let sym;
     let item_name = match item.name {
diff --git a/src/test/ui/binop/issue-77910-1.rs b/src/test/ui/binop/issue-77910-1.rs
new file mode 100644
index 00000000000..d786e335859
--- /dev/null
+++ b/src/test/ui/binop/issue-77910-1.rs
@@ -0,0 +1,11 @@
+fn foo(s: &i32) -> &i32 {
+    let xs;
+    xs
+}
+fn main() {
+    let y;
+    // we shouldn't ice with the bound var here.
+    assert_eq!(foo, y);
+    //~^ ERROR binary operation `==` cannot be applied to type
+    //~| ERROR `for<'r> fn(&'r i32) -> &'r i32 {foo}` doesn't implement `Debug`
+}
diff --git a/src/test/ui/binop/issue-77910-1.stderr b/src/test/ui/binop/issue-77910-1.stderr
new file mode 100644
index 00000000000..e48d3e19996
--- /dev/null
+++ b/src/test/ui/binop/issue-77910-1.stderr
@@ -0,0 +1,26 @@
+error[E0369]: binary operation `==` cannot be applied to type `for<'r> fn(&'r i32) -> &'r i32 {foo}`
+  --> $DIR/issue-77910-1.rs:8:5
+   |
+LL |     assert_eq!(foo, y);
+   |     ^^^^^^^^^^^^^^^^^^^
+   |     |
+   |     for<'r> fn(&'r i32) -> &'r i32 {foo}
+   |     _
+   |
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0277]: `for<'r> fn(&'r i32) -> &'r i32 {foo}` doesn't implement `Debug`
+  --> $DIR/issue-77910-1.rs:8:5
+   |
+LL |     assert_eq!(foo, y);
+   |     ^^^^^^^^^^^^^^^^^^^ `for<'r> fn(&'r i32) -> &'r i32 {foo}` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |
+   = help: the trait `Debug` is not implemented for `for<'r> fn(&'r i32) -> &'r i32 {foo}`
+   = note: required because of the requirements on the impl of `Debug` for `&for<'r> fn(&'r i32) -> &'r i32 {foo}`
+   = note: required by `std::fmt::Debug::fmt`
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0277, E0369.
+For more information about an error, try `rustc --explain E0277`.
diff --git a/src/test/ui/binop/issue-77910-2.rs b/src/test/ui/binop/issue-77910-2.rs
new file mode 100644
index 00000000000..2bb48d36576
--- /dev/null
+++ b/src/test/ui/binop/issue-77910-2.rs
@@ -0,0 +1,9 @@
+fn foo(s: &i32) -> &i32 {
+    let xs;
+    xs
+}
+fn main() {
+    let y;
+    if foo == y {}
+    //~^ ERROR binary operation `==` cannot be applied to type
+}
diff --git a/src/test/ui/binop/issue-77910-2.stderr b/src/test/ui/binop/issue-77910-2.stderr
new file mode 100644
index 00000000000..5477a5762a8
--- /dev/null
+++ b/src/test/ui/binop/issue-77910-2.stderr
@@ -0,0 +1,11 @@
+error[E0369]: binary operation `==` cannot be applied to type `for<'r> fn(&'r i32) -> &'r i32 {foo}`
+  --> $DIR/issue-77910-2.rs:7:12
+   |
+LL |     if foo == y {}
+   |        --- ^^ - _
+   |        |
+   |        for<'r> fn(&'r i32) -> &'r i32 {foo}
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0369`.
diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-5.rs b/src/test/ui/const-generics/occurs-check/unused-substs-5.rs
new file mode 100644
index 00000000000..e5d487d89b9
--- /dev/null
+++ b/src/test/ui/const-generics/occurs-check/unused-substs-5.rs
@@ -0,0 +1,20 @@
+#![feature(const_generics, const_evaluatable_checked)]
+#![allow(incomplete_features)]
+
+// `N + 1` also depends on `T` here even if it doesn't use it.
+fn q<T, const N: usize>(_: T) -> [u8; N + 1] {
+    todo!()
+}
+
+fn supplier<T>() -> T {
+    todo!()
+}
+
+fn catch_me<const N: usize>() where [u8; N + 1]: Default {
+    let mut x = supplier();
+    x = q::<_, N>(x); //~ ERROR mismatched types
+}
+
+fn main() {
+    catch_me::<3>();
+}
diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-5.stderr b/src/test/ui/const-generics/occurs-check/unused-substs-5.stderr
new file mode 100644
index 00000000000..239569dab09
--- /dev/null
+++ b/src/test/ui/const-generics/occurs-check/unused-substs-5.stderr
@@ -0,0 +1,12 @@
+error[E0308]: mismatched types
+  --> $DIR/unused-substs-5.rs:15:9
+   |
+LL |     x = q::<_, N>(x);
+   |         ^^^^^^^^^^^^
+   |         |
+   |         cyclic type of infinite size
+   |         help: try using a conversion method: `q::<_, N>(x).to_vec()`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr b/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr
index 52662ef9eaf..b65e50eb9f4 100644
--- a/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr
+++ b/src/test/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr
@@ -146,6 +146,11 @@ help: skipping check that does not even have a feature gate
    |
 LL |     unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } }
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: skipping check that does not even have a feature gate
+  --> $DIR/const_refers_to_static_cross_crate.rs:32:20
+   |
+LL |     unsafe { match static_cross_crate::OPT_ZERO { Some(ref u) => u, None => panic!() } }
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: skipping check for `const_panic` feature
   --> $DIR/const_refers_to_static_cross_crate.rs:32:77
    |
diff --git a/src/test/ui/error-codes/E0396.rs b/src/test/ui/error-codes/E0396.rs
index b32853e483d..58ed3c2c722 100644
--- a/src/test/ui/error-codes/E0396.rs
+++ b/src/test/ui/error-codes/E0396.rs
@@ -5,5 +5,16 @@ const REG_ADDR: *const u8 = 0x5f3759df as *const u8;
 const VALUE: u8 = unsafe { *REG_ADDR };
 //~^ ERROR dereferencing raw pointers in constants is unstable
 
+const unsafe fn unreachable() -> ! {
+    use std::convert::Infallible;
+
+    const INFALLIBLE: *const Infallible = [].as_ptr();
+    match *INFALLIBLE {}
+    //~^ ERROR dereferencing raw pointers in constant functions is unstable
+
+    const BAD: () = unsafe { match *INFALLIBLE {} };
+    //~^ ERROR dereferencing raw pointers in constants is unstable
+}
+
 fn main() {
 }
diff --git a/src/test/ui/error-codes/E0396.stderr b/src/test/ui/error-codes/E0396.stderr
index 7d2544f939f..20dad1b983c 100644
--- a/src/test/ui/error-codes/E0396.stderr
+++ b/src/test/ui/error-codes/E0396.stderr
@@ -7,6 +7,24 @@ LL | const VALUE: u8 = unsafe { *REG_ADDR };
    = note: see issue #51911 <https://github.com/rust-lang/rust/issues/51911> for more information
    = help: add `#![feature(const_raw_ptr_deref)]` to the crate attributes to enable
 
-error: aborting due to previous error
+error[E0658]: dereferencing raw pointers in constant functions is unstable
+  --> $DIR/E0396.rs:12:11
+   |
+LL |     match *INFALLIBLE {}
+   |           ^^^^^^^^^^^
+   |
+   = note: see issue #51911 <https://github.com/rust-lang/rust/issues/51911> for more information
+   = help: add `#![feature(const_raw_ptr_deref)]` to the crate attributes to enable
+
+error[E0658]: dereferencing raw pointers in constants is unstable
+  --> $DIR/E0396.rs:15:36
+   |
+LL |     const BAD: () = unsafe { match *INFALLIBLE {} };
+   |                                    ^^^^^^^^^^^
+   |
+   = note: see issue #51911 <https://github.com/rust-lang/rust/issues/51911> for more information
+   = help: add `#![feature(const_raw_ptr_deref)]` to the crate attributes to enable
+
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/inherent-impls-overlap-check/auxiliary/repeat.rs b/src/test/ui/inherent-impls-overlap-check/auxiliary/repeat.rs
new file mode 100644
index 00000000000..42ed5d19deb
--- /dev/null
+++ b/src/test/ui/inherent-impls-overlap-check/auxiliary/repeat.rs
@@ -0,0 +1,54 @@
+// force-host
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+use proc_macro::{Ident, Group, TokenStream, TokenTree as Tt};
+
+// This constant has to be above the ALLOCATING_ALGO_THRESHOLD
+// constant in inherent_impls_overlap.rs
+const REPEAT_COUNT: u32 = 501;
+
+#[proc_macro]
+/// Repeats the input many times, while replacing idents
+/// named "IDENT" with "id_$v", where v is a counter.
+pub fn repeat_with_idents(input: TokenStream) -> TokenStream {
+    let mut res = Vec::new();
+    fn visit_stream(res: &mut Vec<Tt>, stream :TokenStream, v: u32) {
+        let mut stream_iter = stream.into_iter();
+        while let Some(tt) = stream_iter.next() {
+            match tt {
+                Tt::Group(group) => {
+                    let tt = Tt::Group(visit_group(group, v));
+                    res.push(tt);
+                },
+                Tt::Ident(id) => {
+                    let id = if &id.to_string() == "IDENT" {
+                        Ident::new(&format!("id_{}", v), id.span())
+                    } else {
+                        id
+                    };
+                    res.push(Tt::Ident(id));
+                },
+                Tt::Punct(p) => {
+                    res.push(Tt::Punct(p));
+                },
+                Tt::Literal(lit) => {
+                    res.push(Tt::Literal(lit));
+                },
+            }
+        }
+    }
+    fn visit_group(group :Group, v: u32) -> Group {
+        let mut res = Vec::new();
+        visit_stream(&mut res, group.stream(), v);
+        let stream = res.into_iter().collect();
+        let delim = group.delimiter();
+        Group::new(delim, stream)
+    }
+    for v in 0 .. REPEAT_COUNT {
+        visit_stream(&mut res, input.clone(), v)
+    }
+    res.into_iter().collect()
+}
diff --git a/src/test/ui/inherent-impls-overlap-check/no-overlap.rs b/src/test/ui/inherent-impls-overlap-check/no-overlap.rs
new file mode 100644
index 00000000000..341bfc7b605
--- /dev/null
+++ b/src/test/ui/inherent-impls-overlap-check/no-overlap.rs
@@ -0,0 +1,34 @@
+// run-pass
+// aux-build:repeat.rs
+
+// This tests the allocating algo branch of the
+// inherent impls overlap checker.
+// This branch was added by PR:
+// https://github.com/rust-lang/rust/pull/78317
+// In this test, we repeat many impl blocks
+// to trigger the allocating branch.
+
+#![allow(unused)]
+
+extern crate repeat;
+
+// Simple case where each impl block is distinct
+
+struct Foo {}
+
+repeat::repeat_with_idents!(impl Foo { fn IDENT() {} });
+
+// There are overlapping impl blocks but due to generics,
+// they may overlap.
+
+struct Bar<T>(T);
+
+struct A;
+struct B;
+
+repeat::repeat_with_idents!(impl Bar<A> { fn IDENT() {} });
+
+impl Bar<A> { fn foo() {} }
+impl Bar<B> { fn foo() {} }
+
+fn main() {}
diff --git a/src/test/ui/inherent-impls-overlap-check/overlap.rs b/src/test/ui/inherent-impls-overlap-check/overlap.rs
new file mode 100644
index 00000000000..6f2801197e9
--- /dev/null
+++ b/src/test/ui/inherent-impls-overlap-check/overlap.rs
@@ -0,0 +1,71 @@
+// aux-build:repeat.rs
+
+#![allow(unused)]
+
+// This tests the allocating algo branch of the
+// inherent impls overlap checker.
+// This branch was added by PR:
+// https://github.com/rust-lang/rust/pull/78317
+// In this test, we repeat many impl blocks
+// to trigger the allocating branch.
+
+// Simple overlap
+
+extern crate repeat;
+
+struct Foo {}
+
+repeat::repeat_with_idents!(impl Foo { fn IDENT() {} });
+
+impl Foo { fn hello() {} } //~ERROR duplicate definitions with name `hello`
+impl Foo { fn hello() {} }
+
+// Transitive overlap
+
+struct Foo2 {}
+
+repeat::repeat_with_idents!(impl Foo2 { fn IDENT() {} });
+
+impl Foo2 {
+    fn bar() {}
+    fn hello2() {} //~ERROR duplicate definitions with name `hello2`
+}
+
+impl Foo2 {
+    fn baz() {}
+    fn hello2() {}
+}
+
+// Slightly stronger transitive overlap
+
+struct Foo3 {}
+
+repeat::repeat_with_idents!(impl Foo3 { fn IDENT() {} });
+
+impl Foo3 {
+    fn bar() {} //~ERROR duplicate definitions with name `bar`
+    fn hello3() {} //~ERROR duplicate definitions with name `hello3`
+}
+
+impl Foo3 {
+    fn bar() {}
+    fn hello3() {}
+}
+
+// Generic overlap
+
+struct Bar<T>(T);
+
+struct A;
+struct B;
+
+repeat::repeat_with_idents!(impl Bar<A> { fn IDENT() {} });
+
+impl Bar<A> { fn foo() {} fn bar2() {} }
+impl Bar<B> {
+    fn foo() {}
+    fn bar2() {} //~ERROR duplicate definitions with name `bar2`
+}
+impl Bar<B> { fn bar2() {} }
+
+fn main() {}
diff --git a/src/test/ui/inherent-impls-overlap-check/overlap.stderr b/src/test/ui/inherent-impls-overlap-check/overlap.stderr
new file mode 100644
index 00000000000..3dd2793712f
--- /dev/null
+++ b/src/test/ui/inherent-impls-overlap-check/overlap.stderr
@@ -0,0 +1,47 @@
+error[E0592]: duplicate definitions with name `hello`
+  --> $DIR/overlap.rs:20:12
+   |
+LL | impl Foo { fn hello() {} }
+   |            ^^^^^^^^^^ duplicate definitions for `hello`
+LL | impl Foo { fn hello() {} }
+   |            ---------- other definition for `hello`
+
+error[E0592]: duplicate definitions with name `hello2`
+  --> $DIR/overlap.rs:31:5
+   |
+LL |     fn hello2() {}
+   |     ^^^^^^^^^^^ duplicate definitions for `hello2`
+...
+LL |     fn hello2() {}
+   |     ----------- other definition for `hello2`
+
+error[E0592]: duplicate definitions with name `bar`
+  --> $DIR/overlap.rs:46:5
+   |
+LL |     fn bar() {}
+   |     ^^^^^^^^ duplicate definitions for `bar`
+...
+LL |     fn bar() {}
+   |     -------- other definition for `bar`
+
+error[E0592]: duplicate definitions with name `hello3`
+  --> $DIR/overlap.rs:47:5
+   |
+LL |     fn hello3() {}
+   |     ^^^^^^^^^^^ duplicate definitions for `hello3`
+...
+LL |     fn hello3() {}
+   |     ----------- other definition for `hello3`
+
+error[E0592]: duplicate definitions with name `bar2`
+  --> $DIR/overlap.rs:67:5
+   |
+LL |     fn bar2() {}
+   |     ^^^^^^^^^ duplicate definitions for `bar2`
+LL | }
+LL | impl Bar<B> { fn bar2() {} }
+   |               --------- other definition for `bar2`
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0592`.
diff --git a/src/test/ui/macros/macro-pat-follow-2018.rs b/src/test/ui/macros/macro-pat-follow-2018.rs
new file mode 100644
index 00000000000..ce2911de986
--- /dev/null
+++ b/src/test/ui/macros/macro-pat-follow-2018.rs
@@ -0,0 +1,15 @@
+// run-pass
+// edition:2018
+
+macro_rules! pat_bar {
+    ($p:pat | $p2:pat) => {{
+        match Some(1u8) {
+            $p | $p2 => {}
+            _ => {}
+        }
+    }};
+}
+
+fn main() {
+    pat_bar!(Some(1u8) | None);
+}
diff --git a/src/test/ui/macros/macro-pat-follow.rs b/src/test/ui/macros/macro-pat-follow.rs
index 8673cf79467..8e02789fdd8 100644
--- a/src/test/ui/macros/macro-pat-follow.rs
+++ b/src/test/ui/macros/macro-pat-follow.rs
@@ -3,29 +3,19 @@ macro_rules! pat_in {
     ($p:pat in $e:expr) => {{
         let mut iter = $e.into_iter();
         while let $p = iter.next() {}
-    }}
+    }};
 }
 
 macro_rules! pat_if {
     ($p:pat if $e:expr) => {{
         match Some(1u8) {
-            $p if $e => {},
+            $p if $e => {}
             _ => {}
         }
-    }}
-}
-
-macro_rules! pat_bar {
-    ($p:pat | $p2:pat) => {{
-        match Some(1u8) {
-            $p | $p2 => {},
-            _ => {}
-        }
-    }}
+    }};
 }
 
 fn main() {
     pat_in!(Some(_) in 0..10);
     pat_if!(Some(x) if x > 0);
-    pat_bar!(Some(1u8) | None);
 }
diff --git a/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr
index 799ed89dcce..4e122d930fc 100644
--- a/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr
+++ b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr
@@ -6,7 +6,7 @@ LL |         let mut closure = expect_sig(|p, y| *p = y);
    |
    = note: defining type: test::{closure#0} with closure substs [
                i16,
-               for<'r, 's, 't0> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) mut &ReLateBound(DebruijnIndex(0), BrNamed('s)) i32, &ReLateBound(DebruijnIndex(0), BrNamed('t0)) i32)),
+               for<'r, 's, 't0> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) mut &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) i32, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t0) }) i32)),
                (),
            ]
 
diff --git a/src/test/ui/nll/closure-requirements/escape-argument.stderr b/src/test/ui/nll/closure-requirements/escape-argument.stderr
index a094fc45178..44d1d2327fc 100644
--- a/src/test/ui/nll/closure-requirements/escape-argument.stderr
+++ b/src/test/ui/nll/closure-requirements/escape-argument.stderr
@@ -6,7 +6,7 @@ LL |         let mut closure = expect_sig(|p, y| *p = y);
    |
    = note: defining type: test::{closure#0} with closure substs [
                i16,
-               for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) mut &ReLateBound(DebruijnIndex(0), BrNamed('s)) i32, &ReLateBound(DebruijnIndex(0), BrNamed('s)) i32)),
+               for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) mut &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) i32, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) i32)),
                (),
            ]
 
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr
index c4f4facae1f..fa9f994c4fa 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr
@@ -10,7 +10,7 @@ LL | |         },
    |
    = note: defining type: supply::{closure#0} with closure substs [
                i16,
-               for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) &'_#3r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>)),
+               for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>, std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) &'_#3r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>)),
                (),
            ]
    = note: late-bound region is '_#4r
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr
index c1450564c45..0555f79bcb0 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr
@@ -11,7 +11,7 @@ LL | |     });
    |
    = note: defining type: supply::{closure#0} with closure substs [
                i16,
-               for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t1)) &'_#2r u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t3)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t1)) u32>)),
+               for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t0) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t1) }) &'_#2r u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t2) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t3) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t1) }) u32>)),
                (),
            ]
    = note: late-bound region is '_#3r
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr
index e7b8dff4e7e..0115f5412f2 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr
@@ -10,7 +10,7 @@ LL | |     })
    |
    = note: defining type: case1::{closure#0} with closure substs [
                i32,
-               for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>)),
+               for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>)),
                (),
            ]
 
@@ -49,7 +49,7 @@ LL | |     })
    |
    = note: defining type: case2::{closure#0} with closure substs [
                i32,
-               for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>)),
+               for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>)),
                (),
            ]
    = note: number of external vids: 2
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr
index c7e68d02dcf..e55d033d2c7 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr
@@ -12,7 +12,7 @@ LL | |     });
    |
    = note: defining type: supply::{closure#0} with closure substs [
                i16,
-               for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t1)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t2)) u32>)),
+               for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t0) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t1) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t2) }) u32>)),
                (),
            ]
    = note: late-bound region is '_#2r
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr
index abbc76eaf4d..ac4a4579c9c 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr
@@ -12,7 +12,7 @@ LL | |     });
    |
    = note: defining type: supply::{closure#0} with closure substs [
                i16,
-               for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t0)) std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex(0), BrNamed('t1)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t3)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t1)) u32>)),
+               for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t0) }) std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t1) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t2) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t3) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t1) }) u32>)),
                (),
            ]
    = note: late-bound region is '_#3r
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr
index c91b514a796..60dca1baa40 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr
@@ -11,7 +11,7 @@ LL | |     });
    |
    = note: defining type: test::{closure#0} with closure substs [
                i16,
-               for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>)),
+               for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>)),
                (),
            ]
    = note: late-bound region is '_#3r
diff --git a/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr b/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr
index 4ddf6f8323f..cbb10eb187e 100644
--- a/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr
@@ -10,7 +10,7 @@ LL | |         },
    |
    = note: defining type: supply::{closure#0} with closure substs [
                i16,
-               for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>)),
+               for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>)),
                (),
            ]
    = note: late-bound region is '_#3r
diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr
index 6dc6f456805..f9f1d8bb6ff 100644
--- a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr
@@ -11,7 +11,7 @@ LL | |     });
    |
    = note: defining type: supply::{closure#0} with closure substs [
                i16,
-               for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) &'_#1r u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t1)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>)),
+               for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) &'_#1r u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t0) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t1) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t2) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>)),
                (),
            ]
    = note: late-bound region is '_#2r
diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr
index 6bcada5c26c..1587c28e1be 100644
--- a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr
@@ -11,7 +11,7 @@ LL | |     });
    |
    = note: defining type: supply::{closure#0} with closure substs [
                i16,
-               for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) &'_#1r u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t1)) &'_#2r u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t3)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t1)) u32>)),
+               for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) &'_#1r u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t0) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t1) }) &'_#2r u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t2) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) u32>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t3) }) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('t1) }) u32>)),
                (),
            ]
    = note: late-bound region is '_#3r
diff --git a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr
index 1da6c6d2c68..44f743310b4 100644
--- a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr
+++ b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr
@@ -6,7 +6,7 @@ LL |     expect_sig(|a, b| b); // ought to return `a`
    |
    = note: defining type: test::{closure#0} with closure substs [
                i16,
-               for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) i32, &ReLateBound(DebruijnIndex(0), BrNamed('s)) i32)) -> &ReLateBound(DebruijnIndex(0), BrNamed('r)) i32,
+               for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) i32, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) i32)) -> &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) i32,
                (),
            ]
 
diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr
index 7c0d63c368b..dbf76cd1329 100644
--- a/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr
+++ b/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr
@@ -6,7 +6,7 @@ LL |     twice(cell, value, |a, b| invoke(a, b));
    |
    = note: defining type: generic::<T>::{closure#0} with closure substs [
                i16,
-               for<'r, 's> extern "rust-call" fn((std::option::Option<std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('r)) ()>>, &ReLateBound(DebruijnIndex(0), BrNamed('s)) T)),
+               for<'r, 's> extern "rust-call" fn((std::option::Option<std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) ()>>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) T)),
                (),
            ]
    = note: number of external vids: 2
@@ -31,7 +31,7 @@ LL |     twice(cell, value, |a, b| invoke(a, b));
    |
    = note: defining type: generic_fail::<T>::{closure#0} with closure substs [
                i16,
-               for<'r, 's> extern "rust-call" fn((std::option::Option<std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('r)) ()>>, &ReLateBound(DebruijnIndex(0), BrNamed('s)) T)),
+               for<'r, 's> extern "rust-call" fn((std::option::Option<std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('r) }) ()>>, &ReLateBound(DebruijnIndex(0), BoundRegion { kind: BrNamed('s) }) T)),
                (),
            ]
    = note: late-bound region is '_#2r
diff --git a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs
index 512f1e283cb..184ffa85c40 100644
--- a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs
+++ b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs
@@ -64,6 +64,35 @@ fn main() {
             | 2, ..] => {}
         _ => {}
     }
+    match &[][..] {
+        [true] => {}
+        [true | false, ..] => {}
+        _ => {}
+    }
+    match &[][..] {
+        [false] => {}
+        [true, ..] => {}
+        [true //~ ERROR unreachable
+            | false, ..] => {}
+        _ => {}
+    }
+    match (true, None) {
+        (true, Some(_)) => {}
+        (false, Some(true)) => {}
+        (true | false, None | Some(true //~ ERROR unreachable
+                                   | false)) => {}
+    }
+    macro_rules! t_or_f {
+        () => {
+            (true // FIXME: should be unreachable
+                        | false)
+        };
+    }
+    match (true, None) {
+        (true, Some(_)) => {}
+        (false, Some(true)) => {}
+        (true | false, None | Some(t_or_f!())) => {}
+    }
     match Some(0) {
         Some(0) => {}
         Some(0 //~ ERROR unreachable
diff --git a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr
index e968310d108..8b1003b5514 100644
--- a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr
+++ b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr
@@ -95,28 +95,40 @@ LL |         [1
    |          ^
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:69:14
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:75:10
+   |
+LL |         [true
+   |          ^^^^
+
+error: unreachable pattern
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:82:36
+   |
+LL |         (true | false, None | Some(true
+   |                                    ^^^^
+
+error: unreachable pattern
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:98:14
    |
 LL |         Some(0
    |              ^
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:88:19
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:117:19
    |
 LL |                 | false) => {}
    |                   ^^^^^
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:96:15
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:125:15
    |
 LL |             | true) => {}
    |               ^^^^
 
 error: unreachable pattern
-  --> $DIR/exhaustiveness-unreachable-pattern.rs:102:15
+  --> $DIR/exhaustiveness-unreachable-pattern.rs:131:15
    |
 LL |             | true,
    |               ^^^^
 
-error: aborting due to 19 previous errors
+error: aborting due to 21 previous errors
 
diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.rs b/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.rs
new file mode 100644
index 00000000000..9c3c5dd360e
--- /dev/null
+++ b/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.rs
@@ -0,0 +1,15 @@
+// Test that :pat doesn't accept top-level or-patterns in edition 2018.
+
+// edition:2018
+
+#![feature(or_patterns)]
+
+fn main() {}
+
+// Test the `pat` macro fragment parser:
+macro_rules! accept_pat {
+    ($p:pat) => {};
+}
+
+accept_pat!(p | q); //~ ERROR no rules expected the token `|`
+accept_pat!(|p| q); //~ ERROR no rules expected the token `|`
diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.stderr b/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.stderr
new file mode 100644
index 00000000000..7dbc3087663
--- /dev/null
+++ b/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.stderr
@@ -0,0 +1,20 @@
+error: no rules expected the token `|`
+  --> $DIR/or-patterns-syntactic-fail-2018.rs:14:15
+   |
+LL | macro_rules! accept_pat {
+   | ----------------------- when calling this macro
+...
+LL | accept_pat!(p | q);
+   |               ^ no rules expected this token in macro call
+
+error: no rules expected the token `|`
+  --> $DIR/or-patterns-syntactic-fail-2018.rs:15:13
+   |
+LL | macro_rules! accept_pat {
+   | ----------------------- when calling this macro
+...
+LL | accept_pat!(|p| q);
+   |             ^ no rules expected this token in macro call
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-fail.rs b/src/test/ui/or-patterns/or-patterns-syntactic-fail.rs
index d2322005652..efe90b3e3c6 100644
--- a/src/test/ui/or-patterns/or-patterns-syntactic-fail.rs
+++ b/src/test/ui/or-patterns/or-patterns-syntactic-fail.rs
@@ -5,16 +5,6 @@
 
 fn main() {}
 
-// Test the `pat` macro fragment parser:
-macro_rules! accept_pat {
-    ($p:pat) => {}
-}
-
-accept_pat!(p | q); //~ ERROR no rules expected the token `|`
-accept_pat!(| p | q); //~ ERROR no rules expected the token `|`
-
-// Non-macro tests:
-
 enum E { A, B }
 use E::*;
 
diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr b/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr
index 861d274ab5c..989aeb52006 100644
--- a/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr
+++ b/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr
@@ -1,53 +1,53 @@
 error: an or-pattern parameter must be wrapped in parenthesis
-  --> $DIR/or-patterns-syntactic-fail.rs:27:13
+  --> $DIR/or-patterns-syntactic-fail.rs:17:13
    |
 LL |     fn fun1(A | B: E) {}
    |             ^^^^^ help: wrap the pattern in parenthesis: `(A | B)`
 
 error: a leading `|` is not allowed in a parameter pattern
-  --> $DIR/or-patterns-syntactic-fail.rs:29:13
+  --> $DIR/or-patterns-syntactic-fail.rs:19:13
    |
 LL |     fn fun2(| A | B: E) {}
    |             ^ help: remove the `|`
 
 error: an or-pattern parameter must be wrapped in parenthesis
-  --> $DIR/or-patterns-syntactic-fail.rs:29:15
+  --> $DIR/or-patterns-syntactic-fail.rs:19:15
    |
 LL |     fn fun2(| A | B: E) {}
    |               ^^^^^ help: wrap the pattern in parenthesis: `(A | B)`
 
 error: a leading `|` is only allowed in a top-level pattern
-  --> $DIR/or-patterns-syntactic-fail.rs:40:11
+  --> $DIR/or-patterns-syntactic-fail.rs:30:11
    |
 LL |     let ( | A | B) = E::A;
    |           ^ help: remove the `|`
 
 error: a leading `|` is only allowed in a top-level pattern
-  --> $DIR/or-patterns-syntactic-fail.rs:41:11
+  --> $DIR/or-patterns-syntactic-fail.rs:31:11
    |
 LL |     let ( | A | B,) = (E::B,);
    |           ^ help: remove the `|`
 
 error: a leading `|` is only allowed in a top-level pattern
-  --> $DIR/or-patterns-syntactic-fail.rs:42:11
+  --> $DIR/or-patterns-syntactic-fail.rs:32:11
    |
 LL |     let [ | A | B ] = [E::A];
    |           ^ help: remove the `|`
 
 error: a leading `|` is only allowed in a top-level pattern
-  --> $DIR/or-patterns-syntactic-fail.rs:43:13
+  --> $DIR/or-patterns-syntactic-fail.rs:33:13
    |
 LL |     let TS( | A | B );
    |             ^ help: remove the `|`
 
 error: a leading `|` is only allowed in a top-level pattern
-  --> $DIR/or-patterns-syntactic-fail.rs:44:17
+  --> $DIR/or-patterns-syntactic-fail.rs:34:17
    |
 LL |     let NS { f: | A | B };
    |                 ^ help: remove the `|`
 
 error: a leading `|` is only allowed in a top-level pattern
-  --> $DIR/or-patterns-syntactic-fail.rs:46:11
+  --> $DIR/or-patterns-syntactic-fail.rs:36:11
    |
 LL |     let ( || A | B) = E::A;
    |           ^^ help: remove the `||`
@@ -55,7 +55,7 @@ LL |     let ( || A | B) = E::A;
    = note: alternatives in or-patterns are separated with `|`, not `||`
 
 error: a leading `|` is only allowed in a top-level pattern
-  --> $DIR/or-patterns-syntactic-fail.rs:47:11
+  --> $DIR/or-patterns-syntactic-fail.rs:37:11
    |
 LL |     let [ || A | B ] = [E::A];
    |           ^^ help: remove the `||`
@@ -63,7 +63,7 @@ LL |     let [ || A | B ] = [E::A];
    = note: alternatives in or-patterns are separated with `|`, not `||`
 
 error: a leading `|` is only allowed in a top-level pattern
-  --> $DIR/or-patterns-syntactic-fail.rs:48:13
+  --> $DIR/or-patterns-syntactic-fail.rs:38:13
    |
 LL |     let TS( || A | B );
    |             ^^ help: remove the `||`
@@ -71,33 +71,15 @@ LL |     let TS( || A | B );
    = note: alternatives in or-patterns are separated with `|`, not `||`
 
 error: a leading `|` is only allowed in a top-level pattern
-  --> $DIR/or-patterns-syntactic-fail.rs:49:17
+  --> $DIR/or-patterns-syntactic-fail.rs:39:17
    |
 LL |     let NS { f: || A | B };
    |                 ^^ help: remove the `||`
    |
    = note: alternatives in or-patterns are separated with `|`, not `||`
 
-error: no rules expected the token `|`
-  --> $DIR/or-patterns-syntactic-fail.rs:13:15
-   |
-LL | macro_rules! accept_pat {
-   | ----------------------- when calling this macro
-...
-LL | accept_pat!(p | q);
-   |               ^ no rules expected this token in macro call
-
-error: no rules expected the token `|`
-  --> $DIR/or-patterns-syntactic-fail.rs:14:13
-   |
-LL | macro_rules! accept_pat {
-   | ----------------------- when calling this macro
-...
-LL | accept_pat!(| p | q);
-   |             ^ no rules expected this token in macro call
-
 error[E0369]: no implementation for `E | ()`
-  --> $DIR/or-patterns-syntactic-fail.rs:23:22
+  --> $DIR/or-patterns-syntactic-fail.rs:13:22
    |
 LL |     let _ = |A | B: E| ();
    |                  ----^ -- ()
@@ -107,7 +89,7 @@ LL |     let _ = |A | B: E| ();
    = note: an implementation of `std::ops::BitOr` might be missing for `E`
 
 error[E0308]: mismatched types
-  --> $DIR/or-patterns-syntactic-fail.rs:51:36
+  --> $DIR/or-patterns-syntactic-fail.rs:41:36
    |
 LL |     let recovery_witness: String = 0;
    |                           ------   ^
@@ -116,7 +98,7 @@ LL |     let recovery_witness: String = 0;
    |                           |        help: try using a conversion method: `0.to_string()`
    |                           expected due to this
 
-error: aborting due to 16 previous errors
+error: aborting due to 14 previous errors
 
 Some errors have detailed explanations: E0308, E0369.
 For more information about an error, try `rustc --explain E0308`.
diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-pass-2021.rs b/src/test/ui/or-patterns/or-patterns-syntactic-pass-2021.rs
new file mode 100644
index 00000000000..f0ce7597aee
--- /dev/null
+++ b/src/test/ui/or-patterns/or-patterns-syntactic-pass-2021.rs
@@ -0,0 +1,14 @@
+// Tests that :pat in macros in edition 2021 allows top-level or-patterns.
+
+// run-pass
+// ignore-test
+// edition:2021
+// FIXME(mark-i-m): unignore when 2021 machinery is in place.
+
+macro_rules! accept_pat {
+    ($p:pat) => {};
+}
+
+accept_pat!(p | q);
+
+fn main() {}
diff --git a/src/test/ui/pattern/or-pattern-macro-pat.rs b/src/test/ui/pattern/or-pattern-macro-pat.rs
new file mode 100644
index 00000000000..8749407675b
--- /dev/null
+++ b/src/test/ui/pattern/or-pattern-macro-pat.rs
@@ -0,0 +1,44 @@
+// run-pass
+// edition:2021
+// ignore-test
+// FIXME(mark-i-m): enable this test again when 2021 machinery is available
+
+#![feature(or_patterns)]
+
+use Foo::*;
+
+#[derive(Eq, PartialEq, Debug)]
+enum Foo {
+    A(u64),
+    B(u64),
+    C,
+    D,
+}
+
+macro_rules! foo {
+    ($orpat:pat, $val:expr) => {
+        match $val {
+            x @ ($orpat) => x, // leading vert would not be allowed in $orpat
+            _ => B(0xDEADBEEFu64),
+        }
+    };
+}
+
+macro_rules! bar {
+    ($orpat:pat, $val:expr) => {
+        match $val {
+            $orpat => 42, // leading vert allowed here
+            _ => 0xDEADBEEFu64,
+        }
+    };
+}
+
+fn main() {
+    // Test or-pattern.
+    let y = foo!(A(_)|B(_), A(32));
+    assert_eq!(y, A(32));
+
+    // Leading vert in or-pattern.
+    let y = bar!(|C| D, C);
+    assert_eq!(y, 42u64);
+}
diff --git a/src/test/ui/pattern/usefulness/issue-15129.rs b/src/test/ui/pattern/usefulness/issue-15129.rs
index ed134c175ed..d2b72a86b74 100644
--- a/src/test/ui/pattern/usefulness/issue-15129.rs
+++ b/src/test/ui/pattern/usefulness/issue-15129.rs
@@ -1,17 +1,17 @@
 pub enum T {
     T1(()),
-    T2(())
+    T2(()),
 }
 
 pub enum V {
     V1(isize),
-    V2(bool)
+    V2(bool),
 }
 
 fn main() {
     match (T::T1(()), V::V2(true)) {
-    //~^ ERROR non-exhaustive patterns: `(T1(()), V2(_))` not covered
+        //~^ ERROR non-exhaustive patterns: `(T1(()), V2(_))` and `(T2(()), V1(_))` not covered
         (T::T1(()), V::V1(i)) => (),
-        (T::T2(()), V::V2(b)) => ()
+        (T::T2(()), V::V2(b)) => (),
     }
 }
diff --git a/src/test/ui/pattern/usefulness/issue-15129.stderr b/src/test/ui/pattern/usefulness/issue-15129.stderr
index aa4434e72b5..79a77240937 100644
--- a/src/test/ui/pattern/usefulness/issue-15129.stderr
+++ b/src/test/ui/pattern/usefulness/issue-15129.stderr
@@ -1,8 +1,8 @@
-error[E0004]: non-exhaustive patterns: `(T1(()), V2(_))` not covered
+error[E0004]: non-exhaustive patterns: `(T1(()), V2(_))` and `(T2(()), V1(_))` not covered
   --> $DIR/issue-15129.rs:12:11
    |
 LL |     match (T::T1(()), V::V2(true)) {
-   |           ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `(T1(()), V2(_))` not covered
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `(T1(()), V2(_))` and `(T2(()), V1(_))` not covered
    |
    = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
    = note: the matched value is of type `(T, V)`
diff --git a/src/test/ui/pattern/usefulness/issue-2111.rs b/src/test/ui/pattern/usefulness/issue-2111.rs
index 7e5835e8697..d27beaeffd6 100644
--- a/src/test/ui/pattern/usefulness/issue-2111.rs
+++ b/src/test/ui/pattern/usefulness/issue-2111.rs
@@ -1,12 +1,11 @@
 fn foo(a: Option<usize>, b: Option<usize>) {
-  match (a,b) {
-  //~^ ERROR: non-exhaustive patterns: `(None, None)` not covered
-    (Some(a), Some(b)) if a == b => { }
-    (Some(_), None) |
-    (None, Some(_)) => { }
-  }
+    match (a, b) {
+        //~^ ERROR: non-exhaustive patterns: `(None, None)` and `(Some(_), Some(_))` not covered
+        (Some(a), Some(b)) if a == b => {}
+        (Some(_), None) | (None, Some(_)) => {}
+    }
 }
 
 fn main() {
-  foo(None, None);
+    foo(None, None);
 }
diff --git a/src/test/ui/pattern/usefulness/issue-2111.stderr b/src/test/ui/pattern/usefulness/issue-2111.stderr
index a39a479e078..60d9b8514b7 100644
--- a/src/test/ui/pattern/usefulness/issue-2111.stderr
+++ b/src/test/ui/pattern/usefulness/issue-2111.stderr
@@ -1,8 +1,8 @@
-error[E0004]: non-exhaustive patterns: `(None, None)` not covered
-  --> $DIR/issue-2111.rs:2:9
+error[E0004]: non-exhaustive patterns: `(None, None)` and `(Some(_), Some(_))` not covered
+  --> $DIR/issue-2111.rs:2:11
    |
-LL |   match (a,b) {
-   |         ^^^^^ pattern `(None, None)` not covered
+LL |     match (a, b) {
+   |           ^^^^^^ patterns `(None, None)` and `(Some(_), Some(_))` not covered
    |
    = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
    = note: the matched value is of type `(Option<usize>, Option<usize>)`
diff --git a/src/test/ui/pattern/usefulness/issue-56379.rs b/src/test/ui/pattern/usefulness/issue-56379.rs
new file mode 100644
index 00000000000..9bccccca9c2
--- /dev/null
+++ b/src/test/ui/pattern/usefulness/issue-56379.rs
@@ -0,0 +1,14 @@
+enum Foo {
+    A(bool),
+    B(bool),
+    C(bool),
+}
+
+fn main() {
+    match Foo::A(true) {
+        //~^ ERROR non-exhaustive patterns: `A(false)`, `B(false)` and `C(false)` not covered
+        Foo::A(true) => {}
+        Foo::B(true) => {}
+        Foo::C(true) => {}
+    }
+}
diff --git a/src/test/ui/pattern/usefulness/issue-56379.stderr b/src/test/ui/pattern/usefulness/issue-56379.stderr
new file mode 100644
index 00000000000..6a231b868c8
--- /dev/null
+++ b/src/test/ui/pattern/usefulness/issue-56379.stderr
@@ -0,0 +1,22 @@
+error[E0004]: non-exhaustive patterns: `A(false)`, `B(false)` and `C(false)` not covered
+  --> $DIR/issue-56379.rs:8:11
+   |
+LL | / enum Foo {
+LL | |     A(bool),
+   | |     - not covered
+LL | |     B(bool),
+   | |     - not covered
+LL | |     C(bool),
+   | |     - not covered
+LL | | }
+   | |_- `Foo` defined here
+...
+LL |       match Foo::A(true) {
+   |             ^^^^^^^^^^^^ patterns `A(false)`, `B(false)` and `C(false)` not covered
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+   = note: the matched value is of type `Foo`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/src/test/ui/pattern/usefulness/non-exhaustive-match.rs b/src/test/ui/pattern/usefulness/non-exhaustive-match.rs
index a28cfb579f4..4ff12aa2ff5 100644
--- a/src/test/ui/pattern/usefulness/non-exhaustive-match.rs
+++ b/src/test/ui/pattern/usefulness/non-exhaustive-match.rs
@@ -15,7 +15,7 @@ fn main() {
                       //  and `(_, _, 5_i32..=i32::MAX)` not covered
       (_, _, 4) => {}
     }
-    match (T::A, T::A) { //~ ERROR non-exhaustive patterns: `(A, A)` not covered
+    match (T::A, T::A) { //~ ERROR non-exhaustive patterns: `(A, A)` and `(B, B)` not covered
       (T::A, T::B) => {}
       (T::B, T::A) => {}
     }
diff --git a/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr b/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr
index 12412743b83..c953cd31440 100644
--- a/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr
+++ b/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr
@@ -45,11 +45,11 @@ LL |     match (2, 3, 4) {
    = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
    = note: the matched value is of type `(i32, i32, i32)`
 
-error[E0004]: non-exhaustive patterns: `(A, A)` not covered
+error[E0004]: non-exhaustive patterns: `(A, A)` and `(B, B)` not covered
   --> $DIR/non-exhaustive-match.rs:18:11
    |
 LL |     match (T::A, T::A) {
-   |           ^^^^^^^^^^^^ pattern `(A, A)` not covered
+   |           ^^^^^^^^^^^^ patterns `(A, A)` and `(B, B)` not covered
    |
    = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
    = note: the matched value is of type `(T, T)`
diff --git a/src/test/ui/print_type_sizes/niche-filling.rs b/src/test/ui/print_type_sizes/niche-filling.rs
index 37ac45f7e05..0716cee21c6 100644
--- a/src/test/ui/print_type_sizes/niche-filling.rs
+++ b/src/test/ui/print_type_sizes/niche-filling.rs
@@ -15,12 +15,19 @@
 // padding and overall computed sizes can be quite different.
 
 #![feature(start)]
+#![feature(rustc_attrs)]
 #![allow(dead_code)]
 
 use std::num::NonZeroU32;
 
 pub enum MyOption<T> { None, Some(T) }
 
+#[rustc_layout_scalar_valid_range_start(0)]
+#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
+pub struct MyNotNegativeOne {
+  _i: i32,
+}
+
 impl<T> Default for MyOption<T> {
     fn default() -> Self { MyOption::None }
 }
@@ -77,17 +84,18 @@ fn start(_: isize, _: *const *const u8) -> isize {
     let _a: MyOption<bool> = Default::default();
     let _b: MyOption<char> = Default::default();
     let _c: MyOption<std::cmp::Ordering> = Default::default();
-    let _b: MyOption<MyOption<u8>> = Default::default();
+    let _d: MyOption<MyOption<u8>> = Default::default();
     let _e: Enum4<(), char, (), ()> = Enum4::One(());
     let _f: Enum4<(), (), bool, ()> = Enum4::One(());
     let _g: Enum4<(), (), (), MyOption<u8>> = Enum4::One(());
+    let _h: MyOption<MyNotNegativeOne> = Default::default();
 
     // Unions do not currently participate in niche filling.
-    let _h: MyOption<Union2<NonZeroU32, u32>> = Default::default();
+    let _i: MyOption<Union2<NonZeroU32, u32>> = Default::default();
 
     // ...even when theoretically possible.
-    let _i: MyOption<Union1<NonZeroU32>> = Default::default();
-    let _j: MyOption<Union2<NonZeroU32, NonZeroU32>> = Default::default();
+    let _j: MyOption<Union1<NonZeroU32>> = Default::default();
+    let _k: MyOption<Union2<NonZeroU32, NonZeroU32>> = Default::default();
 
     0
 }
diff --git a/src/test/ui/print_type_sizes/niche-filling.stdout b/src/test/ui/print_type_sizes/niche-filling.stdout
index 1894cd218ee..d1753c26ca8 100644
--- a/src/test/ui/print_type_sizes/niche-filling.stdout
+++ b/src/test/ui/print_type_sizes/niche-filling.stdout
@@ -43,6 +43,12 @@ print-type-size     variant `Three`: 0 bytes
 print-type-size         field `.0`: 0 bytes
 print-type-size     variant `Four`: 0 bytes
 print-type-size         field `.0`: 0 bytes
+print-type-size type: `MyNotNegativeOne`: 4 bytes, alignment: 4 bytes
+print-type-size     field `._i`: 4 bytes
+print-type-size type: `MyOption<MyNotNegativeOne>`: 4 bytes, alignment: 4 bytes
+print-type-size     variant `Some`: 4 bytes
+print-type-size         field `.0`: 4 bytes
+print-type-size     variant `None`: 0 bytes
 print-type-size type: `MyOption<char>`: 4 bytes, alignment: 4 bytes
 print-type-size     variant `Some`: 4 bytes
 print-type-size         field `.0`: 4 bytes
diff --git a/src/test/ui/suggestions/impl-trait-with-missing-bounds.rs b/src/test/ui/suggestions/impl-trait-with-missing-bounds.rs
index d401328077a..949b2360071 100644
--- a/src/test/ui/suggestions/impl-trait-with-missing-bounds.rs
+++ b/src/test/ui/suggestions/impl-trait-with-missing-bounds.rs
@@ -39,6 +39,14 @@ fn bak(constraints: impl  Iterator + std::fmt::Debug) {
     }
 }
 
+#[rustfmt::skip]
+fn baw<>(constraints: impl Iterator) {
+    for constraint in constraints {
+        qux(constraint);
+//~^ ERROR `<impl Iterator as Iterator>::Item` doesn't implement `Debug`
+    }
+}
+
 fn qux(_: impl std::fmt::Debug) {}
 
 fn main() {}
diff --git a/src/test/ui/suggestions/impl-trait-with-missing-bounds.stderr b/src/test/ui/suggestions/impl-trait-with-missing-bounds.stderr
index 099eb1c9d00..0de3b9aec19 100644
--- a/src/test/ui/suggestions/impl-trait-with-missing-bounds.stderr
+++ b/src/test/ui/suggestions/impl-trait-with-missing-bounds.stderr
@@ -73,6 +73,21 @@ help: introduce a type parameter with a trait bound instead of using `impl Trait
 LL | fn bak<I: Iterator + std::fmt::Debug>(constraints: I) where <I as Iterator>::Item: Debug {
    |       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^              ^  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 5 previous errors
+error[E0277]: `<impl Iterator as Iterator>::Item` doesn't implement `Debug`
+  --> $DIR/impl-trait-with-missing-bounds.rs:45:13
+   |
+LL |         qux(constraint);
+   |             ^^^^^^^^^^ `<impl Iterator as Iterator>::Item` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+...
+LL | fn qux(_: impl std::fmt::Debug) {}
+   |                --------------- required by this bound in `qux`
+   |
+   = help: the trait `Debug` is not implemented for `<impl Iterator as Iterator>::Item`
+help: introduce a type parameter with a trait bound instead of using `impl Trait`
+   |
+LL | fn baw<I: Iterator>(constraints: I) where <I as Iterator>::Item: Debug {
+   |       ^^^^^^^^^^^^^              ^  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 6 previous errors
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/suggestions/issue-79843-impl-trait-with-missing-bounds-on-async-fn.rs b/src/test/ui/suggestions/issue-79843-impl-trait-with-missing-bounds-on-async-fn.rs
new file mode 100644
index 00000000000..3cd6d336e13
--- /dev/null
+++ b/src/test/ui/suggestions/issue-79843-impl-trait-with-missing-bounds-on-async-fn.rs
@@ -0,0 +1,32 @@
+// Regression test: if we suggest replacing an `impl Trait` argument to an async
+// fn with a named type parameter in order to add bounds, the suggested function
+// signature should be well-formed.
+//
+// edition:2018
+
+trait Foo {
+    type Bar;
+    fn bar(&self) -> Self::Bar;
+}
+
+async fn run(_: &(), foo: impl Foo) -> std::io::Result<()> {
+    let bar = foo.bar();
+    assert_is_send(&bar);
+//~^ ERROR: `<impl Foo as Foo>::Bar` cannot be sent between threads safely
+
+    Ok(())
+}
+
+// Test our handling of cases where there is a generic parameter list in the
+// source, but only synthetic generic parameters
+async fn run2< >(_: &(), foo: impl Foo) -> std::io::Result<()> {
+    let bar = foo.bar();
+    assert_is_send(&bar);
+//~^ ERROR: `<impl Foo as Foo>::Bar` cannot be sent between threads safely
+
+    Ok(())
+}
+
+fn assert_is_send<T: Send>(_: &T) {}
+
+fn main() {}
diff --git a/src/test/ui/suggestions/issue-79843-impl-trait-with-missing-bounds-on-async-fn.stderr b/src/test/ui/suggestions/issue-79843-impl-trait-with-missing-bounds-on-async-fn.stderr
new file mode 100644
index 00000000000..9404c3bb583
--- /dev/null
+++ b/src/test/ui/suggestions/issue-79843-impl-trait-with-missing-bounds-on-async-fn.stderr
@@ -0,0 +1,33 @@
+error[E0277]: `<impl Foo as Foo>::Bar` cannot be sent between threads safely
+  --> $DIR/issue-79843-impl-trait-with-missing-bounds-on-async-fn.rs:14:20
+   |
+LL |     assert_is_send(&bar);
+   |                    ^^^^ `<impl Foo as Foo>::Bar` cannot be sent between threads safely
+...
+LL | fn assert_is_send<T: Send>(_: &T) {}
+   |                      ---- required by this bound in `assert_is_send`
+   |
+   = help: the trait `Send` is not implemented for `<impl Foo as Foo>::Bar`
+help: introduce a type parameter with a trait bound instead of using `impl Trait`
+   |
+LL | async fn run<F: Foo>(_: &(), foo: F) -> std::io::Result<()> where <F as Foo>::Bar: Send {
+   |             ^^^^^^^^              ^                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0277]: `<impl Foo as Foo>::Bar` cannot be sent between threads safely
+  --> $DIR/issue-79843-impl-trait-with-missing-bounds-on-async-fn.rs:24:20
+   |
+LL |     assert_is_send(&bar);
+   |                    ^^^^ `<impl Foo as Foo>::Bar` cannot be sent between threads safely
+...
+LL | fn assert_is_send<T: Send>(_: &T) {}
+   |                      ---- required by this bound in `assert_is_send`
+   |
+   = help: the trait `Send` is not implemented for `<impl Foo as Foo>::Bar`
+help: introduce a type parameter with a trait bound instead of using `impl Trait`
+   |
+LL | async fn run2<F: Foo>(_: &(), foo: F) -> std::io::Result<()> where <F as Foo>::Bar: Send {
+   |              ^^^^^^^^              ^                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/traits/impl-evaluation-order.rs b/src/test/ui/traits/impl-evaluation-order.rs
new file mode 100644
index 00000000000..57809d89aa6
--- /dev/null
+++ b/src/test/ui/traits/impl-evaluation-order.rs
@@ -0,0 +1,39 @@
+// Regression test for #79902
+
+// Check that evaluation (which is used to determine whether to copy a type in
+// MIR building) evaluates bounds from normalizing an impl after evaluating
+// any bounds on the impl.
+
+// check-pass
+
+trait A {
+    type B;
+}
+trait M {}
+
+struct G<T, U>(*const T, *const U);
+
+impl<T, U> Clone for G<T, U> {
+    fn clone(&self) -> Self {
+        G { ..*self }
+    }
+}
+
+impl<T, U> Copy for G<T, U::B>
+where
+    T: A<B = U>,
+    U: A,
+{
+}
+
+impl A for () {
+    type B = ();
+}
+
+fn is_m<T: M>(_: T) {}
+
+fn main() {
+    let x = G(&(), &());
+    drop(x);
+    drop(x);
+}
diff --git a/src/tools/clippy/.github/workflows/clippy.yml b/src/tools/clippy/.github/workflows/clippy.yml
index cf4aa39e49b..530e60001f7 100644
--- a/src/tools/clippy/.github/workflows/clippy.yml
+++ b/src/tools/clippy/.github/workflows/clippy.yml
@@ -35,29 +35,11 @@ jobs:
       with:
         github_token: "${{ secrets.github_token }}"
 
-    - name: rust-toolchain
-      uses: actions-rs/toolchain@v1.0.6
-      with:
-        toolchain: nightly
-        target: x86_64-unknown-linux-gnu
-        profile: minimal
-
     - name: Checkout
       uses: actions/checkout@v2.3.3
 
-    - name: Run cargo update
-      run: cargo update
-
-    - name: Cache cargo dir
-      uses: actions/cache@v2
-      with:
-        path: ~/.cargo
-        key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }}
-        restore-keys: |
-          ${{ runner.os }}-x86_64-unknown-linux-gnu
-
-    - name: Master Toolchain Setup
-      run: bash setup-toolchain.sh
+    - name: Install toolchain
+      run: rustup show active-toolchain
 
     # Run
     - name: Set LD_LIBRARY_PATH (Linux)
@@ -66,13 +48,13 @@ jobs:
         echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV
 
     - name: Build
-      run: cargo build --features deny-warnings
+      run: cargo build --features deny-warnings,internal-lints
 
     - name: Test
-      run: cargo test --features deny-warnings
+      run: cargo test --features deny-warnings,internal-lints
 
     - name: Test clippy_lints
-      run: cargo test --features deny-warnings
+      run: cargo test --features deny-warnings,internal-lints
       working-directory: clippy_lints
 
     - name: Test rustc_tools_util
@@ -98,9 +80,3 @@ jobs:
         cargo dev new_lint --name new_late_pass --pass late
         cargo check
         git reset --hard HEAD
-
-    # Cleanup
-    - name: Run cargo-cache --autoclean
-      run: |
-        cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache
-        cargo cache
diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml
index 784463fe0df..5d846eb64c7 100644
--- a/src/tools/clippy/.github/workflows/clippy_bors.yml
+++ b/src/tools/clippy/.github/workflows/clippy_bors.yml
@@ -23,6 +23,7 @@ jobs:
     - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
       with:
         github_token: "${{ secrets.github_token }}"
+
     - name: Checkout
       uses: actions/checkout@v2.3.3
       with:
@@ -84,31 +85,11 @@ jobs:
         sudo apt-get install gcc-multilib libssl-dev:i386 libgit2-dev:i386
       if: matrix.host == 'i686-unknown-linux-gnu'
 
-    - name: rust-toolchain
-      uses: actions-rs/toolchain@v1.0.6
-      with:
-        toolchain: nightly
-        target: ${{ matrix.host }}
-        profile: minimal
-
     - name: Checkout
       uses: actions/checkout@v2.3.3
 
-    - name: Run cargo update
-      run: cargo update
-
-    - name: Cache cargo dir
-      uses: actions/cache@v2
-      with:
-        path: ~/.cargo
-        key: ${{ runner.os }}-${{ matrix.host }}-${{ hashFiles('Cargo.lock') }}
-        restore-keys: |
-          ${{ runner.os }}-${{ matrix.host }}
-
-    - name: Master Toolchain Setup
-      run: bash setup-toolchain.sh
-      env:
-        HOST_TOOLCHAIN: ${{ matrix.host }}
+    - name: Install toolchain
+      run: rustup show active-toolchain
 
     # Run
     - name: Set LD_LIBRARY_PATH (Linux)
@@ -128,13 +109,13 @@ jobs:
         SYSROOT=$(rustc --print sysroot)
         echo "$SYSROOT/bin" >> $GITHUB_PATH
 
-    - name: Build with internal lints
+    - name: Build
       run: cargo build --features deny-warnings,internal-lints
 
-    - name: Test with internal lints
+    - name: Test
       run: cargo test --features deny-warnings,internal-lints
 
-    - name: Test clippy_lints with internal lints
+    - name: Test clippy_lints
       run: cargo test --features deny-warnings,internal-lints
       working-directory: clippy_lints
 
@@ -155,12 +136,6 @@ jobs:
       env:
         OS: ${{ runner.os }}
 
-    # Cleanup
-    - name: Run cargo-cache --autoclean
-      run: |
-        cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache
-        cargo cache
-
   integration_build:
     needs: changelog
     runs-on: ubuntu-latest
@@ -171,29 +146,11 @@ jobs:
       with:
         github_token: "${{ secrets.github_token }}"
 
-    - name: rust-toolchain
-      uses: actions-rs/toolchain@v1.0.6
-      with:
-        toolchain: nightly
-        target: x86_64-unknown-linux-gnu
-        profile: minimal
-
     - name: Checkout
       uses: actions/checkout@v2.3.3
 
-    - name: Run cargo update
-      run: cargo update
-
-    - name: Cache cargo dir
-      uses: actions/cache@v2
-      with:
-        path: ~/.cargo
-        key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }}
-        restore-keys: |
-          ${{ runner.os }}-x86_64-unknown-linux-gnu
-
-    - name: Master Toolchain Setup
-      run: bash setup-toolchain.sh
+    - name: Install toolchain
+      run: rustup show active-toolchain
 
     # Run
     - name: Build Integration Test
@@ -214,11 +171,6 @@ jobs:
         name: target
         path: target
 
-    # Cleanup
-    - name: Run cargo-cache --autoclean
-      run: |
-        cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache
-        cargo cache
   integration:
     needs: integration_build
     strategy:
@@ -252,29 +204,11 @@ jobs:
       with:
         github_token: "${{ secrets.github_token }}"
 
-    - name: rust-toolchain
-      uses: actions-rs/toolchain@v1.0.6
-      with:
-        toolchain: nightly
-        target: x86_64-unknown-linux-gnu
-        profile: minimal
-
     - name: Checkout
       uses: actions/checkout@v2.3.3
 
-    - name: Run cargo update
-      run: cargo update
-
-    - name: Cache cargo dir
-      uses: actions/cache@v2
-      with:
-        path: ~/.cargo
-        key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }}
-        restore-keys: |
-          ${{ runner.os }}-x86_64-unknown-linux-gnu
-
-    - name: Master Toolchain Setup
-      run: bash setup-toolchain.sh
+    - name: Install toolchain
+      run: rustup show active-toolchain
 
     # Download
     - name: Download target dir
@@ -288,16 +222,11 @@ jobs:
 
     # Run
     - name: Test ${{ matrix.integration }}
-      run: $CARGO_TARGET_DIR/debug/integration
+      run: |
+        RUSTUP_TOOLCHAIN="$(rustup show active-toolchain | grep -o -E "nightly-[0-9]{4}-[0-9]{2}-[0-9]{2}")" \
+          $CARGO_TARGET_DIR/debug/integration
       env:
         INTEGRATION: ${{ matrix.integration }}
-        RUSTUP_TOOLCHAIN: master
-
-    # Cleanup
-    - name: Run cargo-cache --autoclean
-      run: |
-        cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache
-        cargo cache
 
   # These jobs doesn't actually test anything, but they're only used to tell
   # bors the build completed, as there is no practical way to detect when a
diff --git a/src/tools/clippy/.github/workflows/clippy_dev.yml b/src/tools/clippy/.github/workflows/clippy_dev.yml
index 5ee157cf23b..95da775b7bc 100644
--- a/src/tools/clippy/.github/workflows/clippy_dev.yml
+++ b/src/tools/clippy/.github/workflows/clippy_dev.yml
@@ -22,6 +22,12 @@ jobs:
 
     steps:
     # Setup
+    - name: Checkout
+      uses: actions/checkout@v2.3.3
+
+    - name: remove toolchain file
+      run: rm rust-toolchain
+
     - name: rust-toolchain
       uses: actions-rs/toolchain@v1.0.6
       with:
@@ -29,9 +35,7 @@ jobs:
         target: x86_64-unknown-linux-gnu
         profile: minimal
         components: rustfmt
-
-    - name: Checkout
-      uses: actions/checkout@v2.3.3
+        default: true
 
     # Run
     - name: Build
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index c7e02aaf4e1..af3b1c1db2a 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -2006,6 +2006,7 @@ Released 2018-09-13
 [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
 [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
 [`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
+[`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr
 [`print_stdout`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout
 [`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline
 [`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string
@@ -2024,6 +2025,7 @@ Released 2018-09-13
 [`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
 [`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
 [`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls
+[`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else
 [`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names
 [`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern
 [`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching
@@ -2169,5 +2171,6 @@ Released 2018-09-13
 [`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero
 [`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal
 [`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr
+[`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values
 [`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
 <!-- end autogenerated links to lint list -->
diff --git a/src/tools/clippy/CONTRIBUTING.md b/src/tools/clippy/CONTRIBUTING.md
index f8c26e2d456..4cfeaa153a0 100644
--- a/src/tools/clippy/CONTRIBUTING.md
+++ b/src/tools/clippy/CONTRIBUTING.md
@@ -19,10 +19,10 @@ All contributors are expected to follow the [Rust Code of Conduct].
   - [Writing code](#writing-code)
   - [Getting code-completion for rustc internals to work](#getting-code-completion-for-rustc-internals-to-work)
   - [How Clippy works](#how-clippy-works)
-  - [Fixing build failures caused by Rust](#fixing-build-failures-caused-by-rust)
+  - [Syncing changes between Clippy and `rust-lang/rust`](#syncing-changes-between-clippy-and-rust-langrust)
     - [Patching git-subtree to work with big repos](#patching-git-subtree-to-work-with-big-repos)
-    - [Performing the sync](#performing-the-sync)
-    - [Syncing back changes in Clippy to [`rust-lang/rust`]](#syncing-back-changes-in-clippy-to-rust-langrust)
+    - [Performing the sync from `rust-lang/rust` to Clippy](#performing-the-sync-from-rust-langrust-to-clippy)
+    - [Performing the sync from Clippy to `rust-lang/rust`](#performing-the-sync-from-clippy-to-rust-langrust)
     - [Defining remotes](#defining-remotes)
   - [Issue and PR triage](#issue-and-pr-triage)
   - [Bors and Homu](#bors-and-homu)
@@ -49,7 +49,7 @@ first read the [Basics docs](doc/basics.md).**
 All issues on Clippy are mentored, if you want help with a bug just ask
 @Manishearth, @flip1995, @phansch or @yaahc.
 
-Some issues are easier than others. The [`good first issue`] label can be used to find the easy issues.
+Some issues are easier than others. The [`good-first-issue`] label can be used to find the easy issues.
 If you want to work on an issue, please leave a comment so that we can assign it to you!
 
 There are also some abandoned PRs, marked with [`S-inactive-closed`].
@@ -68,16 +68,16 @@ To figure out how this syntax structure is encoded in the AST, it is recommended
 Usually the lint will end up to be a nested series of matches and ifs, [like so][deep-nesting].
 But we can make it nest-less by using [if_chain] macro, [like this][nest-less].
 
-[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good first issue`]
+[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good-first-issue`]
 first. Sometimes they are only somewhat involved code wise, but not difficult per-se.
-Note that [`E-medium`] issues may require some knowledge of Clippy internals or some 
-debugging to find the actual problem behind the issue. 
+Note that [`E-medium`] issues may require some knowledge of Clippy internals or some
+debugging to find the actual problem behind the issue.
 
 [`T-middle`] issues can be more involved and require verifying types. The [`ty`] module contains a
 lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of
 an AST expression). `match_def_path()` in Clippy's `utils` module can also be useful.
 
-[`good first issue`]: https://github.com/rust-lang/rust-clippy/labels/good%20first%20issue
+[`good-first-issue`]: https://github.com/rust-lang/rust-clippy/labels/good-first-issue
 [`S-inactive-closed`]: https://github.com/rust-lang/rust-clippy/pulls?q=is%3Aclosed+label%3AS-inactive-closed
 [`T-AST`]: https://github.com/rust-lang/rust-clippy/labels/T-AST
 [`T-middle`]: https://github.com/rust-lang/rust-clippy/labels/T-middle
@@ -111,7 +111,7 @@ To work around this, you need to have a copy of the [rustc-repo][rustc_repo] ava
 `git clone https://github.com/rust-lang/rust/`.
 Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies
 which rust-analyzer will be able to understand.
-Run `cargo dev ra-setup --repo-path <repo-path>` where `<repo-path>` is an absolute path to the rustc repo
+Run `cargo dev ra_setup --repo-path <repo-path>` where `<repo-path>` is an absolute path to the rustc repo
 you just cloned.
 The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to
 Clippys `Cargo.toml`s and should allow rust-analyzer to understand most of the types that Clippy uses.
@@ -182,18 +182,26 @@ That's why the `else_if_without_else` example uses the `register_early_pass` fun
 [early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html
 [late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
 
-## Fixing build failures caused by Rust
+## Syncing changes between Clippy and [`rust-lang/rust`]
 
-Clippy currently gets built with `rustc` of the `rust-lang/rust` `master`
-branch. Most of the times we have to adapt to the changes and only very rarely
-there's an actual bug in Rust.
+Clippy currently gets built with a pinned nightly version.
 
-If you decide to make Clippy work again with a Rust commit that breaks it, you
-have to sync the `rust-lang/rust-clippy` repository with the `subtree` copy of
-Clippy in the `rust-lang/rust` repository.
+In the `rust-lang/rust` repository, where rustc resides, there's a copy of Clippy
+that compiler hackers modify from time to time to adapt to changes in the unstable
+API of the compiler.
 
-For general information about `subtree`s in the Rust repository see [Rust's
-`CONTRIBUTING.md`][subtree].
+We need to sync these changes back to this repository periodically, and the changes
+made to this repository in the meantime also need to be synced to the `rust-lang/rust` repository.
+
+To avoid flooding the `rust-lang/rust` PR queue, this two-way sync process is done
+in a bi-weekly basis if there's no urgent changes. This is done starting on the day of
+the Rust stable release and then every other week. That way we guarantee that we keep
+this repo up to date with the latest compiler API, and every feature in Clippy is available
+for 2 weeks in nightly, before it can get to beta. For reference, the first sync
+following this cadence was performed the 2020-08-27.
+
+This process is described in detail in the following sections. For general information
+about `subtree`s in the Rust repository see [Rust's `CONTRIBUTING.md`][subtree].
 
 ### Patching git-subtree to work with big repos
 
@@ -222,13 +230,14 @@ This shell has a hardcoded recursion limit set to 1000. In order to make this pr
 you need to force the script to run `bash` instead. You can do this by editing the first
 line of the `git-subtree` script and changing `sh` to `bash`.
 
-### Performing the sync
+### Performing the sync from [`rust-lang/rust`] to Clippy
 
 Here is a TL;DR version of the sync process (all of the following commands have
 to be run inside the `rust` directory):
 
-1. Clone the [`rust-lang/rust`] repository
-2. Sync the changes to the rust-copy of Clippy to your Clippy fork:
+1. Clone the [`rust-lang/rust`] repository or make sure it is up to date.
+2. Checkout the commit from the latest available nightly. You can get it using `rustup check`.
+3. Sync the changes to the rust-copy of Clippy to your Clippy fork:
     ```bash
     # Make sure to change `your-github-name` to your github name in the following command
     git subtree push -P src/tools/clippy git@github.com:your-github-name/rust-clippy sync-from-rust
@@ -246,17 +255,11 @@ to be run inside the `rust` directory):
     git checkout sync-from-rust
     git merge upstream/master
     ```
-3. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to
+4. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to
    accelerate the process ping the `@rust-lang/clippy` team in your PR and/or
    ~~annoy~~ ask them in the [Zulip] stream.)
-   
-### Syncing back changes in Clippy to [`rust-lang/rust`]
 
-To avoid flooding the [`rust-lang/rust`] PR queue, changes in Clippy's repo are synced back
-in a bi-weekly basis if there's no urgent changes. This is done starting on the day of
-the Rust stable release and then every other week. That way we guarantee that
-every feature in Clippy is available for 2 weeks in nightly, before it can get to beta.
-For reference, the first sync following this cadence was performed the 2020-08-27.
+### Performing the sync from Clippy to [`rust-lang/rust`]
 
 All of the following commands have to be run inside the `rust` directory.
 
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
index a765390c603..7f9d22e594b 100644
--- a/src/tools/clippy/Cargo.toml
+++ b/src/tools/clippy/Cargo.toml
@@ -20,7 +20,6 @@ publish = false
 
 [[bin]]
 name = "cargo-clippy"
-test = false
 path = "src/main.rs"
 
 [[bin]]
diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md
index fddf0614a0b..dc931963726 100644
--- a/src/tools/clippy/README.md
+++ b/src/tools/clippy/README.md
@@ -82,6 +82,22 @@ Note that this is still experimental and only supported on the nightly channel:
 cargo clippy --fix -Z unstable-options
 ```
 
+#### Workspaces
+
+All the usual workspace options should work with Clippy. For example the following command
+will run Clippy on the `example` crate:
+
+```terminal
+cargo clippy -p example
+```
+
+As with `cargo check`, this includes dependencies that are members of the workspace, like path dependencies.
+If you want to run Clippy **only** on the given crate, use the `--no-deps` option like this:
+
+```terminal
+cargo clippy -p example -- --no-deps 
+```
+
 ### Running Clippy from the command line without installing it
 
 To have cargo compile your crate with Clippy without Clippy installation
@@ -192,7 +208,6 @@ the lint(s) you are interested in:
 ```terminal
 cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::...
 ```
-Note that if you've run clippy before, this may only take effect after you've modified a file or ran `cargo clean`.
 
 ### Specifying the minimum supported Rust version
 
diff --git a/src/tools/clippy/clippy_dev/src/bless.rs b/src/tools/clippy/clippy_dev/src/bless.rs
new file mode 100644
index 00000000000..645098e4cfc
--- /dev/null
+++ b/src/tools/clippy/clippy_dev/src/bless.rs
@@ -0,0 +1,74 @@
+//! `bless` updates the reference files in the repo with changed output files
+//! from the last test run.
+
+use std::env;
+use std::ffi::OsStr;
+use std::fs;
+use std::lazy::SyncLazy;
+use std::path::PathBuf;
+use walkdir::WalkDir;
+
+use crate::clippy_project_root;
+
+// NOTE: this is duplicated with tests/cargo/mod.rs What to do?
+pub static CARGO_TARGET_DIR: SyncLazy<PathBuf> = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") {
+    Some(v) => v.into(),
+    None => env::current_dir().unwrap().join("target"),
+});
+
+pub fn bless() {
+    let test_dirs = [
+        clippy_project_root().join("tests").join("ui"),
+        clippy_project_root().join("tests").join("ui-toml"),
+        clippy_project_root().join("tests").join("ui-cargo"),
+    ];
+    for test_dir in &test_dirs {
+        WalkDir::new(test_dir)
+            .into_iter()
+            .filter_map(Result::ok)
+            .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
+            .for_each(|f| {
+                update_reference_file(f.path().with_extension("stdout"));
+                update_reference_file(f.path().with_extension("stderr"));
+                update_reference_file(f.path().with_extension("fixed"));
+            });
+    }
+}
+
+fn update_reference_file(reference_file_path: PathBuf) {
+    let test_output_path = build_dir().join(PathBuf::from(reference_file_path.file_name().unwrap()));
+    let relative_reference_file_path = reference_file_path.strip_prefix(clippy_project_root()).unwrap();
+
+    // If compiletest did not write any changes during the test run,
+    // we don't have to update anything
+    if !test_output_path.exists() {
+        return;
+    }
+
+    let test_output_file = fs::read(&test_output_path).expect("Unable to read test output file");
+    let reference_file = fs::read(&reference_file_path).unwrap_or_default();
+
+    if test_output_file != reference_file {
+        // If a test run caused an output file to change, update the reference file
+        println!("updating {}", &relative_reference_file_path.display());
+        fs::copy(test_output_path, &reference_file_path).expect("Could not update reference file");
+
+        // We need to re-read the file now because it was potentially updated from copying
+        let reference_file = fs::read(&reference_file_path).unwrap_or_default();
+
+        if reference_file.is_empty() {
+            // If we copied over an empty output file, we remove the now empty reference file
+            println!("removing {}", &relative_reference_file_path.display());
+            fs::remove_file(reference_file_path).expect("Could not remove reference file");
+        }
+    }
+}
+
+fn build_dir() -> PathBuf {
+    let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string());
+    let mut path = PathBuf::new();
+    path.push(CARGO_TARGET_DIR.clone());
+    path.push(profile);
+    path.push("test_build_base");
+    path
+}
diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs
index 6ae3f58c1f2..6b528d219df 100644
--- a/src/tools/clippy/clippy_dev/src/fmt.rs
+++ b/src/tools/clippy/clippy_dev/src/fmt.rs
@@ -1,9 +1,9 @@
 use crate::clippy_project_root;
 use shell_escape::escape;
 use std::ffi::OsStr;
-use std::io;
 use std::path::Path;
 use std::process::{self, Command};
+use std::{fs, io};
 use walkdir::WalkDir;
 
 #[derive(Debug)]
@@ -12,6 +12,7 @@ pub enum CliError {
     IoError(io::Error),
     RustfmtNotInstalled,
     WalkDirError(walkdir::Error),
+    RaSetupActive,
 }
 
 impl From<io::Error> for CliError {
@@ -31,12 +32,23 @@ struct FmtContext {
     verbose: bool,
 }
 
+// the "main" function of cargo dev fmt
 pub fn run(check: bool, verbose: bool) {
     fn try_run(context: &FmtContext) -> Result<bool, CliError> {
         let mut success = true;
 
         let project_root = clippy_project_root();
 
+        // if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to
+        // format because rustfmt would also format the entire rustc repo as it is a local
+        // dependency
+        if fs::read_to_string(project_root.join("Cargo.toml"))
+            .expect("Failed to read clippy Cargo.toml")
+            .contains(&"[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
+        {
+            return Err(CliError::RaSetupActive);
+        }
+
         rustfmt_test(context)?;
 
         success &= cargo_fmt(context, project_root.as_path())?;
@@ -75,6 +87,13 @@ pub fn run(check: bool, verbose: bool) {
             CliError::WalkDirError(err) => {
                 eprintln!("error: {}", err);
             },
+            CliError::RaSetupActive => {
+                eprintln!(
+                    "error: a local rustc repo is enabled as path dependency via `cargo dev ra_setup`.
+Not formatting because that would format the local repo as well!
+Please revert the changes to Cargo.tomls first."
+                );
+            },
         }
     }
 
diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs
index f51c45e9eb5..17cc08ee10f 100644
--- a/src/tools/clippy/clippy_dev/src/lib.rs
+++ b/src/tools/clippy/clippy_dev/src/lib.rs
@@ -10,6 +10,7 @@ use std::lazy::SyncLazy;
 use std::path::{Path, PathBuf};
 use walkdir::WalkDir;
 
+pub mod bless;
 pub mod fmt;
 pub mod new_lint;
 pub mod ra_setup;
diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs
index 7a8cbd5251d..4fdae38e3ab 100644
--- a/src/tools/clippy/clippy_dev/src/main.rs
+++ b/src/tools/clippy/clippy_dev/src/main.rs
@@ -1,10 +1,53 @@
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
 
-use clap::{App, Arg, SubCommand};
-use clippy_dev::{fmt, new_lint, ra_setup, serve, stderr_length_check, update_lints};
+use clap::{App, Arg, ArgMatches, SubCommand};
+use clippy_dev::{bless, fmt, new_lint, ra_setup, serve, stderr_length_check, update_lints};
 
 fn main() {
-    let matches = App::new("Clippy developer tooling")
+    let matches = get_clap_config();
+
+    match matches.subcommand() {
+        ("bless", Some(_)) => {
+            bless::bless();
+        },
+        ("fmt", Some(matches)) => {
+            fmt::run(matches.is_present("check"), matches.is_present("verbose"));
+        },
+        ("update_lints", Some(matches)) => {
+            if matches.is_present("print-only") {
+                update_lints::print_lints();
+            } else if matches.is_present("check") {
+                update_lints::run(update_lints::UpdateMode::Check);
+            } else {
+                update_lints::run(update_lints::UpdateMode::Change);
+            }
+        },
+        ("new_lint", Some(matches)) => {
+            match new_lint::create(
+                matches.value_of("pass"),
+                matches.value_of("name"),
+                matches.value_of("category"),
+            ) {
+                Ok(_) => update_lints::run(update_lints::UpdateMode::Change),
+                Err(e) => eprintln!("Unable to create lint: {}", e),
+            }
+        },
+        ("limit_stderr_length", _) => {
+            stderr_length_check::check();
+        },
+        ("ra_setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")),
+        ("serve", Some(matches)) => {
+            let port = matches.value_of("port").unwrap().parse().unwrap();
+            let lint = matches.value_of("lint");
+            serve::run(port, lint);
+        },
+        _ => {},
+    }
+}
+
+fn get_clap_config<'a>() -> ArgMatches<'a> {
+    App::new("Clippy developer tooling")
+        .subcommand(SubCommand::with_name("bless").about("bless the test output changes"))
         .subcommand(
             SubCommand::with_name("fmt")
                 .about("Run rustfmt on all projects and tests")
@@ -25,16 +68,16 @@ fn main() {
                 .about("Updates lint registration and information from the source code")
                 .long_about(
                     "Makes sure that:\n \
-                     * the lint count in README.md is correct\n \
-                     * the changelog contains markdown link references at the bottom\n \
-                     * all lint groups include the correct lints\n \
-                     * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \
-                     * all lints are registered in the lint store",
+                 * the lint count in README.md is correct\n \
+                 * the changelog contains markdown link references at the bottom\n \
+                 * all lint groups include the correct lints\n \
+                 * lint modules in `clippy_lints/*` are visible in `src/lifb.rs` via `pub mod`\n \
+                 * all lints are registered in the lint store",
                 )
                 .arg(Arg::with_name("print-only").long("print-only").help(
                     "Print a table of lints to STDOUT. \
-                     This does not include deprecated and internal lints. \
-                     (Does not modify any files)",
+                 This does not include deprecated and internal lints. \
+                 (Does not modify any files)",
                 ))
                 .arg(
                     Arg::with_name("check")
@@ -88,7 +131,7 @@ fn main() {
                 .about("Ensures that stderr files do not grow longer than a certain amount of lines."),
         )
         .subcommand(
-            SubCommand::with_name("ra-setup")
+            SubCommand::with_name("ra_setup")
                 .about("Alter dependencies so rust-analyzer can find rustc internals")
                 .arg(
                     Arg::with_name("rustc-repo-path")
@@ -113,40 +156,5 @@ fn main() {
                 )
                 .arg(Arg::with_name("lint").help("Which lint's page to load initially (optional)")),
         )
-        .get_matches();
-
-    match matches.subcommand() {
-        ("fmt", Some(matches)) => {
-            fmt::run(matches.is_present("check"), matches.is_present("verbose"));
-        },
-        ("update_lints", Some(matches)) => {
-            if matches.is_present("print-only") {
-                update_lints::print_lints();
-            } else if matches.is_present("check") {
-                update_lints::run(update_lints::UpdateMode::Check);
-            } else {
-                update_lints::run(update_lints::UpdateMode::Change);
-            }
-        },
-        ("new_lint", Some(matches)) => {
-            match new_lint::create(
-                matches.value_of("pass"),
-                matches.value_of("name"),
-                matches.value_of("category"),
-            ) {
-                Ok(_) => update_lints::run(update_lints::UpdateMode::Change),
-                Err(e) => eprintln!("Unable to create lint: {}", e),
-            }
-        },
-        ("limit_stderr_length", _) => {
-            stderr_length_check::check();
-        },
-        ("ra-setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")),
-        ("serve", Some(matches)) => {
-            let port = matches.value_of("port").unwrap().parse().unwrap();
-            let lint = matches.value_of("lint");
-            serve::run(port, lint);
-        },
-        _ => {},
-    }
+        .get_matches()
 }
diff --git a/src/tools/clippy/clippy_dev/src/ra_setup.rs b/src/tools/clippy/clippy_dev/src/ra_setup.rs
index 9d9e836cc08..40bf4a9505a 100644
--- a/src/tools/clippy/clippy_dev/src/ra_setup.rs
+++ b/src/tools/clippy/clippy_dev/src/ra_setup.rs
@@ -52,7 +52,7 @@ fn inject_deps_into_manifest(
     // do not inject deps if we have aleady done so
     if cargo_toml.contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") {
         eprintln!(
-            "cargo dev ra-setup: warning: deps already found inside {}, doing nothing.",
+            "cargo dev ra_setup: warning: deps already found inside {}, doing nothing.",
             manifest_path
         );
         return Ok(());
diff --git a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
index cfade9cbc46..f136aa4572a 100644
--- a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
+++ b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
@@ -99,7 +99,11 @@ impl LateLintPass<'_> for AwaitHolding {
             };
             let def_id = cx.tcx.hir().body_owner_def_id(body_id);
             let typeck_results = cx.tcx.typeck(def_id);
-            check_interior_types(cx, &typeck_results.generator_interior_types.as_ref().skip_binder(), body.value.span);
+            check_interior_types(
+                cx,
+                &typeck_results.generator_interior_types.as_ref().skip_binder(),
+                body.value.span,
+            );
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs
index 28c1a54d2c5..54bc69e058b 100644
--- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs
+++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs
@@ -6,9 +6,12 @@ use rustc_errors::Applicability;
 use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
 
-use crate::utils::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq};
+use crate::utils::{meets_msrv, snippet_with_applicability, span_lint_and_sugg, SpanlessEq};
+
+const CHECKED_CONVERSIONS_MSRV: RustcVersion = RustcVersion::new(1, 34, 0);
 
 declare_clippy_lint! {
     /// **What it does:** Checks for explicit bounds checking when casting.
@@ -39,10 +42,25 @@ declare_clippy_lint! {
     "`try_from` could replace manual bounds checking when casting"
 }
 
-declare_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
+pub struct CheckedConversions {
+    msrv: Option<RustcVersion>,
+}
+
+impl CheckedConversions {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        Self { msrv }
+    }
+}
+
+impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
 
 impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
     fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
+        if !meets_msrv(self.msrv.as_ref(), &CHECKED_CONVERSIONS_MSRV) {
+            return;
+        }
+
         let result = if_chain! {
             if !in_external_macro(cx.sess(), item.span);
             if let ExprKind::Binary(op, ref left, ref right) = &item.kind;
@@ -74,6 +92,8 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
             }
         }
     }
+
+    extract_msrv_attr!(LateContext);
 }
 
 /// Searches for a single check from unsigned to _ is done
diff --git a/src/tools/clippy/clippy_lints/src/create_dir.rs b/src/tools/clippy/clippy_lints/src/create_dir.rs
index 4002fb655a5..200b6a565cc 100644
--- a/src/tools/clippy/clippy_lints/src/create_dir.rs
+++ b/src/tools/clippy/clippy_lints/src/create_dir.rs
@@ -8,7 +8,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
 declare_clippy_lint! {
     /// **What it does:** Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead.
     ///
-    /// **Why is this bad?** Sometimes `std::fs::crate_dir` is mistakenly chosen over `std::fs::create_dir_all`.
+    /// **Why is this bad?** Sometimes `std::fs::create_dir` is mistakenly chosen over `std::fs::create_dir_all`.
     ///
     /// **Known problems:** None.
     ///
diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs
index edecba57e44..aba65532795 100644
--- a/src/tools/clippy/clippy_lints/src/doc.rs
+++ b/src/tools/clippy/clippy_lints/src/doc.rs
@@ -14,6 +14,7 @@ use rustc_middle::ty;
 use rustc_parse::maybe_new_parser_from_source_str;
 use rustc_session::parse::ParseSess;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::edition::Edition;
 use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span};
 use rustc_span::{sym, FileName, Pos};
 use std::io;
@@ -377,7 +378,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs
     check_doc(cx, valid_idents, events, &spans)
 }
 
-const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail", "edition2018"];
+const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"];
 
 fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize>)>>(
     cx: &LateContext<'_>,
@@ -400,13 +401,24 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
     let mut in_link = None;
     let mut in_heading = false;
     let mut is_rust = false;
+    let mut edition = None;
     for (event, range) in events {
         match event {
             Start(CodeBlock(ref kind)) => {
                 in_code = true;
                 if let CodeBlockKind::Fenced(lang) = kind {
-                    is_rust =
-                        lang.is_empty() || !lang.contains("ignore") && lang.split(',').any(|i| RUST_CODE.contains(&i));
+                    for item in lang.split(',') {
+                        if item == "ignore" {
+                            is_rust = false;
+                            break;
+                        }
+                        if let Some(stripped) = item.strip_prefix("edition") {
+                            is_rust = true;
+                            edition = stripped.parse::<Edition>().ok();
+                        } else if item.is_empty() || RUST_CODE.contains(&item) {
+                            is_rust = true;
+                        }
+                    }
                 }
             },
             End(CodeBlock(_)) => {
@@ -436,7 +448,8 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
                 let (begin, span) = spans[index];
                 if in_code {
                     if is_rust {
-                        check_code(cx, &text, span);
+                        let edition = edition.unwrap_or_else(|| cx.tcx.sess.edition());
+                        check_code(cx, &text, edition, span);
                     }
                 } else {
                     // Adjust for the beginning of the current `Event`
@@ -450,67 +463,73 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
     headers
 }
 
-fn check_code(cx: &LateContext<'_>, text: &str, span: Span) {
-    fn has_needless_main(code: &str) -> bool {
-        let filename = FileName::anon_source_code(code);
-
-        let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
-        let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false);
-        let handler = Handler::with_emitter(false, None, box emitter);
-        let sess = ParseSess::with_span_handler(handler, sm);
-
-        let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) {
-            Ok(p) => p,
-            Err(errs) => {
-                for mut err in errs {
-                    err.cancel();
-                }
-                return false;
-            },
-        };
-
-        let mut relevant_main_found = false;
-        loop {
-            match parser.parse_item() {
-                Ok(Some(item)) => match &item.kind {
-                    // Tests with one of these items are ignored
-                    ItemKind::Static(..)
-                    | ItemKind::Const(..)
-                    | ItemKind::ExternCrate(..)
-                    | ItemKind::ForeignMod(..) => return false,
-                    // We found a main function ...
-                    ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym::main => {
-                        let is_async = matches!(sig.header.asyncness, Async::Yes{..});
-                        let returns_nothing = match &sig.decl.output {
-                            FnRetTy::Default(..) => true,
-                            FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
-                            _ => false,
-                        };
-
-                        if returns_nothing && !is_async && !block.stmts.is_empty() {
-                            // This main function should be linted, but only if there are no other functions
-                            relevant_main_found = true;
-                        } else {
-                            // This main function should not be linted, we're done
-                            return false;
+fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
+    fn has_needless_main(code: &str, edition: Edition) -> bool {
+        rustc_driver::catch_fatal_errors(|| {
+            rustc_span::with_session_globals(edition, || {
+                let filename = FileName::anon_source_code(code);
+
+                let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
+                let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false);
+                let handler = Handler::with_emitter(false, None, box emitter);
+                let sess = ParseSess::with_span_handler(handler, sm);
+
+                let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) {
+                    Ok(p) => p,
+                    Err(errs) => {
+                        for mut err in errs {
+                            err.cancel();
                         }
+                        return false;
                     },
-                    // Another function was found; this case is ignored too
-                    ItemKind::Fn(..) => return false,
-                    _ => {},
-                },
-                Ok(None) => break,
-                Err(mut e) => {
-                    e.cancel();
-                    return false;
-                },
-            }
-        }
+                };
+
+                let mut relevant_main_found = false;
+                loop {
+                    match parser.parse_item() {
+                        Ok(Some(item)) => match &item.kind {
+                            // Tests with one of these items are ignored
+                            ItemKind::Static(..)
+                            | ItemKind::Const(..)
+                            | ItemKind::ExternCrate(..)
+                            | ItemKind::ForeignMod(..) => return false,
+                            // We found a main function ...
+                            ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym::main => {
+                                let is_async = matches!(sig.header.asyncness, Async::Yes { .. });
+                                let returns_nothing = match &sig.decl.output {
+                                    FnRetTy::Default(..) => true,
+                                    FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
+                                    _ => false,
+                                };
+
+                                if returns_nothing && !is_async && !block.stmts.is_empty() {
+                                    // This main function should be linted, but only if there are no other functions
+                                    relevant_main_found = true;
+                                } else {
+                                    // This main function should not be linted, we're done
+                                    return false;
+                                }
+                            },
+                            // Another function was found; this case is ignored too
+                            ItemKind::Fn(..) => return false,
+                            _ => {},
+                        },
+                        Ok(None) => break,
+                        Err(mut e) => {
+                            e.cancel();
+                            return false;
+                        },
+                    }
+                }
 
-        relevant_main_found
+                relevant_main_found
+            })
+        })
+        .ok()
+        .unwrap_or_default()
     }
 
-    if has_needless_main(text) {
+    if has_needless_main(text, edition) {
         span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/functions.rs b/src/tools/clippy/clippy_lints/src/functions.rs
index 8b58d1f2601..fd93548b55c 100644
--- a/src/tools/clippy/clippy_lints/src/functions.rs
+++ b/src/tools/clippy/clippy_lints/src/functions.rs
@@ -405,13 +405,10 @@ impl<'tcx> Functions {
                     break;
                 }
                 if in_comment {
-                    match line.find("*/") {
-                        Some(i) => {
-                            line = &line[i + 2..];
-                            in_comment = false;
-                            continue;
-                        },
-                        None => break,
+                    if let Some(i) = line.find("*/") {
+                        line = &line[i + 2..];
+                        in_comment = false;
+                        continue;
                     }
                 } else {
                     let multi_idx = line.find("/*").unwrap_or_else(|| line.len());
@@ -423,8 +420,8 @@ impl<'tcx> Functions {
                         in_comment = true;
                         continue;
                     }
-                    break;
                 }
+                break;
             }
             if code_in_line {
                 line_count += 1;
diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs
index 8842901d90b..6fe53351090 100644
--- a/src/tools/clippy/clippy_lints/src/len_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/len_zero.rs
@@ -222,9 +222,8 @@ fn check_impl_items(cx: &LateContext<'_>, item: &Item<'_>, impl_items: &[ImplIte
     let is_empty = if let Some(is_empty) = impl_items.iter().find(|i| is_named_self(cx, i, "is_empty")) {
         if cx.access_levels.is_exported(is_empty.id.hir_id) {
             return;
-        } else {
-            "a private"
         }
+        "a private"
     } else {
         "no corresponding"
     };
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 2b99ed570b1..02ba422a2f5 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -27,6 +27,7 @@ extern crate rustc_ast;
 extern crate rustc_ast_pretty;
 extern crate rustc_attr;
 extern crate rustc_data_structures;
+extern crate rustc_driver;
 extern crate rustc_errors;
 extern crate rustc_hir;
 extern crate rustc_hir_pretty;
@@ -294,6 +295,7 @@ mod question_mark;
 mod ranges;
 mod redundant_clone;
 mod redundant_closure_call;
+mod redundant_else;
 mod redundant_field_names;
 mod redundant_pub_crate;
 mod redundant_static_lifetimes;
@@ -344,6 +346,7 @@ mod wildcard_dependencies;
 mod wildcard_imports;
 mod write;
 mod zero_div_zero;
+mod zero_sized_map_values;
 // end lints modules, do not remove this comment, it’s used in `update_lints`
 
 pub use crate::utils::conf::Conf;
@@ -509,6 +512,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         #[cfg(feature = "internal-lints")]
         &utils::internal_lints::DEFAULT_LINT,
         #[cfg(feature = "internal-lints")]
+        &utils::internal_lints::INTERNING_DEFINED_SYMBOL,
+        #[cfg(feature = "internal-lints")]
         &utils::internal_lints::INVALID_PATHS,
         #[cfg(feature = "internal-lints")]
         &utils::internal_lints::LINT_WITHOUT_LINT_PASS,
@@ -831,6 +836,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &ranges::REVERSED_EMPTY_RANGES,
         &redundant_clone::REDUNDANT_CLONE,
         &redundant_closure_call::REDUNDANT_CLOSURE_CALL,
+        &redundant_else::REDUNDANT_ELSE,
         &redundant_field_names::REDUNDANT_FIELD_NAMES,
         &redundant_pub_crate::REDUNDANT_PUB_CRATE,
         &redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
@@ -934,6 +940,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &wildcard_imports::WILDCARD_IMPORTS,
         &write::PRINTLN_EMPTY_STRING,
         &write::PRINT_LITERAL,
+        &write::PRINT_STDERR,
         &write::PRINT_STDOUT,
         &write::PRINT_WITH_NEWLINE,
         &write::USE_DEBUG,
@@ -941,6 +948,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &write::WRITE_LITERAL,
         &write::WRITE_WITH_NEWLINE,
         &zero_div_zero::ZERO_DIVIDED_BY_ZERO,
+        &zero_sized_map_values::ZERO_SIZED_MAP_VALUES,
     ]);
     // end register lints, do not remove this comment, it’s used in `update_lints`
 
@@ -953,6 +961,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls);
         store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new());
         store.register_late_pass(|| box utils::internal_lints::InvalidPaths);
+        store.register_late_pass(|| box utils::internal_lints::InterningDefinedSymbol::default());
         store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default());
         store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem);
         store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass);
@@ -1000,6 +1009,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move || box matches::Matches::new(msrv));
     store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv));
     store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv));
+    store.register_early_pass(move || box redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv));
+    store.register_early_pass(move || box redundant_field_names::RedundantFieldNames::new(msrv));
+    store.register_late_pass(move || box checked_conversions::CheckedConversions::new(msrv));
+    store.register_late_pass(move || box mem_replace::MemReplace::new(msrv));
+    store.register_late_pass(move || box ranges::Ranges::new(msrv));
+    store.register_late_pass(move || box use_self::UseSelf::new(msrv));
+    store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv));
+
     store.register_late_pass(|| box size_of_in_element_count::SizeOfInElementCount);
     store.register_late_pass(|| box map_clone::MapClone);
     store.register_late_pass(|| box map_err_ignore::MapErrIgnore);
@@ -1010,7 +1027,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box main_recursion::MainRecursion::default());
     store.register_late_pass(|| box lifetimes::Lifetimes);
     store.register_late_pass(|| box entry::HashMapPass);
-    store.register_late_pass(|| box ranges::Ranges);
     store.register_late_pass(|| box types::Casts);
     let type_complexity_threshold = conf.type_complexity_threshold;
     store.register_late_pass(move || box types::TypeComplexity::new(type_complexity_threshold));
@@ -1055,7 +1071,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box neg_multiply::NegMultiply);
     store.register_late_pass(|| box mem_discriminant::MemDiscriminant);
     store.register_late_pass(|| box mem_forget::MemForget);
-    store.register_late_pass(|| box mem_replace::MemReplace);
     store.register_late_pass(|| box arithmetic::Arithmetic::default());
     store.register_late_pass(|| box assign_ops::AssignOps);
     store.register_late_pass(|| box let_if_seq::LetIfSeq);
@@ -1077,7 +1092,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move || box pass_by_ref_or_value);
     store.register_late_pass(|| box ref_option_ref::RefOptionRef);
     store.register_late_pass(|| box try_err::TryErr);
-    store.register_late_pass(|| box use_self::UseSelf);
     store.register_late_pass(|| box bytecount::ByteCount);
     store.register_late_pass(|| box infinite_iter::InfiniteIter);
     store.register_late_pass(|| box inline_fn_without_body::InlineFnWithoutBody);
@@ -1103,10 +1117,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box unnecessary_wraps::UnnecessaryWraps);
     store.register_late_pass(|| box types::RefToMut);
     store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants);
-    store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn);
     store.register_late_pass(|| box transmuting_null::TransmutingNull);
     store.register_late_pass(|| box path_buf_push_overwrite::PathBufPushOverwrite);
-    store.register_late_pass(|| box checked_conversions::CheckedConversions);
     store.register_late_pass(|| box integer_division::IntegerDivision);
     store.register_late_pass(|| box inherent_to_string::InherentToString);
     let max_trait_bounds = conf.max_trait_bounds;
@@ -1132,9 +1144,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_early_pass(|| box items_after_statements::ItemsAfterStatements);
     store.register_early_pass(|| box precedence::Precedence);
     store.register_early_pass(|| box needless_continue::NeedlessContinue);
+    store.register_early_pass(|| box redundant_else::RedundantElse);
     store.register_late_pass(|| box create_dir::CreateDir);
     store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType);
-    store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes);
     store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata);
     store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions);
     store.register_late_pass(|| box wildcard_dependencies::WildcardDependencies);
@@ -1174,7 +1186,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box mut_mutex_lock::MutMutexLock);
     store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems);
     store.register_late_pass(|| box manual_async_fn::ManualAsyncFn);
-    store.register_early_pass(|| box redundant_field_names::RedundantFieldNames);
     store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero);
     store.register_late_pass(|| box panic_in_result_fn::PanicInResultFn);
     let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
@@ -1200,6 +1211,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box undropped_manually_drops::UndroppedManuallyDrops);
     store.register_late_pass(|| box strings::StrToString);
     store.register_late_pass(|| box strings::StringToString);
+    store.register_late_pass(|| box zero_sized_map_values::ZeroSizedMapValues);
 
     store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
         LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@@ -1247,6 +1259,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&types::RC_BUFFER),
         LintId::of(&unwrap_in_result::UNWRAP_IN_RESULT),
         LintId::of(&verbose_file_reads::VERBOSE_FILE_READS),
+        LintId::of(&write::PRINT_STDERR),
         LintId::of(&write::PRINT_STDOUT),
         LintId::of(&write::USE_DEBUG),
     ]);
@@ -1308,6 +1321,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
         LintId::of(&ranges::RANGE_MINUS_ONE),
         LintId::of(&ranges::RANGE_PLUS_ONE),
+        LintId::of(&redundant_else::REDUNDANT_ELSE),
         LintId::of(&ref_option_ref::REF_OPTION_REF),
         LintId::of(&shadow::SHADOW_UNRELATED),
         LintId::of(&strings::STRING_ADD_ASSIGN),
@@ -1330,6 +1344,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&unused_self::UNUSED_SELF),
         LintId::of(&wildcard_imports::ENUM_GLOB_USE),
         LintId::of(&wildcard_imports::WILDCARD_IMPORTS),
+        LintId::of(&zero_sized_map_values::ZERO_SIZED_MAP_VALUES),
     ]);
 
     #[cfg(feature = "internal-lints")]
@@ -1338,6 +1353,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS),
         LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS),
         LintId::of(&utils::internal_lints::DEFAULT_LINT),
+        LintId::of(&utils::internal_lints::INTERNING_DEFINED_SYMBOL),
         LintId::of(&utils::internal_lints::INVALID_PATHS),
         LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS),
         LintId::of(&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM),
diff --git a/src/tools/clippy/clippy_lints/src/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/manual_ok_or.rs
index c99d2e35b94..b97d97ea1a5 100644
--- a/src/tools/clippy/clippy_lints/src/manual_ok_or.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_ok_or.rs
@@ -8,6 +8,7 @@ use rustc_lint::LintContext;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::sym;
 
 declare_clippy_lint! {
     /// **What it does:**
@@ -51,7 +52,7 @@ impl LateLintPass<'_> for ManualOkOr {
             if args.len() == 3;
             let method_receiver = &args[0];
             let ty = cx.typeck_results().expr_ty(method_receiver);
-            if is_type_diagnostic_item(cx, ty, sym!(option_type));
+            if is_type_diagnostic_item(cx, ty, sym::option_type);
             let or_expr = &args[1];
             if is_ok_wrapping(cx, &args[2]);
             if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind;
diff --git a/src/tools/clippy/clippy_lints/src/matches.rs b/src/tools/clippy/clippy_lints/src/matches.rs
index 274d20cfa80..04b35835c6b 100644
--- a/src/tools/clippy/clippy_lints/src/matches.rs
+++ b/src/tools/clippy/clippy_lints/src/matches.rs
@@ -4,8 +4,8 @@ use crate::utils::usage::is_unused;
 use crate::utils::{
     expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable,
     is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, remove_blocks,
-    snippet, snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
-    span_lint_and_then,
+    snippet, snippet_block, snippet_opt, snippet_with_applicability, span_lint_and_help, span_lint_and_note,
+    span_lint_and_sugg, span_lint_and_then,
 };
 use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash};
 use if_chain::if_chain;
@@ -689,10 +689,9 @@ fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp
             if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() {
                 // single statement/expr "else" block, don't lint
                 return;
-            } else {
-                // block with 2+ statements or 1 expr and 1+ statement
-                Some(els)
             }
+            // block with 2+ statements or 1 expr and 1+ statement
+            Some(els)
         } else {
             // not a block, don't lint
             return;
@@ -1238,6 +1237,24 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A
     if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
         return;
     }
+
+    // HACK:
+    // This is a hack to deal with arms that are excluded by macros like `#[cfg]`. It is only used here
+    // to prevent false positives as there is currently no better way to detect if code was excluded by
+    // a macro. See PR #6435
+    if_chain! {
+        if let Some(match_snippet) = snippet_opt(cx, expr.span);
+        if let Some(arm_snippet) = snippet_opt(cx, arms[0].span);
+        if let Some(ex_snippet) = snippet_opt(cx, ex.span);
+        let rest_snippet = match_snippet.replace(&arm_snippet, "").replace(&ex_snippet, "");
+        if rest_snippet.contains("=>");
+        then {
+            // The code it self contains another thick arrow "=>"
+            // -> Either another arm or a comment
+            return;
+        }
+    }
+
     let matched_vars = ex.span;
     let bind_names = arms[0].pat.span;
     let match_body = remove_blocks(&arms[0].body);
diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs
index bb0acecc5a9..19087b02077 100644
--- a/src/tools/clippy/clippy_lints/src/mem_replace.rs
+++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs
@@ -1,13 +1,14 @@
 use crate::utils::{
-    in_macro, match_def_path, match_qpath, paths, snippet, snippet_with_applicability, span_lint_and_help,
+    in_macro, match_def_path, match_qpath, meets_msrv, paths, snippet, snippet_with_applicability, span_lint_and_help,
     span_lint_and_sugg, span_lint_and_then,
 };
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::Span;
 use rustc_span::symbol::sym;
 
@@ -94,7 +95,7 @@ declare_clippy_lint! {
     "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`"
 }
 
-declare_lint_pass!(MemReplace =>
+impl_lint_pass!(MemReplace =>
     [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]);
 
 fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
@@ -224,6 +225,19 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<
     }
 }
 
+const MEM_REPLACE_WITH_DEFAULT_MSRV: RustcVersion = RustcVersion::new(1, 40, 0);
+
+pub struct MemReplace {
+    msrv: Option<RustcVersion>,
+}
+
+impl MemReplace {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        Self { msrv }
+    }
+}
+
 impl<'tcx> LateLintPass<'tcx> for MemReplace {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if_chain! {
@@ -236,8 +250,11 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace {
             then {
                 check_replace_option_with_none(cx, src, dest, expr.span);
                 check_replace_with_uninit(cx, src, dest, expr.span);
-                check_replace_with_default(cx, src, dest, expr.span);
+                if meets_msrv(self.msrv.as_ref(), &MEM_REPLACE_WITH_DEFAULT_MSRV) {
+                    check_replace_with_default(cx, src, dest, expr.span);
+                }
             }
         }
     }
+    extract_msrv_attr!(LateContext);
 }
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 8002c27a5e9..e99fe1b97ff 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -22,6 +22,7 @@ use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::Span;
 use rustc_span::symbol::{sym, SymbolStr};
+use rustc_typeck::hir_ty_to_ty;
 
 use crate::consts::{constant, Constant};
 use crate::utils::eager_or_lazy::is_lazyness_candidate;
@@ -1487,7 +1488,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
             ["expect", ..] => lint_expect(cx, expr, arg_lists[0]),
             ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]),
             ["unwrap_or_else", "map"] => {
-                if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]) {
+                if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0], self.msrv.as_ref()) {
                     unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or");
                 }
             },
@@ -1509,7 +1510,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
             ["next", "iter"] => lint_iter_next(cx, expr, arg_lists[1]),
             ["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]),
             ["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]),
-            ["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1]),
+            ["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1], self.msrv.as_ref()),
             ["map", "find"] => lint_find_map(cx, expr, arg_lists[1], arg_lists[0]),
             ["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
             ["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
@@ -1568,7 +1569,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
                 lint_expect_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args);
 
                 let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
-                if args.len() == 1 && method_call.ident.name == sym!(clone) {
+                if args.len() == 1 && method_call.ident.name == sym::clone {
                     lint_clone_on_copy(cx, expr, &args[0], self_ty);
                     lint_clone_on_ref_ptr(cx, expr, &args[0]);
                 }
@@ -1592,7 +1593,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
                             }
                         }
                     },
-                    ty::Ref(..) if method_call.ident.name == sym!(into_iter) => {
+                    ty::Ref(..) if method_call.ident.name == sym::into_iter => {
                         lint_into_iter(cx, expr, self_ty, *method_span);
                     },
                     _ => (),
@@ -1623,10 +1624,15 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
         let item = cx.tcx.hir().expect_item(parent);
         let def_id = cx.tcx.hir().local_def_id(item.hir_id);
         let self_ty = cx.tcx.type_of(def_id);
+
+        // if this impl block implements a trait, lint in trait definition instead
+        if let hir::ItemKind::Impl { of_trait: Some(_), .. } = item.kind {
+            return;
+        }
+
         if_chain! {
             if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind;
             if let Some(first_arg) = iter_input_pats(&sig.decl, cx.tcx.hir().body(id)).next();
-            if let hir::ItemKind::Impl{ of_trait: None, .. } = item.kind;
 
             let method_def_id = cx.tcx.hir().local_def_id(impl_item.hir_id);
             let method_sig = cx.tcx.fn_sig(method_def_id);
@@ -1668,40 +1674,17 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
                     }
                 }
 
-                if let Some((ref conv, self_kinds)) = &CONVENTIONS
-                    .iter()
-                    .find(|(ref conv, _)| conv.check(&name))
-                {
-                    if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) {
-                        let lint = if item.vis.node.is_pub() {
-                            WRONG_PUB_SELF_CONVENTION
-                        } else {
-                            WRONG_SELF_CONVENTION
-                        };
-
-                        span_lint(
-                            cx,
-                            lint,
-                            first_arg.pat.span,
-                            &format!("methods called `{}` usually take {}; consider choosing a less ambiguous name",
-                                conv,
-                                &self_kinds
-                                    .iter()
-                                    .map(|k| k.description())
-                                    .collect::<Vec<_>>()
-                                    .join(" or ")
-                            ),
-                        );
-                    }
-                }
+                lint_wrong_self_convention(
+                    cx,
+                    &name,
+                    item.vis.node.is_pub(),
+                    self_ty,
+                    first_arg_ty,
+                    first_arg.pat.span
+                );
             }
         }
 
-        // if this impl block implements a trait, lint in trait definition instead
-        if let hir::ItemKind::Impl { of_trait: Some(_), .. } = item.kind {
-            return;
-        }
-
         if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
             let ret_ty = return_ty(cx, impl_item.hir_id);
 
@@ -1735,8 +1718,23 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
     }
 
     fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
+        if in_external_macro(cx.tcx.sess, item.span) {
+            return;
+        }
+
+        if_chain! {
+            if let TraitItemKind::Fn(ref sig, _) = item.kind;
+            if let Some(first_arg_ty) = sig.decl.inputs.iter().next();
+            let first_arg_span = first_arg_ty.span;
+            let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty);
+            let self_ty = TraitRef::identity(cx.tcx, item.hir_id.owner.to_def_id()).self_ty();
+
+            then {
+                lint_wrong_self_convention(cx, &item.ident.name.as_str(), false, self_ty, first_arg_ty, first_arg_span);
+            }
+        }
+
         if_chain! {
-            if !in_external_macro(cx.tcx.sess, item.span);
             if item.ident.name == sym::new;
             if let TraitItemKind::Fn(_, _) = item.kind;
             let ret_ty = return_ty(cx, item.hir_id);
@@ -1757,6 +1755,39 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
     extract_msrv_attr!(LateContext);
 }
 
+fn lint_wrong_self_convention<'tcx>(
+    cx: &LateContext<'tcx>,
+    item_name: &str,
+    is_pub: bool,
+    self_ty: &'tcx TyS<'tcx>,
+    first_arg_ty: &'tcx TyS<'tcx>,
+    first_arg_span: Span,
+) {
+    let lint = if is_pub {
+        WRONG_PUB_SELF_CONVENTION
+    } else {
+        WRONG_SELF_CONVENTION
+    };
+    if let Some((ref conv, self_kinds)) = &CONVENTIONS.iter().find(|(ref conv, _)| conv.check(item_name)) {
+        if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) {
+            span_lint(
+                cx,
+                lint,
+                first_arg_span,
+                &format!(
+                    "methods called `{}` usually take {}; consider choosing a less ambiguous name",
+                    conv,
+                    &self_kinds
+                        .iter()
+                        .map(|k| k.description())
+                        .collect::<Vec<_>>()
+                        .join(" or ")
+                ),
+            );
+        }
+    }
+}
+
 /// Checks for the `OR_FUN_CALL` lint.
 #[allow(clippy::too_many_lines)]
 fn lint_or_fun_call<'tcx>(
@@ -2100,8 +2131,11 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp
                 cx,
                 CLONE_DOUBLE_REF,
                 expr.span,
-                "using `clone` on a double-reference; \
-                this will copy the reference instead of cloning the inner type",
+                &format!(
+                    "using `clone` on a double-reference; \
+                    this will copy the reference of type `{}` instead of cloning the inner type",
+                    ty
+                ),
                 |diag| {
                     if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) {
                         let mut ty = innermost;
@@ -2174,11 +2208,17 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp
         } else {
             snip = None;
         }
-        span_lint_and_then(cx, CLONE_ON_COPY, expr.span, "using `clone` on a `Copy` type", |diag| {
-            if let Some((text, snip)) = snip {
-                diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable);
-            }
-        });
+        span_lint_and_then(
+            cx,
+            CLONE_ON_COPY,
+            expr.span,
+            &format!("using `clone` on type `{}` which implements the `Copy` trait", ty),
+            |diag| {
+                if let Some((text, snip)) = snip {
+                    diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable);
+                }
+            },
+        );
     }
 }
 
@@ -2638,9 +2678,9 @@ fn lint_unwrap(cx: &LateContext<'_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::E
 fn lint_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expect_args: &[hir::Expr<'_>]) {
     let obj_ty = cx.typeck_results().expr_ty(&expect_args[0]).peel_refs();
 
-    let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) {
+    let mess = if is_type_diagnostic_item(cx, obj_ty, sym::option_type) {
         Some((EXPECT_USED, "an Option", "None"))
-    } else if is_type_diagnostic_item(cx, obj_ty, sym!(result_type)) {
+    } else if is_type_diagnostic_item(cx, obj_ty, sym::result_type) {
         Some((EXPECT_USED, "a Result", "Err"))
     } else {
         None
@@ -2733,6 +2773,8 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map
     }
 }
 
+const MAP_UNWRAP_OR_MSRV: RustcVersion = RustcVersion::new(1, 41, 0);
+
 /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
 /// Return true if lint triggered
 fn lint_map_unwrap_or_else<'tcx>(
@@ -2740,7 +2782,11 @@ fn lint_map_unwrap_or_else<'tcx>(
     expr: &'tcx hir::Expr<'_>,
     map_args: &'tcx [hir::Expr<'_>],
     unwrap_args: &'tcx [hir::Expr<'_>],
+    msrv: Option<&RustcVersion>,
 ) -> bool {
+    if !meets_msrv(msrv, &MAP_UNWRAP_OR_MSRV) {
+        return false;
+    }
     // lint if the caller of `map()` is an `Option`
     let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type);
     let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::result_type);
@@ -2923,9 +2969,20 @@ fn lint_filter_map<'tcx>(
     }
 }
 
+const FILTER_MAP_NEXT_MSRV: RustcVersion = RustcVersion::new(1, 30, 0);
+
 /// lint use of `filter_map().next()` for `Iterators`
-fn lint_filter_map_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, filter_args: &'tcx [hir::Expr<'_>]) {
+fn lint_filter_map_next<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'_>,
+    filter_args: &'tcx [hir::Expr<'_>],
+    msrv: Option<&RustcVersion>,
+) {
     if match_trait_method(cx, expr, &paths::ITERATOR) {
+        if !meets_msrv(msrv, &FILTER_MAP_NEXT_MSRV) {
+            return;
+        }
+
         let msg = "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling \
                    `.find_map(..)` instead.";
         let filter_snippet = snippet(cx, filter_args[1].span, "..");
@@ -3116,7 +3173,7 @@ fn lint_search_is_some<'tcx>(
     else if search_method == "find" {
         let is_string_or_str_slice = |e| {
             let self_ty = cx.typeck_results().expr_ty(e).peel_refs();
-            if is_type_diagnostic_item(cx, self_ty, sym!(string_type)) {
+            if is_type_diagnostic_item(cx, self_ty, sym::string_type) {
                 true
             } else {
                 *self_ty.kind() == ty::Str
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
index 75e123eb593..d082a88cd2d 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
@@ -69,10 +69,9 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc
                             }
                         }
                         return (true, false);
-                    } else {
-                        // We don't know. It might do anything.
-                        return (true, true);
                     }
+                    // We don't know. It might do anything.
+                    return (true, true);
                 }
             }
             (true, true)
diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
index 25245b3dbf0..6ebeaced62a 100644
--- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
@@ -1,14 +1,19 @@
 use crate::utils::qualify_min_const_fn::is_min_const_fn;
-use crate::utils::{fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, span_lint, trait_ref_of_method};
+use crate::utils::{
+    fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, meets_msrv, span_lint, trait_ref_of_method,
+};
 use rustc_hir as hir;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::Span;
 use rustc_typeck::hir_ty_to_ty;
 
+const MISSING_CONST_FOR_FN_MSRV: RustcVersion = RustcVersion::new(1, 37, 0);
+
 declare_clippy_lint! {
     /// **What it does:**
     ///
@@ -69,7 +74,18 @@ declare_clippy_lint! {
     "Lint functions definitions that could be made `const fn`"
 }
 
-declare_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]);
+impl_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]);
+
+pub struct MissingConstForFn {
+    msrv: Option<RustcVersion>,
+}
+
+impl MissingConstForFn {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        Self { msrv }
+    }
+}
 
 impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
     fn check_fn(
@@ -81,6 +97,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
         span: Span,
         hir_id: HirId,
     ) {
+        if !meets_msrv(self.msrv.as_ref(), &MISSING_CONST_FOR_FN_MSRV) {
+            return;
+        }
+
         let def_id = cx.tcx.hir().local_def_id(hir_id);
 
         if in_external_macro(cx.tcx.sess, span) || is_entrypoint_fn(cx, def_id.to_def_id()) {
@@ -99,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
                 let has_const_generic_params = generics
                     .params
                     .iter()
-                    .any(|param| matches!(param.kind, GenericParamKind::Const{ .. }));
+                    .any(|param| matches!(param.kind, GenericParamKind::Const { .. }));
 
                 if already_const(header) || has_const_generic_params {
                     return;
@@ -126,6 +146,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
             span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`");
         }
     }
+    extract_msrv_attr!(LateContext);
 }
 
 /// Returns true if any of the method parameters is a type that implements `Drop`. The method
diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs
index 4678f6872f3..27f1074a0dd 100644
--- a/src/tools/clippy/clippy_lints/src/missing_doc.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs
@@ -2,7 +2,7 @@
 // *rustc*'s
 // [`missing_doc`].
 //
-// [`missing_doc`]: https://github.com/rust-lang/rust/blob/d6d05904697d89099b55da3331155392f1db9c00/src/librustc_lint/builtin.rs#L246
+// [`missing_doc`]: https://github.com/rust-lang/rust/blob/cf9cf7c923eb01146971429044f216a3ca905e06/compiler/rustc_lint/src/builtin.rs#L415
 //
 
 use crate::utils::span_lint;
@@ -70,7 +70,14 @@ impl MissingDoc {
         }
     }
 
-    fn check_missing_docs_attrs(&self, cx: &LateContext<'_>, attrs: &[ast::Attribute], sp: Span, desc: &'static str) {
+    fn check_missing_docs_attrs(
+        &self,
+        cx: &LateContext<'_>,
+        attrs: &[ast::Attribute],
+        sp: Span,
+        article: &'static str,
+        desc: &'static str,
+    ) {
         // If we're building a test harness, then warning about
         // documentation is probably not really relevant right now.
         if cx.sess().opts.test {
@@ -94,7 +101,7 @@ impl MissingDoc {
                 cx,
                 MISSING_DOCS_IN_PRIVATE_ITEMS,
                 sp,
-                &format!("missing documentation for {}", desc),
+                &format!("missing documentation for {} {}", article, desc),
             );
         }
     }
@@ -120,13 +127,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
     }
 
     fn check_crate(&mut self, cx: &LateContext<'tcx>, krate: &'tcx hir::Crate<'_>) {
-        self.check_missing_docs_attrs(cx, &krate.item.attrs, krate.item.span, "crate");
+        self.check_missing_docs_attrs(cx, &krate.item.attrs, krate.item.span, "the", "crate");
     }
 
     fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) {
-        let desc = match it.kind {
-            hir::ItemKind::Const(..) => "a constant",
-            hir::ItemKind::Enum(..) => "an enum",
+        match it.kind {
             hir::ItemKind::Fn(..) => {
                 // ignore main()
                 if it.ident.name == sym::main {
@@ -136,16 +141,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
                         return;
                     }
                 }
-                "a function"
             },
-            hir::ItemKind::Mod(..) => "a module",
-            hir::ItemKind::Static(..) => "a static",
-            hir::ItemKind::Struct(..) => "a struct",
-            hir::ItemKind::Trait(..) => "a trait",
-            hir::ItemKind::TraitAlias(..) => "a trait alias",
-            hir::ItemKind::TyAlias(..) => "a type alias",
-            hir::ItemKind::Union(..) => "a union",
-            hir::ItemKind::OpaqueTy(..) => "an existential type",
+            hir::ItemKind::Const(..)
+            | hir::ItemKind::Enum(..)
+            | hir::ItemKind::Mod(..)
+            | hir::ItemKind::Static(..)
+            | hir::ItemKind::Struct(..)
+            | hir::ItemKind::Trait(..)
+            | hir::ItemKind::TraitAlias(..)
+            | hir::ItemKind::TyAlias(..)
+            | hir::ItemKind::Union(..)
+            | hir::ItemKind::OpaqueTy(..) => {},
             hir::ItemKind::ExternCrate(..)
             | hir::ItemKind::ForeignMod { .. }
             | hir::ItemKind::GlobalAsm(..)
@@ -153,17 +159,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
             | hir::ItemKind::Use(..) => return,
         };
 
-        self.check_missing_docs_attrs(cx, &it.attrs, it.span, desc);
+        let def_id = cx.tcx.hir().local_def_id(it.hir_id);
+        let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id());
+
+        self.check_missing_docs_attrs(cx, &it.attrs, it.span, article, desc);
     }
 
     fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx hir::TraitItem<'_>) {
-        let desc = match trait_item.kind {
-            hir::TraitItemKind::Const(..) => "an associated constant",
-            hir::TraitItemKind::Fn(..) => "a trait method",
-            hir::TraitItemKind::Type(..) => "an associated type",
-        };
+        let def_id = cx.tcx.hir().local_def_id(trait_item.hir_id);
+        let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id());
 
-        self.check_missing_docs_attrs(cx, &trait_item.attrs, trait_item.span, desc);
+        self.check_missing_docs_attrs(cx, &trait_item.attrs, trait_item.span, article, desc);
     }
 
     fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
@@ -178,21 +184,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
             },
         }
 
-        let desc = match impl_item.kind {
-            hir::ImplItemKind::Const(..) => "an associated constant",
-            hir::ImplItemKind::Fn(..) => "a method",
-            hir::ImplItemKind::TyAlias(_) => "an associated type",
-        };
-        self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, desc);
+        let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id());
+        self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, article, desc);
     }
 
     fn check_struct_field(&mut self, cx: &LateContext<'tcx>, sf: &'tcx hir::StructField<'_>) {
         if !sf.is_positional() {
-            self.check_missing_docs_attrs(cx, &sf.attrs, sf.span, "a struct field");
+            self.check_missing_docs_attrs(cx, &sf.attrs, sf.span, "a", "struct field");
         }
     }
 
     fn check_variant(&mut self, cx: &LateContext<'tcx>, v: &'tcx hir::Variant<'_>) {
-        self.check_missing_docs_attrs(cx, &v.attrs, v.span, "a variant");
+        self.check_missing_docs_attrs(cx, &v.attrs, v.span, "a", "variant");
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/needless_borrow.rs b/src/tools/clippy/clippy_lints/src/needless_borrow.rs
index 405c21d608d..bff53eb8cca 100644
--- a/src/tools/clippy/clippy_lints/src/needless_borrow.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_borrow.rs
@@ -47,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow {
             return;
         }
         if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = e.kind {
-            if let ty::Ref(..) = cx.typeck_results().expr_ty(inner).kind() {
+            if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(inner).kind() {
                 for adj3 in cx.typeck_results().expr_adjustments(e).windows(3) {
                     if let [Adjustment {
                         kind: Adjust::Deref(_), ..
@@ -62,8 +62,11 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow {
                             cx,
                             NEEDLESS_BORROW,
                             e.span,
-                            "this expression borrows a reference that is immediately dereferenced \
+                            &format!(
+                                "this expression borrows a reference (`&{}`) that is immediately dereferenced \
                              by the compiler",
+                                ty
+                            ),
                             |diag| {
                                 if let Some(snippet) = snippet_opt(cx, inner.span) {
                                     diag.span_suggestion(
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
index 532c0266946..5043b7b1fc3 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
@@ -90,9 +90,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
 
         // Exclude non-inherent impls
         if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
-            if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } |
-                ItemKind::Trait(..))
-            {
+            if matches!(
+                item.kind,
+                ItemKind::Impl { of_trait: Some(_), .. } | ItemKind::Trait(..)
+            ) {
                 return;
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/needless_update.rs b/src/tools/clippy/clippy_lints/src/needless_update.rs
index 98e9078094a..41cf541ecf5 100644
--- a/src/tools/clippy/clippy_lints/src/needless_update.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_update.rs
@@ -8,6 +8,9 @@ declare_clippy_lint! {
     /// **What it does:** Checks for needlessly including a base struct on update
     /// when all fields are changed anyway.
     ///
+    /// This lint is not applied to structs marked with
+    /// [non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html).
+    ///
     /// **Why is this bad?** This will cost resources (because the base has to be
     /// somewhere), and make the code less readable.
     ///
@@ -49,7 +52,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate {
         if let ExprKind::Struct(_, ref fields, Some(ref base)) = expr.kind {
             let ty = cx.typeck_results().expr_ty(expr);
             if let ty::Adt(def, _) = ty.kind() {
-                if fields.len() == def.non_enum_variant().fields.len() {
+                if fields.len() == def.non_enum_variant().fields.len()
+                    && !def.variants[0_usize.into()].is_field_list_non_exhaustive()
+                {
                     span_lint(
                         cx,
                         NEEDLESS_UPDATE,
diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
index 5b42b61fcde..446426b3e61 100644
--- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
+++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
@@ -409,11 +409,10 @@ fn levenstein_not_1(a_name: &str, b_name: &str) -> bool {
         if let Some(b2) = b_chars.next() {
             // check if there's just one character inserted
             return a != b2 || a_chars.ne(b_chars);
-        } else {
-            // tuple
-            // ntuple
-            return true;
         }
+        // tuple
+        // ntuple
+        return true;
     }
     // for item in items
     true
diff --git a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
index 72dfccc1089..37e2b50def1 100644
--- a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
@@ -1,18 +1,16 @@
-use crate::utils::{is_expn_of, is_type_diagnostic_item, return_ty, span_lint_and_then};
+use crate::utils::{find_macro_calls, is_type_diagnostic_item, return_ty, span_lint_and_then};
 use rustc_hir as hir;
-use rustc_hir::intravisit::{self, FnKind, NestedVisitorMap, Visitor};
-use rustc_hir::Expr;
+use rustc_hir::intravisit::FnKind;
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::hir::map::Map;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::{sym, Span};
 
 declare_clippy_lint! {
-    /// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!` or `unreachable!` in a function of type result.
+    /// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result.
     ///
-    /// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence unimplemented, panic and unreachable should be avoided.
+    /// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence panicking macros should be avoided.
     ///
-    /// **Known problems:** None.
+    /// **Known problems:** Functions called from a function returning a `Result` may invoke a panicking macro. This is not checked.
     ///
     /// **Example:**
     ///
@@ -22,9 +20,15 @@ declare_clippy_lint! {
     ///     panic!("error");
     /// }
     /// ```
+    /// Use instead:
+    /// ```rust
+    /// fn result_without_panic() -> Result<bool, String> {
+    ///     Err(String::from("error"))
+    /// }
+    /// ```
     pub PANIC_IN_RESULT_FN,
     restriction,
-    "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` "
+    "functions of type `Result<..>` that contain `panic!()`, `todo!()`, `unreachable()`, `unimplemented()` or assertion"
 }
 
 declare_lint_pass!(PanicInResultFn  => [PANIC_IN_RESULT_FN]);
@@ -47,43 +51,33 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn {
     }
 }
 
-struct FindPanicUnimplementedUnreachable {
-    result: Vec<Span>,
-}
-
-impl<'tcx> Visitor<'tcx> for FindPanicUnimplementedUnreachable {
-    type Map = Map<'tcx>;
-
-    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
-        if ["unimplemented", "unreachable", "panic", "todo"]
-            .iter()
-            .any(|fun| is_expn_of(expr.span, fun).is_some())
-        {
-            self.result.push(expr.span);
-        }
-        // and check sub-expressions
-        intravisit::walk_expr(self, expr);
-    }
-
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::None
-    }
-}
-
 fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) {
-    let mut panics = FindPanicUnimplementedUnreachable { result: Vec::new() };
-    panics.visit_expr(&body.value);
-    if !panics.result.is_empty() {
+    let panics = find_macro_calls(
+        &[
+            "unimplemented",
+            "unreachable",
+            "panic",
+            "todo",
+            "assert",
+            "assert_eq",
+            "assert_ne",
+            "debug_assert",
+            "debug_assert_eq",
+            "debug_assert_ne",
+        ],
+        body,
+    );
+    if !panics.is_empty() {
         span_lint_and_then(
             cx,
             PANIC_IN_RESULT_FN,
             impl_span,
-            "used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`",
+            "used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`",
             move |diag| {
                 diag.help(
-                    "`unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing",
+                    "`unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing",
                 );
-                diag.span_note(panics.result, "return Err() instead of panicking");
+                diag.span_note(panics, "return Err() instead of panicking");
             },
         );
     }
diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
index f03facc235e..6a17d654ac9 100644
--- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
+++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
@@ -244,9 +244,10 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue {
 
         // Exclude non-inherent impls
         if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
-            if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } |
-            ItemKind::Trait(..))
-            {
+            if matches!(
+                item.kind,
+                ItemKind::Impl { of_trait: Some(_), .. } | ItemKind::Trait(..)
+            ) {
                 return;
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs
index 4b514bbd42c..3e454eecd97 100644
--- a/src/tools/clippy/clippy_lints/src/ranges.rs
+++ b/src/tools/clippy/clippy_lints/src/ranges.rs
@@ -3,9 +3,10 @@ use if_chain::if_chain;
 use rustc_ast::ast::RangeLimits;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, QPath};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::{Span, Spanned};
 use rustc_span::sym;
 use rustc_span::symbol::Ident;
@@ -13,8 +14,8 @@ use std::cmp::Ordering;
 
 use crate::utils::sugg::Sugg;
 use crate::utils::{
-    get_parent_expr, is_integer_const, single_segment_path, snippet, snippet_opt, snippet_with_applicability,
-    span_lint, span_lint_and_sugg, span_lint_and_then,
+    get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path, snippet, snippet_opt,
+    snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then,
 };
 use crate::utils::{higher, SpanlessEq};
 
@@ -160,7 +161,20 @@ declare_clippy_lint! {
     "manually reimplementing {`Range`, `RangeInclusive`}`::contains`"
 }
 
-declare_lint_pass!(Ranges => [
+const MANUAL_RANGE_CONTAINS_MSRV: RustcVersion = RustcVersion::new(1, 35, 0);
+
+pub struct Ranges {
+    msrv: Option<RustcVersion>,
+}
+
+impl Ranges {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        Self { msrv }
+    }
+}
+
+impl_lint_pass!(Ranges => [
     RANGE_ZIP_WITH_LEN,
     RANGE_PLUS_ONE,
     RANGE_MINUS_ONE,
@@ -175,7 +189,9 @@ impl<'tcx> LateLintPass<'tcx> for Ranges {
                 check_range_zip_with_len(cx, path, args, expr.span);
             },
             ExprKind::Binary(ref op, ref l, ref r) => {
-                check_possible_range_contains(cx, op.node, l, r, expr.span);
+                if meets_msrv(self.msrv.as_ref(), &MANUAL_RANGE_CONTAINS_MSRV) {
+                    check_possible_range_contains(cx, op.node, l, r, expr);
+                }
             },
             _ => {},
         }
@@ -184,9 +200,15 @@ impl<'tcx> LateLintPass<'tcx> for Ranges {
         check_inclusive_range_minus_one(cx, expr);
         check_reversed_empty_range(cx, expr);
     }
+    extract_msrv_attr!(LateContext);
 }
 
-fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, span: Span) {
+fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, expr: &Expr<'_>) {
+    if in_constant(cx, expr.hir_id) {
+        return;
+    }
+
+    let span = expr.span;
     let combine_and = match op {
         BinOpKind::And | BinOpKind::BitAnd => true,
         BinOpKind::Or | BinOpKind::BitOr => false,
diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
index f0e507105a6..06adbb523d7 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
@@ -390,7 +390,10 @@ impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor {
         let local = place.local;
 
         if local == self.used.0
-            && !matches!(ctx, PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_))
+            && !matches!(
+                ctx,
+                PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_)
+            )
         {
             self.used.1 = true;
         }
diff --git a/src/tools/clippy/clippy_lints/src/redundant_else.rs b/src/tools/clippy/clippy_lints/src/redundant_else.rs
new file mode 100644
index 00000000000..3d585cd27a3
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/redundant_else.rs
@@ -0,0 +1,135 @@
+use crate::utils::span_lint_and_help;
+use rustc_ast::ast::{Block, Expr, ExprKind, Stmt, StmtKind};
+use rustc_ast::visit::{walk_expr, Visitor};
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for `else` blocks that can be removed without changing semantics.
+    ///
+    /// **Why is this bad?** The `else` block adds unnecessary indentation and verbosity.
+    ///
+    /// **Known problems:** Some may prefer to keep the `else` block for clarity.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// fn my_func(count: u32) {
+    ///     if count == 0 {
+    ///         print!("Nothing to do");
+    ///         return;
+    ///     } else {
+    ///         print!("Moving on...");
+    ///     }
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// fn my_func(count: u32) {
+    ///     if count == 0 {
+    ///         print!("Nothing to do");
+    ///         return;
+    ///     }
+    ///     print!("Moving on...");
+    /// }
+    /// ```
+    pub REDUNDANT_ELSE,
+    pedantic,
+    "`else` branch that can be removed without changing semantics"
+}
+
+declare_lint_pass!(RedundantElse => [REDUNDANT_ELSE]);
+
+impl EarlyLintPass for RedundantElse {
+    fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &Stmt) {
+        if in_external_macro(cx.sess, stmt.span) {
+            return;
+        }
+        // Only look at expressions that are a whole statement
+        let expr: &Expr = match &stmt.kind {
+            StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr,
+            _ => return,
+        };
+        // if else
+        let (mut then, mut els): (&Block, &Expr) = match &expr.kind {
+            ExprKind::If(_, then, Some(els)) => (then, els),
+            _ => return,
+        };
+        loop {
+            if !BreakVisitor::default().check_block(then) {
+                // then block does not always break
+                return;
+            }
+            match &els.kind {
+                // else if else
+                ExprKind::If(_, next_then, Some(next_els)) => {
+                    then = next_then;
+                    els = next_els;
+                    continue;
+                },
+                // else if without else
+                ExprKind::If(..) => return,
+                // done
+                _ => break,
+            }
+        }
+        span_lint_and_help(
+            cx,
+            REDUNDANT_ELSE,
+            els.span,
+            "redundant else block",
+            None,
+            "remove the `else` block and move the contents out",
+        );
+    }
+}
+
+/// Call `check` functions to check if an expression always breaks control flow
+#[derive(Default)]
+struct BreakVisitor {
+    is_break: bool,
+}
+
+impl<'ast> Visitor<'ast> for BreakVisitor {
+    fn visit_block(&mut self, block: &'ast Block) {
+        self.is_break = match block.stmts.as_slice() {
+            [.., last] => self.check_stmt(last),
+            _ => false,
+        };
+    }
+
+    fn visit_expr(&mut self, expr: &'ast Expr) {
+        self.is_break = match expr.kind {
+            ExprKind::Break(..) | ExprKind::Continue(..) | ExprKind::Ret(..) => true,
+            ExprKind::Match(_, ref arms) => arms.iter().all(|arm| self.check_expr(&arm.body)),
+            ExprKind::If(_, ref then, Some(ref els)) => self.check_block(then) && self.check_expr(els),
+            ExprKind::If(_, _, None)
+            // ignore loops for simplicity
+            | ExprKind::While(..) | ExprKind::ForLoop(..) | ExprKind::Loop(..) => false,
+            _ => {
+                walk_expr(self, expr);
+                return;
+            },
+        };
+    }
+}
+
+impl BreakVisitor {
+    fn check<T>(&mut self, item: T, visit: fn(&mut Self, T)) -> bool {
+        visit(self, item);
+        std::mem::replace(&mut self.is_break, false)
+    }
+
+    fn check_block(&mut self, block: &Block) -> bool {
+        self.check(block, Self::visit_block)
+    }
+
+    fn check_expr(&mut self, expr: &Expr) -> bool {
+        self.check(expr, Self::visit_expr)
+    }
+
+    fn check_stmt(&mut self, stmt: &Stmt) -> bool {
+        self.check(stmt, Self::visit_stmt)
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs
index 2a81170e49e..38dcf7a192c 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs
@@ -1,9 +1,12 @@
-use crate::utils::span_lint_and_sugg;
+use crate::utils::{meets_msrv, span_lint_and_sugg};
 use rustc_ast::ast::{Expr, ExprKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_middle::lint::in_external_macro;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+
+const REDUNDANT_FIELD_NAMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0);
 
 declare_clippy_lint! {
     /// **What it does:** Checks for fields in struct literals where shorthands
@@ -33,10 +36,25 @@ declare_clippy_lint! {
     "checks for fields in struct literals where shorthands could be used"
 }
 
-declare_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]);
+pub struct RedundantFieldNames {
+    msrv: Option<RustcVersion>,
+}
+
+impl RedundantFieldNames {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        Self { msrv }
+    }
+}
+
+impl_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]);
 
 impl EarlyLintPass for RedundantFieldNames {
     fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
+        if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_FIELD_NAMES_MSRV) {
+            return;
+        }
+
         if in_external_macro(cx.sess, expr.span) {
             return;
         }
@@ -64,4 +82,5 @@ impl EarlyLintPass for RedundantFieldNames {
             }
         }
     }
+    extract_msrv_attr!(EarlyContext);
 }
diff --git a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
index 7bbcc67aa2d..fcfa3c12755 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
@@ -1,8 +1,11 @@
-use crate::utils::{snippet, span_lint_and_then};
+use crate::utils::{meets_msrv, snippet, span_lint_and_then};
 use rustc_ast::ast::{Item, ItemKind, Ty, TyKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+
+const REDUNDANT_STATIC_LIFETIMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0);
 
 declare_clippy_lint! {
     /// **What it does:** Checks for constants and statics with an explicit `'static` lifetime.
@@ -29,7 +32,18 @@ declare_clippy_lint! {
     "Using explicit `'static` lifetime for constants or statics when elision rules would allow omitting them."
 }
 
-declare_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]);
+pub struct RedundantStaticLifetimes {
+    msrv: Option<RustcVersion>,
+}
+
+impl RedundantStaticLifetimes {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        Self { msrv }
+    }
+}
+
+impl_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]);
 
 impl RedundantStaticLifetimes {
     // Recursively visit types
@@ -84,6 +98,10 @@ impl RedundantStaticLifetimes {
 
 impl EarlyLintPass for RedundantStaticLifetimes {
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+        if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_STATIC_LIFETIMES_MSRV) {
+            return;
+        }
+
         if !item.span.from_expansion() {
             if let ItemKind::Const(_, ref var_type, _) = item.kind {
                 self.visit_type(var_type, cx, "constants have by default a `'static` lifetime");
@@ -96,4 +114,6 @@ impl EarlyLintPass for RedundantStaticLifetimes {
             }
         }
     }
+
+    extract_msrv_attr!(EarlyContext);
 }
diff --git a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs
index a914a77d48b..803ebada54b 100644
--- a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs
@@ -2,6 +2,7 @@ use crate::utils::{last_path_segment, snippet, span_lint_and_sugg};
 use rustc_hir::{GenericArg, Mutability, Ty, TyKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::sym;
 
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -41,7 +42,7 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef {
             if let Some(res) = last.res;
             if let Some(def_id) = res.opt_def_id();
 
-            if cx.tcx.is_diagnostic_item(sym!(option_type), def_id);
+            if cx.tcx.is_diagnostic_item(sym::option_type, def_id);
             if let Some(ref params) = last_path_segment(qpath).args ;
             if !params.parenthesized;
             if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg {
diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs
index 77e79073378..31dd5965473 100644
--- a/src/tools/clippy/clippy_lints/src/strings.rs
+++ b/src/tools/clippy/clippy_lints/src/strings.rs
@@ -372,7 +372,7 @@ impl LateLintPass<'_> for StringToString {
             if let ExprKind::MethodCall(path, _, args, _) = &expr.kind;
             if path.ident.name == sym!(to_string);
             let ty = cx.typeck_results().expr_ty(&args[0]);
-            if is_type_diagnostic_item(cx, ty, sym!(string_type));
+            if is_type_diagnostic_item(cx, ty, sym::string_type);
             then {
                 span_lint_and_help(
                     cx,
diff --git a/src/tools/clippy/clippy_lints/src/types.rs b/src/tools/clippy/clippy_lints/src/types.rs
index 74ba53e6a9a..fd74783335d 100644
--- a/src/tools/clippy/clippy_lints/src/types.rs
+++ b/src/tools/clippy/clippy_lints/src/types.rs
@@ -1104,7 +1104,9 @@ fn is_empty_block(expr: &Expr<'_>) -> bool {
         expr.kind,
         ExprKind::Block(
             Block {
-                stmts: &[], expr: None, ..
+                stmts: &[],
+                expr: None,
+                ..
             },
             _,
         )
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
index 5d801511a0b..5b9a80f92db 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
@@ -9,6 +9,7 @@ use rustc_hir::{Body, ExprKind, FnDecl, HirId, ItemKind, Node};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::subst::GenericArgKind;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::sym;
 use rustc_span::Span;
 
 declare_clippy_lint! {
@@ -74,14 +75,17 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
         }
 
         if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
-            if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), ..} | ItemKind::Trait(..)) {
+            if matches!(
+                item.kind,
+                ItemKind::Impl { of_trait: Some(_), .. } | ItemKind::Trait(..)
+            ) {
                 return;
             }
         }
 
-        let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) {
+        let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::option_type) {
             ("Option", &paths::OPTION_SOME)
-        } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) {
+        } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type) {
             ("Result", &paths::RESULT_OK)
         } else {
             return;
diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs
index 5ac4797680b..3b23f885e08 100644
--- a/src/tools/clippy/clippy_lints/src/use_self.rs
+++ b/src/tools/clippy/clippy_lints/src/use_self.rs
@@ -12,11 +12,12 @@ use rustc_middle::hir::map::Map;
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty;
 use rustc_middle::ty::{DefIdTree, Ty};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::symbol::kw;
 use rustc_typeck::hir_ty_to_ty;
 
-use crate::utils::{differing_macro_contexts, span_lint_and_sugg};
+use crate::utils::{differing_macro_contexts, meets_msrv, span_lint_and_sugg};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for unnecessary repetition of structure name when a
@@ -53,7 +54,7 @@ declare_clippy_lint! {
     "unnecessary structure name repetition whereas `Self` is applicable"
 }
 
-declare_lint_pass!(UseSelf => [USE_SELF]);
+impl_lint_pass!(UseSelf => [USE_SELF]);
 
 const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element";
 
@@ -157,8 +158,25 @@ fn check_trait_method_impl_decl<'tcx>(
     }
 }
 
+const USE_SELF_MSRV: RustcVersion = RustcVersion::new(1, 37, 0);
+
+pub struct UseSelf {
+    msrv: Option<RustcVersion>,
+}
+
+impl UseSelf {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        Self { msrv }
+    }
+}
+
 impl<'tcx> LateLintPass<'tcx> for UseSelf {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
+        if !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) {
+            return;
+        }
+
         if in_external_macro(cx.sess(), item.span) {
             return;
         }
@@ -204,6 +222,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
             }
         }
     }
+    extract_msrv_attr!(LateContext);
 }
 
 struct UseSelfVisitor<'a, 'tcx> {
diff --git a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs
index 31b4e25411b..f0267e4c792 100644
--- a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs
@@ -408,7 +408,10 @@ pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool {
 }
 
 pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool {
-    matches!((l, r), (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_)))
+    matches!(
+        (l, r),
+        (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_))
+    )
 }
 
 pub fn eq_vis(l: &Visibility, r: &Visibility) -> bool {
diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs
index 6403ff6dad1..32d7840a451 100644
--- a/src/tools/clippy/clippy_lints/src/utils/conf.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs
@@ -106,7 +106,7 @@ macro_rules! define_Conf {
 
 pub use self::helpers::Conf;
 define_Conf! {
-    /// Lint: MANUAL_NON_EXHAUSTIVE, MANUAL_STRIP, OPTION_AS_REF_DEREF, MATCH_LIKE_MATCHES_MACRO. The minimum rust version that the project supports
+    /// Lint: REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN. The minimum rust version that the project supports
     (msrv, "msrv": Option<String>, None),
     /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses
     (blacklisted_names, "blacklisted_names": Vec<String>, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
index 8b59a9541a7..9ba39f73ee8 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
@@ -15,6 +15,7 @@ use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
 use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind};
 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
 use rustc_middle::hir::map::Map;
+use rustc_middle::mir::interpret::ConstValue;
 use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::{Span, Spanned};
@@ -247,6 +248,30 @@ declare_clippy_lint! {
     "invalid path"
 }
 
+declare_clippy_lint! {
+    /// **What it does:**
+    /// Checks for interning symbols that have already been pre-interned and defined as constants.
+    ///
+    /// **Why is this bad?**
+    /// It's faster and easier to use the symbol constant.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// Bad:
+    /// ```rust,ignore
+    /// let _ = sym!(f32);
+    /// ```
+    ///
+    /// Good:
+    /// ```rust,ignore
+    /// let _ = sym::f32;
+    /// ```
+    pub INTERNING_DEFINED_SYMBOL,
+    internal,
+    "interning a symbol that is pre-interned and defined as a constant"
+}
+
 declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
 
 impl EarlyLintPass for ClippyLintsInternal {
@@ -840,3 +865,56 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
         }
     }
 }
+
+#[derive(Default)]
+pub struct InterningDefinedSymbol {
+    // Maps the symbol value to the constant name.
+    symbol_map: FxHashMap<u32, String>,
+}
+
+impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL]);
+
+impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
+    fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
+        if !self.symbol_map.is_empty() {
+            return;
+        }
+
+        if let Some(Res::Def(_, def_id)) = path_to_res(cx, &paths::SYM_MODULE) {
+            for item in cx.tcx.item_children(def_id).iter() {
+                if_chain! {
+                    if let Res::Def(DefKind::Const, item_def_id) = item.res;
+                    let ty = cx.tcx.type_of(item_def_id);
+                    if match_type(cx, ty, &paths::SYMBOL);
+                    if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id);
+                    if let Ok(value) = value.to_u32();
+                    then {
+                        self.symbol_map.insert(value, item.ident.to_string());
+                    }
+                }
+            }
+        }
+    }
+
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if_chain! {
+            if let ExprKind::Call(func, [arg]) = &expr.kind;
+            if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind();
+            if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN);
+            if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg);
+            let value = Symbol::intern(&arg).as_u32();
+            if let Some(symbol_const) = self.symbol_map.get(&value);
+            then {
+                span_lint_and_sugg(
+                    cx,
+                    INTERNING_DEFINED_SYMBOL,
+                    is_expn_of(expr.span, "sym").unwrap_or(expr.span),
+                    "interning a defined symbol",
+                    "try",
+                    format!("rustc_span::symbol::sym::{}", symbol_const),
+                    Applicability::MachineApplicable,
+                );
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs
index 0deaee3a944..424856090f2 100644
--- a/src/tools/clippy/clippy_lints/src/utils/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs
@@ -41,7 +41,7 @@ use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
-use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
 use rustc_hir::Node;
 use rustc_hir::{
     def, Arm, Block, Body, Constness, Crate, Expr, ExprKind, FnDecl, HirId, ImplItem, ImplItemKind, Item, ItemKind,
@@ -603,6 +603,37 @@ pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
     visitor.found
 }
 
+struct FindMacroCalls<'a, 'b> {
+    names: &'a [&'b str],
+    result: Vec<Span>,
+}
+
+impl<'a, 'b, 'tcx> Visitor<'tcx> for FindMacroCalls<'a, 'b> {
+    type Map = Map<'tcx>;
+
+    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+        if self.names.iter().any(|fun| is_expn_of(expr.span, fun).is_some()) {
+            self.result.push(expr.span);
+        }
+        // and check sub-expressions
+        intravisit::walk_expr(self, expr);
+    }
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
+}
+
+/// Finds calls of the specified macros in a function body.
+pub fn find_macro_calls(names: &[&str], body: &Body<'_>) -> Vec<Span> {
+    let mut fmc = FindMacroCalls {
+        names,
+        result: Vec::new(),
+    };
+    fmc.visit_expr(&body.value);
+    fmc.result
+}
+
 /// Converts a span to a code snippet if available, otherwise use default.
 ///
 /// This is useful if you want to provide suggestions for your lint or more generally, if you want
@@ -1500,7 +1531,7 @@ pub fn is_no_std_crate(krate: &Crate<'_>) -> bool {
 /// ```
 pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
     if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
-        matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. })
+        matches!(item.kind, ItemKind::Impl { of_trait: Some(_), .. })
     } else {
         false
     }
diff --git a/src/tools/clippy/clippy_lints/src/utils/paths.rs b/src/tools/clippy/clippy_lints/src/utils/paths.rs
index 6fdc7b4587f..2080a49a11c 100644
--- a/src/tools/clippy/clippy_lints/src/utils/paths.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/paths.rs
@@ -146,6 +146,12 @@ pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
 pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
 pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
 #[cfg(feature = "internal-lints")]
+pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"];
+#[cfg(feature = "internal-lints")]
+pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"];
+#[cfg(feature = "internal-lints")]
+pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
+#[cfg(feature = "internal-lints")]
 pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
 pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"];
 pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
diff --git a/src/tools/clippy/clippy_lints/src/utils/usage.rs b/src/tools/clippy/clippy_lints/src/utils/usage.rs
index a7d0ea6ccfb..fc0db7f64ec 100644
--- a/src/tools/clippy/clippy_lints/src/utils/usage.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/usage.rs
@@ -116,20 +116,27 @@ pub struct ParamBindingIdCollector {
 }
 impl<'tcx> ParamBindingIdCollector {
     fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec<hir::HirId> {
-        let mut finder = ParamBindingIdCollector {
-            binding_hir_ids: Vec::new(),
-        };
-        finder.visit_body(body);
-        finder.binding_hir_ids
+        let mut hir_ids: Vec<hir::HirId> = Vec::new();
+        for param in body.params.iter() {
+            let mut finder = ParamBindingIdCollector {
+                binding_hir_ids: Vec::new(),
+            };
+            finder.visit_param(param);
+            for hir_id in &finder.binding_hir_ids {
+                hir_ids.push(*hir_id);
+            }
+        }
+        hir_ids
     }
 }
 impl<'tcx> intravisit::Visitor<'tcx> for ParamBindingIdCollector {
     type Map = Map<'tcx>;
 
-    fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
-        if let hir::PatKind::Binding(_, hir_id, ..) = param.pat.kind {
+    fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) {
+        if let hir::PatKind::Binding(_, hir_id, ..) = pat.kind {
             self.binding_hir_ids.push(hir_id);
         }
+        intravisit::walk_pat(self, pat);
     }
 
     fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index ff414f748ef..337f7a229b9 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -76,6 +76,24 @@ declare_clippy_lint! {
 }
 
 declare_clippy_lint! {
+    /// **What it does:** Checks for printing on *stderr*. The purpose of this lint
+    /// is to catch debugging remnants.
+    ///
+    /// **Why is this bad?** People often print on *stderr* while debugging an
+    /// application and might forget to remove those prints afterward.
+    ///
+    /// **Known problems:** Only catches `eprint!` and `eprintln!` calls.
+    ///
+    /// **Example:**
+    /// ```rust
+    /// eprintln!("Hello world!");
+    /// ```
+    pub PRINT_STDERR,
+    restriction,
+    "printing on stderr"
+}
+
+declare_clippy_lint! {
     /// **What it does:** Checks for use of `Debug` formatting. The purpose of this
     /// lint is to catch debugging remnants.
     ///
@@ -201,6 +219,7 @@ impl_lint_pass!(Write => [
     PRINT_WITH_NEWLINE,
     PRINTLN_EMPTY_STRING,
     PRINT_STDOUT,
+    PRINT_STDERR,
     USE_DEBUG,
     PRINT_LITERAL,
     WRITE_WITH_NEWLINE,
@@ -243,47 +262,22 @@ impl EarlyLintPass for Write {
                 .map_or(false, |crate_name| crate_name == "build_script_build")
         }
 
-        if mac.path == sym!(println) {
-            if !is_build_script(cx) {
-                span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`");
-            }
-            if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
-                if fmt_str.symbol == Symbol::intern("") {
-                    span_lint_and_sugg(
-                        cx,
-                        PRINTLN_EMPTY_STRING,
-                        mac.span(),
-                        "using `println!(\"\")`",
-                        "replace it with",
-                        "println!()".to_string(),
-                        Applicability::MachineApplicable,
-                    );
-                }
-            }
-        } else if mac.path == sym!(print) {
+        if mac.path == sym!(print) {
             if !is_build_script(cx) {
                 span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`");
             }
-            if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
-                if check_newlines(&fmt_str) {
-                    span_lint_and_then(
-                        cx,
-                        PRINT_WITH_NEWLINE,
-                        mac.span(),
-                        "using `print!()` with a format string that ends in a single newline",
-                        |err| {
-                            err.multipart_suggestion(
-                                "use `println!` instead",
-                                vec![
-                                    (mac.path.span, String::from("println")),
-                                    (newline_span(&fmt_str), String::new()),
-                                ],
-                                Applicability::MachineApplicable,
-                            );
-                        },
-                    );
-                }
+            self.lint_print_with_newline(cx, mac);
+        } else if mac.path == sym!(println) {
+            if !is_build_script(cx) {
+                span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`");
             }
+            self.lint_println_empty_string(cx, mac);
+        } else if mac.path == sym!(eprint) {
+            span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprint!`");
+            self.lint_print_with_newline(cx, mac);
+        } else if mac.path == sym!(eprintln) {
+            span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`");
+            self.lint_println_empty_string(cx, mac);
         } else if mac.path == sym!(write) {
             if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), true) {
                 if check_newlines(&fmt_str) {
@@ -487,6 +481,45 @@ impl Write {
             }
         }
     }
+
+    fn lint_println_empty_string(&self, cx: &EarlyContext<'_>, mac: &MacCall) {
+        if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
+            if fmt_str.symbol == Symbol::intern("") {
+                let name = mac.path.segments[0].ident.name;
+                span_lint_and_sugg(
+                    cx,
+                    PRINTLN_EMPTY_STRING,
+                    mac.span(),
+                    &format!("using `{}!(\"\")`", name),
+                    "replace it with",
+                    format!("{}!()", name),
+                    Applicability::MachineApplicable,
+                );
+            }
+        }
+    }
+
+    fn lint_print_with_newline(&self, cx: &EarlyContext<'_>, mac: &MacCall) {
+        if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
+            if check_newlines(&fmt_str) {
+                let name = mac.path.segments[0].ident.name;
+                let suggested = format!("{}ln", name);
+                span_lint_and_then(
+                    cx,
+                    PRINT_WITH_NEWLINE,
+                    mac.span(),
+                    &format!("using `{}!()` with a format string that ends in a single newline", name),
+                    |err| {
+                        err.multipart_suggestion(
+                            &format!("use `{}!` instead", suggested),
+                            vec![(mac.path.span, suggested), (newline_span(&fmt_str), String::new())],
+                            Applicability::MachineApplicable,
+                        );
+                    },
+                );
+            }
+        }
+    }
 }
 
 /// Checks if the format string contains a single newline that terminates it.
diff --git a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs
new file mode 100644
index 00000000000..1d5fa8d06a9
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs
@@ -0,0 +1,82 @@
+use if_chain::if_chain;
+use rustc_hir::{self as hir, HirId, ItemKind, Node};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::{Adt, Ty};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_target::abi::LayoutOf as _;
+use rustc_typeck::hir_ty_to_ty;
+
+use crate::utils::{is_type_diagnostic_item, match_type, paths, span_lint_and_help};
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for maps with zero-sized value types anywhere in the code.
+    ///
+    /// **Why is this bad?** Since there is only a single value for a zero-sized type, a map
+    /// containing zero sized values is effectively a set. Using a set in that case improves
+    /// readability and communicates intent more clearly.
+    ///
+    /// **Known problems:**
+    /// * A zero-sized type cannot be recovered later if it contains private fields.
+    /// * This lints the signature of public items
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// # use std::collections::HashMap;
+    /// fn unique_words(text: &str) -> HashMap<&str, ()> {
+    ///     todo!();
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # use std::collections::HashSet;
+    /// fn unique_words(text: &str) -> HashSet<&str> {
+    ///     todo!();
+    /// }
+    /// ```
+    pub ZERO_SIZED_MAP_VALUES,
+    pedantic,
+    "usage of map with zero-sized value type"
+}
+
+declare_lint_pass!(ZeroSizedMapValues => [ZERO_SIZED_MAP_VALUES]);
+
+impl LateLintPass<'_> for ZeroSizedMapValues {
+    fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
+        if_chain! {
+            if !hir_ty.span.from_expansion();
+            if !in_trait_impl(cx, hir_ty.hir_id);
+            let ty = ty_from_hir_ty(cx, hir_ty);
+            if is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) || match_type(cx, ty, &paths::BTREEMAP);
+            if let Adt(_, ref substs) = ty.kind();
+            let ty = substs.type_at(1);
+            if let Ok(layout) = cx.layout_of(ty);
+            if layout.is_zst();
+            then {
+                span_lint_and_help(cx, ZERO_SIZED_MAP_VALUES, hir_ty.span, "map with zero-sized value type", None, "consider using a set instead");
+            }
+        }
+    }
+}
+
+fn in_trait_impl(cx: &LateContext<'_>, hir_id: HirId) -> bool {
+    let parent_id = cx.tcx.hir().get_parent_item(hir_id);
+    if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_item(parent_id)) {
+        if let ItemKind::Impl { of_trait: Some(_), .. } = item.kind {
+            return true;
+        }
+    }
+    false
+}
+
+fn ty_from_hir_ty<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> {
+    cx.maybe_typeck_results()
+        .and_then(|results| {
+            if results.hir_owner == hir_ty.hir_id.owner {
+                results.node_type_opt(hir_ty.hir_id)
+            } else {
+                None
+            }
+        })
+        .unwrap_or_else(|| hir_ty_to_ty(cx.tcx, hir_ty))
+}
diff --git a/src/tools/clippy/clippy_workspace_tests/path_dep/Cargo.toml b/src/tools/clippy/clippy_workspace_tests/path_dep/Cargo.toml
new file mode 100644
index 00000000000..85a91cd2dec
--- /dev/null
+++ b/src/tools/clippy/clippy_workspace_tests/path_dep/Cargo.toml
@@ -0,0 +1,3 @@
+[package]
+name = "path_dep"
+version = "0.1.0"
diff --git a/src/tools/clippy/clippy_workspace_tests/path_dep/src/lib.rs b/src/tools/clippy/clippy_workspace_tests/path_dep/src/lib.rs
new file mode 100644
index 00000000000..35ce524f2b1
--- /dev/null
+++ b/src/tools/clippy/clippy_workspace_tests/path_dep/src/lib.rs
@@ -0,0 +1,6 @@
+#![deny(clippy::empty_loop)]
+
+#[cfg(feature = "primary_package_test")]
+pub fn lint_me() {
+    loop {}
+}
diff --git a/src/tools/clippy/clippy_workspace_tests/subcrate/Cargo.toml b/src/tools/clippy/clippy_workspace_tests/subcrate/Cargo.toml
index 83ea5868160..45362c11b85 100644
--- a/src/tools/clippy/clippy_workspace_tests/subcrate/Cargo.toml
+++ b/src/tools/clippy/clippy_workspace_tests/subcrate/Cargo.toml
@@ -1,3 +1,6 @@
 [package]
 name = "subcrate"
 version = "0.1.0"
+
+[dependencies]
+path_dep = { path = "../path_dep" }
diff --git a/src/tools/clippy/doc/adding_lints.md b/src/tools/clippy/doc/adding_lints.md
index b1dacfc9c6d..60dfdb76650 100644
--- a/src/tools/clippy/doc/adding_lints.md
+++ b/src/tools/clippy/doc/adding_lints.md
@@ -98,12 +98,12 @@ While we are working on implementing our lint, we can keep running the UI
 test. That allows us to check if the output is turning into what we want.
 
 Once we are satisfied with the output, we need to run
-`tests/ui/update-all-references.sh` to update the `.stderr` file for our lint.
+`cargo dev bless` to update the `.stderr` file for our lint.
 Please note that, we should run `TESTNAME=foo_functions cargo uitest`
-every time before running `tests/ui/update-all-references.sh`.
+every time before running `cargo dev bless`.
 Running `TESTNAME=foo_functions cargo uitest` should pass then. When we commit
 our lint, we need to commit the generated `.stderr` files, too. In general, you
-should only commit files changed by `tests/ui/update-all-references.sh` for the
+should only commit files changed by `cargo dev bless` for the
 specific lint you are creating/editing. Note that if the generated files are
 empty, they should be removed.
 
@@ -122,8 +122,7 @@ we will find by default two new crates, each with its manifest file:
 If you need more cases, you can copy one of those crates (under `foo_categories`) and rename it.
 
 The process of generating the `.stderr` file is the same, and prepending the `TESTNAME`
-variable to `cargo uitest` works too, but the script to update the references
-is in another path: `tests/ui-cargo/update-all-references.sh`.
+variable to `cargo uitest` works too.
 
 ## Rustfix tests
 
@@ -133,7 +132,7 @@ additionally run [rustfix] for that test. Rustfix will apply the suggestions
 from the lint to the code of the test file and compare that to the contents of
 a `.fixed` file.
 
-Use `tests/ui/update-all-references.sh` to automatically generate the
+Use `cargo dev bless` to automatically generate the
 `.fixed` file after running the tests.
 
 [rustfix]: https://github.com/rust-lang/rustfix
@@ -226,13 +225,13 @@ store.register_early_pass(|| box foo_functions::FooFunctions);
 ```
 
 As one may expect, there is a corresponding `register_late_pass` method
-available as well. Without a call to one of `register_early_pass` or 
+available as well. Without a call to one of `register_early_pass` or
 `register_late_pass`, the lint pass in question will not be run.
 
-One reason that `cargo dev` does not automate this step is that multiple lints 
+One reason that `cargo dev` does not automate this step is that multiple lints
 can use the same lint pass, so registering the lint pass may already be done
 when adding a new lint. Another reason that this step is not automated is that
-the order that the passes are registered determines the order the passes 
+the order that the passes are registered determines the order the passes
 actually run, which in turn affects the order that any emitted lints are output
 in.
 
@@ -368,7 +367,7 @@ fn is_foo_fn(fn_kind: FnKind<'_>) -> bool {
 
 Now we should also run the full test suite with `cargo test`. At this point
 running `cargo test` should produce the expected output. Remember to run
-`tests/ui/update-all-references.sh` to update the `.stderr` file.
+`cargo dev bless` to update the `.stderr` file.
 
 `cargo test` (as opposed to `cargo uitest`) will also ensure that our lint
 implementation is not violating any Clippy lints itself.
@@ -380,6 +379,57 @@ pass.
 [`FnKind::Fn`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html#variant.Fn
 [ident]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Ident.html
 
+## Specifying the lint's minimum supported Rust version (msrv)
+
+Projects supporting older versions of Rust would need to disable a lint if it targets features
+present in later versions. Support for this can be added by specifying an msrv in your lint like so,
+
+```rust
+const MANUAL_STRIP_MSRV: RustcVersion = RustcVersion::new(1, 45, 0);
+```
+
+The project's msrv will also have to be an attribute in the lint so you'll have to add a struct
+and constructor for your lint. The project's msrv needs to be passed when the lint is registered
+in `lib.rs`
+
+```rust
+pub struct ManualStrip {
+    msrv: Option<RustcVersion>,
+}
+
+impl ManualStrip {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        Self { msrv }
+    }
+}
+```
+
+The project's msrv can then be matched against the lint's msrv in the LintPass using the `meets_msrv` utility
+function.
+
+``` rust
+if !meets_msrv(self.msrv.as_ref(), &MANUAL_STRIP_MSRV) {
+    return;
+}
+```
+
+The project's msrv can also be specified as an inner attribute, which overrides the value from
+`clippy.toml`. This can be accounted for using the `extract_msrv_attr!(LintContext)` macro and passing
+LateContext/EarlyContext.
+
+```rust
+impl<'tcx> LateLintPass<'tcx> for ManualStrip {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        ...
+    }
+    extract_msrv_attr!(LateContext);
+}
+```
+
+Once the msrv is added to the lint, a relevant test case should be added to `tests/ui/min_rust_version_attr.rs`
+which verifies that the lint isn't emitted if the project's msrv is lower.
+
 ## Author lint
 
 If you have trouble implementing your lint, there is also the internal `author`
diff --git a/src/tools/clippy/doc/basics.md b/src/tools/clippy/doc/basics.md
index f25edb793e2..954474a17aa 100644
--- a/src/tools/clippy/doc/basics.md
+++ b/src/tools/clippy/doc/basics.md
@@ -1,16 +1,14 @@
 # Basics for hacking on Clippy
 
 This document explains the basics for hacking on Clippy. Besides others, this
-includes how to set-up the development environment, how to build and how to test
-Clippy. For a more in depth description on the codebase take a look at [Adding
-Lints] or [Common Tools].
+includes how to build and test Clippy. For a more in depth description on
+the codebase take a look at [Adding Lints] or [Common Tools].
 
 [Adding Lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
 [Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md
 
 - [Basics for hacking on Clippy](#basics-for-hacking-on-clippy)
-  - [Get the code](#get-the-code)
-  - [Setup](#setup)
+  - [Get the Code](#get-the-code)
   - [Building and Testing](#building-and-testing)
   - [`cargo dev`](#cargo-dev)
   - [PR](#pr)
@@ -38,29 +36,9 @@ git rebase upstream/master
 git push
 ```
 
-## Setup
-
-Next we need to setup the toolchain to compile Clippy. Since Clippy heavily
-relies on compiler internals it is build with the latest rustc master. To get
-this toolchain, you can just use the `setup-toolchain.sh` script or use
-`rustup-toolchain-install-master`:
-
-```bash
-bash setup-toolchain.sh
-# OR
-cargo install rustup-toolchain-install-master
-# For better IDE integration also add `-c rustfmt -c rust-src` (optional)
-rustup-toolchain-install-master -f -n master -c rustc-dev -c llvm-tools
-rustup override set master
-```
-
-_Note:_ Sometimes you may get compiler errors when building Clippy, even if you
-didn't change anything. Normally those will be fixed by a maintainer in a few hours. 
-
 ## Building and Testing
 
-Once the `master` toolchain is installed, you can build and test Clippy like
-every other Rust project:
+You can build and test Clippy like every other Rust project:
 
 ```bash
 cargo build  # builds Clippy
@@ -83,7 +61,7 @@ If the output of a [UI test] differs from the expected output, you can update th
 reference file with:
 
 ```bash
-sh tests/ui/update-all-references.sh
+cargo dev bless
 ```
 
 For example, this is necessary, if you fix a typo in an error message of a lint
@@ -109,7 +87,7 @@ cargo dev update_lints
 # create a new lint and register it
 cargo dev new_lint
 # (experimental) Setup Clippy to work with rust-analyzer
-cargo dev ra-setup
+cargo dev ra_setup
 ```
 
 ## PR
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index bf867e0ae5b..d2e84132f4e 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1 +1,3 @@
-nightly
+[toolchain]
+channel = "nightly-2020-12-20"
+components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"]
diff --git a/src/tools/clippy/setup-toolchain.sh b/src/tools/clippy/setup-toolchain.sh
deleted file mode 100755
index 191ea4315a6..00000000000
--- a/src/tools/clippy/setup-toolchain.sh
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/env bash
-# Set up the appropriate rustc toolchain
-
-set -e
-
-cd "$(dirname "$0")"
-
-RTIM_PATH=$(command -v rustup-toolchain-install-master) || INSTALLED=false
-CARGO_HOME=${CARGO_HOME:-$HOME/.cargo}
-
-# Check if RTIM is not installed or installed in other locations not in ~/.cargo/bin
-if [[ "$INSTALLED" == false || "$RTIM_PATH" == $CARGO_HOME/bin/rustup-toolchain-install-master ]]; then
-    cargo +nightly install rustup-toolchain-install-master
-else
-    VERSION=$(rustup-toolchain-install-master -V | grep -o "[0-9.]*")
-    REMOTE=$(cargo +nightly search rustup-toolchain-install-master | grep -o "[0-9.]*")
-    echo "info: skipping updating rustup-toolchain-install-master at $RTIM_PATH"
-    echo "      current version : $VERSION"
-    echo "      remote version  : $REMOTE"
-fi
-
-RUST_COMMIT=$(git ls-remote https://github.com/rust-lang/rust master | awk '{print $1}')
-
-if rustc +master -Vv 2>/dev/null | grep -q "$RUST_COMMIT"; then
-    echo "info: master toolchain is up-to-date"
-    exit 0
-fi
-
-if [[ -n "$HOST_TOOLCHAIN" ]]; then
-    TOOLCHAIN=('--host' "$HOST_TOOLCHAIN")
-else
-    TOOLCHAIN=()
-fi
-
-rustup-toolchain-install-master -f -n master "${TOOLCHAIN[@]}" -c rustc-dev -c llvm-tools -- "$RUST_COMMIT"
-rustup override set master
diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs
index 87dd19c5d4d..40f1b802e60 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -1,5 +1,6 @@
 #![feature(rustc_private)]
 #![feature(once_cell)]
+#![feature(bool_to_option)]
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
 // warn on lints, that are included in `rust-lang/rust`s bootstrap
 #![warn(rust_2018_idioms, unused_lifetimes)]
@@ -19,6 +20,7 @@ use rustc_tools_util::VersionInfo;
 
 use std::borrow::Cow;
 use std::env;
+use std::iter;
 use std::lazy::SyncLazy;
 use std::ops::Deref;
 use std::panic;
@@ -41,26 +43,12 @@ fn arg_value<'a, T: Deref<Target = str>>(
 
         match arg.next().or_else(|| args.next()) {
             Some(v) if pred(v) => return Some(v),
-            _ => {}
+            _ => {},
         }
     }
     None
 }
 
-#[test]
-fn test_arg_value() {
-    let args = &["--bar=bar", "--foobar", "123", "--foo"];
-
-    assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None);
-    assert_eq!(arg_value(args, "--bar", |_| false), None);
-    assert_eq!(arg_value(args, "--bar", |_| true), Some("bar"));
-    assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar"));
-    assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None);
-    assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None);
-    assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123"));
-    assert_eq!(arg_value(args, "--foo", |_| true), None);
-}
-
 struct DefaultCallbacks;
 impl rustc_driver::Callbacks for DefaultCallbacks {}
 
@@ -121,12 +109,11 @@ You can use tool lints to allow or deny lints from your code, eg.:
 
 const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new";
 
-static ICE_HOOK: SyncLazy<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> =
-    SyncLazy::new(|| {
-        let hook = panic::take_hook();
-        panic::set_hook(Box::new(|info| report_clippy_ice(info, BUG_REPORT_URL)));
-        hook
-    });
+static ICE_HOOK: SyncLazy<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> = SyncLazy::new(|| {
+    let hook = panic::take_hook();
+    panic::set_hook(Box::new(|info| report_clippy_ice(info, BUG_REPORT_URL)));
+    hook
+});
 
 fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
     // Invoke our ICE handler, which prints the actual panic message and optionally a backtrace
@@ -183,6 +170,29 @@ fn toolchain_path(home: Option<String>, toolchain: Option<String>) -> Option<Pat
     })
 }
 
+fn remove_clippy_args<'a, T, U, I>(args: &mut Vec<T>, clippy_args: I)
+where
+    T: AsRef<str>,
+    U: AsRef<str> + ?Sized + 'a,
+    I: Iterator<Item = &'a U> + Clone,
+{
+    let args_iter = clippy_args.map(AsRef::as_ref);
+    let args_count = args_iter.clone().count();
+
+    if args_count > 0 {
+        if let Some(start) = args.windows(args_count).enumerate().find_map(|(current, window)| {
+            window
+                .iter()
+                .map(AsRef::as_ref)
+                .eq(args_iter.clone())
+                .then_some(current)
+        }) {
+            args.drain(start..start + args_count);
+        }
+    }
+}
+
+#[allow(clippy::too_many_lines)]
 pub fn main() {
     rustc_driver::init_rustc_env_logger();
     SyncLazy::force(&ICE_HOOK);
@@ -258,17 +268,14 @@ pub fn main() {
 
         // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument.
         // We're invoking the compiler programmatically, so we ignore this/
-        let wrapper_mode =
-            orig_args.get(1).map(Path::new).and_then(Path::file_stem) == Some("rustc".as_ref());
+        let wrapper_mode = orig_args.get(1).map(Path::new).and_then(Path::file_stem) == Some("rustc".as_ref());
 
         if wrapper_mode {
             // we still want to be able to invoke it normally though
             orig_args.remove(1);
         }
 
-        if !wrapper_mode
-            && (orig_args.iter().any(|a| a == "--help" || a == "-h") || orig_args.len() == 1)
-        {
+        if !wrapper_mode && (orig_args.iter().any(|a| a == "--help" || a == "-h") || orig_args.len() == 1) {
             display_help();
             exit(0);
         }
@@ -281,25 +288,88 @@ pub fn main() {
             args.extend(vec!["--sysroot".into(), sys_root]);
         };
 
-        // this check ensures that dependencies are built but not linted and the final
-        // crate is linted but not built
-        let clippy_enabled = env::var("CLIPPY_TESTS").map_or(false, |val| val == "true")
-            || arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_none();
+        let clippy_args = env::var("CLIPPY_ARGS").unwrap_or_default();
+        let clippy_args = clippy_args.split_whitespace();
+        let no_deps = clippy_args.clone().any(|flag| flag == "--no-deps");
+
+        // We enable Clippy if one of the following conditions is met
+        // - IF Clippy is run on its test suite OR
+        // - IF Clippy is run on the main crate, not on deps (`!cap_lints_allow`) THEN
+        //    - IF `--no-deps` is not set (`!no_deps`) OR
+        //    - IF `--no-deps` is set and Clippy is run on the specified primary package
+        let clippy_tests_set = env::var("CLIPPY_TESTS").map_or(false, |val| val == "true");
+        let cap_lints_allow = arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_some();
+        let in_primary_package = env::var("CARGO_PRIMARY_PACKAGE").is_ok();
 
+        let clippy_enabled = clippy_tests_set || (!cap_lints_allow && (!no_deps || in_primary_package));
         if clippy_enabled {
+            remove_clippy_args(&mut args, iter::once("--no-deps"));
             args.extend(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]);
-            if let Ok(extra_args) = env::var("CLIPPY_ARGS") {
-                args.extend(
-                    extra_args
-                        .split("__CLIPPY_HACKERY__")
-                        .filter_map(|s| if s.is_empty() { None } else { Some(s.to_string()) }),
-                );
-            }
+        } else {
+            // Remove all flags passed through RUSTFLAGS if Clippy is not enabled.
+            remove_clippy_args(&mut args, clippy_args);
         }
+
         let mut clippy = ClippyCallbacks;
         let mut default = DefaultCallbacks;
         let callbacks: &mut (dyn rustc_driver::Callbacks + Send) =
             if clippy_enabled { &mut clippy } else { &mut default };
+
         rustc_driver::RunCompiler::new(&args, callbacks).run()
     }))
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_arg_value() {
+        let args = &["--bar=bar", "--foobar", "123", "--foo"];
+
+        assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None);
+        assert_eq!(arg_value(args, "--bar", |_| false), None);
+        assert_eq!(arg_value(args, "--bar", |_| true), Some("bar"));
+        assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar"));
+        assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None);
+        assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None);
+        assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123"));
+        assert_eq!(arg_value(args, "--foo", |_| true), None);
+    }
+
+    #[test]
+    fn removes_clippy_args_from_start() {
+        let mut args = vec!["-D", "clippy::await_holding_lock", "--cfg", r#"feature="some_feat""#];
+        let clippy_args = ["-D", "clippy::await_holding_lock"].iter();
+
+        remove_clippy_args(&mut args, clippy_args);
+        assert_eq!(args, &["--cfg", r#"feature="some_feat""#]);
+    }
+
+    #[test]
+    fn removes_clippy_args_from_end() {
+        let mut args = vec!["-Zui-testing", "-A", "clippy::empty_loop", "--no-deps"];
+        let clippy_args = ["-A", "clippy::empty_loop", "--no-deps"].iter();
+
+        remove_clippy_args(&mut args, clippy_args);
+        assert_eq!(args, &["-Zui-testing"]);
+    }
+
+    #[test]
+    fn removes_clippy_args_from_middle() {
+        let mut args = vec!["-Zui-testing", "-W", "clippy::filter_map", "-L", "serde"];
+        let clippy_args = ["-W", "clippy::filter_map"].iter();
+
+        remove_clippy_args(&mut args, clippy_args);
+        assert_eq!(args, &["-Zui-testing", "-L", "serde"]);
+    }
+
+    #[test]
+    fn no_clippy_args_to_remove() {
+        let mut args = vec!["-Zui-testing", "-L", "serde"];
+        let clippy_args: [&str; 0] = [];
+
+        remove_clippy_args(&mut args, clippy_args.iter());
+        assert_eq!(args, &["-Zui-testing", "-L", "serde"]);
+    }
+}
diff --git a/src/tools/clippy/src/main.rs b/src/tools/clippy/src/main.rs
index 6739a4cf224..1c0e04689a9 100644
--- a/src/tools/clippy/src/main.rs
+++ b/src/tools/clippy/src/main.rs
@@ -1,3 +1,5 @@
+#![feature(bool_to_option)]
+#![feature(command_access)]
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
 // warn on lints, that are included in `rust-lang/rust`s bootstrap
 #![warn(rust_2018_idioms, unused_lifetimes)]
@@ -62,7 +64,7 @@ struct ClippyCmd {
     unstable_options: bool,
     cargo_subcommand: &'static str,
     args: Vec<String>,
-    clippy_args: String,
+    clippy_args: Option<String>,
 }
 
 impl ClippyCmd {
@@ -99,13 +101,17 @@ impl ClippyCmd {
             args.insert(0, "+nightly".to_string());
         }
 
-        let clippy_args: String = old_args.map(|arg| format!("{}__CLIPPY_HACKERY__", arg)).collect();
+        let mut clippy_args = old_args.collect::<Vec<String>>().join(" ");
+        if cargo_subcommand == "fix" && !clippy_args.contains("--no-deps") {
+            clippy_args = format!("{} --no-deps", clippy_args);
+        }
 
+        let has_args = !clippy_args.is_empty();
         ClippyCmd {
             unstable_options,
             cargo_subcommand,
             args,
-            clippy_args,
+            clippy_args: has_args.then_some(clippy_args),
         }
     }
 
@@ -145,15 +151,24 @@ impl ClippyCmd {
             .map(|p| ("CARGO_TARGET_DIR", p))
     }
 
-    fn into_std_cmd(self) -> Command {
+    fn into_std_cmd(self, rustflags: Option<String>) -> Command {
         let mut cmd = Command::new("cargo");
 
         cmd.env(self.path_env(), Self::path())
             .envs(ClippyCmd::target_dir())
-            .env("CLIPPY_ARGS", self.clippy_args)
             .arg(self.cargo_subcommand)
             .args(&self.args);
 
+        // HACK: pass Clippy args to the driver *also* through RUSTFLAGS.
+        // This guarantees that new builds will be triggered when Clippy flags change.
+        if let Some(clippy_args) = self.clippy_args {
+            cmd.env(
+                "RUSTFLAGS",
+                rustflags.map_or(clippy_args.clone(), |flags| format!("{} {}", clippy_args, flags)),
+            );
+            cmd.env("CLIPPY_ARGS", clippy_args);
+        }
+
         cmd
     }
 }
@@ -164,7 +179,7 @@ where
 {
     let cmd = ClippyCmd::new(old_args);
 
-    let mut cmd = cmd.into_std_cmd();
+    let mut cmd = cmd.into_std_cmd(env::var("RUSTFLAGS").ok());
 
     let exit_status = cmd
         .spawn()
@@ -182,6 +197,7 @@ where
 #[cfg(test)]
 mod tests {
     use super::ClippyCmd;
+    use std::ffi::OsStr;
 
     #[test]
     #[should_panic]
@@ -196,15 +212,37 @@ mod tests {
             .split_whitespace()
             .map(ToString::to_string);
         let cmd = ClippyCmd::new(args);
+
         assert_eq!("fix", cmd.cargo_subcommand);
         assert_eq!("RUSTC_WORKSPACE_WRAPPER", cmd.path_env());
         assert!(cmd.args.iter().any(|arg| arg.ends_with("unstable-options")));
     }
 
     #[test]
+    fn fix_implies_no_deps() {
+        let args = "cargo clippy --fix -Zunstable-options"
+            .split_whitespace()
+            .map(ToString::to_string);
+        let cmd = ClippyCmd::new(args);
+
+        assert!(cmd.clippy_args.unwrap().contains("--no-deps"));
+    }
+
+    #[test]
+    fn no_deps_not_duplicated_with_fix() {
+        let args = "cargo clippy --fix -Zunstable-options -- --no-deps"
+            .split_whitespace()
+            .map(ToString::to_string);
+        let cmd = ClippyCmd::new(args);
+
+        assert_eq!(1, cmd.clippy_args.unwrap().matches("--no-deps").count());
+    }
+
+    #[test]
     fn check() {
         let args = "cargo clippy".split_whitespace().map(ToString::to_string);
         let cmd = ClippyCmd::new(args);
+
         assert_eq!("check", cmd.cargo_subcommand);
         assert_eq!("RUSTC_WRAPPER", cmd.path_env());
     }
@@ -215,7 +253,63 @@ mod tests {
             .split_whitespace()
             .map(ToString::to_string);
         let cmd = ClippyCmd::new(args);
+
         assert_eq!("check", cmd.cargo_subcommand);
         assert_eq!("RUSTC_WORKSPACE_WRAPPER", cmd.path_env());
     }
+
+    #[test]
+    fn clippy_args_into_rustflags() {
+        let args = "cargo clippy -- -W clippy::as_conversions"
+            .split_whitespace()
+            .map(ToString::to_string);
+        let cmd = ClippyCmd::new(args);
+
+        let rustflags = None;
+        let cmd = cmd.into_std_cmd(rustflags);
+
+        assert!(cmd
+            .get_envs()
+            .any(|(key, val)| key == "RUSTFLAGS" && val == Some(OsStr::new("-W clippy::as_conversions"))));
+    }
+
+    #[test]
+    fn clippy_args_respect_existing_rustflags() {
+        let args = "cargo clippy -- -D clippy::await_holding_lock"
+            .split_whitespace()
+            .map(ToString::to_string);
+        let cmd = ClippyCmd::new(args);
+
+        let rustflags = Some(r#"--cfg feature="some_feat""#.into());
+        let cmd = cmd.into_std_cmd(rustflags);
+
+        assert!(cmd.get_envs().any(|(key, val)| key == "RUSTFLAGS"
+            && val == Some(OsStr::new(r#"-D clippy::await_holding_lock --cfg feature="some_feat""#))));
+    }
+
+    #[test]
+    fn no_env_change_if_no_clippy_args() {
+        let args = "cargo clippy".split_whitespace().map(ToString::to_string);
+        let cmd = ClippyCmd::new(args);
+
+        let rustflags = Some(r#"--cfg feature="some_feat""#.into());
+        let cmd = cmd.into_std_cmd(rustflags);
+
+        assert!(!cmd
+            .get_envs()
+            .any(|(key, _)| key == "RUSTFLAGS" || key == "CLIPPY_ARGS"));
+    }
+
+    #[test]
+    fn no_env_change_if_no_clippy_args_nor_rustflags() {
+        let args = "cargo clippy".split_whitespace().map(ToString::to_string);
+        let cmd = ClippyCmd::new(args);
+
+        let rustflags = None;
+        let cmd = cmd.into_std_cmd(rustflags);
+
+        assert!(!cmd
+            .get_envs()
+            .any(|(key, _)| key == "RUSTFLAGS" || key == "CLIPPY_ARGS"))
+    }
 }
diff --git a/src/tools/clippy/tests/dogfood.rs b/src/tools/clippy/tests/dogfood.rs
index a6163a83d76..fda1413868e 100644
--- a/src/tools/clippy/tests/dogfood.rs
+++ b/src/tools/clippy/tests/dogfood.rs
@@ -3,7 +3,7 @@
 #![feature(once_cell)]
 
 use std::lazy::SyncLazy;
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
 use std::process::Command;
 
 mod cargo;
@@ -23,7 +23,7 @@ fn dogfood_clippy() {
         .current_dir(root_dir)
         .env("CLIPPY_DOGFOOD", "1")
         .env("CARGO_INCREMENTAL", "0")
-        .arg("clippy-preview")
+        .arg("clippy")
         .arg("--all-targets")
         .arg("--all-features")
         .arg("--")
@@ -47,12 +47,77 @@ fn dogfood_clippy() {
 
 #[test]
 fn dogfood_subprojects() {
+    fn test_no_deps_ignores_path_deps_in_workspaces() {
+        fn clean(cwd: &Path, target_dir: &Path) {
+            Command::new("cargo")
+                .current_dir(cwd)
+                .env("CARGO_TARGET_DIR", target_dir)
+                .arg("clean")
+                .args(&["-p", "subcrate"])
+                .args(&["-p", "path_dep"])
+                .output()
+                .unwrap();
+        }
+
+        if cargo::is_rustc_test_suite() {
+            return;
+        }
+        let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
+        let target_dir = root.join("target").join("dogfood");
+        let cwd = root.join("clippy_workspace_tests");
+
+        // Make sure we start with a clean state
+        clean(&cwd, &target_dir);
+
+        // `path_dep` is a path dependency of `subcrate` that would trigger a denied lint.
+        // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`.
+        let output = Command::new(&*CLIPPY_PATH)
+            .current_dir(&cwd)
+            .env("CLIPPY_DOGFOOD", "1")
+            .env("CARGO_INCREMENTAL", "0")
+            .arg("clippy")
+            .args(&["-p", "subcrate"])
+            .arg("--")
+            .arg("--no-deps")
+            .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
+            .args(&["--cfg", r#"feature="primary_package_test""#])
+            .output()
+            .unwrap();
+        println!("status: {}", output.status);
+        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
+        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
+
+        assert!(output.status.success());
+
+        // Make sure we start with a clean state
+        clean(&cwd, &target_dir);
+
+        // Test that without the `--no-deps` argument, `path_dep` is linted.
+        let output = Command::new(&*CLIPPY_PATH)
+            .current_dir(&cwd)
+            .env("CLIPPY_DOGFOOD", "1")
+            .env("CARGO_INCREMENTAL", "0")
+            .arg("clippy")
+            .args(&["-p", "subcrate"])
+            .arg("--")
+            .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
+            .args(&["--cfg", r#"feature="primary_package_test""#])
+            .output()
+            .unwrap();
+        println!("status: {}", output.status);
+        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
+        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
+
+        assert!(!output.status.success());
+    }
+
     // run clippy on remaining subprojects and fail the test if lint warnings are reported
     if cargo::is_rustc_test_suite() {
         return;
     }
     let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
 
+    // NOTE: `path_dep` crate is omitted on purpose here
     for d in &[
         "clippy_workspace_tests",
         "clippy_workspace_tests/src",
@@ -78,4 +143,8 @@ fn dogfood_subprojects() {
 
         assert!(output.status.success());
     }
+
+    // NOTE: Since tests run in parallel we can't run cargo commands on the same workspace at the
+    // same time, so we test this immediately after the dogfood for workspaces.
+    test_no_deps_ignores_path_deps_in_workspaces();
 }
diff --git a/src/tools/clippy/tests/integration.rs b/src/tools/clippy/tests/integration.rs
index a78273ce0da..1718089e8bd 100644
--- a/src/tools/clippy/tests/integration.rs
+++ b/src/tools/clippy/tests/integration.rs
@@ -72,6 +72,8 @@ fn integration_test() {
         panic!("incompatible crate versions");
     } else if stderr.contains("failed to run `rustc` to learn about target-specific information") {
         panic!("couldn't find librustc_driver, consider setting `LD_LIBRARY_PATH`");
+    } else if stderr.contains("toolchain") && stderr.contains("is not installed") {
+        panic!("missing required toolchain");
     }
 
     match output.status.code() {
diff --git a/src/tools/clippy/tests/ui-cargo/update-all-references.sh b/src/tools/clippy/tests/ui-cargo/update-all-references.sh
index 7028b251ea0..4391499a1e1 100755
--- a/src/tools/clippy/tests/ui-cargo/update-all-references.sh
+++ b/src/tools/clippy/tests/ui-cargo/update-all-references.sh
@@ -1,18 +1,3 @@
 #!/bin/bash
-#
-# A script to update the references for all tests. The idea is that
-# you do a run, which will generate files in the build directory
-# containing the (normalized) actual output of the compiler. You then
-# run this script, which will copy those files over. If you find
-# yourself manually editing a foo.stderr file, you're doing it wrong.
-#
-# See all `update-references.sh`, if you just want to update a single test.
 
-if [[ "$1" == "--help" || "$1" == "-h" ]]; then
-    echo "usage: $0"
-fi
-
-BUILD_DIR=$PWD/target/debug/test_build_base
-MY_DIR=$(dirname "$0")
-cd "$MY_DIR" || exit
-find . -name '*.rs' -exec ./update-references.sh "$BUILD_DIR" {} +
+echo "Please use 'cargo dev bless' instead."
diff --git a/src/tools/clippy/tests/ui-cargo/update-references.sh b/src/tools/clippy/tests/ui-cargo/update-references.sh
deleted file mode 100755
index 2ab51168bca..00000000000
--- a/src/tools/clippy/tests/ui-cargo/update-references.sh
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/bash
-
-# A script to update the references for particular tests. The idea is
-# that you do a run, which will generate files in the build directory
-# containing the (normalized) actual output of the compiler. This
-# script will then copy that output and replace the "expected output"
-# files. You can then commit the changes.
-#
-# If you find yourself manually editing a foo.stderr file, you're
-# doing it wrong.
-
-if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then
-    echo "usage: $0 <build-directory> <relative-path-to-rs-files>"
-    echo ""
-    echo "For example:"
-    echo "   $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs"
-fi
-
-MYDIR=$(dirname "$0")
-
-BUILD_DIR="$1"
-shift
-
-while [[ "$1" != "" ]]; do
-    STDERR_NAME="${1/%.rs/.stderr}"
-    STDOUT_NAME="${1/%.rs/.stdout}"
-    shift
-    if [[ -f "$BUILD_DIR"/"$STDOUT_NAME" ]] && \
-           ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then
-        echo updating "$MYDIR"/"$STDOUT_NAME"
-        cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"
-        if [[ ! -s "$MYDIR"/"$STDOUT_NAME" ]]; then
-            echo removing "$MYDIR"/"$STDOUT_NAME"
-            rm "$MYDIR"/"$STDOUT_NAME"
-        fi
-    fi
-    if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \
-           ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then
-        echo updating "$MYDIR"/"$STDERR_NAME"
-        cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"
-        if [[ ! -s "$MYDIR"/"$STDERR_NAME" ]]; then
-            echo removing "$MYDIR"/"$STDERR_NAME"
-            rm "$MYDIR"/"$STDERR_NAME"
-        fi
-    fi
-done
diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed
new file mode 100644
index 00000000000..c6b84d2ef65
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed
@@ -0,0 +1,33 @@
+// run-rustfix
+#![deny(clippy::internal)]
+#![feature(rustc_private)]
+
+extern crate rustc_span;
+
+use rustc_span::symbol::Symbol;
+
+macro_rules! sym {
+    ($tt:tt) => {
+        rustc_span::symbol::Symbol::intern(stringify!($tt))
+    };
+}
+
+fn main() {
+    // Direct use of Symbol::intern
+    let _ = rustc_span::symbol::sym::f32;
+
+    // Using a sym macro
+    let _ = rustc_span::symbol::sym::f32;
+
+    // Correct suggestion when symbol isn't stringified constant name
+    let _ = rustc_span::symbol::sym::proc_dash_macro;
+
+    // Interning a symbol that is not defined
+    let _ = Symbol::intern("xyz123");
+    let _ = sym!(xyz123);
+
+    // Using a different `intern` function
+    let _ = intern("f32");
+}
+
+fn intern(_: &str) {}
diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs
new file mode 100644
index 00000000000..9ec82d4ad0b
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs
@@ -0,0 +1,33 @@
+// run-rustfix
+#![deny(clippy::internal)]
+#![feature(rustc_private)]
+
+extern crate rustc_span;
+
+use rustc_span::symbol::Symbol;
+
+macro_rules! sym {
+    ($tt:tt) => {
+        rustc_span::symbol::Symbol::intern(stringify!($tt))
+    };
+}
+
+fn main() {
+    // Direct use of Symbol::intern
+    let _ = Symbol::intern("f32");
+
+    // Using a sym macro
+    let _ = sym!(f32);
+
+    // Correct suggestion when symbol isn't stringified constant name
+    let _ = Symbol::intern("proc-macro");
+
+    // Interning a symbol that is not defined
+    let _ = Symbol::intern("xyz123");
+    let _ = sym!(xyz123);
+
+    // Using a different `intern` function
+    let _ = intern("f32");
+}
+
+fn intern(_: &str) {}
diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr
new file mode 100644
index 00000000000..74b906c8a57
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr
@@ -0,0 +1,27 @@
+error: interning a defined symbol
+  --> $DIR/interning_defined_symbol.rs:17:13
+   |
+LL |     let _ = Symbol::intern("f32");
+   |             ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::sym::f32`
+   |
+note: the lint level is defined here
+  --> $DIR/interning_defined_symbol.rs:2:9
+   |
+LL | #![deny(clippy::internal)]
+   |         ^^^^^^^^^^^^^^^^
+   = note: `#[deny(clippy::interning_defined_symbol)]` implied by `#[deny(clippy::internal)]`
+
+error: interning a defined symbol
+  --> $DIR/interning_defined_symbol.rs:20:13
+   |
+LL |     let _ = sym!(f32);
+   |             ^^^^^^^^^ help: try: `rustc_span::symbol::sym::f32`
+
+error: interning a defined symbol
+  --> $DIR/interning_defined_symbol.rs:23:13
+   |
+LL |     let _ = Symbol::intern("proc-macro");
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::sym::proc_dash_macro`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/update-all-references.sh b/src/tools/clippy/tests/ui-toml/update-all-references.sh
index 7028b251ea0..4391499a1e1 100755
--- a/src/tools/clippy/tests/ui-toml/update-all-references.sh
+++ b/src/tools/clippy/tests/ui-toml/update-all-references.sh
@@ -1,18 +1,3 @@
 #!/bin/bash
-#
-# A script to update the references for all tests. The idea is that
-# you do a run, which will generate files in the build directory
-# containing the (normalized) actual output of the compiler. You then
-# run this script, which will copy those files over. If you find
-# yourself manually editing a foo.stderr file, you're doing it wrong.
-#
-# See all `update-references.sh`, if you just want to update a single test.
 
-if [[ "$1" == "--help" || "$1" == "-h" ]]; then
-    echo "usage: $0"
-fi
-
-BUILD_DIR=$PWD/target/debug/test_build_base
-MY_DIR=$(dirname "$0")
-cd "$MY_DIR" || exit
-find . -name '*.rs' -exec ./update-references.sh "$BUILD_DIR" {} +
+echo "Please use 'cargo dev bless' instead."
diff --git a/src/tools/clippy/tests/ui-toml/update-references.sh b/src/tools/clippy/tests/ui-toml/update-references.sh
deleted file mode 100755
index 2ab51168bca..00000000000
--- a/src/tools/clippy/tests/ui-toml/update-references.sh
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/bash
-
-# A script to update the references for particular tests. The idea is
-# that you do a run, which will generate files in the build directory
-# containing the (normalized) actual output of the compiler. This
-# script will then copy that output and replace the "expected output"
-# files. You can then commit the changes.
-#
-# If you find yourself manually editing a foo.stderr file, you're
-# doing it wrong.
-
-if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then
-    echo "usage: $0 <build-directory> <relative-path-to-rs-files>"
-    echo ""
-    echo "For example:"
-    echo "   $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs"
-fi
-
-MYDIR=$(dirname "$0")
-
-BUILD_DIR="$1"
-shift
-
-while [[ "$1" != "" ]]; do
-    STDERR_NAME="${1/%.rs/.stderr}"
-    STDOUT_NAME="${1/%.rs/.stdout}"
-    shift
-    if [[ -f "$BUILD_DIR"/"$STDOUT_NAME" ]] && \
-           ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then
-        echo updating "$MYDIR"/"$STDOUT_NAME"
-        cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"
-        if [[ ! -s "$MYDIR"/"$STDOUT_NAME" ]]; then
-            echo removing "$MYDIR"/"$STDOUT_NAME"
-            rm "$MYDIR"/"$STDOUT_NAME"
-        fi
-    fi
-    if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \
-           ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then
-        echo updating "$MYDIR"/"$STDERR_NAME"
-        cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"
-        if [[ ! -s "$MYDIR"/"$STDERR_NAME" ]]; then
-            echo removing "$MYDIR"/"$STDERR_NAME"
-            rm "$MYDIR"/"$STDERR_NAME"
-        fi
-    fi
-done
diff --git a/src/tools/clippy/tests/ui/clone_on_copy.stderr b/src/tools/clippy/tests/ui/clone_on_copy.stderr
index ec2faf4ab40..14a700886a7 100644
--- a/src/tools/clippy/tests/ui/clone_on_copy.stderr
+++ b/src/tools/clippy/tests/ui/clone_on_copy.stderr
@@ -1,4 +1,4 @@
-error: using `clone` on a `Copy` type
+error: using `clone` on type `i32` which implements the `Copy` trait
   --> $DIR/clone_on_copy.rs:22:5
    |
 LL |     42.clone();
@@ -6,25 +6,25 @@ LL |     42.clone();
    |
    = note: `-D clippy::clone-on-copy` implied by `-D warnings`
 
-error: using `clone` on a `Copy` type
+error: using `clone` on type `i32` which implements the `Copy` trait
   --> $DIR/clone_on_copy.rs:26:5
    |
 LL |     (&42).clone();
    |     ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)`
 
-error: using `clone` on a `Copy` type
+error: using `clone` on type `i32` which implements the `Copy` trait
   --> $DIR/clone_on_copy.rs:29:5
    |
 LL |     rc.borrow().clone();
    |     ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()`
 
-error: using `clone` on a `Copy` type
+error: using `clone` on type `char` which implements the `Copy` trait
   --> $DIR/clone_on_copy.rs:35:14
    |
 LL |     is_ascii('z'.clone());
    |              ^^^^^^^^^^^ help: try removing the `clone` call: `'z'`
 
-error: using `clone` on a `Copy` type
+error: using `clone` on type `i32` which implements the `Copy` trait
   --> $DIR/clone_on_copy.rs:39:14
    |
 LL |     vec.push(42.clone());
diff --git a/src/tools/clippy/tests/ui/eprint_with_newline.rs b/src/tools/clippy/tests/ui/eprint_with_newline.rs
new file mode 100644
index 00000000000..8df32649ad9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/eprint_with_newline.rs
@@ -0,0 +1,49 @@
+#![allow(clippy::print_literal)]
+#![warn(clippy::print_with_newline)]
+
+fn main() {
+    eprint!("Hello\n");
+    eprint!("Hello {}\n", "world");
+    eprint!("Hello {} {}\n", "world", "#2");
+    eprint!("{}\n", 1265);
+    eprint!("\n");
+
+    // these are all fine
+    eprint!("");
+    eprint!("Hello");
+    eprintln!("Hello");
+    eprintln!("Hello\n");
+    eprintln!("Hello {}\n", "world");
+    eprint!("Issue\n{}", 1265);
+    eprint!("{}", 1265);
+    eprint!("\n{}", 1275);
+    eprint!("\n\n");
+    eprint!("like eof\n\n");
+    eprint!("Hello {} {}\n\n", "world", "#2");
+    eprintln!("\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126
+    eprintln!("\nbla\n\n"); // #3126
+
+    // Escaping
+    eprint!("\\n"); // #3514
+    eprint!("\\\n"); // should fail
+    eprint!("\\\\n");
+
+    // Raw strings
+    eprint!(r"\n"); // #3778
+
+    // Literal newlines should also fail
+    eprint!(
+        "
+"
+    );
+    eprint!(
+        r"
+"
+    );
+
+    // Don't warn on CRLF (#4208)
+    eprint!("\r\n");
+    eprint!("foo\r\n");
+    eprint!("\\r\n"); //~ ERROR
+    eprint!("foo\rbar\n") // ~ ERROR
+}
diff --git a/src/tools/clippy/tests/ui/eprint_with_newline.stderr b/src/tools/clippy/tests/ui/eprint_with_newline.stderr
new file mode 100644
index 00000000000..31811d1d92a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/eprint_with_newline.stderr
@@ -0,0 +1,121 @@
+error: using `eprint!()` with a format string that ends in a single newline
+  --> $DIR/eprint_with_newline.rs:5:5
+   |
+LL |     eprint!("Hello/n");
+   |     ^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::print-with-newline` implied by `-D warnings`
+help: use `eprintln!` instead
+   |
+LL |     eprintln!("Hello");
+   |     ^^^^^^^^       --
+
+error: using `eprint!()` with a format string that ends in a single newline
+  --> $DIR/eprint_with_newline.rs:6:5
+   |
+LL |     eprint!("Hello {}/n", "world");
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `eprintln!` instead
+   |
+LL |     eprintln!("Hello {}", "world");
+   |     ^^^^^^^^          --
+
+error: using `eprint!()` with a format string that ends in a single newline
+  --> $DIR/eprint_with_newline.rs:7:5
+   |
+LL |     eprint!("Hello {} {}/n", "world", "#2");
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `eprintln!` instead
+   |
+LL |     eprintln!("Hello {} {}", "world", "#2");
+   |     ^^^^^^^^             --
+
+error: using `eprint!()` with a format string that ends in a single newline
+  --> $DIR/eprint_with_newline.rs:8:5
+   |
+LL |     eprint!("{}/n", 1265);
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `eprintln!` instead
+   |
+LL |     eprintln!("{}", 1265);
+   |     ^^^^^^^^    --
+
+error: using `eprint!()` with a format string that ends in a single newline
+  --> $DIR/eprint_with_newline.rs:9:5
+   |
+LL |     eprint!("/n");
+   |     ^^^^^^^^^^^^^
+   |
+help: use `eprintln!` instead
+   |
+LL |     eprintln!();
+   |     ^^^^^^^^ --
+
+error: using `eprint!()` with a format string that ends in a single newline
+  --> $DIR/eprint_with_newline.rs:28:5
+   |
+LL |     eprint!("//n"); // should fail
+   |     ^^^^^^^^^^^^^^^
+   |
+help: use `eprintln!` instead
+   |
+LL |     eprintln!("/"); // should fail
+   |     ^^^^^^^^    --
+
+error: using `eprint!()` with a format string that ends in a single newline
+  --> $DIR/eprint_with_newline.rs:35:5
+   |
+LL | /     eprint!(
+LL | |         "
+LL | | "
+LL | |     );
+   | |_____^
+   |
+help: use `eprintln!` instead
+   |
+LL |     eprintln!(
+LL |         ""
+   |
+
+error: using `eprint!()` with a format string that ends in a single newline
+  --> $DIR/eprint_with_newline.rs:39:5
+   |
+LL | /     eprint!(
+LL | |         r"
+LL | | "
+LL | |     );
+   | |_____^
+   |
+help: use `eprintln!` instead
+   |
+LL |     eprintln!(
+LL |         r""
+   |
+
+error: using `eprint!()` with a format string that ends in a single newline
+  --> $DIR/eprint_with_newline.rs:47:5
+   |
+LL |     eprint!("/r/n"); //~ ERROR
+   |     ^^^^^^^^^^^^^^^^
+   |
+help: use `eprintln!` instead
+   |
+LL |     eprintln!("/r"); //~ ERROR
+   |     ^^^^^^^^     --
+
+error: using `eprint!()` with a format string that ends in a single newline
+  --> $DIR/eprint_with_newline.rs:48:5
+   |
+LL |     eprint!("foo/rbar/n") // ~ ERROR
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `eprintln!` instead
+   |
+LL |     eprintln!("foo/rbar") // ~ ERROR
+   |     ^^^^^^^^          --
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/eta.stderr b/src/tools/clippy/tests/ui/eta.stderr
index c4713ca8083..16aa1b07733 100644
--- a/src/tools/clippy/tests/ui/eta.stderr
+++ b/src/tools/clippy/tests/ui/eta.stderr
@@ -12,7 +12,7 @@ error: redundant closure found
 LL |     meta(|a| foo(a));
    |          ^^^^^^^^^^ help: remove closure as shown: `foo`
 
-error: this expression borrows a reference that is immediately dereferenced by the compiler
+error: this expression borrows a reference (`&u8`) that is immediately dereferenced by the compiler
   --> $DIR/eta.rs:24:21
    |
 LL |     all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
diff --git a/src/tools/clippy/tests/ui/formatting.rs b/src/tools/clippy/tests/ui/formatting.rs
index f54b3f2bfe2..0d14807ff1c 100644
--- a/src/tools/clippy/tests/ui/formatting.rs
+++ b/src/tools/clippy/tests/ui/formatting.rs
@@ -10,91 +10,6 @@ fn foo() -> bool {
 
 #[rustfmt::skip]
 fn main() {
-    // weird `else` formatting:
-    if foo() {
-    } {
-    }
-
-    if foo() {
-    } if foo() {
-    }
-
-    let _ = { // if as the last expression
-        let _ = 0;
-
-        if foo() {
-        } if foo() {
-        }
-        else {
-        }
-    };
-
-    let _ = { // if in the middle of a block
-        if foo() {
-        } if foo() {
-        }
-        else {
-        }
-
-        let _ = 0;
-    };
-
-    if foo() {
-    } else
-    {
-    }
-
-    if foo() {
-    }
-    else
-    {
-    }
-
-    if foo() {
-    } else
-    if foo() { // the span of the above error should continue here
-    }
-
-    if foo() {
-    }
-    else
-    if foo() { // the span of the above error should continue here
-    }
-
-    // those are ok:
-    if foo() {
-    }
-    {
-    }
-
-    if foo() {
-    } else {
-    }
-
-    if foo() {
-    }
-    else {
-    }
-
-    if foo() {
-    }
-    if foo() {
-    }
-
-    if foo() {
-    } else if foo() {
-    }
-
-    if foo() {
-    }
-    else if foo() {
-    }
-
-    if foo() {
-    }
-    else if
-    foo() {}
-
     // weird op_eq formatting:
     let mut a = 42;
     a =- 35;
@@ -146,7 +61,7 @@ fn main() {
 
     // don't lint if the indentation suggests not to
     let _ = &[
-        1 + 2, 3 
+        1 + 2, 3
                 - 4, 5
     ];
     // lint if it doesn't
diff --git a/src/tools/clippy/tests/ui/formatting.stderr b/src/tools/clippy/tests/ui/formatting.stderr
index e2095cc125b..bde434c7e2e 100644
--- a/src/tools/clippy/tests/ui/formatting.stderr
+++ b/src/tools/clippy/tests/ui/formatting.stderr
@@ -1,80 +1,5 @@
-error: this looks like an `else {..}` but the `else` is missing
-  --> $DIR/formatting.rs:15:6
-   |
-LL |     } {
-   |      ^
-   |
-   = note: `-D clippy::suspicious-else-formatting` implied by `-D warnings`
-   = note: to remove this lint, add the missing `else` or add a new line before the next block
-
-error: this looks like an `else if` but the `else` is missing
-  --> $DIR/formatting.rs:19:6
-   |
-LL |     } if foo() {
-   |      ^
-   |
-   = note: to remove this lint, add the missing `else` or add a new line before the second `if`
-
-error: this looks like an `else if` but the `else` is missing
-  --> $DIR/formatting.rs:26:10
-   |
-LL |         } if foo() {
-   |          ^
-   |
-   = note: to remove this lint, add the missing `else` or add a new line before the second `if`
-
-error: this looks like an `else if` but the `else` is missing
-  --> $DIR/formatting.rs:34:10
-   |
-LL |         } if foo() {
-   |          ^
-   |
-   = note: to remove this lint, add the missing `else` or add a new line before the second `if`
-
-error: this is an `else {..}` but the formatting might hide it
-  --> $DIR/formatting.rs:43:6
-   |
-LL |       } else
-   |  ______^
-LL | |     {
-   | |____^
-   |
-   = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
-
-error: this is an `else {..}` but the formatting might hide it
-  --> $DIR/formatting.rs:48:6
-   |
-LL |       }
-   |  ______^
-LL | |     else
-LL | |     {
-   | |____^
-   |
-   = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
-
-error: this is an `else if` but the formatting might hide it
-  --> $DIR/formatting.rs:54:6
-   |
-LL |       } else
-   |  ______^
-LL | |     if foo() { // the span of the above error should continue here
-   | |____^
-   |
-   = note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
-
-error: this is an `else if` but the formatting might hide it
-  --> $DIR/formatting.rs:59:6
-   |
-LL |       }
-   |  ______^
-LL | |     else
-LL | |     if foo() { // the span of the above error should continue here
-   | |____^
-   |
-   = note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
-
 error: this looks like you are trying to use `.. -= ..`, but you really are doing `.. = (- ..)`
-  --> $DIR/formatting.rs:100:6
+  --> $DIR/formatting.rs:15:6
    |
 LL |     a =- 35;
    |      ^^^^
@@ -83,7 +8,7 @@ LL |     a =- 35;
    = note: to remove this lint, use either `-=` or `= -`
 
 error: this looks like you are trying to use `.. *= ..`, but you really are doing `.. = (* ..)`
-  --> $DIR/formatting.rs:101:6
+  --> $DIR/formatting.rs:16:6
    |
 LL |     a =* &191;
    |      ^^^^
@@ -91,7 +16,7 @@ LL |     a =* &191;
    = note: to remove this lint, use either `*=` or `= *`
 
 error: this looks like you are trying to use `.. != ..`, but you really are doing `.. = (! ..)`
-  --> $DIR/formatting.rs:104:6
+  --> $DIR/formatting.rs:19:6
    |
 LL |     b =! false;
    |      ^^^^
@@ -99,7 +24,7 @@ LL |     b =! false;
    = note: to remove this lint, use either `!=` or `= !`
 
 error: possibly missing a comma here
-  --> $DIR/formatting.rs:113:19
+  --> $DIR/formatting.rs:28:19
    |
 LL |         -1, -2, -3 // <= no comma here
    |                   ^
@@ -108,7 +33,7 @@ LL |         -1, -2, -3 // <= no comma here
    = note: to remove this lint, add a comma or write the expr in a single line
 
 error: possibly missing a comma here
-  --> $DIR/formatting.rs:117:19
+  --> $DIR/formatting.rs:32:19
    |
 LL |         -1, -2, -3 // <= no comma here
    |                   ^
@@ -116,12 +41,12 @@ LL |         -1, -2, -3 // <= no comma here
    = note: to remove this lint, add a comma or write the expr in a single line
 
 error: possibly missing a comma here
-  --> $DIR/formatting.rs:154:11
+  --> $DIR/formatting.rs:69:11
    |
 LL |         -1
    |           ^
    |
    = note: to remove this lint, add a comma or write the expr in a single line
 
-error: aborting due to 14 previous errors
+error: aborting due to 6 previous errors
 
diff --git a/src/tools/clippy/tests/ui/match_single_binding.fixed b/src/tools/clippy/tests/ui/match_single_binding.fixed
index f3627902eec..526e94b10bd 100644
--- a/src/tools/clippy/tests/ui/match_single_binding.fixed
+++ b/src/tools/clippy/tests/ui/match_single_binding.fixed
@@ -87,4 +87,32 @@ fn main() {
             unwrapped
         })
         .collect::<Vec<u8>>();
+    // Ok
+    let x = 1;
+    match x {
+        #[cfg(disabled_feature)]
+        0 => println!("Disabled branch"),
+        _ => println!("Enabled branch"),
+    }
+    // Lint
+    let x = 1;
+    let y = 1;
+    println!("Single branch");
+    // Ok
+    let x = 1;
+    let y = 1;
+    match match y {
+        0 => 1,
+        _ => 2,
+    } {
+        #[cfg(disabled_feature)]
+        0 => println!("Array index start"),
+        _ => println!("Not an array index start"),
+    }
+    // False negative
+    let x = 1;
+    match x {
+        // =>
+        _ => println!("Not an array index start"),
+    }
 }
diff --git a/src/tools/clippy/tests/ui/match_single_binding.rs b/src/tools/clippy/tests/ui/match_single_binding.rs
index 8c182148ae1..6a2ca7c5e93 100644
--- a/src/tools/clippy/tests/ui/match_single_binding.rs
+++ b/src/tools/clippy/tests/ui/match_single_binding.rs
@@ -99,4 +99,37 @@ fn main() {
             unwrapped => unwrapped,
         })
         .collect::<Vec<u8>>();
+    // Ok
+    let x = 1;
+    match x {
+        #[cfg(disabled_feature)]
+        0 => println!("Disabled branch"),
+        _ => println!("Enabled branch"),
+    }
+    // Lint
+    let x = 1;
+    let y = 1;
+    match match y {
+        0 => 1,
+        _ => 2,
+    } {
+        _ => println!("Single branch"),
+    }
+    // Ok
+    let x = 1;
+    let y = 1;
+    match match y {
+        0 => 1,
+        _ => 2,
+    } {
+        #[cfg(disabled_feature)]
+        0 => println!("Array index start"),
+        _ => println!("Not an array index start"),
+    }
+    // False negative
+    let x = 1;
+    match x {
+        // =>
+        _ => println!("Not an array index start"),
+    }
 }
diff --git a/src/tools/clippy/tests/ui/match_single_binding.stderr b/src/tools/clippy/tests/ui/match_single_binding.stderr
index 795c8c3e24d..cbbf5d29c02 100644
--- a/src/tools/clippy/tests/ui/match_single_binding.stderr
+++ b/src/tools/clippy/tests/ui/match_single_binding.stderr
@@ -167,5 +167,16 @@ LL |             unwrapped
 LL |         })
    |
 
-error: aborting due to 11 previous errors
+error: this match could be replaced by its body itself
+  --> $DIR/match_single_binding.rs:112:5
+   |
+LL | /     match match y {
+LL | |         0 => 1,
+LL | |         _ => 2,
+LL | |     } {
+LL | |         _ => println!("Single branch"),
+LL | |     }
+   | |_____^ help: consider using the match body instead: `println!("Single branch");`
+
+error: aborting due to 12 previous errors
 
diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.rs b/src/tools/clippy/tests/ui/min_rust_version_attr.rs
index 1026cc40d3b..3848bca3207 100644
--- a/src/tools/clippy/tests/ui/min_rust_version_attr.rs
+++ b/src/tools/clippy/tests/ui/min_rust_version_attr.rs
@@ -2,7 +2,7 @@
 #![feature(custom_inner_attributes)]
 #![clippy::msrv = "1.0.0"]
 
-use std::ops::Deref;
+use std::ops::{Deref, RangeFrom};
 
 fn option_as_ref_deref() {
     let mut opt = Some(String::from("123"));
@@ -42,12 +42,94 @@ pub fn manual_strip_msrv() {
     }
 }
 
+pub fn redundant_fieldnames() {
+    let start = 0;
+    let _ = RangeFrom { start: start };
+}
+
+pub fn redundant_static_lifetime() {
+    const VAR_ONE: &'static str = "Test constant #1";
+}
+
+pub fn checked_conversion() {
+    let value: i64 = 42;
+    let _ = value <= (u32::max_value() as i64) && value >= 0;
+    let _ = value <= (u32::MAX as i64) && value >= 0;
+}
+
+pub fn filter_map_next() {
+    let a = ["1", "lol", "3", "NaN", "5"];
+
+    #[rustfmt::skip]
+    let _: Option<u32> = vec![1, 2, 3, 4, 5, 6]
+        .into_iter()
+        .filter_map(|x| {
+            if x == 2 {
+                Some(x * 2)
+            } else {
+                None
+            }
+        })
+        .next();
+}
+
+#[allow(clippy::no_effect)]
+#[allow(clippy::short_circuit_statement)]
+#[allow(clippy::unnecessary_operation)]
+pub fn manual_range_contains() {
+    let x = 5;
+    x >= 8 && x < 12;
+}
+
+pub fn use_self() {
+    struct Foo {}
+
+    impl Foo {
+        fn new() -> Foo {
+            Foo {}
+        }
+        fn test() -> Foo {
+            Foo::new()
+        }
+    }
+}
+
+fn replace_with_default() {
+    let mut s = String::from("foo");
+    let _ = std::mem::replace(&mut s, String::default());
+}
+
+fn map_unwrap_or() {
+    let opt = Some(1);
+
+    // Check for `option.map(_).unwrap_or(_)` use.
+    // Single line case.
+    let _ = opt
+        .map(|x| x + 1)
+        // Should lint even though this call is on a separate line.
+        .unwrap_or(0);
+}
+
+// Could be const
+fn missing_const_for_fn() -> i32 {
+    1
+}
+
 fn main() {
+    filter_map_next();
+    checked_conversion();
+    redundant_fieldnames();
+    redundant_static_lifetime();
     option_as_ref_deref();
     match_like_matches();
     match_same_arms();
     match_same_arms2();
     manual_strip_msrv();
+    manual_range_contains();
+    use_self();
+    replace_with_default();
+    map_unwrap_or();
+    missing_const_for_fn();
 }
 
 mod meets_msrv {
diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr
index 3e1af046e7a..34805263104 100644
--- a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr
+++ b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr
@@ -1,12 +1,12 @@
 error: stripping a prefix manually
-  --> $DIR/min_rust_version_attr.rs:60:24
+  --> $DIR/min_rust_version_attr.rs:142:24
    |
 LL |             assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
    |                        ^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::manual-strip` implied by `-D warnings`
 note: the prefix was tested here
-  --> $DIR/min_rust_version_attr.rs:59:9
+  --> $DIR/min_rust_version_attr.rs:141:9
    |
 LL |         if s.starts_with("hello, ") {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -17,13 +17,13 @@ LL |             assert_eq!(<stripped>.to_uppercase(), "WORLD!");
    |
 
 error: stripping a prefix manually
-  --> $DIR/min_rust_version_attr.rs:72:24
+  --> $DIR/min_rust_version_attr.rs:154:24
    |
 LL |             assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
    |                        ^^^^^^^^^^^^^^^^^^^^
    |
 note: the prefix was tested here
-  --> $DIR/min_rust_version_attr.rs:71:9
+  --> $DIR/min_rust_version_attr.rs:153:9
    |
 LL |         if s.starts_with("hello, ") {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/missing-doc-crate-missing.stderr b/src/tools/clippy/tests/ui/missing-doc-crate-missing.stderr
index da46f988636..d56c5cc4c3a 100644
--- a/src/tools/clippy/tests/ui/missing-doc-crate-missing.stderr
+++ b/src/tools/clippy/tests/ui/missing-doc-crate-missing.stderr
@@ -1,4 +1,4 @@
-error: missing documentation for crate
+error: missing documentation for the crate
   --> $DIR/missing-doc-crate-missing.rs:1:1
    |
 LL | / #![warn(clippy::missing_docs_in_private_items)]
diff --git a/src/tools/clippy/tests/ui/missing-doc-impl.stderr b/src/tools/clippy/tests/ui/missing-doc-impl.stderr
index 9656a39abce..7e10404ca00 100644
--- a/src/tools/clippy/tests/ui/missing-doc-impl.stderr
+++ b/src/tools/clippy/tests/ui/missing-doc-impl.stderr
@@ -51,13 +51,13 @@ LL | |     fn foo_with_impl(&self) {}
 LL | | }
    | |_^
 
-error: missing documentation for a trait method
+error: missing documentation for an associated function
   --> $DIR/missing-doc-impl.rs:39:5
    |
 LL |     fn foo(&self);
    |     ^^^^^^^^^^^^^^
 
-error: missing documentation for a trait method
+error: missing documentation for an associated function
   --> $DIR/missing-doc-impl.rs:40:5
    |
 LL |     fn foo_with_impl(&self) {}
@@ -75,25 +75,25 @@ error: missing documentation for an associated type
 LL |     type AssociatedTypeDef = Self;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: missing documentation for a method
+error: missing documentation for an associated function
   --> $DIR/missing-doc-impl.rs:62:5
    |
 LL |     pub fn foo() {}
    |     ^^^^^^^^^^^^^^^
 
-error: missing documentation for a method
+error: missing documentation for an associated function
   --> $DIR/missing-doc-impl.rs:63:5
    |
 LL |     fn bar() {}
    |     ^^^^^^^^^^^
 
-error: missing documentation for a method
+error: missing documentation for an associated function
   --> $DIR/missing-doc-impl.rs:67:5
    |
 LL |     pub fn foo() {}
    |     ^^^^^^^^^^^^^^^
 
-error: missing documentation for a method
+error: missing documentation for an associated function
   --> $DIR/missing-doc-impl.rs:70:5
    |
 LL |     fn foo2() {}
diff --git a/src/tools/clippy/tests/ui/needless_borrow.stderr b/src/tools/clippy/tests/ui/needless_borrow.stderr
index 0bfeda7914d..bea4b41b803 100644
--- a/src/tools/clippy/tests/ui/needless_borrow.stderr
+++ b/src/tools/clippy/tests/ui/needless_borrow.stderr
@@ -1,4 +1,4 @@
-error: this expression borrows a reference that is immediately dereferenced by the compiler
+error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
   --> $DIR/needless_borrow.rs:14:15
    |
 LL |     let c = x(&&a);
@@ -12,7 +12,7 @@ error: this pattern creates a reference to a reference
 LL |     if let Some(ref cake) = Some(&5) {}
    |                 ^^^^^^^^ help: change this to: `cake`
 
-error: this expression borrows a reference that is immediately dereferenced by the compiler
+error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
   --> $DIR/needless_borrow.rs:28:15
    |
 LL |         46 => &&a,
diff --git a/src/tools/clippy/tests/ui/needless_doc_main.rs b/src/tools/clippy/tests/ui/needless_doc_main.rs
index 883683e08a2..83e9bbaa3af 100644
--- a/src/tools/clippy/tests/ui/needless_doc_main.rs
+++ b/src/tools/clippy/tests/ui/needless_doc_main.rs
@@ -10,7 +10,7 @@
 /// ```
 ///
 /// With an explicit return type it should lint too
-/// ```
+/// ```edition2015
 /// fn main() -> () {
 ///     unimplemented!();
 /// }
@@ -39,7 +39,7 @@ fn bad_doctests() {}
 /// ```
 ///
 /// This shouldn't lint either, because main is async:
-/// ```
+/// ```edition2018
 /// async fn main() {
 ///     assert_eq!(42, ANSWER);
 /// }
@@ -128,6 +128,12 @@ fn bad_doctests() {}
 /// ```
 fn no_false_positives() {}
 
+/// Yields a parse error when interpreted as rust code:
+/// ```
+/// r#"hi"
+/// ```
+fn issue_6022() {}
+
 fn main() {
     bad_doctests();
     no_false_positives();
diff --git a/src/tools/clippy/tests/ui/needless_update.rs b/src/tools/clippy/tests/ui/needless_update.rs
index bfa005a19f9..b93ff048a62 100644
--- a/src/tools/clippy/tests/ui/needless_update.rs
+++ b/src/tools/clippy/tests/ui/needless_update.rs
@@ -6,9 +6,20 @@ struct S {
     pub b: i32,
 }
 
+#[non_exhaustive]
+struct T {
+    pub x: i32,
+    pub y: i32,
+}
+
 fn main() {
     let base = S { a: 0, b: 0 };
     S { ..base }; // no error
     S { a: 1, ..base }; // no error
     S { a: 1, b: 1, ..base };
+
+    let base = T { x: 0, y: 0 };
+    T { ..base }; // no error
+    T { x: 1, ..base }; // no error
+    T { x: 1, y: 1, ..base }; // no error
 }
diff --git a/src/tools/clippy/tests/ui/needless_update.stderr b/src/tools/clippy/tests/ui/needless_update.stderr
index 133c834880d..b154b3b306d 100644
--- a/src/tools/clippy/tests/ui/needless_update.stderr
+++ b/src/tools/clippy/tests/ui/needless_update.stderr
@@ -1,5 +1,5 @@
 error: struct update has no effect, all the fields in the struct have already been specified
-  --> $DIR/needless_update.rs:13:23
+  --> $DIR/needless_update.rs:19:23
    |
 LL |     S { a: 1, b: 1, ..base };
    |                       ^^^^
diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn.stderr b/src/tools/clippy/tests/ui/panic_in_result_fn.stderr
index ca73ac5a411..eb744b0c198 100644
--- a/src/tools/clippy/tests/ui/panic_in_result_fn.stderr
+++ b/src/tools/clippy/tests/ui/panic_in_result_fn.stderr
@@ -1,4 +1,4 @@
-error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
   --> $DIR/panic_in_result_fn.rs:7:5
    |
 LL | /     fn result_with_panic() -> Result<bool, String> // should emit lint
@@ -8,7 +8,7 @@ LL | |     }
    | |_____^
    |
    = note: `-D clippy::panic-in-result-fn` implied by `-D warnings`
-   = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
+   = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
 note: return Err() instead of panicking
   --> $DIR/panic_in_result_fn.rs:9:9
    |
@@ -16,7 +16,7 @@ LL |         panic!("error");
    |         ^^^^^^^^^^^^^^^^
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
   --> $DIR/panic_in_result_fn.rs:12:5
    |
 LL | /     fn result_with_unimplemented() -> Result<bool, String> // should emit lint
@@ -25,7 +25,7 @@ LL | |         unimplemented!();
 LL | |     }
    | |_____^
    |
-   = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
+   = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
 note: return Err() instead of panicking
   --> $DIR/panic_in_result_fn.rs:14:9
    |
@@ -33,7 +33,7 @@ LL |         unimplemented!();
    |         ^^^^^^^^^^^^^^^^^
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
   --> $DIR/panic_in_result_fn.rs:17:5
    |
 LL | /     fn result_with_unreachable() -> Result<bool, String> // should emit lint
@@ -42,7 +42,7 @@ LL | |         unreachable!();
 LL | |     }
    | |_____^
    |
-   = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
+   = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
 note: return Err() instead of panicking
   --> $DIR/panic_in_result_fn.rs:19:9
    |
@@ -50,7 +50,7 @@ LL |         unreachable!();
    |         ^^^^^^^^^^^^^^^
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
   --> $DIR/panic_in_result_fn.rs:22:5
    |
 LL | /     fn result_with_todo() -> Result<bool, String> // should emit lint
@@ -59,7 +59,7 @@ LL | |         todo!("Finish this");
 LL | |     }
    | |_____^
    |
-   = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
+   = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
 note: return Err() instead of panicking
   --> $DIR/panic_in_result_fn.rs:24:9
    |
@@ -67,7 +67,7 @@ LL |         todo!("Finish this");
    |         ^^^^^^^^^^^^^^^^^^^^^
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
   --> $DIR/panic_in_result_fn.rs:53:1
    |
 LL | / fn function_result_with_panic() -> Result<bool, String> // should emit lint
@@ -76,7 +76,7 @@ LL | |     panic!("error");
 LL | | }
    | |_^
    |
-   = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
+   = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
 note: return Err() instead of panicking
   --> $DIR/panic_in_result_fn.rs:55:5
    |
@@ -84,7 +84,7 @@ LL |     panic!("error");
    |     ^^^^^^^^^^^^^^^^
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
   --> $DIR/panic_in_result_fn.rs:68:1
    |
 LL | / fn main() -> Result<(), String> {
@@ -93,7 +93,7 @@ LL | |     Ok(())
 LL | | }
    | |_^
    |
-   = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
+   = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
 note: return Err() instead of panicking
   --> $DIR/panic_in_result_fn.rs:69:5
    |
diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.rs b/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.rs
new file mode 100644
index 00000000000..ffdf8288adc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.rs
@@ -0,0 +1,48 @@
+#![warn(clippy::panic_in_result_fn)]
+#![allow(clippy::unnecessary_wraps)]
+
+struct A;
+
+impl A {
+    fn result_with_assert_with_message(x: i32) -> Result<bool, String> // should emit lint
+    {
+        assert!(x == 5, "wrong argument");
+        Ok(true)
+    }
+
+    fn result_with_assert_eq(x: i32) -> Result<bool, String> // should emit lint
+    {
+        assert_eq!(x, 5);
+        Ok(true)
+    }
+
+    fn result_with_assert_ne(x: i32) -> Result<bool, String> // should emit lint
+    {
+        assert_ne!(x, 1);
+        Ok(true)
+    }
+
+    fn other_with_assert_with_message(x: i32) // should not emit lint
+    {
+        assert!(x == 5, "wrong argument");
+    }
+
+    fn other_with_assert_eq(x: i32) // should not emit lint
+    {
+        assert_eq!(x, 5);
+    }
+
+    fn other_with_assert_ne(x: i32) // should not emit lint
+    {
+        assert_ne!(x, 1);
+    }
+
+    fn result_without_banned_functions() -> Result<bool, String> // should not emit lint
+    {
+        let assert = "assert!";
+        println!("No {}", assert);
+        Ok(true)
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.stderr b/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.stderr
new file mode 100644
index 00000000000..86f61ad718a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/panic_in_result_fn_assertions.stderr
@@ -0,0 +1,57 @@
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
+  --> $DIR/panic_in_result_fn_assertions.rs:7:5
+   |
+LL | /     fn result_with_assert_with_message(x: i32) -> Result<bool, String> // should emit lint
+LL | |     {
+LL | |         assert!(x == 5, "wrong argument");
+LL | |         Ok(true)
+LL | |     }
+   | |_____^
+   |
+   = note: `-D clippy::panic-in-result-fn` implied by `-D warnings`
+   = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
+note: return Err() instead of panicking
+  --> $DIR/panic_in_result_fn_assertions.rs:9:9
+   |
+LL |         assert!(x == 5, "wrong argument");
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
+  --> $DIR/panic_in_result_fn_assertions.rs:13:5
+   |
+LL | /     fn result_with_assert_eq(x: i32) -> Result<bool, String> // should emit lint
+LL | |     {
+LL | |         assert_eq!(x, 5);
+LL | |         Ok(true)
+LL | |     }
+   | |_____^
+   |
+   = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
+note: return Err() instead of panicking
+  --> $DIR/panic_in_result_fn_assertions.rs:15:9
+   |
+LL |         assert_eq!(x, 5);
+   |         ^^^^^^^^^^^^^^^^^
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
+  --> $DIR/panic_in_result_fn_assertions.rs:19:5
+   |
+LL | /     fn result_with_assert_ne(x: i32) -> Result<bool, String> // should emit lint
+LL | |     {
+LL | |         assert_ne!(x, 1);
+LL | |         Ok(true)
+LL | |     }
+   | |_____^
+   |
+   = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
+note: return Err() instead of panicking
+  --> $DIR/panic_in_result_fn_assertions.rs:21:9
+   |
+LL |         assert_ne!(x, 1);
+   |         ^^^^^^^^^^^^^^^^^
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.rs b/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.rs
new file mode 100644
index 00000000000..b60c79f97c8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.rs
@@ -0,0 +1,48 @@
+#![warn(clippy::panic_in_result_fn)]
+#![allow(clippy::unnecessary_wraps)]
+
+struct A;
+
+impl A {
+    fn result_with_debug_assert_with_message(x: i32) -> Result<bool, String> // should emit lint
+    {
+        debug_assert!(x == 5, "wrong argument");
+        Ok(true)
+    }
+
+    fn result_with_debug_assert_eq(x: i32) -> Result<bool, String> // should emit lint
+    {
+        debug_assert_eq!(x, 5);
+        Ok(true)
+    }
+
+    fn result_with_debug_assert_ne(x: i32) -> Result<bool, String> // should emit lint
+    {
+        debug_assert_ne!(x, 1);
+        Ok(true)
+    }
+
+    fn other_with_debug_assert_with_message(x: i32) // should not emit lint
+    {
+        debug_assert!(x == 5, "wrong argument");
+    }
+
+    fn other_with_debug_assert_eq(x: i32) // should not emit lint
+    {
+        debug_assert_eq!(x, 5);
+    }
+
+    fn other_with_debug_assert_ne(x: i32) // should not emit lint
+    {
+        debug_assert_ne!(x, 1);
+    }
+
+    fn result_without_banned_functions() -> Result<bool, String> // should not emit lint
+    {
+        let debug_assert = "debug_assert!";
+        println!("No {}", debug_assert);
+        Ok(true)
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.stderr b/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.stderr
new file mode 100644
index 00000000000..ec18e89698c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.stderr
@@ -0,0 +1,57 @@
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
+  --> $DIR/panic_in_result_fn_debug_assertions.rs:7:5
+   |
+LL | /     fn result_with_debug_assert_with_message(x: i32) -> Result<bool, String> // should emit lint
+LL | |     {
+LL | |         debug_assert!(x == 5, "wrong argument");
+LL | |         Ok(true)
+LL | |     }
+   | |_____^
+   |
+   = note: `-D clippy::panic-in-result-fn` implied by `-D warnings`
+   = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
+note: return Err() instead of panicking
+  --> $DIR/panic_in_result_fn_debug_assertions.rs:9:9
+   |
+LL |         debug_assert!(x == 5, "wrong argument");
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
+  --> $DIR/panic_in_result_fn_debug_assertions.rs:13:5
+   |
+LL | /     fn result_with_debug_assert_eq(x: i32) -> Result<bool, String> // should emit lint
+LL | |     {
+LL | |         debug_assert_eq!(x, 5);
+LL | |         Ok(true)
+LL | |     }
+   | |_____^
+   |
+   = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
+note: return Err() instead of panicking
+  --> $DIR/panic_in_result_fn_debug_assertions.rs:15:9
+   |
+LL |         debug_assert_eq!(x, 5);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
+  --> $DIR/panic_in_result_fn_debug_assertions.rs:19:5
+   |
+LL | /     fn result_with_debug_assert_ne(x: i32) -> Result<bool, String> // should emit lint
+LL | |     {
+LL | |         debug_assert_ne!(x, 1);
+LL | |         Ok(true)
+LL | |     }
+   | |_____^
+   |
+   = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
+note: return Err() instead of panicking
+  --> $DIR/panic_in_result_fn_debug_assertions.rs:21:9
+   |
+LL |         debug_assert_ne!(x, 1);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/print_stderr.rs b/src/tools/clippy/tests/ui/print_stderr.rs
new file mode 100644
index 00000000000..fa07e74a7be
--- /dev/null
+++ b/src/tools/clippy/tests/ui/print_stderr.rs
@@ -0,0 +1,8 @@
+#![warn(clippy::print_stderr)]
+
+fn main() {
+    eprintln!("Hello");
+    println!("This should not do anything");
+    eprint!("World");
+    print!("Nor should this");
+}
diff --git a/src/tools/clippy/tests/ui/print_stderr.stderr b/src/tools/clippy/tests/ui/print_stderr.stderr
new file mode 100644
index 00000000000..5af735af657
--- /dev/null
+++ b/src/tools/clippy/tests/ui/print_stderr.stderr
@@ -0,0 +1,16 @@
+error: use of `eprintln!`
+  --> $DIR/print_stderr.rs:4:5
+   |
+LL |     eprintln!("Hello");
+   |     ^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::print-stderr` implied by `-D warnings`
+
+error: use of `eprint!`
+  --> $DIR/print_stderr.rs:6:5
+   |
+LL |     eprint!("World");
+   |     ^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/println_empty_string.fixed b/src/tools/clippy/tests/ui/println_empty_string.fixed
index 2b889b62ea9..9760680927a 100644
--- a/src/tools/clippy/tests/ui/println_empty_string.fixed
+++ b/src/tools/clippy/tests/ui/println_empty_string.fixed
@@ -8,4 +8,11 @@ fn main() {
     match "a" {
         _ => println!(),
     }
+
+    eprintln!();
+    eprintln!();
+
+    match "a" {
+        _ => eprintln!(),
+    }
 }
diff --git a/src/tools/clippy/tests/ui/println_empty_string.rs b/src/tools/clippy/tests/ui/println_empty_string.rs
index 890f5f68476..80fdb3e6e21 100644
--- a/src/tools/clippy/tests/ui/println_empty_string.rs
+++ b/src/tools/clippy/tests/ui/println_empty_string.rs
@@ -8,4 +8,11 @@ fn main() {
     match "a" {
         _ => println!(""),
     }
+
+    eprintln!();
+    eprintln!("");
+
+    match "a" {
+        _ => eprintln!(""),
+    }
 }
diff --git a/src/tools/clippy/tests/ui/println_empty_string.stderr b/src/tools/clippy/tests/ui/println_empty_string.stderr
index 23112b88168..17fe4ea7479 100644
--- a/src/tools/clippy/tests/ui/println_empty_string.stderr
+++ b/src/tools/clippy/tests/ui/println_empty_string.stderr
@@ -12,5 +12,17 @@ error: using `println!("")`
 LL |         _ => println!(""),
    |              ^^^^^^^^^^^^ help: replace it with: `println!()`
 
-error: aborting due to 2 previous errors
+error: using `eprintln!("")`
+  --> $DIR/println_empty_string.rs:13:5
+   |
+LL |     eprintln!("");
+   |     ^^^^^^^^^^^^^ help: replace it with: `eprintln!()`
+
+error: using `eprintln!("")`
+  --> $DIR/println_empty_string.rs:16:14
+   |
+LL |         _ => eprintln!(""),
+   |              ^^^^^^^^^^^^^ help: replace it with: `eprintln!()`
+
+error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui/range_contains.fixed b/src/tools/clippy/tests/ui/range_contains.fixed
index 048874a7f82..47c974e614b 100644
--- a/src/tools/clippy/tests/ui/range_contains.fixed
+++ b/src/tools/clippy/tests/ui/range_contains.fixed
@@ -44,3 +44,8 @@ fn main() {
     (0. ..1.).contains(&y);
     !(0. ..=1.).contains(&y);
 }
+
+// Fix #6373
+pub const fn in_range(a: i32) -> bool {
+    3 <= a && a <= 20
+}
diff --git a/src/tools/clippy/tests/ui/range_contains.rs b/src/tools/clippy/tests/ui/range_contains.rs
index 60ad259f404..835deced5e4 100644
--- a/src/tools/clippy/tests/ui/range_contains.rs
+++ b/src/tools/clippy/tests/ui/range_contains.rs
@@ -44,3 +44,8 @@ fn main() {
     y >= 0. && y < 1.;
     y < 0. || y > 1.;
 }
+
+// Fix #6373
+pub const fn in_range(a: i32) -> bool {
+    3 <= a && a <= 20
+}
diff --git a/src/tools/clippy/tests/ui/redundant_else.rs b/src/tools/clippy/tests/ui/redundant_else.rs
new file mode 100644
index 00000000000..737c8a9f8db
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_else.rs
@@ -0,0 +1,154 @@
+#![warn(clippy::redundant_else)]
+#![allow(clippy::needless_return)]
+
+fn main() {
+    loop {
+        // break
+        if foo() {
+            println!("Love your neighbor;");
+            break;
+        } else {
+            println!("yet don't pull down your hedge.");
+        }
+        // continue
+        if foo() {
+            println!("He that lies down with Dogs,");
+            continue;
+        } else {
+            println!("shall rise up with fleas.");
+        }
+        // match block
+        if foo() {
+            match foo() {
+                1 => break,
+                _ => return,
+            }
+        } else {
+            println!("You may delay, but time will not.");
+        }
+    }
+    // else if
+    if foo() {
+        return;
+    } else if foo() {
+        return;
+    } else {
+        println!("A fat kitchen makes a lean will.");
+    }
+    // let binding outside of block
+    let _ = {
+        if foo() {
+            return;
+        } else {
+            1
+        }
+    };
+    // else if with let binding outside of block
+    let _ = {
+        if foo() {
+            return;
+        } else if foo() {
+            return;
+        } else {
+            2
+        }
+    };
+    // inside if let
+    let _ = if let Some(1) = foo() {
+        let _ = 1;
+        if foo() {
+            return;
+        } else {
+            1
+        }
+    } else {
+        1
+    };
+
+    //
+    // non-lint cases
+    //
+
+    // sanity check
+    if foo() {
+        let _ = 1;
+    } else {
+        println!("Who is wise? He that learns from every one.");
+    }
+    // else if without else
+    if foo() {
+        return;
+    } else if foo() {
+        foo()
+    };
+    // nested if return
+    if foo() {
+        if foo() {
+            return;
+        }
+    } else {
+        foo()
+    };
+    // match with non-breaking branch
+    if foo() {
+        match foo() {
+            1 => foo(),
+            _ => return,
+        }
+    } else {
+        println!("Three may keep a secret, if two of them are dead.");
+    }
+    // let binding
+    let _ = if foo() {
+        return;
+    } else {
+        1
+    };
+    // assign
+    let a;
+    a = if foo() {
+        return;
+    } else {
+        1
+    };
+    // assign-op
+    a += if foo() {
+        return;
+    } else {
+        1
+    };
+    // if return else if else
+    if foo() {
+        return;
+    } else if foo() {
+        1
+    } else {
+        2
+    };
+    // if else if return else
+    if foo() {
+        1
+    } else if foo() {
+        return;
+    } else {
+        2
+    };
+    // else if with let binding
+    let _ = if foo() {
+        return;
+    } else if foo() {
+        return;
+    } else {
+        2
+    };
+    // inside function call
+    Box::new(if foo() {
+        return;
+    } else {
+        1
+    });
+}
+
+fn foo<T>() -> T {
+    unimplemented!("I'm not Santa Claus")
+}
diff --git a/src/tools/clippy/tests/ui/redundant_else.stderr b/src/tools/clippy/tests/ui/redundant_else.stderr
new file mode 100644
index 00000000000..9000cdc814b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_else.stderr
@@ -0,0 +1,80 @@
+error: redundant else block
+  --> $DIR/redundant_else.rs:10:16
+   |
+LL |           } else {
+   |  ________________^
+LL | |             println!("yet don't pull down your hedge.");
+LL | |         }
+   | |_________^
+   |
+   = note: `-D clippy::redundant-else` implied by `-D warnings`
+   = help: remove the `else` block and move the contents out
+
+error: redundant else block
+  --> $DIR/redundant_else.rs:17:16
+   |
+LL |           } else {
+   |  ________________^
+LL | |             println!("shall rise up with fleas.");
+LL | |         }
+   | |_________^
+   |
+   = help: remove the `else` block and move the contents out
+
+error: redundant else block
+  --> $DIR/redundant_else.rs:26:16
+   |
+LL |           } else {
+   |  ________________^
+LL | |             println!("You may delay, but time will not.");
+LL | |         }
+   | |_________^
+   |
+   = help: remove the `else` block and move the contents out
+
+error: redundant else block
+  --> $DIR/redundant_else.rs:35:12
+   |
+LL |       } else {
+   |  ____________^
+LL | |         println!("A fat kitchen makes a lean will.");
+LL | |     }
+   | |_____^
+   |
+   = help: remove the `else` block and move the contents out
+
+error: redundant else block
+  --> $DIR/redundant_else.rs:42:16
+   |
+LL |           } else {
+   |  ________________^
+LL | |             1
+LL | |         }
+   | |_________^
+   |
+   = help: remove the `else` block and move the contents out
+
+error: redundant else block
+  --> $DIR/redundant_else.rs:52:16
+   |
+LL |           } else {
+   |  ________________^
+LL | |             2
+LL | |         }
+   | |_________^
+   |
+   = help: remove the `else` block and move the contents out
+
+error: redundant else block
+  --> $DIR/redundant_else.rs:61:16
+   |
+LL |           } else {
+   |  ________________^
+LL | |             1
+LL | |         }
+   | |_________^
+   |
+   = help: remove the `else` block and move the contents out
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/suspicious_else_formatting.rs b/src/tools/clippy/tests/ui/suspicious_else_formatting.rs
new file mode 100644
index 00000000000..226010ec6df
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_else_formatting.rs
@@ -0,0 +1,79 @@
+#![warn(clippy::suspicious_else_formatting)]
+
+fn foo() -> bool {
+    true
+}
+
+#[rustfmt::skip]
+fn main() {
+    // weird `else` formatting:
+    if foo() {
+    } {
+    }
+
+    if foo() {
+    } if foo() {
+    }
+
+    let _ = { // if as the last expression
+        let _ = 0;
+
+        if foo() {
+        } if foo() {
+        }
+        else {
+        }
+    };
+
+    let _ = { // if in the middle of a block
+        if foo() {
+        } if foo() {
+        }
+        else {
+        }
+
+        let _ = 0;
+    };
+
+    if foo() {
+    } else
+    {
+    }
+
+    if foo() {
+    }
+    else
+    {
+    }
+
+    if foo() {
+    } else
+    if foo() { // the span of the above error should continue here
+    }
+
+    if foo() {
+    }
+    else
+    if foo() { // the span of the above error should continue here
+    }
+
+    // those are ok:
+    if foo() {
+    }
+    {
+    }
+
+    if foo() {
+    } else {
+    }
+
+    if foo() {
+    }
+    else {
+    }
+
+    if foo() {
+    }
+    if foo() {
+    }
+}
diff --git a/src/tools/clippy/tests/ui/suspicious_else_formatting.stderr b/src/tools/clippy/tests/ui/suspicious_else_formatting.stderr
new file mode 100644
index 00000000000..bbc036d376f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/suspicious_else_formatting.stderr
@@ -0,0 +1,77 @@
+error: this looks like an `else {..}` but the `else` is missing
+  --> $DIR/suspicious_else_formatting.rs:11:6
+   |
+LL |     } {
+   |      ^
+   |
+   = note: `-D clippy::suspicious-else-formatting` implied by `-D warnings`
+   = note: to remove this lint, add the missing `else` or add a new line before the next block
+
+error: this looks like an `else if` but the `else` is missing
+  --> $DIR/suspicious_else_formatting.rs:15:6
+   |
+LL |     } if foo() {
+   |      ^
+   |
+   = note: to remove this lint, add the missing `else` or add a new line before the second `if`
+
+error: this looks like an `else if` but the `else` is missing
+  --> $DIR/suspicious_else_formatting.rs:22:10
+   |
+LL |         } if foo() {
+   |          ^
+   |
+   = note: to remove this lint, add the missing `else` or add a new line before the second `if`
+
+error: this looks like an `else if` but the `else` is missing
+  --> $DIR/suspicious_else_formatting.rs:30:10
+   |
+LL |         } if foo() {
+   |          ^
+   |
+   = note: to remove this lint, add the missing `else` or add a new line before the second `if`
+
+error: this is an `else {..}` but the formatting might hide it
+  --> $DIR/suspicious_else_formatting.rs:39:6
+   |
+LL |       } else
+   |  ______^
+LL | |     {
+   | |____^
+   |
+   = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
+
+error: this is an `else {..}` but the formatting might hide it
+  --> $DIR/suspicious_else_formatting.rs:44:6
+   |
+LL |       }
+   |  ______^
+LL | |     else
+LL | |     {
+   | |____^
+   |
+   = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
+
+error: this is an `else if` but the formatting might hide it
+  --> $DIR/suspicious_else_formatting.rs:50:6
+   |
+LL |       } else
+   |  ______^
+LL | |     if foo() { // the span of the above error should continue here
+   | |____^
+   |
+   = note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
+
+error: this is an `else if` but the formatting might hide it
+  --> $DIR/suspicious_else_formatting.rs:55:6
+   |
+LL |       }
+   |  ______^
+LL | |     else
+LL | |     if foo() { // the span of the above error should continue here
+   | |____^
+   |
+   = note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_clone.stderr b/src/tools/clippy/tests/ui/unnecessary_clone.stderr
index 5ffa6c4fd06..9df1ae56867 100644
--- a/src/tools/clippy/tests/ui/unnecessary_clone.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_clone.stderr
@@ -30,7 +30,7 @@ error: using `.clone()` on a ref-counted pointer
 LL |     let _: Arc<dyn SomeTrait> = x.clone();
    |                                 ^^^^^^^^^ help: try this: `Arc::<SomeImpl>::clone(&x)`
 
-error: using `clone` on a `Copy` type
+error: using `clone` on type `T` which implements the `Copy` trait
   --> $DIR/unnecessary_clone.rs:40:5
    |
 LL |     t.clone();
@@ -38,13 +38,13 @@ LL |     t.clone();
    |
    = note: `-D clippy::clone-on-copy` implied by `-D warnings`
 
-error: using `clone` on a `Copy` type
+error: using `clone` on type `std::option::Option<T>` which implements the `Copy` trait
   --> $DIR/unnecessary_clone.rs:42:5
    |
 LL |     Some(t).clone();
    |     ^^^^^^^^^^^^^^^ help: try removing the `clone` call: `Some(t)`
 
-error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type
+error: using `clone` on a double-reference; this will copy the reference of type `&std::vec::Vec<i32>` instead of cloning the inner type
   --> $DIR/unnecessary_clone.rs:48:22
    |
 LL |     let z: &Vec<_> = y.clone();
@@ -60,13 +60,13 @@ help: or try being explicit if you are sure, that you want to clone a reference
 LL |     let z: &Vec<_> = <&std::vec::Vec<i32>>::clone(y);
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: using `clone` on a `Copy` type
+error: using `clone` on type `many_derefs::E` which implements the `Copy` trait
   --> $DIR/unnecessary_clone.rs:84:20
    |
 LL |         let _: E = a.clone();
    |                    ^^^^^^^^^ help: try dereferencing it: `*****a`
 
-error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type
+error: using `clone` on a double-reference; this will copy the reference of type `&[u8]` instead of cloning the inner type
   --> $DIR/unnecessary_clone.rs:89:22
    |
 LL |         let _ = &mut encoded.clone();
@@ -81,7 +81,7 @@ help: or try being explicit if you are sure, that you want to clone a reference
 LL |         let _ = &mut <&[u8]>::clone(encoded);
    |                      ^^^^^^^^^^^^^^^^^^^^^^^
 
-error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type
+error: using `clone` on a double-reference; this will copy the reference of type `&[u8]` instead of cloning the inner type
   --> $DIR/unnecessary_clone.rs:90:18
    |
 LL |         let _ = &encoded.clone();
diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs
index 2e923bc97a2..b05dd143bfd 100644
--- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs
@@ -15,4 +15,8 @@ fn main() {
     }
     let _ = Ok(1).unwrap_or_else(|e::E| 2);
     let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2);
+
+    // Fix #6343
+    let arr = [(Some(1),)];
+    Some(&0).and_then(|&i| arr[i].0);
 }
diff --git a/src/tools/clippy/tests/ui/update-all-references.sh b/src/tools/clippy/tests/ui/update-all-references.sh
index 30ba9188db4..4391499a1e1 100755
--- a/src/tools/clippy/tests/ui/update-all-references.sh
+++ b/src/tools/clippy/tests/ui/update-all-references.sh
@@ -1,21 +1,3 @@
 #!/bin/bash
 
-# A script to update the references for all tests. The idea is that
-# you do a run, which will generate files in the build directory
-# containing the (normalized) actual output of the compiler. You then
-# run this script, which will copy those files over. If you find
-# yourself manually editing a foo.stderr file, you're doing it wrong.
-#
-# See all `update-references.sh`, if you just want to update a single test.
-
-if [[ "$1" == "--help" || "$1" == "-h" ]]; then
-    echo "usage: $0"
-fi
-
-CARGO_TARGET_DIR=${CARGO_TARGET_DIR:-$PWD/target}
-PROFILE=${PROFILE:-debug}
-BUILD_DIR=${CARGO_TARGET_DIR}/${PROFILE}/test_build_base
-
-MY_DIR=$(dirname "$0")
-cd "$MY_DIR" || exit
-find . -name '*.rs' -exec ./update-references.sh "$BUILD_DIR" {} +
+echo "Please use 'cargo dev bless' instead."
diff --git a/src/tools/clippy/tests/ui/update-references.sh b/src/tools/clippy/tests/ui/update-references.sh
deleted file mode 100755
index e16ed600ef8..00000000000
--- a/src/tools/clippy/tests/ui/update-references.sh
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/bin/bash
-
-# A script to update the references for particular tests. The idea is
-# that you do a run, which will generate files in the build directory
-# containing the (normalized) actual output of the compiler. This
-# script will then copy that output and replace the "expected output"
-# files. You can then commit the changes.
-#
-# If you find yourself manually editing a `foo.stderr` file, you're
-# doing it wrong.
-
-if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then
-    echo "usage: $0 <build-directory> <relative-path-to-rs-files>"
-    echo ""
-    echo "For example:"
-    echo "   $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs"
-fi
-
-MYDIR=$(dirname "$0")
-
-BUILD_DIR="$1"
-shift
-
-while [[ "$1" != "" ]]; do
-    STDERR_NAME="${1/%.rs/.stderr}"
-    STDOUT_NAME="${1/%.rs/.stdout}"
-    FIXED_NAME="${1/%.rs/.fixed}"
-    shift
-    if [[ -f "$BUILD_DIR"/"$STDOUT_NAME" ]] && \
-           ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then
-        echo updating "$MYDIR"/"$STDOUT_NAME"
-        cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"
-        if [[ ! -s "$MYDIR"/"$STDOUT_NAME" ]]; then
-            echo removing "$MYDIR"/"$STDOUT_NAME"
-            rm "$MYDIR"/"$STDOUT_NAME"
-        fi
-    fi
-    if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \
-           ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then
-        echo updating "$MYDIR"/"$STDERR_NAME"
-        cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"
-        if [[ ! -s "$MYDIR"/"$STDERR_NAME" ]]; then
-            echo removing "$MYDIR"/"$STDERR_NAME"
-            rm "$MYDIR"/"$STDERR_NAME"
-        fi
-    fi
-    if [[ -f "$BUILD_DIR"/"$FIXED_NAME" ]] && \
-           ! (cmp -s -- "$BUILD_DIR"/"$FIXED_NAME" "$MYDIR"/"$FIXED_NAME"); then
-        echo updating "$MYDIR"/"$FIXED_NAME"
-        cp "$BUILD_DIR"/"$FIXED_NAME" "$MYDIR"/"$FIXED_NAME"
-        if [[ ! -s "$MYDIR"/"$FIXED_NAME" ]]; then
-            echo removing "$MYDIR"/"$FIXED_NAME"
-            rm "$MYDIR"/"$FIXED_NAME"
-        fi
-    fi
-done
diff --git a/src/tools/clippy/tests/ui/use_self.fixed b/src/tools/clippy/tests/ui/use_self.fixed
index ebb3aa28daf..d6a890014e6 100644
--- a/src/tools/clippy/tests/ui/use_self.fixed
+++ b/src/tools/clippy/tests/ui/use_self.fixed
@@ -71,6 +71,7 @@ mod lifetimes {
 
 mod issue2894 {
     trait IntoBytes {
+        #[allow(clippy::wrong_self_convention)]
         fn into_bytes(&self) -> Vec<u8>;
     }
 
diff --git a/src/tools/clippy/tests/ui/use_self.rs b/src/tools/clippy/tests/ui/use_self.rs
index 8a182192ab3..b04d9ce75b2 100644
--- a/src/tools/clippy/tests/ui/use_self.rs
+++ b/src/tools/clippy/tests/ui/use_self.rs
@@ -71,6 +71,7 @@ mod lifetimes {
 
 mod issue2894 {
     trait IntoBytes {
+        #[allow(clippy::wrong_self_convention)]
         fn into_bytes(&self) -> Vec<u8>;
     }
 
diff --git a/src/tools/clippy/tests/ui/use_self.stderr b/src/tools/clippy/tests/ui/use_self.stderr
index b33928597c1..80e1bfc75e8 100644
--- a/src/tools/clippy/tests/ui/use_self.stderr
+++ b/src/tools/clippy/tests/ui/use_self.stderr
@@ -37,19 +37,19 @@ LL |             Foo::new()
    |             ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:89:56
+  --> $DIR/use_self.rs:90:56
    |
 LL |         fn bad(foos: &[Self]) -> impl Iterator<Item = &Foo> {
    |                                                        ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:104:13
+  --> $DIR/use_self.rs:105:13
    |
 LL |             TS(0)
    |             ^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:112:25
+  --> $DIR/use_self.rs:113:25
    |
 LL |             fn new() -> Foo {
    |                         ^^^ help: use the applicable keyword: `Self`
@@ -60,7 +60,7 @@ LL |         use_self_expand!(); // Should lint in local macros
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:113:17
+  --> $DIR/use_self.rs:114:17
    |
 LL |                 Foo {}
    |                 ^^^ help: use the applicable keyword: `Self`
@@ -71,91 +71,91 @@ LL |         use_self_expand!(); // Should lint in local macros
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:148:21
+  --> $DIR/use_self.rs:149:21
    |
 LL |         fn baz() -> Foo {
    |                     ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:149:13
+  --> $DIR/use_self.rs:150:13
    |
 LL |             Foo {}
    |             ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:136:29
+  --> $DIR/use_self.rs:137:29
    |
 LL |                 fn bar() -> Bar {
    |                             ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:137:21
+  --> $DIR/use_self.rs:138:21
    |
 LL |                     Bar { foo: Foo {} }
    |                     ^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:166:21
+  --> $DIR/use_self.rs:167:21
    |
 LL |             let _ = Enum::B(42);
    |                     ^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:167:21
+  --> $DIR/use_self.rs:168:21
    |
 LL |             let _ = Enum::C { field: true };
    |                     ^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:168:21
+  --> $DIR/use_self.rs:169:21
    |
 LL |             let _ = Enum::A;
    |                     ^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:199:13
+  --> $DIR/use_self.rs:200:13
    |
 LL |             nested::A::fun_1();
    |             ^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:200:13
+  --> $DIR/use_self.rs:201:13
    |
 LL |             nested::A::A;
    |             ^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:202:13
+  --> $DIR/use_self.rs:203:13
    |
 LL |             nested::A {};
    |             ^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:221:13
+  --> $DIR/use_self.rs:222:13
    |
 LL |             TestStruct::from_something()
    |             ^^^^^^^^^^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:235:25
+  --> $DIR/use_self.rs:236:25
    |
 LL |         async fn g() -> S {
    |                         ^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:236:13
+  --> $DIR/use_self.rs:237:13
    |
 LL |             S {}
    |             ^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:240:16
+  --> $DIR/use_self.rs:241:16
    |
 LL |             &p[S::A..S::B]
    |                ^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> $DIR/use_self.rs:240:22
+  --> $DIR/use_self.rs:241:22
    |
 LL |             &p[S::A..S::B]
    |                      ^ help: use the applicable keyword: `Self`
diff --git a/src/tools/clippy/tests/ui/wrong_self_convention.rs b/src/tools/clippy/tests/ui/wrong_self_convention.rs
index f44305d7e48..5282eba74fd 100644
--- a/src/tools/clippy/tests/ui/wrong_self_convention.rs
+++ b/src/tools/clippy/tests/ui/wrong_self_convention.rs
@@ -88,3 +88,52 @@ mod issue4037 {
         }
     }
 }
+
+// Lint also in trait definition (see #6307)
+mod issue6307 {
+    trait T: Sized {
+        fn as_i32(self) {}
+        fn as_u32(&self) {}
+        fn into_i32(&self) {}
+        fn into_u32(self) {}
+        fn is_i32(self) {}
+        fn is_u32(&self) {}
+        fn to_i32(self) {}
+        fn to_u32(&self) {}
+        fn from_i32(self) {}
+        // check whether the lint can be allowed at the function level
+        #[allow(clippy::wrong_self_convention)]
+        fn from_cake(self) {}
+
+        // test for false positives
+        fn as_(self) {}
+        fn into_(&self) {}
+        fn is_(self) {}
+        fn to_(self) {}
+        fn from_(self) {}
+        fn to_mut(&mut self) {}
+    }
+
+    trait U {
+        fn as_i32(self);
+        fn as_u32(&self);
+        fn into_i32(&self);
+        fn into_u32(self);
+        fn is_i32(self);
+        fn is_u32(&self);
+        fn to_i32(self);
+        fn to_u32(&self);
+        fn from_i32(self);
+        // check whether the lint can be allowed at the function level
+        #[allow(clippy::wrong_self_convention)]
+        fn from_cake(self);
+
+        // test for false positives
+        fn as_(self);
+        fn into_(&self);
+        fn is_(self);
+        fn to_(self);
+        fn from_(self);
+        fn to_mut(&mut self);
+    }
+}
diff --git a/src/tools/clippy/tests/ui/wrong_self_convention.stderr b/src/tools/clippy/tests/ui/wrong_self_convention.stderr
index ef3ad73ebc7..86467eb0fc7 100644
--- a/src/tools/clippy/tests/ui/wrong_self_convention.stderr
+++ b/src/tools/clippy/tests/ui/wrong_self_convention.stderr
@@ -72,5 +72,65 @@ error: methods called `from_*` usually take no self; consider choosing a less am
 LL |     pub fn from_i64(self) {}
    |                     ^^^^
 
-error: aborting due to 12 previous errors
+error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name
+  --> $DIR/wrong_self_convention.rs:95:19
+   |
+LL |         fn as_i32(self) {}
+   |                   ^^^^
+
+error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name
+  --> $DIR/wrong_self_convention.rs:97:21
+   |
+LL |         fn into_i32(&self) {}
+   |                     ^^^^^
+
+error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name
+  --> $DIR/wrong_self_convention.rs:99:19
+   |
+LL |         fn is_i32(self) {}
+   |                   ^^^^
+
+error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name
+  --> $DIR/wrong_self_convention.rs:101:19
+   |
+LL |         fn to_i32(self) {}
+   |                   ^^^^
+
+error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
+  --> $DIR/wrong_self_convention.rs:103:21
+   |
+LL |         fn from_i32(self) {}
+   |                     ^^^^
+
+error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name
+  --> $DIR/wrong_self_convention.rs:118:19
+   |
+LL |         fn as_i32(self);
+   |                   ^^^^
+
+error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name
+  --> $DIR/wrong_self_convention.rs:120:21
+   |
+LL |         fn into_i32(&self);
+   |                     ^^^^^
+
+error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name
+  --> $DIR/wrong_self_convention.rs:122:19
+   |
+LL |         fn is_i32(self);
+   |                   ^^^^
+
+error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name
+  --> $DIR/wrong_self_convention.rs:124:19
+   |
+LL |         fn to_i32(self);
+   |                   ^^^^
+
+error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
+  --> $DIR/wrong_self_convention.rs:126:21
+   |
+LL |         fn from_i32(self);
+   |                     ^^^^
+
+error: aborting due to 22 previous errors
 
diff --git a/src/tools/clippy/tests/ui/zero_sized_btreemap_values.rs b/src/tools/clippy/tests/ui/zero_sized_btreemap_values.rs
new file mode 100644
index 00000000000..5cd254787d8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/zero_sized_btreemap_values.rs
@@ -0,0 +1,68 @@
+#![warn(clippy::zero_sized_map_values)]
+use std::collections::BTreeMap;
+
+const CONST_OK: Option<BTreeMap<String, usize>> = None;
+const CONST_NOT_OK: Option<BTreeMap<String, ()>> = None;
+
+static STATIC_OK: Option<BTreeMap<String, usize>> = None;
+static STATIC_NOT_OK: Option<BTreeMap<String, ()>> = None;
+
+type OkMap = BTreeMap<String, usize>;
+type NotOkMap = BTreeMap<String, ()>;
+
+enum TestEnum {
+    Ok(BTreeMap<String, usize>),
+    NotOk(BTreeMap<String, ()>),
+}
+
+struct Test {
+    ok: BTreeMap<String, usize>,
+    not_ok: BTreeMap<String, ()>,
+
+    also_not_ok: Vec<BTreeMap<usize, ()>>,
+}
+
+trait TestTrait {
+    type Output;
+
+    fn produce_output() -> Self::Output;
+
+    fn weird_map(&self, map: BTreeMap<usize, ()>);
+}
+
+impl Test {
+    fn ok(&self) -> BTreeMap<String, usize> {
+        todo!()
+    }
+
+    fn not_ok(&self) -> BTreeMap<String, ()> {
+        todo!()
+    }
+}
+
+impl TestTrait for Test {
+    type Output = BTreeMap<String, ()>;
+
+    fn produce_output() -> Self::Output {
+        todo!();
+    }
+
+    fn weird_map(&self, map: BTreeMap<usize, ()>) {
+        todo!();
+    }
+}
+
+fn test(map: BTreeMap<String, ()>, key: &str) -> BTreeMap<String, ()> {
+    todo!();
+}
+
+fn test2(map: BTreeMap<String, usize>, key: &str) -> BTreeMap<String, usize> {
+    todo!();
+}
+
+fn main() {
+    let _: BTreeMap<String, ()> = BTreeMap::new();
+    let _: BTreeMap<String, usize> = BTreeMap::new();
+
+    let _: BTreeMap<_, _> = std::iter::empty::<(String, ())>().collect();
+}
diff --git a/src/tools/clippy/tests/ui/zero_sized_btreemap_values.stderr b/src/tools/clippy/tests/ui/zero_sized_btreemap_values.stderr
new file mode 100644
index 00000000000..334d921a9af
--- /dev/null
+++ b/src/tools/clippy/tests/ui/zero_sized_btreemap_values.stderr
@@ -0,0 +1,107 @@
+error: map with zero-sized value type
+  --> $DIR/zero_sized_btreemap_values.rs:5:28
+   |
+LL | const CONST_NOT_OK: Option<BTreeMap<String, ()>> = None;
+   |                            ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::zero-sized-map-values` implied by `-D warnings`
+   = help: consider using a set instead
+
+error: map with zero-sized value type
+  --> $DIR/zero_sized_btreemap_values.rs:8:30
+   |
+LL | static STATIC_NOT_OK: Option<BTreeMap<String, ()>> = None;
+   |                              ^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using a set instead
+
+error: map with zero-sized value type
+  --> $DIR/zero_sized_btreemap_values.rs:11:17
+   |
+LL | type NotOkMap = BTreeMap<String, ()>;
+   |                 ^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using a set instead
+
+error: map with zero-sized value type
+  --> $DIR/zero_sized_btreemap_values.rs:15:11
+   |
+LL |     NotOk(BTreeMap<String, ()>),
+   |           ^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using a set instead
+
+error: map with zero-sized value type
+  --> $DIR/zero_sized_btreemap_values.rs:20:13
+   |
+LL |     not_ok: BTreeMap<String, ()>,
+   |             ^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using a set instead
+
+error: map with zero-sized value type
+  --> $DIR/zero_sized_btreemap_values.rs:22:22
+   |
+LL |     also_not_ok: Vec<BTreeMap<usize, ()>>,
+   |                      ^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using a set instead
+
+error: map with zero-sized value type
+  --> $DIR/zero_sized_btreemap_values.rs:30:30
+   |
+LL |     fn weird_map(&self, map: BTreeMap<usize, ()>);
+   |                              ^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using a set instead
+
+error: map with zero-sized value type
+  --> $DIR/zero_sized_btreemap_values.rs:38:25
+   |
+LL |     fn not_ok(&self) -> BTreeMap<String, ()> {
+   |                         ^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using a set instead
+
+error: map with zero-sized value type
+  --> $DIR/zero_sized_btreemap_values.rs:55:14
+   |
+LL | fn test(map: BTreeMap<String, ()>, key: &str) -> BTreeMap<String, ()> {
+   |              ^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using a set instead
+
+error: map with zero-sized value type
+  --> $DIR/zero_sized_btreemap_values.rs:55:50
+   |
+LL | fn test(map: BTreeMap<String, ()>, key: &str) -> BTreeMap<String, ()> {
+   |                                                  ^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using a set instead
+
+error: map with zero-sized value type
+  --> $DIR/zero_sized_btreemap_values.rs:64:35
+   |
+LL |     let _: BTreeMap<String, ()> = BTreeMap::new();
+   |                                   ^^^^^^^^^^^^^
+   |
+   = help: consider using a set instead
+
+error: map with zero-sized value type
+  --> $DIR/zero_sized_btreemap_values.rs:64:12
+   |
+LL |     let _: BTreeMap<String, ()> = BTreeMap::new();
+   |            ^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using a set instead
+
+error: map with zero-sized value type
+  --> $DIR/zero_sized_btreemap_values.rs:67:12
+   |
+LL |     let _: BTreeMap<_, _> = std::iter::empty::<(String, ())>().collect();
+   |            ^^^^^^^^^^^^^^
+   |
+   = help: consider using a set instead
+
+error: aborting due to 13 previous errors
+
diff --git a/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs
new file mode 100644
index 00000000000..a1608d863fb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs
@@ -0,0 +1,68 @@
+#![warn(clippy::zero_sized_map_values)]
+use std::collections::HashMap;
+
+const CONST_OK: Option<HashMap<String, usize>> = None;
+const CONST_NOT_OK: Option<HashMap<String, ()>> = None;
+
+static STATIC_OK: Option<HashMap<String, usize>> = None;
+static STATIC_NOT_OK: Option<HashMap<String, ()>> = None;
+
+type OkMap = HashMap<String, usize>;
+type NotOkMap = HashMap<String, ()>;
+
+enum TestEnum {
+    Ok(HashMap<String, usize>),
+    NotOk(HashMap<String, ()>),
+}
+
+struct Test {
+    ok: HashMap<String, usize>,
+    not_ok: HashMap<String, ()>,
+
+    also_not_ok: Vec<HashMap<usize, ()>>,
+}
+
+trait TestTrait {
+    type Output;
+
+    fn produce_output() -> Self::Output;
+
+    fn weird_map(&self, map: HashMap<usize, ()>);
+}
+
+impl Test {
+    fn ok(&self) -> HashMap<String, usize> {
+        todo!()
+    }
+
+    fn not_ok(&self) -> HashMap<String, ()> {
+        todo!()
+    }
+}
+
+impl TestTrait for Test {
+    type Output = HashMap<String, ()>;
+
+    fn produce_output() -> Self::Output {
+        todo!();
+    }
+
+    fn weird_map(&self, map: HashMap<usize, ()>) {
+        todo!();
+    }
+}
+
+fn test(map: HashMap<String, ()>, key: &str) -> HashMap<String, ()> {
+    todo!();
+}
+
+fn test2(map: HashMap<String, usize>, key: &str) -> HashMap<String, usize> {
+    todo!();
+}
+
+fn main() {
+    let _: HashMap<String, ()> = HashMap::new();
+    let _: HashMap<String, usize> = HashMap::new();
+
+    let _: HashMap<_, _> = std::iter::empty::<(String, ())>().collect();
+}
diff --git a/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr
new file mode 100644
index 00000000000..43987b3d01d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr
@@ -0,0 +1,107 @@
+error: map with zero-sized value type
+  --> $DIR/zero_sized_hashmap_values.rs:5:28
+   |
+LL | const CONST_NOT_OK: Option<HashMap<String, ()>> = None;
+   |                            ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::zero-sized-map-values` implied by `-D warnings`
+   = help: consider using a set instead
+
+error: map with zero-sized value type
+  --> $DIR/zero_sized_hashmap_values.rs:8:30
+   |
+LL | static STATIC_NOT_OK: Option<HashMap<String, ()>> = None;
+   |                              ^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using a set instead
+
+error: map with zero-sized value type
+  --> $DIR/zero_sized_hashmap_values.rs:11:17
+   |
+LL | type NotOkMap = HashMap<String, ()>;
+   |                 ^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using a set instead
+
+error: map with zero-sized value type
+  --> $DIR/zero_sized_hashmap_values.rs:15:11
+   |
+LL |     NotOk(HashMap<String, ()>),
+   |           ^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using a set instead
+
+error: map with zero-sized value type
+  --> $DIR/zero_sized_hashmap_values.rs:20:13
+   |
+LL |     not_ok: HashMap<String, ()>,
+   |             ^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using a set instead
+
+error: map with zero-sized value type
+  --> $DIR/zero_sized_hashmap_values.rs:22:22
+   |
+LL |     also_not_ok: Vec<HashMap<usize, ()>>,
+   |                      ^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using a set instead
+
+error: map with zero-sized value type
+  --> $DIR/zero_sized_hashmap_values.rs:30:30
+   |
+LL |     fn weird_map(&self, map: HashMap<usize, ()>);
+   |                              ^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using a set instead
+
+error: map with zero-sized value type
+  --> $DIR/zero_sized_hashmap_values.rs:38:25
+   |
+LL |     fn not_ok(&self) -> HashMap<String, ()> {
+   |                         ^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using a set instead
+
+error: map with zero-sized value type
+  --> $DIR/zero_sized_hashmap_values.rs:55:14
+   |
+LL | fn test(map: HashMap<String, ()>, key: &str) -> HashMap<String, ()> {
+   |              ^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using a set instead
+
+error: map with zero-sized value type
+  --> $DIR/zero_sized_hashmap_values.rs:55:49
+   |
+LL | fn test(map: HashMap<String, ()>, key: &str) -> HashMap<String, ()> {
+   |                                                 ^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using a set instead
+
+error: map with zero-sized value type
+  --> $DIR/zero_sized_hashmap_values.rs:64:34
+   |
+LL |     let _: HashMap<String, ()> = HashMap::new();
+   |                                  ^^^^^^^^^^^^
+   |
+   = help: consider using a set instead
+
+error: map with zero-sized value type
+  --> $DIR/zero_sized_hashmap_values.rs:64:12
+   |
+LL |     let _: HashMap<String, ()> = HashMap::new();
+   |            ^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using a set instead
+
+error: map with zero-sized value type
+  --> $DIR/zero_sized_hashmap_values.rs:67:12
+   |
+LL |     let _: HashMap<_, _> = std::iter::empty::<(String, ())>().collect();
+   |            ^^^^^^^^^^^^^
+   |
+   = help: consider using a set instead
+
+error: aborting due to 13 previous errors
+
diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml
index b7b20b40e68..b9549be3a8b 100644
--- a/src/tools/clippy/triagebot.toml
+++ b/src/tools/clippy/triagebot.toml
@@ -1,7 +1,7 @@
 [relabel]
 allow-unauthenticated = [
     "A-*", "C-*", "E-*", "L-*", "M-*", "O-*", "P-*", "S-*", "T-*",
-    "good first issue"
+    "good-first-issue"
 ]
 
 [assign]
diff --git a/src/tools/clippy/util/gh-pages/index.html b/src/tools/clippy/util/gh-pages/index.html
index e11f2eeba3b..428708136cb 100644
--- a/src/tools/clippy/util/gh-pages/index.html
+++ b/src/tools/clippy/util/gh-pages/index.html
@@ -89,7 +89,7 @@
             </div>
 
             <article class="panel panel-default" id="{{lint.id}}"
-                ng-repeat="lint in data | filter:byLevels | filter:byGroups | filter:search | orderBy:'id' track by lint.id" on-finish-render="ngRepeatFinished">
+                ng-repeat="lint in data | filter:byLevels | filter:byGroups | filter:bySearch | orderBy:'id' track by lint.id">
                 <header class="panel-heading" ng-click="open[lint.id] = !open[lint.id]">
                     <h2 class="panel-title">
                         <div class="panel-title-name">
@@ -215,6 +215,46 @@
                 return $scope.groups[lint.group];
             };
 
+            $scope.bySearch = function (lint, index, array) {
+                let search_str = $scope.search;
+                // It can be `null` I haven't missed this value 
+                if (search_str == null || search_str.length == 0) {
+                    return true;
+                }
+                search_str = search_str.toLowerCase();
+
+                // Search by id
+                let id_search = search_str.trim().replace(/(\-| )/g, "_");
+                if (lint.id.includes(id_search)) {
+                    return true;
+                }
+
+                // Search the description
+                // The use of `for`-loops instead of `foreach` enables us to return early 
+                let search_lint = (lint, therm) => {
+                    for (const field in lint.docs) {
+                        // Continue if it's not a property
+                        if (!lint.docs.hasOwnProperty(field)) {
+                            continue;
+                        }
+
+                        // Return if not found
+                        if (lint.docs[field].toLowerCase().includes(therm)) {
+                            return true;
+                        }
+                    }
+                    return false;
+                };
+                let therms = search_str.split(" ");
+                for (index = 0; index < therms.length; index++) {
+                    if (!search_lint(lint, therms[index])) {
+                        return false;
+                    }
+                }
+
+                return true;
+            }
+
             // Get data
             $scope.open = {};
             $scope.loading = true;