diff --git a/Cargo.lock b/Cargo.lock
index 905f523aa53d6..5309c03ee23ae 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -94,6 +94,12 @@ dependencies = [
  "nodrop",
 ]
 
+[[package]]
+name = "arrayvec"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
+
 [[package]]
 name = "atty"
 version = "0.2.14"
@@ -164,7 +170,7 @@ version = "0.2.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400"
 dependencies = [
- "arrayvec",
+ "arrayvec 0.4.7",
  "constant_time_eq",
 ]
 
@@ -714,7 +720,7 @@ version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9"
 dependencies = [
- "arrayvec",
+ "arrayvec 0.4.7",
  "cfg-if",
  "crossbeam-utils 0.6.5",
  "lazy_static",
@@ -3494,8 +3500,8 @@ dependencies = [
 name = "rustc_index"
 version = "0.0.0"
 dependencies = [
+ "arrayvec 0.5.1",
  "rustc_serialize",
- "smallvec 1.4.0",
 ]
 
 [[package]]
@@ -3996,6 +4002,7 @@ dependencies = [
  "rustc_data_structures",
  "rustc_errors",
  "rustc_hir",
+ "rustc_hir_pretty",
  "rustc_index",
  "rustc_infer",
  "rustc_middle",
diff --git a/src/liballoc/task.rs b/src/liballoc/task.rs
index 0d1cc99df47c5..252e04a410548 100644
--- a/src/liballoc/task.rs
+++ b/src/liballoc/task.rs
@@ -69,14 +69,13 @@ fn raw_waker<W: Wake + Send + Sync + 'static>(waker: Arc<W>) -> RawWaker {
 
     // Wake by value, moving the Arc into the Wake::wake function
     unsafe fn wake<W: Wake + Send + Sync + 'static>(waker: *const ()) {
-        let waker: Arc<W> = unsafe { Arc::from_raw(waker as *const W) };
+        let waker = unsafe { Arc::from_raw(waker as *const W) };
         <W as Wake>::wake(waker);
     }
 
     // Wake by reference, wrap the waker in ManuallyDrop to avoid dropping it
     unsafe fn wake_by_ref<W: Wake + Send + Sync + 'static>(waker: *const ()) {
-        let waker: ManuallyDrop<Arc<W>> =
-            unsafe { ManuallyDrop::new(Arc::from_raw(waker as *const W)) };
+        let waker = unsafe { ManuallyDrop::new(Arc::from_raw(waker as *const W)) };
         <W as Wake>::wake_by_ref(&waker);
     }
 
diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs
index 820c0a49e7f03..c7496c209bcb0 100644
--- a/src/libcore/lib.rs
+++ b/src/libcore/lib.rs
@@ -148,6 +148,7 @@
 #![feature(associated_type_bounds)]
 #![feature(const_type_id)]
 #![feature(const_caller_location)]
+#![feature(slice_ptr_get)]
 #![feature(no_niche)] // rust-lang/rust#68303
 #![feature(unsafe_block_in_unsafe_fn)]
 #![deny(unsafe_op_in_unsafe_fn)]
diff --git a/src/libcore/ptr/const_ptr.rs b/src/libcore/ptr/const_ptr.rs
index d1d7a71523822..39d4aca636a05 100644
--- a/src/libcore/ptr/const_ptr.rs
+++ b/src/libcore/ptr/const_ptr.rs
@@ -2,6 +2,7 @@ use super::*;
 use crate::cmp::Ordering::{self, Equal, Greater, Less};
 use crate::intrinsics;
 use crate::mem;
+use crate::slice::SliceIndex;
 
 #[lang = "const_ptr"]
 impl<T: ?Sized> *const T {
@@ -826,6 +827,55 @@ impl<T> *const [T] {
         // Only `std` can make this guarantee.
         unsafe { Repr { rust: self }.raw }.len
     }
+
+    /// Returns a raw pointer to the slice's buffer.
+    ///
+    /// This is equivalent to casting `self` to `*const T`, but more type-safe.
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// #![feature(slice_ptr_get)]
+    /// use std::ptr;
+    ///
+    /// let slice: *const [i8] = ptr::slice_from_raw_parts(ptr::null(), 3);
+    /// assert_eq!(slice.as_ptr(), 0 as *const i8);
+    /// ```
+    #[inline]
+    #[unstable(feature = "slice_ptr_get", issue = "74265")]
+    #[rustc_const_unstable(feature = "slice_ptr_get", issue = "74265")]
+    pub const fn as_ptr(self) -> *const T {
+        self as *const T
+    }
+
+    /// Returns a raw pointer to an element or subslice, without doing bounds
+    /// checking.
+    ///
+    /// Calling this method with an out-of-bounds index or when `self` is not dereferencable
+    /// is *[undefined behavior]* even if the resulting pointer is not used.
+    ///
+    /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(slice_ptr_get)]
+    ///
+    /// let x = &[1, 2, 4] as *const [i32];
+    ///
+    /// unsafe {
+    ///     assert_eq!(x.get_unchecked(1), x.as_ptr().add(1));
+    /// }
+    /// ```
+    #[unstable(feature = "slice_ptr_get", issue = "74265")]
+    #[inline]
+    pub unsafe fn get_unchecked<I>(self, index: I) -> *const I::Output
+    where
+        I: SliceIndex<[T]>,
+    {
+        // SAFETY: the caller ensures that `self` is dereferencable and `index` in-bounds.
+        unsafe { index.get_unchecked(self) }
+    }
 }
 
 // Equality for pointers
diff --git a/src/libcore/ptr/mut_ptr.rs b/src/libcore/ptr/mut_ptr.rs
index 7d4b6339b511f..644465d7d17f8 100644
--- a/src/libcore/ptr/mut_ptr.rs
+++ b/src/libcore/ptr/mut_ptr.rs
@@ -1,6 +1,7 @@
 use super::*;
 use crate::cmp::Ordering::{self, Equal, Greater, Less};
 use crate::intrinsics;
+use crate::slice::SliceIndex;
 
 #[lang = "mut_ptr"]
 impl<T: ?Sized> *mut T {
@@ -1014,7 +1015,6 @@ impl<T> *mut [T] {
     ///
     /// ```rust
     /// #![feature(slice_ptr_len)]
-    ///
     /// use std::ptr;
     ///
     /// let slice: *mut [i8] = ptr::slice_from_raw_parts_mut(ptr::null_mut(), 3);
@@ -1028,6 +1028,55 @@ impl<T> *mut [T] {
         // Only `std` can make this guarantee.
         unsafe { Repr { rust_mut: self }.raw }.len
     }
+
+    /// Returns a raw pointer to the slice's buffer.
+    ///
+    /// This is equivalent to casting `self` to `*mut T`, but more type-safe.
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// #![feature(slice_ptr_get)]
+    /// use std::ptr;
+    ///
+    /// let slice: *mut [i8] = ptr::slice_from_raw_parts_mut(ptr::null_mut(), 3);
+    /// assert_eq!(slice.as_mut_ptr(), 0 as *mut i8);
+    /// ```
+    #[inline]
+    #[unstable(feature = "slice_ptr_get", issue = "74265")]
+    #[rustc_const_unstable(feature = "slice_ptr_get", issue = "74265")]
+    pub const fn as_mut_ptr(self) -> *mut T {
+        self as *mut T
+    }
+
+    /// Returns a raw pointer to an element or subslice, without doing bounds
+    /// checking.
+    ///
+    /// Calling this method with an out-of-bounds index or when `self` is not dereferencable
+    /// is *[undefined behavior]* even if the resulting pointer is not used.
+    ///
+    /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(slice_ptr_get)]
+    ///
+    /// let x = &mut [1, 2, 4] as *mut [i32];
+    ///
+    /// unsafe {
+    ///     assert_eq!(x.get_unchecked_mut(1), x.as_mut_ptr().add(1));
+    /// }
+    /// ```
+    #[unstable(feature = "slice_ptr_get", issue = "74265")]
+    #[inline]
+    pub unsafe fn get_unchecked_mut<I>(self, index: I) -> *mut I::Output
+    where
+        I: SliceIndex<[T]>,
+    {
+        // SAFETY: the caller ensures that `self` is dereferencable and `index` in-bounds.
+        unsafe { index.get_unchecked_mut(self) }
+    }
 }
 
 // Equality for pointers
diff --git a/src/libcore/ptr/non_null.rs b/src/libcore/ptr/non_null.rs
index c2d31bfb6a4ee..b362a49d604e7 100644
--- a/src/libcore/ptr/non_null.rs
+++ b/src/libcore/ptr/non_null.rs
@@ -6,6 +6,7 @@ use crate::marker::Unsize;
 use crate::mem;
 use crate::ops::{CoerceUnsized, DispatchFromDyn};
 use crate::ptr::Unique;
+use crate::slice::SliceIndex;
 
 /// `*mut T` but non-zero and covariant.
 ///
@@ -192,7 +193,6 @@ impl<T> NonNull<[T]> {
     ///
     /// ```rust
     /// #![feature(slice_ptr_len, nonnull_slice_from_raw_parts)]
-    ///
     /// use std::ptr::NonNull;
     ///
     /// let slice: NonNull<[i8]> = NonNull::slice_from_raw_parts(NonNull::dangling(), 3);
@@ -204,6 +204,57 @@ impl<T> NonNull<[T]> {
     pub const fn len(self) -> usize {
         self.as_ptr().len()
     }
+
+    /// Returns a non-null pointer to the slice's buffer.
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// #![feature(slice_ptr_get, nonnull_slice_from_raw_parts)]
+    /// use std::ptr::NonNull;
+    ///
+    /// let slice: NonNull<[i8]> = NonNull::slice_from_raw_parts(NonNull::dangling(), 3);
+    /// assert_eq!(slice.as_non_null_ptr(), NonNull::new(1 as *mut i8).unwrap());
+    /// ```
+    #[inline]
+    #[unstable(feature = "slice_ptr_get", issue = "74265")]
+    #[rustc_const_unstable(feature = "slice_ptr_get", issue = "74265")]
+    pub const fn as_non_null_ptr(self) -> NonNull<T> {
+        // SAFETY: We know `self` is non-null.
+        unsafe { NonNull::new_unchecked(self.as_ptr().as_mut_ptr()) }
+    }
+
+    /// Returns a raw pointer to an element or subslice, without doing bounds
+    /// checking.
+    ///
+    /// Calling this method with an out-of-bounds index or when `self` is not dereferencable
+    /// is *[undefined behavior]* even if the resulting pointer is not used.
+    ///
+    /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(slice_ptr_get, nonnull_slice_from_raw_parts)]
+    /// use std::ptr::NonNull;
+    ///
+    /// let x = &mut [1, 2, 4];
+    /// let x = NonNull::slice_from_raw_parts(NonNull::new(x.as_mut_ptr()).unwrap(), x.len());
+    ///
+    /// unsafe {
+    ///     assert_eq!(x.get_unchecked_mut(1).as_ptr(), x.as_non_null_ptr().as_ptr().add(1));
+    /// }
+    /// ```
+    #[unstable(feature = "slice_ptr_get", issue = "74265")]
+    #[inline]
+    pub unsafe fn get_unchecked_mut<I>(self, index: I) -> NonNull<I::Output>
+    where
+        I: SliceIndex<[T]>,
+    {
+        // SAFETY: the caller ensures that `self` is dereferencable and `index` in-bounds.
+        // As a consequence, the resulting pointer cannot be NULL.
+        unsafe { NonNull::new_unchecked(self.as_ptr().get_unchecked_mut(index)) }
+    }
 }
 
 #[stable(feature = "nonnull", since = "1.25.0")]
diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs
index bed8495993f43..0e202bb7801cf 100644
--- a/src/libcore/slice/mod.rs
+++ b/src/libcore/slice/mod.rs
@@ -310,8 +310,10 @@ impl<T> [T] {
     where
         I: SliceIndex<Self>,
     {
-        // SAFETY: the caller must uphold the safety requirements for `get_unchecked`.
-        unsafe { index.get_unchecked(self) }
+        // SAFETY: the caller must uphold most of the safety requirements for `get_unchecked`;
+        // the slice is dereferencable because `self` is a safe reference.
+        // The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is.
+        unsafe { &*index.get_unchecked(self) }
     }
 
     /// Returns a mutable reference to an element or subslice, without doing
@@ -342,8 +344,10 @@ impl<T> [T] {
     where
         I: SliceIndex<Self>,
     {
-        // SAFETY: the caller must uphold the safety requirements for `get_unchecked_mut`.
-        unsafe { index.get_unchecked_mut(self) }
+        // SAFETY: the caller must uphold the safety requirements for `get_unchecked_mut`;
+        // the slice is dereferencable because `self` is a safe reference.
+        // The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is.
+        unsafe { &mut *index.get_unchecked_mut(self) }
     }
 
     /// Returns a raw pointer to the slice's buffer.
@@ -3010,6 +3014,9 @@ mod private_slice_index {
 }
 
 /// A helper trait used for indexing operations.
+///
+/// Implementations of this trait have to promise that if the argument
+/// to `get_(mut_)unchecked` is a safe reference, then so is the result.
 #[stable(feature = "slice_get_slice", since = "1.28.0")]
 #[rustc_on_unimplemented(
     on(T = "str", label = "string indices are ranges of `usize`",),
@@ -3021,7 +3028,7 @@ see chapter in The Book <https://doc.rust-lang.org/book/ch08-02-strings.html#ind
     message = "the type `{T}` cannot be indexed by `{Self}`",
     label = "slice indices are of type `usize` or ranges of `usize`"
 )]
-pub trait SliceIndex<T: ?Sized>: private_slice_index::Sealed {
+pub unsafe trait SliceIndex<T: ?Sized>: private_slice_index::Sealed {
     /// The output type returned by methods.
     #[stable(feature = "slice_get_slice", since = "1.28.0")]
     type Output: ?Sized;
@@ -3038,21 +3045,21 @@ pub trait SliceIndex<T: ?Sized>: private_slice_index::Sealed {
 
     /// Returns a shared reference to the output at this location, without
     /// performing any bounds checking.
-    /// Calling this method with an out-of-bounds index is *[undefined behavior]*
-    /// even if the resulting reference is not used.
+    /// Calling this method with an out-of-bounds index or a dangling `slice` pointer
+    /// is *[undefined behavior]* even if the resulting reference is not used.
     ///
     /// [undefined behavior]: ../../reference/behavior-considered-undefined.html
     #[unstable(feature = "slice_index_methods", issue = "none")]
-    unsafe fn get_unchecked(self, slice: &T) -> &Self::Output;
+    unsafe fn get_unchecked(self, slice: *const T) -> *const Self::Output;
 
     /// Returns a mutable reference to the output at this location, without
     /// performing any bounds checking.
-    /// Calling this method with an out-of-bounds index is *[undefined behavior]*
-    /// even if the resulting reference is not used.
+    /// Calling this method with an out-of-bounds index or a dangling `slice` pointer
+    /// is *[undefined behavior]* even if the resulting reference is not used.
     ///
     /// [undefined behavior]: ../../reference/behavior-considered-undefined.html
     #[unstable(feature = "slice_index_methods", issue = "none")]
-    unsafe fn get_unchecked_mut(self, slice: &mut T) -> &mut Self::Output;
+    unsafe fn get_unchecked_mut(self, slice: *mut T) -> *mut Self::Output;
 
     /// Returns a shared reference to the output at this location, panicking
     /// if out of bounds.
@@ -3068,33 +3075,32 @@ pub trait SliceIndex<T: ?Sized>: private_slice_index::Sealed {
 }
 
 #[stable(feature = "slice_get_slice_impls", since = "1.15.0")]
-impl<T> SliceIndex<[T]> for usize {
+unsafe impl<T> SliceIndex<[T]> for usize {
     type Output = T;
 
     #[inline]
     fn get(self, slice: &[T]) -> Option<&T> {
-        if self < slice.len() { unsafe { Some(self.get_unchecked(slice)) } } else { None }
+        if self < slice.len() { unsafe { Some(&*self.get_unchecked(slice)) } } else { None }
     }
 
     #[inline]
     fn get_mut(self, slice: &mut [T]) -> Option<&mut T> {
-        if self < slice.len() { unsafe { Some(self.get_unchecked_mut(slice)) } } else { None }
+        if self < slice.len() { unsafe { Some(&mut *self.get_unchecked_mut(slice)) } } else { None }
     }
 
     #[inline]
-    unsafe fn get_unchecked(self, slice: &[T]) -> &T {
-        // SAFETY: `slice` cannot be longer than `isize::MAX` and
-        // the caller guarantees that `self` is in bounds of `slice`
-        // so `self` cannot overflow an `isize`, so the call to `add` is safe.
-        // The obtained pointer comes from a reference which is guaranteed
-        // to be valid.
-        unsafe { &*slice.as_ptr().add(self) }
+    unsafe fn get_unchecked(self, slice: *const [T]) -> *const T {
+        // SAFETY: the caller guarantees that `slice` is not dangling, so it
+        // cannot be longer than `isize::MAX`. They also guarantee that
+        // `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
+        // so the call to `add` is safe.
+        unsafe { slice.as_ptr().add(self) }
     }
 
     #[inline]
-    unsafe fn get_unchecked_mut(self, slice: &mut [T]) -> &mut T {
+    unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut T {
         // SAFETY: see comments for `get_unchecked` above.
-        unsafe { &mut *slice.as_mut_ptr().add(self) }
+        unsafe { slice.as_mut_ptr().add(self) }
     }
 
     #[inline]
@@ -3111,7 +3117,7 @@ impl<T> SliceIndex<[T]> for usize {
 }
 
 #[stable(feature = "slice_get_slice_impls", since = "1.15.0")]
-impl<T> SliceIndex<[T]> for ops::Range<usize> {
+unsafe impl<T> SliceIndex<[T]> for ops::Range<usize> {
     type Output = [T];
 
     #[inline]
@@ -3119,7 +3125,7 @@ impl<T> SliceIndex<[T]> for ops::Range<usize> {
         if self.start > self.end || self.end > slice.len() {
             None
         } else {
-            unsafe { Some(self.get_unchecked(slice)) }
+            unsafe { Some(&*self.get_unchecked(slice)) }
         }
     }
 
@@ -3128,24 +3134,25 @@ impl<T> SliceIndex<[T]> for ops::Range<usize> {
         if self.start > self.end || self.end > slice.len() {
             None
         } else {
-            unsafe { Some(self.get_unchecked_mut(slice)) }
+            unsafe { Some(&mut *self.get_unchecked_mut(slice)) }
         }
     }
 
     #[inline]
-    unsafe fn get_unchecked(self, slice: &[T]) -> &[T] {
-        // SAFETY: `slice` cannot be longer than `isize::MAX` and
-        // the caller guarantees that `self` is in bounds of `slice`
-        // so `self` cannot overflow an `isize`, so the call to `add` is safe.
-        // Also, since the caller guarantees that `self` is in bounds of `slice`,
-        // `from_raw_parts` will give a subslice of `slice` which is always safe.
-        unsafe { from_raw_parts(slice.as_ptr().add(self.start), self.end - self.start) }
+    unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
+        // SAFETY: the caller guarantees that `slice` is not dangling, so it
+        // cannot be longer than `isize::MAX`. They also guarantee that
+        // `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
+        // so the call to `add` is safe.
+        unsafe { ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), self.end - self.start) }
     }
 
     #[inline]
-    unsafe fn get_unchecked_mut(self, slice: &mut [T]) -> &mut [T] {
+    unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] {
         // SAFETY: see comments for `get_unchecked` above.
-        unsafe { from_raw_parts_mut(slice.as_mut_ptr().add(self.start), self.end - self.start) }
+        unsafe {
+            ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), self.end - self.start)
+        }
     }
 
     #[inline]
@@ -3155,7 +3162,7 @@ impl<T> SliceIndex<[T]> for ops::Range<usize> {
         } else if self.end > slice.len() {
             slice_index_len_fail(self.end, slice.len());
         }
-        unsafe { self.get_unchecked(slice) }
+        unsafe { &*self.get_unchecked(slice) }
     }
 
     #[inline]
@@ -3165,12 +3172,12 @@ impl<T> SliceIndex<[T]> for ops::Range<usize> {
         } else if self.end > slice.len() {
             slice_index_len_fail(self.end, slice.len());
         }
-        unsafe { self.get_unchecked_mut(slice) }
+        unsafe { &mut *self.get_unchecked_mut(slice) }
     }
 }
 
 #[stable(feature = "slice_get_slice_impls", since = "1.15.0")]
-impl<T> SliceIndex<[T]> for ops::RangeTo<usize> {
+unsafe impl<T> SliceIndex<[T]> for ops::RangeTo<usize> {
     type Output = [T];
 
     #[inline]
@@ -3184,13 +3191,13 @@ impl<T> SliceIndex<[T]> for ops::RangeTo<usize> {
     }
 
     #[inline]
-    unsafe fn get_unchecked(self, slice: &[T]) -> &[T] {
+    unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
         // SAFETY: the caller has to uphold the safety contract for `get_unchecked`.
         unsafe { (0..self.end).get_unchecked(slice) }
     }
 
     #[inline]
-    unsafe fn get_unchecked_mut(self, slice: &mut [T]) -> &mut [T] {
+    unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] {
         // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`.
         unsafe { (0..self.end).get_unchecked_mut(slice) }
     }
@@ -3207,7 +3214,7 @@ impl<T> SliceIndex<[T]> for ops::RangeTo<usize> {
 }
 
 #[stable(feature = "slice_get_slice_impls", since = "1.15.0")]
-impl<T> SliceIndex<[T]> for ops::RangeFrom<usize> {
+unsafe impl<T> SliceIndex<[T]> for ops::RangeFrom<usize> {
     type Output = [T];
 
     #[inline]
@@ -3221,13 +3228,13 @@ impl<T> SliceIndex<[T]> for ops::RangeFrom<usize> {
     }
 
     #[inline]
-    unsafe fn get_unchecked(self, slice: &[T]) -> &[T] {
+    unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
         // SAFETY: the caller has to uphold the safety contract for `get_unchecked`.
         unsafe { (self.start..slice.len()).get_unchecked(slice) }
     }
 
     #[inline]
-    unsafe fn get_unchecked_mut(self, slice: &mut [T]) -> &mut [T] {
+    unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] {
         // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`.
         unsafe { (self.start..slice.len()).get_unchecked_mut(slice) }
     }
@@ -3244,7 +3251,7 @@ impl<T> SliceIndex<[T]> for ops::RangeFrom<usize> {
 }
 
 #[stable(feature = "slice_get_slice_impls", since = "1.15.0")]
-impl<T> SliceIndex<[T]> for ops::RangeFull {
+unsafe impl<T> SliceIndex<[T]> for ops::RangeFull {
     type Output = [T];
 
     #[inline]
@@ -3258,12 +3265,12 @@ impl<T> SliceIndex<[T]> for ops::RangeFull {
     }
 
     #[inline]
-    unsafe fn get_unchecked(self, slice: &[T]) -> &[T] {
+    unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
         slice
     }
 
     #[inline]
-    unsafe fn get_unchecked_mut(self, slice: &mut [T]) -> &mut [T] {
+    unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] {
         slice
     }
 
@@ -3279,7 +3286,7 @@ impl<T> SliceIndex<[T]> for ops::RangeFull {
 }
 
 #[stable(feature = "inclusive_range", since = "1.26.0")]
-impl<T> SliceIndex<[T]> for ops::RangeInclusive<usize> {
+unsafe impl<T> SliceIndex<[T]> for ops::RangeInclusive<usize> {
     type Output = [T];
 
     #[inline]
@@ -3297,13 +3304,13 @@ impl<T> SliceIndex<[T]> for ops::RangeInclusive<usize> {
     }
 
     #[inline]
-    unsafe fn get_unchecked(self, slice: &[T]) -> &[T] {
+    unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
         // SAFETY: the caller has to uphold the safety contract for `get_unchecked`.
         unsafe { (*self.start()..self.end() + 1).get_unchecked(slice) }
     }
 
     #[inline]
-    unsafe fn get_unchecked_mut(self, slice: &mut [T]) -> &mut [T] {
+    unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] {
         // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`.
         unsafe { (*self.start()..self.end() + 1).get_unchecked_mut(slice) }
     }
@@ -3326,7 +3333,7 @@ impl<T> SliceIndex<[T]> for ops::RangeInclusive<usize> {
 }
 
 #[stable(feature = "inclusive_range", since = "1.26.0")]
-impl<T> SliceIndex<[T]> for ops::RangeToInclusive<usize> {
+unsafe impl<T> SliceIndex<[T]> for ops::RangeToInclusive<usize> {
     type Output = [T];
 
     #[inline]
@@ -3340,13 +3347,13 @@ impl<T> SliceIndex<[T]> for ops::RangeToInclusive<usize> {
     }
 
     #[inline]
-    unsafe fn get_unchecked(self, slice: &[T]) -> &[T] {
+    unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
         // SAFETY: the caller has to uphold the safety contract for `get_unchecked`.
         unsafe { (0..=self.end).get_unchecked(slice) }
     }
 
     #[inline]
-    unsafe fn get_unchecked_mut(self, slice: &mut [T]) -> &mut [T] {
+    unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] {
         // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`.
         unsafe { (0..=self.end).get_unchecked_mut(slice) }
     }
diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs
index 003ed7df36e2a..393911675c73e 100644
--- a/src/libcore/str/mod.rs
+++ b/src/libcore/str/mod.rs
@@ -1731,7 +1731,8 @@ Section: Trait implementations
 mod traits {
     use crate::cmp::Ordering;
     use crate::ops;
-    use crate::slice::{self, SliceIndex};
+    use crate::ptr;
+    use crate::slice::SliceIndex;
 
     /// Implements ordering of strings.
     ///
@@ -1822,7 +1823,7 @@ mod traits {
     ///
     /// Equivalent to `&self[0 .. len]` or `&mut self[0 .. len]`.
     #[stable(feature = "str_checked_slicing", since = "1.20.0")]
-    impl SliceIndex<str> for ops::RangeFull {
+    unsafe impl SliceIndex<str> for ops::RangeFull {
         type Output = str;
         #[inline]
         fn get(self, slice: &str) -> Option<&Self::Output> {
@@ -1833,11 +1834,11 @@ mod traits {
             Some(slice)
         }
         #[inline]
-        unsafe fn get_unchecked(self, slice: &str) -> &Self::Output {
+        unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
             slice
         }
         #[inline]
-        unsafe fn get_unchecked_mut(self, slice: &mut str) -> &mut Self::Output {
+        unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output {
             slice
         }
         #[inline]
@@ -1886,7 +1887,7 @@ mod traits {
     /// // &s[3 .. 100];
     /// ```
     #[stable(feature = "str_checked_slicing", since = "1.20.0")]
-    impl SliceIndex<str> for ops::Range<usize> {
+    unsafe impl SliceIndex<str> for ops::Range<usize> {
         type Output = str;
         #[inline]
         fn get(self, slice: &str) -> Option<&Self::Output> {
@@ -1894,8 +1895,10 @@ mod traits {
                 && slice.is_char_boundary(self.start)
                 && slice.is_char_boundary(self.end)
             {
-                // SAFETY: just checked that `start` and `end` are on a char boundary.
-                Some(unsafe { self.get_unchecked(slice) })
+                // SAFETY: just checked that `start` and `end` are on a char boundary,
+                // and we are passing in a safe reference, so the return value will also be one.
+                // We also checked char boundaries, so this is valid UTF-8.
+                Some(unsafe { &*self.get_unchecked(slice) })
             } else {
                 None
             }
@@ -1907,34 +1910,28 @@ mod traits {
                 && slice.is_char_boundary(self.end)
             {
                 // SAFETY: just checked that `start` and `end` are on a char boundary.
-                Some(unsafe { self.get_unchecked_mut(slice) })
+                // We know the pointer is unique because we got it from `slice`.
+                Some(unsafe { &mut *self.get_unchecked_mut(slice) })
             } else {
                 None
             }
         }
         #[inline]
-        unsafe fn get_unchecked(self, slice: &str) -> &Self::Output {
+        unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
+            let slice = slice as *const [u8];
             // SAFETY: the caller guarantees that `self` is in bounds of `slice`
             // which satisfies all the conditions for `add`.
             let ptr = unsafe { slice.as_ptr().add(self.start) };
             let len = self.end - self.start;
-            // SAFETY: as the caller guarantees that `self` is in bounds of `slice`,
-            // we can safely construct a subslice with `from_raw_parts` and use it
-            // since we return a shared thus immutable reference.
-            // The call to `from_utf8_unchecked` is safe since the data comes from
-            // a `str` which is guaranteed to be valid utf8, since the caller
-            // must guarantee that `self.start` and `self.end` are char boundaries.
-            unsafe { super::from_utf8_unchecked(slice::from_raw_parts(ptr, len)) }
+            ptr::slice_from_raw_parts(ptr, len) as *const str
         }
         #[inline]
-        unsafe fn get_unchecked_mut(self, slice: &mut str) -> &mut Self::Output {
+        unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output {
+            let slice = slice as *mut [u8];
             // SAFETY: see comments for `get_unchecked`.
             let ptr = unsafe { slice.as_mut_ptr().add(self.start) };
             let len = self.end - self.start;
-            // SAFETY: mostly identical to the comments for `get_unchecked`, except that we
-            // can return a mutable reference since the caller passed a mutable reference
-            // and is thus guaranteed to have exclusive write access to `slice`.
-            unsafe { super::from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr, len)) }
+            ptr::slice_from_raw_parts_mut(ptr, len) as *mut str
         }
         #[inline]
         fn index(self, slice: &str) -> &Self::Output {
@@ -1949,8 +1946,9 @@ mod traits {
                 && slice.is_char_boundary(self.start)
                 && slice.is_char_boundary(self.end)
             {
-                // SAFETY: just checked that `start` and `end` are on a char boundary.
-                unsafe { self.get_unchecked_mut(slice) }
+                // SAFETY: just checked that `start` and `end` are on a char boundary,
+                // and we are passing in a safe reference, so the return value will also be one.
+                unsafe { &mut *self.get_unchecked_mut(slice) }
             } else {
                 super::slice_error_fail(slice, self.start, self.end)
             }
@@ -1973,13 +1971,14 @@ mod traits {
     /// Panics if `end` does not point to the starting byte offset of a
     /// character (as defined by `is_char_boundary`), or if `end > len`.
     #[stable(feature = "str_checked_slicing", since = "1.20.0")]
-    impl SliceIndex<str> for ops::RangeTo<usize> {
+    unsafe impl SliceIndex<str> for ops::RangeTo<usize> {
         type Output = str;
         #[inline]
         fn get(self, slice: &str) -> Option<&Self::Output> {
             if slice.is_char_boundary(self.end) {
-                // SAFETY: just checked that `end` is on a char boundary.
-                Some(unsafe { self.get_unchecked(slice) })
+                // SAFETY: just checked that `end` is on a char boundary,
+                // and we are passing in a safe reference, so the return value will also be one.
+                Some(unsafe { &*self.get_unchecked(slice) })
             } else {
                 None
             }
@@ -1987,30 +1986,24 @@ mod traits {
         #[inline]
         fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> {
             if slice.is_char_boundary(self.end) {
-                // SAFETY: just checked that `end` is on a char boundary.
-                Some(unsafe { self.get_unchecked_mut(slice) })
+                // SAFETY: just checked that `end` is on a char boundary,
+                // and we are passing in a safe reference, so the return value will also be one.
+                Some(unsafe { &mut *self.get_unchecked_mut(slice) })
             } else {
                 None
             }
         }
         #[inline]
-        unsafe fn get_unchecked(self, slice: &str) -> &Self::Output {
+        unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
+            let slice = slice as *const [u8];
             let ptr = slice.as_ptr();
-            // SAFETY: as the caller guarantees that `self` is in bounds of `slice`,
-            // we can safely construct a subslice with `from_raw_parts` and use it
-            // since we return a shared thus immutable reference.
-            // The call to `from_utf8_unchecked` is safe since the data comes from
-            // a `str` which is guaranteed to be valid utf8, since the caller
-            // must guarantee that `self.end` is a char boundary.
-            unsafe { super::from_utf8_unchecked(slice::from_raw_parts(ptr, self.end)) }
+            ptr::slice_from_raw_parts(ptr, self.end) as *const str
         }
         #[inline]
-        unsafe fn get_unchecked_mut(self, slice: &mut str) -> &mut Self::Output {
+        unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output {
+            let slice = slice as *mut [u8];
             let ptr = slice.as_mut_ptr();
-            // SAFETY: mostly identical to `get_unchecked`, except that we can safely
-            // return a mutable reference since the caller passed a mutable reference
-            // and is thus guaranteed to have exclusive write access to `slice`.
-            unsafe { super::from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr, self.end)) }
+            ptr::slice_from_raw_parts_mut(ptr, self.end) as *mut str
         }
         #[inline]
         fn index(self, slice: &str) -> &Self::Output {
@@ -2020,8 +2013,9 @@ mod traits {
         #[inline]
         fn index_mut(self, slice: &mut str) -> &mut Self::Output {
             if slice.is_char_boundary(self.end) {
-                // SAFETY: just checked that `end` is on a char boundary.
-                unsafe { self.get_unchecked_mut(slice) }
+                // SAFETY: just checked that `end` is on a char boundary,
+                // and we are passing in a safe reference, so the return value will also be one.
+                unsafe { &mut *self.get_unchecked_mut(slice) }
             } else {
                 super::slice_error_fail(slice, 0, self.end)
             }
@@ -2045,13 +2039,14 @@ mod traits {
     /// Panics if `begin` does not point to the starting byte offset of
     /// a character (as defined by `is_char_boundary`), or if `begin >= len`.
     #[stable(feature = "str_checked_slicing", since = "1.20.0")]
-    impl SliceIndex<str> for ops::RangeFrom<usize> {
+    unsafe impl SliceIndex<str> for ops::RangeFrom<usize> {
         type Output = str;
         #[inline]
         fn get(self, slice: &str) -> Option<&Self::Output> {
             if slice.is_char_boundary(self.start) {
-                // SAFETY: just checked that `start` is on a char boundary.
-                Some(unsafe { self.get_unchecked(slice) })
+                // SAFETY: just checked that `start` is on a char boundary,
+                // and we are passing in a safe reference, so the return value will also be one.
+                Some(unsafe { &*self.get_unchecked(slice) })
             } else {
                 None
             }
@@ -2059,35 +2054,29 @@ mod traits {
         #[inline]
         fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> {
             if slice.is_char_boundary(self.start) {
-                // SAFETY: just checked that `start` is on a char boundary.
-                Some(unsafe { self.get_unchecked_mut(slice) })
+                // SAFETY: just checked that `start` is on a char boundary,
+                // and we are passing in a safe reference, so the return value will also be one.
+                Some(unsafe { &mut *self.get_unchecked_mut(slice) })
             } else {
                 None
             }
         }
         #[inline]
-        unsafe fn get_unchecked(self, slice: &str) -> &Self::Output {
+        unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
+            let slice = slice as *const [u8];
             // SAFETY: the caller guarantees that `self` is in bounds of `slice`
             // which satisfies all the conditions for `add`.
             let ptr = unsafe { slice.as_ptr().add(self.start) };
             let len = slice.len() - self.start;
-            // SAFETY: as the caller guarantees that `self` is in bounds of `slice`,
-            // we can safely construct a subslice with `from_raw_parts` and use it
-            // since we return a shared thus immutable reference.
-            // The call to `from_utf8_unchecked` is safe since the data comes from
-            // a `str` which is guaranteed to be valid utf8, since the caller
-            // must guarantee that `self.start` is a char boundary.
-            unsafe { super::from_utf8_unchecked(slice::from_raw_parts(ptr, len)) }
+            ptr::slice_from_raw_parts(ptr, len) as *const str
         }
         #[inline]
-        unsafe fn get_unchecked_mut(self, slice: &mut str) -> &mut Self::Output {
+        unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output {
+            let slice = slice as *mut [u8];
             // SAFETY: identical to `get_unchecked`.
             let ptr = unsafe { slice.as_mut_ptr().add(self.start) };
             let len = slice.len() - self.start;
-            // SAFETY: mostly identical to `get_unchecked`, except that we can safely
-            // return a mutable reference since the caller passed a mutable reference
-            // and is thus guaranteed to have exclusive write access to `slice`.
-            unsafe { super::from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr, len)) }
+            ptr::slice_from_raw_parts_mut(ptr, len) as *mut str
         }
         #[inline]
         fn index(self, slice: &str) -> &Self::Output {
@@ -2097,8 +2086,9 @@ mod traits {
         #[inline]
         fn index_mut(self, slice: &mut str) -> &mut Self::Output {
             if slice.is_char_boundary(self.start) {
-                // SAFETY: just checked that `start` is on a char boundary.
-                unsafe { self.get_unchecked_mut(slice) }
+                // SAFETY: just checked that `start` is on a char boundary,
+                // and we are passing in a safe reference, so the return value will also be one.
+                unsafe { &mut *self.get_unchecked_mut(slice) }
             } else {
                 super::slice_error_fail(slice, self.start, slice.len())
             }
@@ -2122,7 +2112,7 @@ mod traits {
     /// to the ending byte offset of a character (`end + 1` is either a starting
     /// byte offset or equal to `len`), if `begin > end`, or if `end >= len`.
     #[stable(feature = "inclusive_range", since = "1.26.0")]
-    impl SliceIndex<str> for ops::RangeInclusive<usize> {
+    unsafe impl SliceIndex<str> for ops::RangeInclusive<usize> {
         type Output = str;
         #[inline]
         fn get(self, slice: &str) -> Option<&Self::Output> {
@@ -2141,12 +2131,12 @@ mod traits {
             }
         }
         #[inline]
-        unsafe fn get_unchecked(self, slice: &str) -> &Self::Output {
+        unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
             // SAFETY: the caller must uphold the safety contract for `get_unchecked`.
             unsafe { (*self.start()..self.end() + 1).get_unchecked(slice) }
         }
         #[inline]
-        unsafe fn get_unchecked_mut(self, slice: &mut str) -> &mut Self::Output {
+        unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output {
             // SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`.
             unsafe { (*self.start()..self.end() + 1).get_unchecked_mut(slice) }
         }
@@ -2181,7 +2171,7 @@ mod traits {
     /// (`end + 1` is either a starting byte offset as defined by
     /// `is_char_boundary`, or equal to `len`), or if `end >= len`.
     #[stable(feature = "inclusive_range", since = "1.26.0")]
-    impl SliceIndex<str> for ops::RangeToInclusive<usize> {
+    unsafe impl SliceIndex<str> for ops::RangeToInclusive<usize> {
         type Output = str;
         #[inline]
         fn get(self, slice: &str) -> Option<&Self::Output> {
@@ -2192,12 +2182,12 @@ mod traits {
             if self.end == usize::MAX { None } else { (..self.end + 1).get_mut(slice) }
         }
         #[inline]
-        unsafe fn get_unchecked(self, slice: &str) -> &Self::Output {
+        unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
             // SAFETY: the caller must uphold the safety contract for `get_unchecked`.
             unsafe { (..self.end + 1).get_unchecked(slice) }
         }
         #[inline]
-        unsafe fn get_unchecked_mut(self, slice: &mut str) -> &mut Self::Output {
+        unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output {
             // SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`.
             unsafe { (..self.end + 1).get_unchecked_mut(slice) }
         }
@@ -2560,8 +2550,10 @@ impl str {
     #[stable(feature = "str_checked_slicing", since = "1.20.0")]
     #[inline]
     pub unsafe fn get_unchecked<I: SliceIndex<str>>(&self, i: I) -> &I::Output {
-        // SAFETY: the caller must uphold the safety contract for `get_unchecked`.
-        unsafe { i.get_unchecked(self) }
+        // SAFETY: the caller must uphold the safety contract for `get_unchecked`;
+        // the slice is dereferencable because `self` is a safe reference.
+        // The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is.
+        unsafe { &*i.get_unchecked(self) }
     }
 
     /// Returns a mutable, unchecked subslice of `str`.
@@ -2593,8 +2585,10 @@ impl str {
     #[stable(feature = "str_checked_slicing", since = "1.20.0")]
     #[inline]
     pub unsafe fn get_unchecked_mut<I: SliceIndex<str>>(&mut self, i: I) -> &mut I::Output {
-        // SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`.
-        unsafe { i.get_unchecked_mut(self) }
+        // SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`;
+        // the slice is dereferencable because `self` is a safe reference.
+        // The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is.
+        unsafe { &mut *i.get_unchecked_mut(self) }
     }
 
     /// Creates a string slice from another string slice, bypassing safety
@@ -2644,8 +2638,10 @@ impl str {
     #[rustc_deprecated(since = "1.29.0", reason = "use `get_unchecked(begin..end)` instead")]
     #[inline]
     pub unsafe fn slice_unchecked(&self, begin: usize, end: usize) -> &str {
-        // SAFETY: the caller must uphold the safety contract for `get_unchecked`.
-        unsafe { (begin..end).get_unchecked(self) }
+        // SAFETY: the caller must uphold the safety contract for `get_unchecked`;
+        // the slice is dereferencable because `self` is a safe reference.
+        // The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is.
+        unsafe { &*(begin..end).get_unchecked(self) }
     }
 
     /// Creates a string slice from another string slice, bypassing safety
@@ -2676,8 +2672,10 @@ impl str {
     #[rustc_deprecated(since = "1.29.0", reason = "use `get_unchecked_mut(begin..end)` instead")]
     #[inline]
     pub unsafe fn slice_mut_unchecked(&mut self, begin: usize, end: usize) -> &mut str {
-        // SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`.
-        unsafe { (begin..end).get_unchecked_mut(self) }
+        // SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`;
+        // the slice is dereferencable because `self` is a safe reference.
+        // The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is.
+        unsafe { &mut *(begin..end).get_unchecked_mut(self) }
     }
 
     /// Divide one string slice into two at an index.
@@ -3158,11 +3156,11 @@ impl str {
     /// Simple patterns:
     ///
     /// ```
-    /// let s = "Löwe 老虎 Léopard";
+    /// let s = "Löwe 老虎 Léopard Gepardi";
     ///
     /// assert_eq!(s.find('L'), Some(0));
     /// assert_eq!(s.find('é'), Some(14));
-    /// assert_eq!(s.find("Léopard"), Some(13));
+    /// assert_eq!(s.find("pard"), Some(17));
     /// ```
     ///
     /// More complex patterns using point-free style and closures:
@@ -3190,8 +3188,8 @@ impl str {
         pat.into_searcher(self).next_match().map(|(i, _)| i)
     }
 
-    /// Returns the byte index of the last character of this string slice that
-    /// matches the pattern.
+    /// Returns the byte index for the first character of the rightmost match of the pattern in
+    /// this string slice.
     ///
     /// Returns [`None`] if the pattern doesn't match.
     ///
@@ -3207,10 +3205,11 @@ impl str {
     /// Simple patterns:
     ///
     /// ```
-    /// let s = "Löwe 老虎 Léopard";
+    /// let s = "Löwe 老虎 Léopard Gepardi";
     ///
     /// assert_eq!(s.rfind('L'), Some(13));
     /// assert_eq!(s.rfind('é'), Some(14));
+    /// assert_eq!(s.rfind("pard"), Some(24));
     /// ```
     ///
     /// More complex patterns with closures:
diff --git a/src/librustc_error_codes/error_codes.rs b/src/librustc_error_codes/error_codes.rs
index 136230d52d975..60aa12b0511c7 100644
--- a/src/librustc_error_codes/error_codes.rs
+++ b/src/librustc_error_codes/error_codes.rs
@@ -451,6 +451,7 @@ E0765: include_str!("./error_codes/E0765.md"),
 E0766: include_str!("./error_codes/E0766.md"),
 E0767: include_str!("./error_codes/E0767.md"),
 E0768: include_str!("./error_codes/E0768.md"),
+E0769: include_str!("./error_codes/E0769.md"),
 ;
 //  E0006, // merged with E0005
 //  E0008, // cannot bind by-move into a pattern guard
diff --git a/src/librustc_error_codes/error_codes/E0704.md b/src/librustc_error_codes/error_codes/E0704.md
index cde46f52c27d7..c22b274fb223e 100644
--- a/src/librustc_error_codes/error_codes/E0704.md
+++ b/src/librustc_error_codes/error_codes/E0704.md
@@ -1,6 +1,6 @@
-This error indicates that a incorrect visibility restriction was specified.
+An incorrect visibility restriction was specified.
 
-Example of erroneous code:
+Erroneous code example:
 
 ```compile_fail,E0704
 mod foo {
@@ -12,6 +12,7 @@ mod foo {
 
 To make struct `Bar` only visible in module `foo` the `in` keyword should be
 used:
+
 ```
 mod foo {
     pub(in crate::foo) struct Bar {
diff --git a/src/librustc_error_codes/error_codes/E0769.md b/src/librustc_error_codes/error_codes/E0769.md
new file mode 100644
index 0000000000000..d1995be9899b1
--- /dev/null
+++ b/src/librustc_error_codes/error_codes/E0769.md
@@ -0,0 +1,39 @@
+A tuple struct or tuple variant was used in a pattern as if it were a
+struct or struct variant.
+
+Erroneous code example:
+
+```compile_fail,E0769
+enum E {
+    A(i32),
+}
+let e = E::A(42);
+match e {
+    E::A { number } => println!("{}", x),
+}
+```
+
+To fix this error, you can use the tuple pattern:
+
+```
+# enum E {
+#     A(i32),
+# }
+# let e = E::A(42);
+match e {
+    E::A(number) => println!("{}", number),
+}
+```
+
+Alternatively, you can also use the struct pattern by using the correct
+field names and binding them to new identifiers:
+
+```
+# enum E {
+#     A(i32),
+# }
+# let e = E::A(42);
+match e {
+    E::A { 0: number } => println!("{}", number),
+}
+```
diff --git a/src/librustc_index/Cargo.toml b/src/librustc_index/Cargo.toml
index f0422b1af1b97..00b23760182a2 100644
--- a/src/librustc_index/Cargo.toml
+++ b/src/librustc_index/Cargo.toml
@@ -11,4 +11,4 @@ doctest = false
 
 [dependencies]
 rustc_serialize = { path = "../librustc_serialize" }
-smallvec = { version = "1.0", features = ["union", "may_dangle"] }
+arrayvec = "0.5.1"
diff --git a/src/librustc_index/bit_set.rs b/src/librustc_index/bit_set.rs
index cb8b30830c5de..3e1d4b68c6fa1 100644
--- a/src/librustc_index/bit_set.rs
+++ b/src/librustc_index/bit_set.rs
@@ -1,5 +1,5 @@
 use crate::vec::{Idx, IndexVec};
-use smallvec::SmallVec;
+use arrayvec::ArrayVec;
 use std::fmt;
 use std::iter;
 use std::marker::PhantomData;
@@ -355,20 +355,19 @@ where
 const SPARSE_MAX: usize = 8;
 
 /// A fixed-size bitset type with a sparse representation and a maximum of
-/// `SPARSE_MAX` elements. The elements are stored as a sorted `SmallVec` with
-/// no duplicates; although `SmallVec` can spill its elements to the heap, that
-/// never happens within this type because of the `SPARSE_MAX` limit.
+/// `SPARSE_MAX` elements. The elements are stored as a sorted `ArrayVec` with
+/// no duplicates.
 ///
 /// This type is used by `HybridBitSet`; do not use directly.
 #[derive(Clone, Debug)]
 pub struct SparseBitSet<T: Idx> {
     domain_size: usize,
-    elems: SmallVec<[T; SPARSE_MAX]>,
+    elems: ArrayVec<[T; SPARSE_MAX]>,
 }
 
 impl<T: Idx> SparseBitSet<T> {
     fn new_empty(domain_size: usize) -> Self {
-        SparseBitSet { domain_size, elems: SmallVec::new() }
+        SparseBitSet { domain_size, elems: ArrayVec::new() }
     }
 
     fn len(&self) -> usize {
diff --git a/src/librustc_middle/ty/layout.rs b/src/librustc_middle/ty/layout.rs
index 66ad923b8c007..82daae7d921b2 100644
--- a/src/librustc_middle/ty/layout.rs
+++ b/src/librustc_middle/ty/layout.rs
@@ -774,12 +774,12 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
                     (present_variants.next(), present_variants.next())
                 };
                 let present_first = match present_first {
-                    present_first @ Some(_) => present_first,
+                    Some(present_first) => present_first,
                     // Uninhabited because it has no variants, or only absent ones.
                     None if def.is_enum() => return tcx.layout_raw(param_env.and(tcx.types.never)),
                     // If it's a struct, still compute a layout so that we can still compute the
                     // field offsets.
-                    None => Some(VariantIdx::new(0)),
+                    None => VariantIdx::new(0),
                 };
 
                 let is_struct = !def.is_enum() ||
@@ -791,7 +791,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
                     // Struct, or univariant enum equivalent to a struct.
                     // (Typechecking will reject discriminant-sizing attrs.)
 
-                    let v = present_first.unwrap();
+                    let v = present_first;
                     let kind = if def.is_enum() || variants[v].is_empty() {
                         StructKind::AlwaysSized
                     } else {
diff --git a/src/librustc_target/spec/linux_kernel_base.rs b/src/librustc_target/spec/linux_kernel_base.rs
index 201d6a0fff93b..6d929d1244789 100644
--- a/src/librustc_target/spec/linux_kernel_base.rs
+++ b/src/librustc_target/spec/linux_kernel_base.rs
@@ -17,7 +17,6 @@ pub fn opts() -> TargetOptions {
         needs_plt: true,
         relro_level: RelroLevel::Full,
         relocation_model: RelocModel::Static,
-        target_family: Some("unix".to_string()),
         pre_link_args,
 
         ..Default::default()
diff --git a/src/librustc_typeck/Cargo.toml b/src/librustc_typeck/Cargo.toml
index 9329069c48dd1..93b503c976be4 100644
--- a/src/librustc_typeck/Cargo.toml
+++ b/src/librustc_typeck/Cargo.toml
@@ -18,6 +18,7 @@ rustc_attr = { path = "../librustc_attr" }
 rustc_data_structures = { path = "../librustc_data_structures" }
 rustc_errors = { path = "../librustc_errors" }
 rustc_hir = { path = "../librustc_hir" }
+rustc_hir_pretty = { path = "../librustc_hir_pretty" }
 rustc_target = { path = "../librustc_target" }
 rustc_session = { path = "../librustc_session" }
 smallvec = { version = "1.0", features = ["union", "may_dangle"] }
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index 5d1949626dd84..7581940b6ac69 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -3049,14 +3049,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         let bare_fn_ty =
             ty::Binder::bind(tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, unsafety, abi));
 
-        if let (false, Some(ident_span)) = (self.allow_ty_infer(), ident_span) {
+        if !self.allow_ty_infer() {
             // We always collect the spans for placeholder types when evaluating `fn`s, but we
             // only want to emit an error complaining about them if infer types (`_`) are not
             // allowed. `allow_ty_infer` gates this behavior. We check for the presence of
             // `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`.
             crate::collect::placeholder_type_error(
                 tcx,
-                ident_span.shrink_to_hi(),
+                ident_span.map(|sp| sp.shrink_to_hi()),
                 &generics.params[..],
                 visitor.0,
                 true,
diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs
index 1ea7bf25ef2ed..8948e5a3e00db 100644
--- a/src/librustc_typeck/check/cast.rs
+++ b/src/librustc_typeck/check/cast.rs
@@ -387,6 +387,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                             // Check for infer types because cases like `Option<{integer}>` would
                             // panic otherwise.
                             if !expr_ty.has_infer_types()
+                                && !ty.has_infer_types()
                                 && fcx.tcx.type_implements_trait((
                                     from_trait,
                                     ty,
diff --git a/src/librustc_typeck/check/pat.rs b/src/librustc_typeck/check/pat.rs
index ea47ae68ce7d3..a654fc3dfc2df 100644
--- a/src/librustc_typeck/check/pat.rs
+++ b/src/librustc_typeck/check/pat.rs
@@ -1082,20 +1082,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             .filter(|ident| !used_fields.contains_key(&ident))
             .collect::<Vec<_>>();
 
-        if !inexistent_fields.is_empty() && !variant.recovered {
-            self.error_inexistent_fields(
+        let inexistent_fields_err = if !inexistent_fields.is_empty() && !variant.recovered {
+            Some(self.error_inexistent_fields(
                 adt.variant_descr(),
                 &inexistent_fields,
                 &mut unmentioned_fields,
                 variant,
-            );
-        }
+            ))
+        } else {
+            None
+        };
 
         // Require `..` if struct has non_exhaustive attribute.
         if variant.is_field_list_non_exhaustive() && !adt.did.is_local() && !etc {
             self.error_foreign_non_exhaustive_spat(pat, adt.variant_descr(), fields.is_empty());
         }
 
+        let mut unmentioned_err = None;
         // Report an error if incorrect number of the fields were specified.
         if adt.is_union() {
             if fields.len() != 1 {
@@ -1107,7 +1110,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 tcx.sess.struct_span_err(pat.span, "`..` cannot be used in union patterns").emit();
             }
         } else if !etc && !unmentioned_fields.is_empty() {
-            self.error_unmentioned_fields(pat.span, &unmentioned_fields, variant);
+            unmentioned_err = Some(self.error_unmentioned_fields(pat.span, &unmentioned_fields));
+        }
+        match (inexistent_fields_err, unmentioned_err) {
+            (Some(mut i), Some(mut u)) => {
+                if let Some(mut e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) {
+                    // We don't want to show the inexistent fields error when this was
+                    // `Foo { a, b }` when it should have been `Foo(a, b)`.
+                    i.delay_as_bug();
+                    u.delay_as_bug();
+                    e.emit();
+                } else {
+                    i.emit();
+                    u.emit();
+                }
+            }
+            (None, Some(mut err)) | (Some(mut err), None) => {
+                err.emit();
+            }
+            (None, None) => {}
         }
         no_field_errors
     }
@@ -1154,7 +1175,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         inexistent_fields: &[Ident],
         unmentioned_fields: &mut Vec<Ident>,
         variant: &ty::VariantDef,
-    ) {
+    ) -> DiagnosticBuilder<'tcx> {
         let tcx = self.tcx;
         let (field_names, t, plural) = if inexistent_fields.len() == 1 {
             (format!("a field named `{}`", inexistent_fields[0]), "this", "")
@@ -1221,15 +1242,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     it explicitly.",
             );
         }
-        err.emit();
+        err
+    }
+
+    fn error_tuple_variant_as_struct_pat(
+        &self,
+        pat: &Pat<'_>,
+        fields: &'tcx [hir::FieldPat<'tcx>],
+        variant: &ty::VariantDef,
+    ) -> Option<DiagnosticBuilder<'tcx>> {
+        if let (CtorKind::Fn, PatKind::Struct(qpath, ..)) = (variant.ctor_kind, &pat.kind) {
+            let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
+                s.print_qpath(qpath, false)
+            });
+            let mut err = struct_span_err!(
+                self.tcx.sess,
+                pat.span,
+                E0769,
+                "tuple variant `{}` written as struct variant",
+                path
+            );
+            let (sugg, appl) = if fields.len() == variant.fields.len() {
+                (
+                    fields
+                        .iter()
+                        .map(|f| match self.tcx.sess.source_map().span_to_snippet(f.pat.span) {
+                            Ok(f) => f,
+                            Err(_) => rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
+                                s.print_pat(f.pat)
+                            }),
+                        })
+                        .collect::<Vec<String>>()
+                        .join(", "),
+                    Applicability::MachineApplicable,
+                )
+            } else {
+                (
+                    variant.fields.iter().map(|_| "_").collect::<Vec<&str>>().join(", "),
+                    Applicability::MaybeIncorrect,
+                )
+            };
+            err.span_suggestion(
+                pat.span,
+                "use the tuple variant pattern syntax instead",
+                format!("{}({})", path, sugg),
+                appl,
+            );
+            return Some(err);
+        }
+        None
     }
 
     fn error_unmentioned_fields(
         &self,
         span: Span,
         unmentioned_fields: &[Ident],
-        variant: &ty::VariantDef,
-    ) {
+    ) -> DiagnosticBuilder<'tcx> {
         let field_names = if unmentioned_fields.len() == 1 {
             format!("field `{}`", unmentioned_fields[0])
         } else {
@@ -1248,9 +1316,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             field_names
         );
         diag.span_label(span, format!("missing {}", field_names));
-        if variant.ctor_kind == CtorKind::Fn {
-            diag.note("trying to match a tuple variant with a struct variant pattern");
-        }
         if self.tcx.sess.teach(&diag.get_code().unwrap()) {
             diag.note(
                 "This error indicates that a pattern for a struct fails to specify a \
@@ -1259,7 +1324,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     ignore unwanted fields.",
             );
         }
-        diag.emit();
+        diag
     }
 
     fn check_pat_box(
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 15481660a5218..625b72091a6cc 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -129,7 +129,7 @@ struct CollectItemTypesVisitor<'tcx> {
 /// all already existing generic type parameters to avoid suggesting a name that is already in use.
 crate fn placeholder_type_error(
     tcx: TyCtxt<'tcx>,
-    span: Span,
+    span: Option<Span>,
     generics: &[hir::GenericParam<'_>],
     placeholder_types: Vec<Span>,
     suggest: bool,
@@ -137,12 +137,15 @@ crate fn placeholder_type_error(
     if placeholder_types.is_empty() {
         return;
     }
-    let type_name = generics.next_type_param_name(None);
 
+    let type_name = generics.next_type_param_name(None);
     let mut sugg: Vec<_> =
         placeholder_types.iter().map(|sp| (*sp, (*type_name).to_string())).collect();
+
     if generics.is_empty() {
-        sugg.push((span, format!("<{}>", type_name)));
+        if let Some(span) = span {
+            sugg.push((span, format!("<{}>", type_name)));
+        }
     } else if let Some(arg) = generics.iter().find(|arg| match arg.name {
         hir::ParamName::Plain(Ident { name: kw::Underscore, .. }) => true,
         _ => false,
@@ -158,6 +161,7 @@ crate fn placeholder_type_error(
             format!(", {}", type_name),
         ));
     }
+
     let mut err = bad_placeholder_type(tcx, placeholder_types);
     if suggest {
         err.multipart_suggestion(
@@ -186,7 +190,7 @@ fn reject_placeholder_type_signatures_in_item(tcx: TyCtxt<'tcx>, item: &'tcx hir
     let mut visitor = PlaceholderHirTyCollector::default();
     visitor.visit_item(item);
 
-    placeholder_type_error(tcx, generics.span, &generics.params[..], visitor.0, suggest);
+    placeholder_type_error(tcx, Some(generics.span), &generics.params[..], visitor.0, suggest);
 }
 
 impl Visitor<'tcx> for CollectItemTypesVisitor<'tcx> {
@@ -722,7 +726,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::HirId) {
             // Account for `const C: _;` and `type T = _;`.
             let mut visitor = PlaceholderHirTyCollector::default();
             visitor.visit_trait_item(trait_item);
-            placeholder_type_error(tcx, DUMMY_SP, &[], visitor.0, false);
+            placeholder_type_error(tcx, None, &[], visitor.0, false);
         }
 
         hir::TraitItemKind::Type(_, None) => {}
@@ -745,7 +749,7 @@ fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::HirId) {
             // Account for `type T = _;`
             let mut visitor = PlaceholderHirTyCollector::default();
             visitor.visit_impl_item(impl_item);
-            placeholder_type_error(tcx, DUMMY_SP, &[], visitor.0, false);
+            placeholder_type_error(tcx, None, &[], visitor.0, false);
         }
         hir::ImplItemKind::Const(..) => {}
     }
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index 14a6f3c89a3c9..39e33da44964e 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -20,6 +20,7 @@ use crate::core::new_handler;
 use crate::externalfiles::ExternalHtml;
 use crate::html;
 use crate::html::markdown::IdMap;
+use crate::html::render::StylePath;
 use crate::html::static_files;
 use crate::opts;
 use crate::passes::{self, Condition, DefaultPassOption};
@@ -207,7 +208,7 @@ pub struct RenderOptions {
     pub sort_modules_alphabetically: bool,
     /// List of themes to extend the docs with. Original argument name is included to assist in
     /// displaying errors if it fails a theme check.
-    pub themes: Vec<PathBuf>,
+    pub themes: Vec<StylePath>,
     /// If present, CSS file that contains rules to add to the default CSS.
     pub extension_css: Option<PathBuf>,
     /// A map of crate names to the URL to use instead of querying the crate's `html_root_url`.
@@ -410,7 +411,7 @@ impl Options {
                     ))
                     .emit();
                 }
-                themes.push(theme_file);
+                themes.push(StylePath { path: theme_file, disabled: true });
             }
         }
 
diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs
index ea65b3905272e..cc6b38ebcdb7f 100644
--- a/src/librustdoc/html/layout.rs
+++ b/src/librustdoc/html/layout.rs
@@ -3,7 +3,7 @@ use std::path::PathBuf;
 use crate::externalfiles::ExternalHtml;
 use crate::html::escape::Escape;
 use crate::html::format::{Buffer, Print};
-use crate::html::render::ensure_trailing_slash;
+use crate::html::render::{ensure_trailing_slash, StylePath};
 
 #[derive(Clone)]
 pub struct Layout {
@@ -36,7 +36,7 @@ pub fn render<T: Print, S: Print>(
     page: &Page<'_>,
     sidebar: S,
     t: T,
-    themes: &[PathBuf],
+    style_files: &[StylePath],
 ) -> String {
     let static_root_path = page.static_root_path.unwrap_or(page.root_path);
     format!(
@@ -52,10 +52,7 @@ pub fn render<T: Print, S: Print>(
     <link rel=\"stylesheet\" type=\"text/css\" href=\"{static_root_path}normalize{suffix}.css\">\
     <link rel=\"stylesheet\" type=\"text/css\" href=\"{static_root_path}rustdoc{suffix}.css\" \
           id=\"mainThemeStyle\">\
-    {themes}\
-    <link rel=\"stylesheet\" type=\"text/css\" href=\"{static_root_path}dark{suffix}.css\">\
-    <link rel=\"stylesheet\" type=\"text/css\" href=\"{static_root_path}light{suffix}.css\" \
-          id=\"themeStyle\">\
+    {style_files}\
     <script src=\"{static_root_path}storage{suffix}.js\"></script>\
     <noscript><link rel=\"stylesheet\" href=\"{static_root_path}noscript{suffix}.css\"></noscript>\
     {css_extension}\
@@ -172,13 +169,19 @@ pub fn render<T: Print, S: Print>(
         after_content = layout.external_html.after_content,
         sidebar = Buffer::html().to_display(sidebar),
         krate = layout.krate,
-        themes = themes
+        style_files = style_files
             .iter()
-            .filter_map(|t| t.file_stem())
-            .filter_map(|t| t.to_str())
+            .filter_map(|t| {
+                if let Some(stem) = t.path.file_stem() { Some((stem, t.disabled)) } else { None }
+            })
+            .filter_map(|t| {
+                if let Some(path) = t.0.to_str() { Some((path, t.1)) } else { None }
+            })
             .map(|t| format!(
-                r#"<link rel="stylesheet" type="text/css" href="{}.css">"#,
-                Escape(&format!("{}{}{}", static_root_path, t, page.resource_suffix))
+                r#"<link rel="stylesheet" type="text/css" href="{}.css" {} {}>"#,
+                Escape(&format!("{}{}{}", static_root_path, t.0, page.resource_suffix)),
+                if t.1 { "disabled" } else { "" },
+                if t.0 == "light" { "id=\"themeStyle\"" } else { "" }
             ))
             .collect::<String>(),
         suffix = page.resource_suffix,
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 301896fd2c1ad..8fa581180ef60 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -188,8 +188,8 @@ crate struct SharedContext {
     /// This flag indicates whether listings of modules (in the side bar and documentation itself)
     /// should be ordered alphabetically or in order of appearance (in the source code).
     pub sort_modules_alphabetically: bool,
-    /// Additional themes to be added to the generated docs.
-    pub themes: Vec<PathBuf>,
+    /// Additional CSS files to be added to the generated docs.
+    pub style_files: Vec<StylePath>,
     /// Suffix to be added on resource files (if suffix is "-v2" then "light.css" becomes
     /// "light-v2.css").
     pub resource_suffix: String,
@@ -418,6 +418,14 @@ impl Serialize for TypeWithKind {
     }
 }
 
+#[derive(Debug, Clone)]
+pub struct StylePath {
+    /// The path to the theme
+    pub path: PathBuf,
+    /// What the `disabled` attribute should be set to in the HTML tag
+    pub disabled: bool,
+}
+
 thread_local!(static CACHE_KEY: RefCell<Arc<Cache>> = Default::default());
 thread_local!(pub static CURRENT_DEPTH: Cell<usize> = Cell::new(0));
 
@@ -461,7 +469,7 @@ pub fn run(
         id_map,
         playground_url,
         sort_modules_alphabetically,
-        themes,
+        themes: style_files,
         extension_css,
         extern_html_root_urls,
         resource_suffix,
@@ -531,7 +539,7 @@ pub fn run(
         layout,
         created_dirs: Default::default(),
         sort_modules_alphabetically,
-        themes,
+        style_files,
         resource_suffix,
         static_root_path,
         fs: DocFS::new(&errors),
@@ -540,6 +548,19 @@ pub fn run(
         playground,
     };
 
+    // Add the default themes to the `Vec` of stylepaths
+    //
+    // Note that these must be added before `sources::render` is called
+    // so that the resulting source pages are styled
+    //
+    // `light.css` is not disabled because it is the stylesheet that stays loaded
+    // by the browser as the theme stylesheet. The theme system (hackily) works by
+    // changing the href to this stylesheet. All other themes are disabled to
+    // prevent rule conflicts
+    scx.style_files.push(StylePath { path: PathBuf::from("light.css"), disabled: false });
+    scx.style_files.push(StylePath { path: PathBuf::from("dark.css"), disabled: true });
+    scx.style_files.push(StylePath { path: PathBuf::from("ayu.css"), disabled: true });
+
     let dst = output;
     scx.ensure_dir(&dst)?;
     krate = sources::render(&dst, &mut scx, krate)?;
@@ -616,11 +637,40 @@ fn write_shared(
     // then we'll run over the "official" styles.
     let mut themes: FxHashSet<String> = FxHashSet::default();
 
-    for entry in &cx.shared.themes {
-        let content = try_err!(fs::read(&entry), &entry);
-        let theme = try_none!(try_none!(entry.file_stem(), &entry).to_str(), &entry);
-        let extension = try_none!(try_none!(entry.extension(), &entry).to_str(), &entry);
-        cx.shared.fs.write(cx.path(&format!("{}.{}", theme, extension)), content.as_slice())?;
+    for entry in &cx.shared.style_files {
+        let theme = try_none!(try_none!(entry.path.file_stem(), &entry.path).to_str(), &entry.path);
+        let extension =
+            try_none!(try_none!(entry.path.extension(), &entry.path).to_str(), &entry.path);
+
+        // Handle the official themes
+        match theme {
+            "light" => write_minify(
+                &cx.shared.fs,
+                cx.path("light.css"),
+                static_files::themes::LIGHT,
+                options.enable_minification,
+            )?,
+            "dark" => write_minify(
+                &cx.shared.fs,
+                cx.path("dark.css"),
+                static_files::themes::DARK,
+                options.enable_minification,
+            )?,
+            "ayu" => write_minify(
+                &cx.shared.fs,
+                cx.path("ayu.css"),
+                static_files::themes::AYU,
+                options.enable_minification,
+            )?,
+            _ => {
+                // Handle added third-party themes
+                let content = try_err!(fs::read(&entry.path), &entry.path);
+                cx.shared
+                    .fs
+                    .write(cx.path(&format!("{}.{}", theme, extension)), content.as_slice())?;
+            }
+        };
+
         themes.insert(theme.to_owned());
     }
 
@@ -634,20 +684,6 @@ fn write_shared(
     write(cx.path("brush.svg"), static_files::BRUSH_SVG)?;
     write(cx.path("wheel.svg"), static_files::WHEEL_SVG)?;
     write(cx.path("down-arrow.svg"), static_files::DOWN_ARROW_SVG)?;
-    write_minify(
-        &cx.shared.fs,
-        cx.path("light.css"),
-        static_files::themes::LIGHT,
-        options.enable_minification,
-    )?;
-    themes.insert("light".to_owned());
-    write_minify(
-        &cx.shared.fs,
-        cx.path("dark.css"),
-        static_files::themes::DARK,
-        options.enable_minification,
-    )?;
-    themes.insert("dark".to_owned());
 
     let mut themes: Vec<&String> = themes.iter().collect();
     themes.sort();
@@ -958,7 +994,7 @@ themePicker.onblur = handleThemeButtonsBlur;
                     })
                     .collect::<String>()
             );
-            let v = layout::render(&cx.shared.layout, &page, "", content, &cx.shared.themes);
+            let v = layout::render(&cx.shared.layout, &page, "", content, &cx.shared.style_files);
             cx.shared.fs.write(&dst, v.as_bytes())?;
         }
     }
@@ -1376,7 +1412,7 @@ impl Context {
             &page,
             sidebar,
             |buf: &mut Buffer| all.print(buf),
-            &self.shared.themes,
+            &self.shared.style_files,
         );
         self.shared.fs.write(&final_file, v.as_bytes())?;
 
@@ -1385,9 +1421,9 @@ impl Context {
         page.description = "Settings of Rustdoc";
         page.root_path = "./";
 
-        let mut themes = self.shared.themes.clone();
+        let mut style_files = self.shared.style_files.clone();
         let sidebar = "<p class='location'>Settings</p><div class='sidebar-elems'></div>";
-        themes.push(PathBuf::from("settings.css"));
+        style_files.push(StylePath { path: PathBuf::from("settings.css"), disabled: false });
         let v = layout::render(
             &self.shared.layout,
             &page,
@@ -1396,7 +1432,7 @@ impl Context {
                 self.shared.static_root_path.as_deref().unwrap_or("./"),
                 &self.shared.resource_suffix,
             ),
-            &themes,
+            &style_files,
         );
         self.shared.fs.write(&settings_file, v.as_bytes())?;
 
@@ -1458,7 +1494,7 @@ impl Context {
                 &page,
                 |buf: &mut _| print_sidebar(self, it, buf),
                 |buf: &mut _| print_item(self, it, buf),
-                &self.shared.themes,
+                &self.shared.style_files,
             )
         } else {
             let mut url = self.root_path();
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index f0900c34a4ba3..03f79b931868b 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -123,7 +123,7 @@ impl<'a> SourceCollector<'a> {
             &page,
             "",
             |buf: &mut _| print_src(buf, &contents),
-            &self.scx.themes,
+            &self.scx.style_files,
         );
         self.scx.fs.write(&cur, v.as_bytes())?;
         self.scx.local_sources.insert(p, href);
diff --git a/src/librustdoc/html/static/themes/ayu.css b/src/librustdoc/html/static/themes/ayu.css
new file mode 100644
index 0000000000000..bc21c28750fd8
--- /dev/null
+++ b/src/librustdoc/html/static/themes/ayu.css
@@ -0,0 +1,561 @@
+/*
+Based off of the Ayu theme
+Original by Dempfi (https://github.com/dempfi/ayu)
+*/
+
+/* General structure and fonts */
+
+body {
+	background-color: #0f1419;
+	color: #c5c5c5;
+}
+
+h1, h2, h3:not(.impl):not(.method):not(.type):not(.tymethod), h4:not(.method):not(.type):not(.tymethod) {
+	color: white;
+}
+h1.fqn {
+	border-bottom-color: #5c6773;
+}
+h1.fqn  a {
+	color: #fff;
+}
+h2, h3:not(.impl):not(.method):not(.type):not(.tymethod) {
+	border-bottom-color: #5c6773;
+}
+h4:not(.method):not(.type):not(.tymethod):not(.associatedconstant) {
+	border: none;
+}
+
+.in-band {
+	background-color: #0f1419;
+}
+
+.invisible {
+	background: rgba(0, 0, 0, 0);
+}
+
+code {
+	color: #ffb454;
+}
+h3 > code, h4 > code, h5 > code {
+	color: #e6e1cf;
+}
+pre > code {
+    color: #e6e1cf; 
+}
+span code {
+    color: #e6e1cf;
+}
+.docblock a > code {
+    color: #39AFD7 !important;
+}
+.docblock code, .docblock-short code {
+	background-color: #191f26;
+}
+pre {
+	color: #e6e1cf;
+	background-color: #191f26;
+}
+
+.sidebar {
+	background-color: #14191f;
+}
+
+/* Improve the scrollbar display on firefox */
+* {
+	scrollbar-color: #5c6773 transparent;
+}
+
+.sidebar {
+	scrollbar-color: #5c6773 transparent;
+}
+
+/* Improve the scrollbar display on webkit-based browsers */
+::-webkit-scrollbar-track {
+	background-color: transparent;
+}
+::-webkit-scrollbar-thumb {
+	background-color: #5c6773;
+}
+.sidebar::-webkit-scrollbar-track {
+	background-color: transparent;
+}
+.sidebar::-webkit-scrollbar-thumb {
+	background-color: #5c6773;
+}
+
+.sidebar .current {
+	background-color: transparent;
+	color: #ffb44c;
+}
+
+.source .sidebar {
+	background-color: #0f1419;
+}
+
+.sidebar .location {
+	border-color: #000;
+	background-color: #0f1419;
+	color: #fff;
+}
+
+.sidebar-elems .location {
+    color: #ff7733;
+}
+
+.sidebar-elems .location a {
+    color: #fff;
+}
+
+.sidebar .version {
+	border-bottom-color: #DDD;
+}
+
+.sidebar-title {
+	border-top-color: #5c6773;
+	border-bottom-color: #5c6773;
+}
+
+.block a:hover {
+	background: transparent;
+	color: #ffb44c;
+}
+
+.line-numbers span { color: #5c6773ab; }
+.line-numbers .line-highlighted {
+    background-color: rgba(255, 236, 164, 0.06) !important;
+    padding-right: 4px;
+    border-right: 1px solid #ffb44c;
+}
+
+.docblock h1, .docblock h2, .docblock h3, .docblock h4, .docblock h5 {
+	border-bottom-color: #5c6773;
+}
+
+.docblock table, .docblock table td, .docblock table th {
+	border-color: #5c6773;
+}
+
+.content .method .where,
+.content .fn .where,
+.content .where.fmt-newline {
+	color: #c5c5c5;
+}
+
+.content .highlighted {
+	color: #000 !important;
+	background-color: #c6afb3;
+}
+.content .highlighted a, .content .highlighted span { color: #000 !important; }
+.content .highlighted {
+	background-color: #c6afb3;
+}
+.search-results a {
+	color: #0096cf;
+}
+.search-results a span.desc {
+	color: #c5c5c5;
+}
+
+.content .stability::before { color: #ccc; }
+
+.content span.foreigntype, .content a.foreigntype { color: #ef57ff; }
+.content span.union, .content a.union { color: #98a01c; }
+.content span.constant, .content a.constant,
+.content span.static, .content a.static { color: #6380a0; }
+.content span.primitive, .content a.primitive { color: #32889b; }
+.content span.traitalias, .content a.traitalias { color: #57d399; }
+.content span.keyword, .content a.keyword { color: #de5249; }
+
+.content span.externcrate, .content span.mod, .content a.mod {
+    color: #acccf9;
+}
+.content span.struct, .content a.struct {
+    color: #ffa0a5;
+}
+.content span.enum, .content a.enum {
+    color: #99e0c9;
+}
+.content span.trait, .content a.trait {
+    color: #39AFD7;
+}
+.content span.type, .content a.type {
+    color: #cfbcf5;
+}
+.content span.fn, .content a.fn, .content span.method,
+.content a.method, .content span.tymethod,
+.content a.tymethod, .content .fnname {
+    color: #fdd687;
+}
+.content span.attr, .content a.attr, .content span.derive,
+.content a.derive, .content span.macro, .content a.macro {
+    color: #a37acc;
+}
+
+pre.rust .comment, pre.rust .doccomment { 
+	color: #788797;
+	font-style: italic;
+}
+
+nav:not(.sidebar) {
+	border-bottom-color: #e0e0e0;
+}
+nav.main .current {
+	border-top-color: #5c6773;
+	border-bottom-color: #5c6773;
+}
+nav.main .separator {
+	border: 1px solid #5c6773;
+}
+a {
+	color: #c5c5c5;
+}
+
+.docblock:not(.type-decl) a:not(.srclink):not(.test-arrow),
+.docblock-short a:not(.srclink):not(.test-arrow), .stability a {
+	color: #39AFD7;
+}
+
+.stab.internal a {
+	color: #304FFE;
+}
+
+.collapse-toggle {
+	color: #999;
+}
+
+#crate-search {
+	color: #c5c5c5;
+	background-color: #141920;
+	border-radius: 4px;
+	box-shadow: none;
+	border-color: #5c6773;
+}
+
+.search-input {
+    color: #ffffff;
+    background-color: #141920;
+    box-shadow: none;
+    transition: box-shadow 150ms ease-in-out;
+    border-radius: 4px;
+    margin-left: 8px;
+}
+
+#crate-search+.search-input:focus {
+    box-shadow: 0px 6px 20px 0px black;
+}
+
+.search-focus:disabled {
+	color: #929292;
+}
+
+.module-item .stab {
+	color: #000;
+}
+
+.stab.unstable,
+.stab.internal,
+.stab.deprecated,
+.stab.portability {
+    color: #c5c5c5;
+	background: #314559 !important;
+	border-style: none !important;
+	border-radius: 4px;
+	padding: 3px 6px 3px 6px;
+}
+
+.stab.portability > code {
+	color: #e6e1cf;
+	background-color: transparent;
+}
+
+#help > div {
+    background: #14191f;
+    box-shadow: 0px 6px 20px 0px black;
+    border: none;
+    border-radius: 4px;
+}
+
+.since {
+	color: grey;
+}
+
+tr.result span.primitive::after, tr.result span.keyword::after {
+	color: #788797;
+}
+
+.line-numbers :target { background-color: transparent; }
+
+/* Code highlighting */
+pre.rust .number, pre.rust .string { color: #b8cc52; }
+pre.rust .kw, pre.rust .kw-2, pre.rust .prelude-ty,
+pre.rust .bool-val, pre.rust .prelude-val,
+pre.rust .op, pre.rust .lifetime { color: #ff7733; }
+pre.rust .macro, pre.rust .macro-nonterminal { color: #a37acc; }
+pre.rust .question-mark {
+	color: #ff9011;
+}
+pre.rust .self {
+    color: #36a3d9;
+    font-style: italic;
+}
+pre.rust .attribute {
+    color: #e6e1cf;
+}
+pre.rust .attribute .ident, pre.rust .attribute .op {
+    color: #e6e1cf;
+}
+
+.example-wrap > pre.line-number {
+	color: #5c67736e;
+	border: none;
+}
+
+a.test-arrow {
+    font-size: 100%;
+    color: #788797;
+    border-radius: 4px;
+    background-color: rgba(255, 255, 255, 0);
+}
+
+a.test-arrow:hover {
+    background-color: rgba(242, 151, 24, 0.05);
+    color: #ffb44c;
+}
+
+.toggle-label {
+	color: #999;
+}
+
+:target > code, :target > .in-band {
+	background: rgba(255, 236, 164, 0.06);
+	border-right: 3px solid #ffb44c;
+}
+
+pre.compile_fail {
+	border-left: 2px solid rgba(255,0,0,.4);
+}
+
+pre.compile_fail:hover, .information:hover + pre.compile_fail {
+	border-left: 2px solid #f00;
+}
+
+pre.should_panic {
+	border-left: 2px solid rgba(255,0,0,.4);
+}
+
+pre.should_panic:hover, .information:hover + pre.should_panic {
+	border-left: 2px solid #f00;
+}
+
+pre.ignore {
+	border-left: 2px solid rgba(255,142,0,.6);
+}
+
+pre.ignore:hover, .information:hover + pre.ignore {
+	border-left: 2px solid #ff9200;
+}
+
+.tooltip.compile_fail {
+	color: rgba(255,0,0,.5);
+}
+
+.information > .compile_fail:hover {
+	color: #f00;
+}
+
+.tooltip.should_panic {
+	color: rgba(255,0,0,.5);
+}
+
+.information > .should_panic:hover {
+	color: #f00;
+}
+
+.tooltip.ignore {
+	color: rgba(255,142,0,.6);
+}
+
+.information > .ignore:hover {
+	color: #ff9200;
+}
+
+.search-failed a {
+	color: #39AFD7;
+}
+
+.tooltip .tooltiptext {
+    background-color: #314559;
+    color: #c5c5c5;
+    border: 1px solid #5c6773;
+}
+
+.tooltip .tooltiptext::after {
+	border-color: transparent #314559 transparent transparent;
+}
+
+#titles > div.selected {
+    background-color: #141920 !important;
+	border-bottom: 1px solid #ffb44c !important;
+	border-top: none;
+}
+
+#titles > div:not(.selected) {
+	background-color: transparent !important;
+	border: none;
+}
+
+#titles > div:hover {
+    border-bottom: 1px solid rgba(242, 151, 24, 0.3);
+}
+
+#titles > div > div.count {
+	color: #888;
+}
+
+/* rules that this theme does not need to set, here to satisfy the rule checker */
+/* note that a lot of these are partially set in some way (meaning they are set
+individually rather than as a group) */
+/* TODO: these rules should be at the bottom of the file but currently must be
+above the `@media (max-width: 700px)` rules due to a bug in the css checker */
+/* see https://github.com/rust-lang/rust/pull/71237#issuecomment-618170143 */
+.content .highlighted.mod, .content .highlighted.externcrate {}
+.search-input:focus {}
+.content span.attr,.content a.attr,.block a.current.attr,.content span.derive,.content a.derive,.block a.current.derive,.content span.macro,.content a.macro,.block a.current.macro {}
+.content .highlighted.trait {}
+.content span.struct,.content a.struct,.block a.current.struct {}
+#titles>div:hover,#titles>div.selected {}
+.content .highlighted.traitalias {}
+.content span.type,.content a.type,.block a.current.type {}
+.content span.union,.content a.union,.block a.current.union {}
+.content .highlighted.foreigntype {}
+pre.rust .lifetime {}
+.content .highlighted.primitive {}
+.content .highlighted.constant,.content .highlighted.static {}
+.stab.unstable {}
+.content .highlighted.fn,.content .highlighted.method,.content .highlighted.tymethod {}
+h2,h3:not(.impl):not(.method):not(.type):not(.tymethod),h4:not(.method):not(.type):not(.tymethod) {}
+.content span.enum,.content a.enum,.block a.current.enum {}
+.content span.constant,.content a.constant,.block a.current.constant,.content span.static,.content a.static,.block a.current.static {}
+.content span.keyword,.content a.keyword,.block a.current.keyword {}
+pre.rust .comment {}
+.content .highlighted.enum {}
+.content .highlighted.struct {}
+.content .highlighted.keyword {}
+.content span.traitalias,.content a.traitalias,.block a.current.traitalias {}
+.content span.fn,.content a.fn,.block a.current.fn,.content span.method,.content a.method,.block a.current.method,.content span.tymethod,.content a.tymethod,.block a.current.tymethod,.content .fnname {}
+pre.rust .kw {}
+pre.rust .self,pre.rust .bool-val,pre.rust .prelude-val,pre.rust .attribute,pre.rust .attribute .ident {}
+.content span.foreigntype,.content a.foreigntype,.block a.current.foreigntype {}
+pre.rust .doccomment {}
+.stab.deprecated {}
+.content .highlighted.attr,.content .highlighted.derive,.content .highlighted.macro {}
+.stab.portability {}
+.content .highlighted.union {}
+.content span.primitive,.content a.primitive,.block a.current.primitive {}
+.content span.externcrate,.content span.mod,.content a.mod,.block a.current.mod {}
+.content .highlighted.type {}
+pre.rust .kw-2,pre.rust .prelude-ty {}
+.content span.trait,.content a.trait,.block a.current.trait {}
+.stab.internal {}
+
+@media (max-width: 700px) {
+	.sidebar-menu {
+		background-color: #14191f;
+		border-bottom-color: #5c6773;
+		border-right-color: #5c6773;
+	}
+
+	.sidebar-elems {
+		background-color: #14191f;
+		border-right-color: #5c6773;
+	}
+
+	#sidebar-filler {
+		background-color: #14191f;
+		border-bottom-color: #5c6773;
+	}
+}
+
+kbd {
+	color: #c5c5c5;
+	background-color: #314559;
+	border-color: #5c6773;
+	border-bottom-color: #5c6773;
+	box-shadow-color: #c6cbd1;
+}
+
+#theme-picker, #settings-menu {
+	border-color: #5c6773;
+    background-color: #0f1419;
+}
+
+#theme-picker > img, #settings-menu > img {
+    filter: invert(100);
+}
+
+#theme-picker:hover, #theme-picker:focus,
+#settings-menu:hover, #settings-menu:focus {
+	border-color: #e0e0e0;
+}
+
+#theme-choices {
+	border-color: #5c6773;
+	background-color: #0f1419;
+}
+
+#theme-choices > button:not(:first-child) {
+	border-top-color: #c5c5c5;
+}
+
+#theme-choices > button:hover, #theme-choices > button:focus {
+	background-color: rgba(70, 70, 70, 0.33);
+}
+
+@media (max-width: 700px) {
+	#theme-picker {
+		background: #0f1419;
+	}
+}
+
+#all-types {
+	background-color: #14191f;
+}
+#all-types:hover {
+	background-color: rgba(70, 70, 70, 0.33);
+}
+
+.search-results td span.alias {
+	color: #c5c5c5;
+}
+.search-results td span.grey {
+	color: #999;
+}
+
+#sidebar-toggle {
+	background-color: #14191f;
+}
+#sidebar-toggle:hover {
+	background-color: rgba(70, 70, 70, 0.33);
+}
+#source-sidebar {
+	background-color: #14191f;
+}
+#source-sidebar > .title {
+	color: #fff;
+	border-bottom-color: #5c6773;
+}
+div.files > a:hover, div.name:hover {
+	background-color: #14191f;
+	color: #ffb44c;
+}
+div.files > .selected {
+	background-color: #14191f;
+	color: #ffb44c;
+}
+.setting-line > .title {
+	border-bottom-color: #5c6773;
+}
+input:checked + .slider {
+	background-color: #ffb454 !important;
+}
diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs
index 6790f3bd5d0b1..6bd7e53cdfbe2 100644
--- a/src/librustdoc/html/static_files.rs
+++ b/src/librustdoc/html/static_files.rs
@@ -64,6 +64,9 @@ pub mod themes {
 
     /// The "dark" theme.
     pub static DARK: &str = include_str!("static/themes/dark.css");
+
+    /// The "ayu" theme.
+    pub static AYU: &str = include_str!("static/themes/ayu.css");
 }
 
 /// Files related to the Fira Sans font.
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index bd585d39c242f..5215db7cdb3ce 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -242,8 +242,8 @@
 #![feature(atomic_mut_ptr)]
 #![feature(box_syntax)]
 #![feature(c_variadic)]
-#![feature(cfg_accessible)]
 #![feature(can_vector)]
+#![feature(cfg_accessible)]
 #![feature(cfg_target_has_atomic)]
 #![feature(cfg_target_thread_local)]
 #![feature(char_error_internals)]
@@ -276,8 +276,8 @@
 #![feature(hashmap_internals)]
 #![feature(int_error_internals)]
 #![feature(int_error_matching)]
-#![feature(into_future)]
 #![feature(integer_atomics)]
+#![feature(into_future)]
 #![feature(lang_items)]
 #![feature(libc)]
 #![feature(link_args)]
@@ -286,6 +286,7 @@
 #![feature(log_syntax)]
 #![feature(maybe_uninit_ref)]
 #![feature(maybe_uninit_slice)]
+#![feature(min_specialization)]
 #![feature(needs_panic_runtime)]
 #![feature(negative_impls)]
 #![feature(never_type)]
@@ -305,7 +306,7 @@
 #![feature(shrink_to)]
 #![feature(slice_concat_ext)]
 #![feature(slice_internals)]
-#![feature(min_specialization)]
+#![feature(slice_strip)]
 #![feature(staged_api)]
 #![feature(std_internals)]
 #![feature(stdsimd)]
diff --git a/src/libstd/sys/sgx/abi/mod.rs b/src/libstd/sys/sgx/abi/mod.rs
index 5ef26d4cc4dc6..b0693b63a48fd 100644
--- a/src/libstd/sys/sgx/abi/mod.rs
+++ b/src/libstd/sys/sgx/abi/mod.rs
@@ -17,6 +17,9 @@ pub mod usercalls;
 #[cfg(not(test))]
 global_asm!(include_str!("entry.S"));
 
+#[repr(C)]
+struct EntryReturn(u64, u64);
+
 #[cfg(not(test))]
 #[no_mangle]
 unsafe extern "C" fn tcs_init(secondary: bool) {
@@ -56,8 +59,7 @@ unsafe extern "C" fn tcs_init(secondary: bool) {
 // able to specify this
 #[cfg(not(test))]
 #[no_mangle]
-#[allow(improper_ctypes_definitions)]
-extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64) -> (u64, u64) {
+extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64) -> EntryReturn {
     // FIXME: how to support TLS in library mode?
     let tls = Box::new(tls::Tls::new());
     let _tls_guard = unsafe { tls.activate() };
@@ -65,7 +67,7 @@ extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64
     if secondary {
         super::thread::Thread::entry();
 
-        (0, 0)
+        EntryReturn(0, 0)
     } else {
         extern "C" {
             fn main(argc: isize, argv: *const *const u8) -> isize;
diff --git a/src/libstd/sys/windows/path.rs b/src/libstd/sys/windows/path.rs
index 524f21f889bc2..dda3ed68cfc95 100644
--- a/src/libstd/sys/windows/path.rs
+++ b/src/libstd/sys/windows/path.rs
@@ -2,6 +2,16 @@ use crate::ffi::OsStr;
 use crate::mem;
 use crate::path::Prefix;
 
+#[cfg(test)]
+mod tests;
+
+pub const MAIN_SEP_STR: &str = "\\";
+pub const MAIN_SEP: char = '\\';
+
+// The unsafety here stems from converting between `&OsStr` and `&[u8]`
+// and back. This is safe to do because (1) we only look at ASCII
+// contents of the encoding and (2) new &OsStr values are produced
+// only from ASCII-bounded slices of existing &OsStr values.
 fn os_str_as_u8_slice(s: &OsStr) -> &[u8] {
     unsafe { mem::transmute(s) }
 }
@@ -19,76 +29,79 @@ pub fn is_verbatim_sep(b: u8) -> bool {
     b == b'\\'
 }
 
+// In most DOS systems, it is not possible to have more than 26 drive letters.
+// See <https://en.wikipedia.org/wiki/Drive_letter_assignment#Common_assignments>.
+pub fn is_valid_drive_letter(disk: u8) -> bool {
+    disk.is_ascii_alphabetic()
+}
+
 pub fn parse_prefix(path: &OsStr) -> Option<Prefix<'_>> {
-    use crate::path::Prefix::*;
-    unsafe {
-        // The unsafety here stems from converting between &OsStr and &[u8]
-        // and back. This is safe to do because (1) we only look at ASCII
-        // contents of the encoding and (2) new &OsStr values are produced
-        // only from ASCII-bounded slices of existing &OsStr values.
-        let mut path = os_str_as_u8_slice(path);
+    use Prefix::{DeviceNS, Disk, Verbatim, VerbatimDisk, VerbatimUNC, UNC};
+
+    let path = os_str_as_u8_slice(path);
 
-        if path.starts_with(br"\\") {
-            // \\
-            path = &path[2..];
-            if path.starts_with(br"?\") {
-                // \\?\
-                path = &path[2..];
-                if path.starts_with(br"UNC\") {
-                    // \\?\UNC\server\share
-                    path = &path[4..];
-                    let (server, share) = match parse_two_comps(path, is_verbatim_sep) {
-                        Some((server, share)) => {
-                            (u8_slice_as_os_str(server), u8_slice_as_os_str(share))
-                        }
-                        None => (u8_slice_as_os_str(path), u8_slice_as_os_str(&[])),
-                    };
-                    return Some(VerbatimUNC(server, share));
-                } else {
-                    // \\?\path
-                    let idx = path.iter().position(|&b| b == b'\\');
-                    if idx == Some(2) && path[1] == b':' {
-                        let c = path[0];
-                        if c.is_ascii() && (c as char).is_alphabetic() {
-                            // \\?\C:\ path
-                            return Some(VerbatimDisk(c.to_ascii_uppercase()));
-                        }
+    // \\
+    if let Some(path) = path.strip_prefix(br"\\") {
+        // \\?\
+        if let Some(path) = path.strip_prefix(br"?\") {
+            // \\?\UNC\server\share
+            if let Some(path) = path.strip_prefix(br"UNC\") {
+                let (server, share) = match get_first_two_components(path, is_verbatim_sep) {
+                    Some((server, share)) => unsafe {
+                        (u8_slice_as_os_str(server), u8_slice_as_os_str(share))
+                    },
+                    None => (unsafe { u8_slice_as_os_str(path) }, OsStr::new("")),
+                };
+                return Some(VerbatimUNC(server, share));
+            } else {
+                // \\?\path
+                match path {
+                    // \\?\C:\path
+                    [c, b':', b'\\', ..] if is_valid_drive_letter(*c) => {
+                        return Some(VerbatimDisk(c.to_ascii_uppercase()));
+                    }
+                    // \\?\cat_pics
+                    _ => {
+                        let idx = path.iter().position(|&b| b == b'\\').unwrap_or(path.len());
+                        let slice = &path[..idx];
+                        return Some(Verbatim(unsafe { u8_slice_as_os_str(slice) }));
                     }
-                    let slice = &path[..idx.unwrap_or(path.len())];
-                    return Some(Verbatim(u8_slice_as_os_str(slice)));
-                }
-            } else if path.starts_with(b".\\") {
-                // \\.\path
-                path = &path[2..];
-                let pos = path.iter().position(|&b| b == b'\\');
-                let slice = &path[..pos.unwrap_or(path.len())];
-                return Some(DeviceNS(u8_slice_as_os_str(slice)));
-            }
-            match parse_two_comps(path, is_sep_byte) {
-                Some((server, share)) if !server.is_empty() && !share.is_empty() => {
-                    // \\server\share
-                    return Some(UNC(u8_slice_as_os_str(server), u8_slice_as_os_str(share)));
                 }
-                _ => (),
             }
-        } else if path.get(1) == Some(&b':') {
-            // C:
-            let c = path[0];
-            if c.is_ascii() && (c as char).is_alphabetic() {
-                return Some(Disk(c.to_ascii_uppercase()));
+        } else if let Some(path) = path.strip_prefix(b".\\") {
+            // \\.\COM42
+            let idx = path.iter().position(|&b| b == b'\\').unwrap_or(path.len());
+            let slice = &path[..idx];
+            return Some(DeviceNS(unsafe { u8_slice_as_os_str(slice) }));
+        }
+        match get_first_two_components(path, is_sep_byte) {
+            Some((server, share)) if !server.is_empty() && !share.is_empty() => {
+                // \\server\share
+                return Some(unsafe { UNC(u8_slice_as_os_str(server), u8_slice_as_os_str(share)) });
             }
+            _ => {}
+        }
+    } else if let [c, b':', ..] = path {
+        // C:
+        if is_valid_drive_letter(*c) {
+            return Some(Disk(c.to_ascii_uppercase()));
         }
-        return None;
-    }
-
-    fn parse_two_comps(mut path: &[u8], f: fn(u8) -> bool) -> Option<(&[u8], &[u8])> {
-        let first = &path[..path.iter().position(|x| f(*x))?];
-        path = &path[(first.len() + 1)..];
-        let idx = path.iter().position(|x| f(*x));
-        let second = &path[..idx.unwrap_or(path.len())];
-        Some((first, second))
     }
+    None
 }
 
-pub const MAIN_SEP_STR: &str = "\\";
-pub const MAIN_SEP: char = '\\';
+/// Returns the first two path components with predicate `f`.
+///
+/// The two components returned will be use by caller
+/// to construct `VerbatimUNC` or `UNC` Windows path prefix.
+///
+/// Returns [`None`] if there are no separators in path.
+fn get_first_two_components(path: &[u8], f: fn(u8) -> bool) -> Option<(&[u8], &[u8])> {
+    let idx = path.iter().position(|&x| f(x))?;
+    // Panic safe
+    // The max `idx+1` is `path.len()` and `path[path.len()..]` is a valid index.
+    let (first, path) = (&path[..idx], &path[idx + 1..]);
+    let idx = path.iter().position(|&x| f(x)).unwrap_or(path.len());
+    let second = &path[..idx];
+    Some((first, second))
+}
diff --git a/src/libstd/sys/windows/path/tests.rs b/src/libstd/sys/windows/path/tests.rs
new file mode 100644
index 0000000000000..fbac1dc1ca17a
--- /dev/null
+++ b/src/libstd/sys/windows/path/tests.rs
@@ -0,0 +1,21 @@
+use super::*;
+
+#[test]
+fn test_get_first_two_components() {
+    assert_eq!(
+        get_first_two_components(br"server\share", is_verbatim_sep),
+        Some((&b"server"[..], &b"share"[..])),
+    );
+
+    assert_eq!(
+        get_first_two_components(br"server\", is_verbatim_sep),
+        Some((&b"server"[..], &b""[..]))
+    );
+
+    assert_eq!(
+        get_first_two_components(br"\server\", is_verbatim_sep),
+        Some((&b""[..], &b"server"[..]))
+    );
+
+    assert_eq!(get_first_two_components(br"there are no separators here", is_verbatim_sep), None,);
+}
diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs
index d435ca6842518..d354a9b1842c2 100644
--- a/src/libstd/thread/mod.rs
+++ b/src/libstd/thread/mod.rs
@@ -641,9 +641,8 @@ where
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn current() -> Thread {
     thread_info::current_thread().expect(
-        "use of std::thread::current() is not \
-                                          possible after the thread's local \
-                                          data has been destroyed",
+        "use of std::thread::current() is not possible \
+         after the thread's local data has been destroyed",
     )
 }
 
diff --git a/src/llvm-project b/src/llvm-project
index d134a53927fa0..86b120e6f302d 160000
--- a/src/llvm-project
+++ b/src/llvm-project
@@ -1 +1 @@
-Subproject commit d134a53927fa033ae7e0f3e8ee872ff2dc71468d
+Subproject commit 86b120e6f302d39cd6973b6391fb299d7bc22122
diff --git a/src/test/ui/issues/issue-73886.rs b/src/test/ui/issues/issue-73886.rs
new file mode 100644
index 0000000000000..2f1ec8c6d6227
--- /dev/null
+++ b/src/test/ui/issues/issue-73886.rs
@@ -0,0 +1,6 @@
+fn main() {
+    let _ = &&[0] as &[_];
+    //~^ ERROR non-primitive cast: `&&[i32; 1]` as `&[_]`
+    let _ = 7u32 as Option<_>;
+    //~^ ERROR non-primitive cast: `u32` as `std::option::Option<_>`
+}
diff --git a/src/test/ui/issues/issue-73886.stderr b/src/test/ui/issues/issue-73886.stderr
new file mode 100644
index 0000000000000..e8ab7db6b82c2
--- /dev/null
+++ b/src/test/ui/issues/issue-73886.stderr
@@ -0,0 +1,15 @@
+error[E0605]: non-primitive cast: `&&[i32; 1]` as `&[_]`
+  --> $DIR/issue-73886.rs:2:13
+   |
+LL |     let _ = &&[0] as &[_];
+   |             ^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
+
+error[E0605]: non-primitive cast: `u32` as `std::option::Option<_>`
+  --> $DIR/issue-73886.rs:4:13
+   |
+LL |     let _ = 7u32 as Option<_>;
+   |             ^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0605`.
diff --git a/src/test/ui/issues/issue-74086.rs b/src/test/ui/issues/issue-74086.rs
new file mode 100644
index 0000000000000..f68a665b2f38d
--- /dev/null
+++ b/src/test/ui/issues/issue-74086.rs
@@ -0,0 +1,4 @@
+fn main() {
+    static BUG: fn(_) -> u8 = |_| 8;
+    //~^ ERROR the type placeholder `_` is not allowed within types on item signatures [E0121]
+}
diff --git a/src/test/ui/issues/issue-74086.stderr b/src/test/ui/issues/issue-74086.stderr
new file mode 100644
index 0000000000000..4127f48a093f4
--- /dev/null
+++ b/src/test/ui/issues/issue-74086.stderr
@@ -0,0 +1,12 @@
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+  --> $DIR/issue-74086.rs:2:20
+   |
+LL |     static BUG: fn(_) -> u8 = |_| 8;
+   |                    ^
+   |                    |
+   |                    not allowed in type signatures
+   |                    help: use type parameters instead: `T`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0121`.
diff --git a/src/test/ui/missing/missing-fields-in-struct-pattern.rs b/src/test/ui/missing/missing-fields-in-struct-pattern.rs
index 24b6b55db6692..40304a674a633 100644
--- a/src/test/ui/missing/missing-fields-in-struct-pattern.rs
+++ b/src/test/ui/missing/missing-fields-in-struct-pattern.rs
@@ -2,8 +2,7 @@ struct S(usize, usize, usize, usize);
 
 fn main() {
     if let S { a, b, c, d } = S(1, 2, 3, 4) {
-    //~^ ERROR struct `S` does not have fields named `a`, `b`, `c`, `d` [E0026]
-    //~| ERROR pattern does not mention fields `0`, `1`, `2`, `3` [E0027]
+    //~^ ERROR tuple variant `S` written as struct variant
         println!("hi");
     }
 }
diff --git a/src/test/ui/missing/missing-fields-in-struct-pattern.stderr b/src/test/ui/missing/missing-fields-in-struct-pattern.stderr
index f7037468996f4..6583524aad18f 100644
--- a/src/test/ui/missing/missing-fields-in-struct-pattern.stderr
+++ b/src/test/ui/missing/missing-fields-in-struct-pattern.stderr
@@ -1,18 +1,9 @@
-error[E0026]: struct `S` does not have fields named `a`, `b`, `c`, `d`
-  --> $DIR/missing-fields-in-struct-pattern.rs:4:16
-   |
-LL |     if let S { a, b, c, d } = S(1, 2, 3, 4) {
-   |                ^  ^  ^  ^ struct `S` does not have these fields
-
-error[E0027]: pattern does not mention fields `0`, `1`, `2`, `3`
+error[E0769]: tuple variant `S` written as struct variant
   --> $DIR/missing-fields-in-struct-pattern.rs:4:12
    |
 LL |     if let S { a, b, c, d } = S(1, 2, 3, 4) {
-   |            ^^^^^^^^^^^^^^^^ missing fields `0`, `1`, `2`, `3`
-   |
-   = note: trying to match a tuple variant with a struct variant pattern
+   |            ^^^^^^^^^^^^^^^^ help: use the tuple variant pattern syntax instead: `S(a, b, c, d)`
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
-Some errors have detailed explanations: E0026, E0027.
-For more information about an error, try `rustc --explain E0026`.
+For more information about this error, try `rustc --explain E0769`.
diff --git a/src/test/ui/type/type-check/issue-41314.rs b/src/test/ui/type/type-check/issue-41314.rs
index 856d4ff6334bc..cbd39f5f9e6ed 100644
--- a/src/test/ui/type/type-check/issue-41314.rs
+++ b/src/test/ui/type/type-check/issue-41314.rs
@@ -4,7 +4,7 @@ enum X {
 
 fn main() {
     match X::Y(0) {
-        X::Y { number } => {} //~ ERROR does not have a field named `number`
-        //~^ ERROR pattern does not mention field `0`
+        X::Y { number } => {}
+        //~^ ERROR tuple variant `X::Y` written as struct variant
     }
 }
diff --git a/src/test/ui/type/type-check/issue-41314.stderr b/src/test/ui/type/type-check/issue-41314.stderr
index c2bba98d10a83..bd4d2071c2059 100644
--- a/src/test/ui/type/type-check/issue-41314.stderr
+++ b/src/test/ui/type/type-check/issue-41314.stderr
@@ -1,18 +1,9 @@
-error[E0026]: variant `X::Y` does not have a field named `number`
-  --> $DIR/issue-41314.rs:7:16
-   |
-LL |         X::Y { number } => {}
-   |                ^^^^^^ variant `X::Y` does not have this field
-
-error[E0027]: pattern does not mention field `0`
+error[E0769]: tuple variant `X::Y` written as struct variant
   --> $DIR/issue-41314.rs:7:9
    |
 LL |         X::Y { number } => {}
-   |         ^^^^^^^^^^^^^^^ missing field `0`
-   |
-   = note: trying to match a tuple variant with a struct variant pattern
+   |         ^^^^^^^^^^^^^^^ help: use the tuple variant pattern syntax instead: `X::Y(number)`
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
-Some errors have detailed explanations: E0026, E0027.
-For more information about an error, try `rustc --explain E0026`.
+For more information about this error, try `rustc --explain E0769`.
diff --git a/src/test/ui/typeck/typeck_type_placeholder_item.rs b/src/test/ui/typeck/typeck_type_placeholder_item.rs
index 99a7023089283..133c5231031fd 100644
--- a/src/test/ui/typeck/typeck_type_placeholder_item.rs
+++ b/src/test/ui/typeck/typeck_type_placeholder_item.rs
@@ -32,6 +32,7 @@ fn test7(x: _) { let _x: usize = x; }
 
 fn test8(_f: fn() -> _) { }
 //~^ ERROR the type placeholder `_` is not allowed within types on item signatures
+//~^^ ERROR the type placeholder `_` is not allowed within types on item signatures
 
 struct Test9;
 
@@ -98,6 +99,7 @@ pub fn main() {
 
     fn fn_test8(_f: fn() -> _) { }
     //~^ ERROR the type placeholder `_` is not allowed within types on item signatures
+    //~^^ ERROR the type placeholder `_` is not allowed within types on item signatures
 
     struct FnTest9;
 
diff --git a/src/test/ui/typeck/typeck_type_placeholder_item.stderr b/src/test/ui/typeck/typeck_type_placeholder_item.stderr
index 6c0653d5fcb7c..a1945f2b9cf4e 100644
--- a/src/test/ui/typeck/typeck_type_placeholder_item.stderr
+++ b/src/test/ui/typeck/typeck_type_placeholder_item.stderr
@@ -1,35 +1,35 @@
 error: expected identifier, found reserved identifier `_`
-  --> $DIR/typeck_type_placeholder_item.rs:152:18
+  --> $DIR/typeck_type_placeholder_item.rs:154:18
    |
 LL | struct BadStruct<_>(_);
    |                  ^ expected identifier, found reserved identifier
 
 error: expected identifier, found reserved identifier `_`
-  --> $DIR/typeck_type_placeholder_item.rs:155:16
+  --> $DIR/typeck_type_placeholder_item.rs:157:16
    |
 LL | trait BadTrait<_> {}
    |                ^ expected identifier, found reserved identifier
 
 error: expected identifier, found reserved identifier `_`
-  --> $DIR/typeck_type_placeholder_item.rs:165:19
+  --> $DIR/typeck_type_placeholder_item.rs:167:19
    |
 LL | struct BadStruct1<_, _>(_);
    |                   ^ expected identifier, found reserved identifier
 
 error: expected identifier, found reserved identifier `_`
-  --> $DIR/typeck_type_placeholder_item.rs:165:22
+  --> $DIR/typeck_type_placeholder_item.rs:167:22
    |
 LL | struct BadStruct1<_, _>(_);
    |                      ^ expected identifier, found reserved identifier
 
 error: expected identifier, found reserved identifier `_`
-  --> $DIR/typeck_type_placeholder_item.rs:170:19
+  --> $DIR/typeck_type_placeholder_item.rs:172:19
    |
 LL | struct BadStruct2<_, T>(_, T);
    |                   ^ expected identifier, found reserved identifier
 
 error: associated constant in `impl` without body
-  --> $DIR/typeck_type_placeholder_item.rs:201:5
+  --> $DIR/typeck_type_placeholder_item.rs:203:5
    |
 LL |     const C: _;
    |     ^^^^^^^^^^-
@@ -37,7 +37,7 @@ LL |     const C: _;
    |               help: provide a definition for the constant: `= <expr>;`
 
 error[E0403]: the name `_` is already used for a generic parameter in this item's generic parameters
-  --> $DIR/typeck_type_placeholder_item.rs:165:22
+  --> $DIR/typeck_type_placeholder_item.rs:167:22
    |
 LL | struct BadStruct1<_, _>(_);
    |                   -  ^ already used
@@ -131,6 +131,15 @@ help: use type parameters instead
 LL | fn test7<T>(x: T) { let _x: usize = x; }
    |         ^^^    ^
 
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+  --> $DIR/typeck_type_placeholder_item.rs:33:22
+   |
+LL | fn test8(_f: fn() -> _) { }
+   |                      ^
+   |                      |
+   |                      not allowed in type signatures
+   |                      help: use type parameters instead: `T`
+
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
   --> $DIR/typeck_type_placeholder_item.rs:33:22
    |
@@ -143,7 +152,7 @@ LL | fn test8<T>(_f: fn() -> T) { }
    |         ^^^             ^
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:46:26
+  --> $DIR/typeck_type_placeholder_item.rs:47:26
    |
 LL | fn test11(x: &usize) -> &_ {
    |                         -^
@@ -152,7 +161,7 @@ LL | fn test11(x: &usize) -> &_ {
    |                         help: replace with the correct return type: `&&usize`
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:51:52
+  --> $DIR/typeck_type_placeholder_item.rs:52:52
    |
 LL | unsafe fn test12(x: *const usize) -> *const *const _ {
    |                                      --------------^
@@ -161,7 +170,7 @@ LL | unsafe fn test12(x: *const usize) -> *const *const _ {
    |                                      help: replace with the correct return type: `*const *const usize`
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:65:8
+  --> $DIR/typeck_type_placeholder_item.rs:66:8
    |
 LL |     a: _,
    |        ^ not allowed in type signatures
@@ -180,13 +189,13 @@ LL |     b: (T, T),
    |
 
 error: missing type for `static` item
-  --> $DIR/typeck_type_placeholder_item.rs:71:12
+  --> $DIR/typeck_type_placeholder_item.rs:72:12
    |
 LL |     static A = 42;
    |            ^ help: provide a type for the item: `A: i32`
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:73:15
+  --> $DIR/typeck_type_placeholder_item.rs:74:15
    |
 LL |     static B: _ = 42;
    |               ^
@@ -195,13 +204,13 @@ LL |     static B: _ = 42;
    |               help: replace `_` with the correct type: `i32`
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:75:15
+  --> $DIR/typeck_type_placeholder_item.rs:76:15
    |
 LL |     static C: Option<_> = Some(42);
    |               ^^^^^^^^^ not allowed in type signatures
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:78:21
+  --> $DIR/typeck_type_placeholder_item.rs:79:21
    |
 LL |     fn fn_test() -> _ { 5 }
    |                     ^
@@ -210,7 +219,7 @@ LL |     fn fn_test() -> _ { 5 }
    |                     help: replace with the correct return type: `i32`
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:81:23
+  --> $DIR/typeck_type_placeholder_item.rs:82:23
    |
 LL |     fn fn_test2() -> (_, _) { (5, 5) }
    |                      -^--^-
@@ -220,7 +229,7 @@ LL |     fn fn_test2() -> (_, _) { (5, 5) }
    |                      help: replace with the correct return type: `(i32, i32)`
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:84:22
+  --> $DIR/typeck_type_placeholder_item.rs:85:22
    |
 LL |     static FN_TEST3: _ = "test";
    |                      ^
@@ -229,7 +238,7 @@ LL |     static FN_TEST3: _ = "test";
    |                      help: replace `_` with the correct type: `&str`
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:87:22
+  --> $DIR/typeck_type_placeholder_item.rs:88:22
    |
 LL |     static FN_TEST4: _ = 145;
    |                      ^
@@ -238,13 +247,13 @@ LL |     static FN_TEST4: _ = 145;
    |                      help: replace `_` with the correct type: `i32`
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:90:22
+  --> $DIR/typeck_type_placeholder_item.rs:91:22
    |
 LL |     static FN_TEST5: (_, _) = (1, 2);
    |                      ^^^^^^ not allowed in type signatures
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:93:20
+  --> $DIR/typeck_type_placeholder_item.rs:94:20
    |
 LL |     fn fn_test6(_: _) { }
    |                    ^ not allowed in type signatures
@@ -255,7 +264,7 @@ LL |     fn fn_test6<T>(_: T) { }
    |                ^^^    ^
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:96:20
+  --> $DIR/typeck_type_placeholder_item.rs:97:20
    |
 LL |     fn fn_test7(x: _) { let _x: usize = x; }
    |                    ^ not allowed in type signatures
@@ -266,7 +275,16 @@ LL |     fn fn_test7<T>(x: T) { let _x: usize = x; }
    |                ^^^    ^
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:99:29
+  --> $DIR/typeck_type_placeholder_item.rs:100:29
+   |
+LL |     fn fn_test8(_f: fn() -> _) { }
+   |                             ^
+   |                             |
+   |                             not allowed in type signatures
+   |                             help: use type parameters instead: `T`
+
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+  --> $DIR/typeck_type_placeholder_item.rs:100:29
    |
 LL |     fn fn_test8(_f: fn() -> _) { }
    |                             ^ not allowed in type signatures
@@ -277,7 +295,7 @@ LL |     fn fn_test8<T>(_f: fn() -> T) { }
    |                ^^^             ^
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:121:12
+  --> $DIR/typeck_type_placeholder_item.rs:123:12
    |
 LL |         a: _,
    |            ^ not allowed in type signatures
@@ -296,13 +314,13 @@ LL |         b: (T, T),
    |
 
 error[E0282]: type annotations needed
-  --> $DIR/typeck_type_placeholder_item.rs:126:18
+  --> $DIR/typeck_type_placeholder_item.rs:128:18
    |
 LL |     fn fn_test11(_: _) -> (_, _) { panic!() }
    |                  ^ cannot infer type
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:126:28
+  --> $DIR/typeck_type_placeholder_item.rs:128:28
    |
 LL |     fn fn_test11(_: _) -> (_, _) { panic!() }
    |                            ^  ^ not allowed in type signatures
@@ -310,7 +328,7 @@ LL |     fn fn_test11(_: _) -> (_, _) { panic!() }
    |                            not allowed in type signatures
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:130:30
+  --> $DIR/typeck_type_placeholder_item.rs:132:30
    |
 LL |     fn fn_test12(x: i32) -> (_, _) { (x, x) }
    |                             -^--^-
@@ -320,7 +338,7 @@ LL |     fn fn_test12(x: i32) -> (_, _) { (x, x) }
    |                             help: replace with the correct return type: `(i32, i32)`
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:133:33
+  --> $DIR/typeck_type_placeholder_item.rs:135:33
    |
 LL |     fn fn_test13(x: _) -> (i32, _) { (x, x) }
    |                           ------^-
@@ -329,7 +347,7 @@ LL |     fn fn_test13(x: _) -> (i32, _) { (x, x) }
    |                           help: replace with the correct return type: `(i32, i32)`
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:152:21
+  --> $DIR/typeck_type_placeholder_item.rs:154:21
    |
 LL | struct BadStruct<_>(_);
    |                     ^ not allowed in type signatures
@@ -340,7 +358,7 @@ LL | struct BadStruct<T>(T);
    |                  ^  ^
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:157:15
+  --> $DIR/typeck_type_placeholder_item.rs:159:15
    |
 LL | impl BadTrait<_> for BadStruct<_> {}
    |               ^                ^ not allowed in type signatures
@@ -353,13 +371,13 @@ LL | impl<T> BadTrait<T> for BadStruct<T> {}
    |     ^^^          ^                ^
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:160:34
+  --> $DIR/typeck_type_placeholder_item.rs:162:34
    |
 LL | fn impl_trait() -> impl BadTrait<_> {
    |                                  ^ not allowed in type signatures
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:165:25
+  --> $DIR/typeck_type_placeholder_item.rs:167:25
    |
 LL | struct BadStruct1<_, _>(_);
    |                         ^ not allowed in type signatures
@@ -370,7 +388,7 @@ LL | struct BadStruct1<T, _>(T);
    |                   ^     ^
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:170:25
+  --> $DIR/typeck_type_placeholder_item.rs:172:25
    |
 LL | struct BadStruct2<_, T>(_, T);
    |                         ^ not allowed in type signatures
@@ -381,13 +399,13 @@ LL | struct BadStruct2<U, T>(U, T);
    |                   ^     ^
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:174:14
+  --> $DIR/typeck_type_placeholder_item.rs:176:14
    |
 LL | type X = Box<_>;
    |              ^ not allowed in type signatures
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:42:27
+  --> $DIR/typeck_type_placeholder_item.rs:43:27
    |
 LL |     fn test10(&self, _x : _) { }
    |                           ^ not allowed in type signatures
@@ -398,7 +416,7 @@ LL |     fn test10<T>(&self, _x : T) { }
    |              ^^^             ^
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:138:31
+  --> $DIR/typeck_type_placeholder_item.rs:140:31
    |
 LL |     fn method_test1(&self, x: _);
    |                               ^ not allowed in type signatures
@@ -409,7 +427,7 @@ LL |     fn method_test1<T>(&self, x: T);
    |                    ^^^           ^
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:140:31
+  --> $DIR/typeck_type_placeholder_item.rs:142:31
    |
 LL |     fn method_test2(&self, x: _) -> _;
    |                               ^     ^ not allowed in type signatures
@@ -422,7 +440,7 @@ LL |     fn method_test2<T>(&self, x: T) -> T;
    |                    ^^^           ^     ^
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:142:31
+  --> $DIR/typeck_type_placeholder_item.rs:144:31
    |
 LL |     fn method_test3(&self) -> _;
    |                               ^ not allowed in type signatures
@@ -433,7 +451,7 @@ LL |     fn method_test3<T>(&self) -> T;
    |                    ^^^           ^
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:144:26
+  --> $DIR/typeck_type_placeholder_item.rs:146:26
    |
 LL |     fn assoc_fn_test1(x: _);
    |                          ^ not allowed in type signatures
@@ -444,7 +462,7 @@ LL |     fn assoc_fn_test1<T>(x: T);
    |                      ^^^    ^
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:146:26
+  --> $DIR/typeck_type_placeholder_item.rs:148:26
    |
 LL |     fn assoc_fn_test2(x: _) -> _;
    |                          ^     ^ not allowed in type signatures
@@ -457,7 +475,7 @@ LL |     fn assoc_fn_test2<T>(x: T) -> T;
    |                      ^^^    ^     ^
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:148:28
+  --> $DIR/typeck_type_placeholder_item.rs:150:28
    |
 LL |     fn assoc_fn_test3() -> _;
    |                            ^ not allowed in type signatures
@@ -468,7 +486,7 @@ LL |     fn assoc_fn_test3<T>() -> T;
    |                      ^^^      ^
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:60:37
+  --> $DIR/typeck_type_placeholder_item.rs:61:37
    |
 LL |     fn clone_from(&mut self, other: _) { *self = Test9; }
    |                                     ^ not allowed in type signatures
@@ -479,7 +497,7 @@ LL |     fn clone_from<T>(&mut self, other: T) { *self = Test9; }
    |                  ^^^                   ^
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:108:34
+  --> $DIR/typeck_type_placeholder_item.rs:110:34
    |
 LL |         fn fn_test10(&self, _x : _) { }
    |                                  ^ not allowed in type signatures
@@ -490,7 +508,7 @@ LL |         fn fn_test10<T>(&self, _x : T) { }
    |                     ^^^             ^
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:116:41
+  --> $DIR/typeck_type_placeholder_item.rs:118:41
    |
 LL |         fn clone_from(&mut self, other: _) { *self = FnTest9; }
    |                                         ^ not allowed in type signatures
@@ -501,25 +519,25 @@ LL |         fn clone_from<T>(&mut self, other: T) { *self = FnTest9; }
    |                      ^^^                   ^
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:180:21
+  --> $DIR/typeck_type_placeholder_item.rs:182:21
    |
 LL | type Y = impl Trait<_>;
    |                     ^ not allowed in type signatures
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:188:14
+  --> $DIR/typeck_type_placeholder_item.rs:190:14
    |
 LL |     type B = _;
    |              ^ not allowed in type signatures
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:190:14
+  --> $DIR/typeck_type_placeholder_item.rs:192:14
    |
 LL |     const C: _;
    |              ^ not allowed in type signatures
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:192:14
+  --> $DIR/typeck_type_placeholder_item.rs:194:14
    |
 LL |     const D: _ = 42;
    |              ^
@@ -528,7 +546,7 @@ LL |     const D: _ = 42;
    |              help: replace `_` with the correct type: `i32`
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:39:24
+  --> $DIR/typeck_type_placeholder_item.rs:40:24
    |
 LL |     fn test9(&self) -> _ { () }
    |                        ^
@@ -537,7 +555,7 @@ LL |     fn test9(&self) -> _ { () }
    |                        help: replace with the correct return type: `()`
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:57:24
+  --> $DIR/typeck_type_placeholder_item.rs:58:24
    |
 LL |     fn clone(&self) -> _ { Test9 }
    |                        ^
@@ -546,7 +564,7 @@ LL |     fn clone(&self) -> _ { Test9 }
    |                        help: replace with the correct return type: `Test9`
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:105:31
+  --> $DIR/typeck_type_placeholder_item.rs:107:31
    |
 LL |         fn fn_test9(&self) -> _ { () }
    |                               ^
@@ -555,7 +573,7 @@ LL |         fn fn_test9(&self) -> _ { () }
    |                               help: replace with the correct return type: `()`
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:113:28
+  --> $DIR/typeck_type_placeholder_item.rs:115:28
    |
 LL |         fn clone(&self) -> _ { FnTest9 }
    |                            ^
@@ -564,25 +582,25 @@ LL |         fn clone(&self) -> _ { FnTest9 }
    |                            help: replace with the correct return type: `main::FnTest9`
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:197:14
+  --> $DIR/typeck_type_placeholder_item.rs:199:14
    |
 LL |     type A = _;
    |              ^ not allowed in type signatures
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:199:14
+  --> $DIR/typeck_type_placeholder_item.rs:201:14
    |
 LL |     type B = _;
    |              ^ not allowed in type signatures
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:201:14
+  --> $DIR/typeck_type_placeholder_item.rs:203:14
    |
 LL |     const C: _;
    |              ^ not allowed in type signatures
 
 error[E0121]: the type placeholder `_` is not allowed within types on item signatures
-  --> $DIR/typeck_type_placeholder_item.rs:204:14
+  --> $DIR/typeck_type_placeholder_item.rs:206:14
    |
 LL |     const D: _ = 42;
    |              ^
@@ -590,7 +608,7 @@ LL |     const D: _ = 42;
    |              not allowed in type signatures
    |              help: replace `_` with the correct type: `i32`
 
-error: aborting due to 64 previous errors
+error: aborting due to 66 previous errors
 
 Some errors have detailed explanations: E0121, E0282, E0403.
 For more information about an error, try `rustc --explain E0121`.
diff --git a/src/test/ui/union/union-fields-2.stderr b/src/test/ui/union/union-fields-2.stderr
index 68cb66d89d218..48654347285d3 100644
--- a/src/test/ui/union/union-fields-2.stderr
+++ b/src/test/ui/union/union-fields-2.stderr
@@ -48,18 +48,18 @@ error: union patterns should have exactly one field
 LL |     let U { a, b } = u;
    |         ^^^^^^^^^^
 
-error[E0026]: union `U` does not have a field named `c`
-  --> $DIR/union-fields-2.rs:18:19
-   |
-LL |     let U { a, b, c } = u;
-   |                   ^ union `U` does not have this field
-
 error: union patterns should have exactly one field
   --> $DIR/union-fields-2.rs:18:9
    |
 LL |     let U { a, b, c } = u;
    |         ^^^^^^^^^^^^^
 
+error[E0026]: union `U` does not have a field named `c`
+  --> $DIR/union-fields-2.rs:18:19
+   |
+LL |     let U { a, b, c } = u;
+   |                   ^ union `U` does not have this field
+
 error: union patterns should have exactly one field
   --> $DIR/union-fields-2.rs:20:9
    |
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.md b/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.md
index 70445d7ef2503..98fd0df685fdb 100644
--- a/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.md
+++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.md
@@ -12,7 +12,7 @@ labels: L-lint
 
 - Kind: *See <https://github.com/rust-lang/rust-clippy/blob/master/README.md#clippy> for list of lint kinds*
 
-*What benefit of this lint over old code?*
+*What is the advantage of the recommended code over the original code*
 
 For example:
 - Remove bounce checking inserted by ...
diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml
index 0c80394f03e3c..fd0cd7a1890bd 100644
--- a/src/tools/clippy/.github/workflows/clippy_bors.yml
+++ b/src/tools/clippy/.github/workflows/clippy_bors.yml
@@ -240,7 +240,8 @@ jobs:
         - 'Geal/nom'
         - 'rust-lang/stdarch'
         - 'serde-rs/serde'
-        - 'chronotope/chrono'
+        # FIXME: chrono currently cannot be compiled with `--all-targets`
+        # - 'chronotope/chrono'
         - 'hyperium/hyper'
         - 'rust-random/rand'
         - 'rust-lang/futures-rs'
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index adc945a69441d..5d08b44ba404f 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -1352,6 +1352,7 @@ Released 2018-09-13
 [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
 [`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map
 [`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
+[`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints
 [`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions
 [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
 [`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
@@ -1508,9 +1509,11 @@ Released 2018-09-13
 [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone
 [`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry
 [`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten
+[`map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_identity
 [`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or
 [`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref
 [`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool
+[`match_like_matches_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro
 [`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items
 [`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm
 [`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats
@@ -1575,6 +1578,7 @@ Released 2018-09-13
 [`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
 [`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
 [`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap
+[`option_if_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
 [`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none
 [`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn
 [`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option
@@ -1586,6 +1590,7 @@ Released 2018-09-13
 [`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
 [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
 [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
+[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
 [`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
@@ -1612,6 +1617,7 @@ Released 2018-09-13
 [`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
 [`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
 [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
+[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
 [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
 [`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
 [`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option
diff --git a/src/tools/clippy/CONTRIBUTING.md b/src/tools/clippy/CONTRIBUTING.md
index 9f7bdcb1be7e5..69a734e4ee4c2 100644
--- a/src/tools/clippy/CONTRIBUTING.md
+++ b/src/tools/clippy/CONTRIBUTING.md
@@ -245,7 +245,7 @@ this to work, you will need the fix of `git subtree` available
 [here][gitgitgadget-pr].
 
 [gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493
-[subtree]: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#external-dependencies-subtree
+[subtree]: https://rustc-dev-guide.rust-lang.org/contributing.html#external-dependencies-subtree
 [`rust-lang/rust`]: https://github.com/rust-lang/rust
 
 ## Issue and PR triage
diff --git a/src/tools/clippy/clippy_lints/src/attrs.rs b/src/tools/clippy/clippy_lints/src/attrs.rs
index 3f7d6ba646770..ef01364b7d965 100644
--- a/src/tools/clippy/clippy_lints/src/attrs.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs.rs
@@ -2,8 +2,8 @@
 
 use crate::reexport::Name;
 use crate::utils::{
-    first_line_of_span, is_present_in_source, match_def_path, paths, snippet_opt, span_lint, span_lint_and_sugg,
-    span_lint_and_then, without_block_comments,
+    first_line_of_span, is_present_in_source, match_def_path, paths, snippet_opt, span_lint, span_lint_and_help,
+    span_lint_and_sugg, span_lint_and_then, without_block_comments,
 };
 use if_chain::if_chain;
 use rustc_ast::ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
@@ -17,7 +17,7 @@ use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Span;
-use rustc_span::symbol::Symbol;
+use rustc_span::symbol::{Symbol, SymbolStr};
 use semver::Version;
 
 static UNIX_SYSTEMS: &[&str] = &[
@@ -182,6 +182,29 @@ declare_clippy_lint! {
     "unknown_lints for scoped Clippy lints"
 }
 
+declare_clippy_lint! {
+    /// **What it does:** Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category.
+    ///
+    /// **Why is this bad?** Restriction lints sometimes are in contrast with other lints or even go against idiomatic rust.
+    /// These lints should only be enabled on a lint-by-lint basis and with careful consideration.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// Bad:
+    /// ```rust
+    /// #![deny(clippy::restriction)]
+    /// ```
+    ///
+    /// Good:
+    /// ```rust
+    /// #![deny(clippy::as_conversions)]
+    /// ```
+    pub BLANKET_CLIPPY_RESTRICTION_LINTS,
+    style,
+    "enabling the complete restriction group"
+}
+
 declare_clippy_lint! {
     /// **What it does:** Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it
     /// with `#[rustfmt::skip]`.
@@ -249,15 +272,17 @@ declare_lint_pass!(Attributes => [
     DEPRECATED_SEMVER,
     USELESS_ATTRIBUTE,
     UNKNOWN_CLIPPY_LINTS,
+    BLANKET_CLIPPY_RESTRICTION_LINTS,
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Attributes {
     fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) {
         if let Some(items) = &attr.meta_item_list() {
             if let Some(ident) = attr.ident() {
-                match &*ident.as_str() {
+                let ident = &*ident.as_str();
+                match ident {
                     "allow" | "warn" | "deny" | "forbid" => {
-                        check_clippy_lint_names(cx, items);
+                        check_clippy_lint_names(cx, ident, items);
                     },
                     _ => {},
                 }
@@ -363,38 +388,43 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
     }
 }
 
-#[allow(clippy::single_match_else)]
-fn check_clippy_lint_names(cx: &LateContext<'_>, items: &[NestedMetaItem]) {
-    let lint_store = cx.lints();
-    for lint in items {
+fn check_clippy_lint_names(cx: &LateContext<'_>, ident: &str, items: &[NestedMetaItem]) {
+    fn extract_name(lint: &NestedMetaItem) -> Option<SymbolStr> {
         if_chain! {
             if let Some(meta_item) = lint.meta_item();
             if meta_item.path.segments.len() > 1;
             if let tool_name = meta_item.path.segments[0].ident;
             if tool_name.as_str() == "clippy";
-            let name = meta_item.path.segments.last().unwrap().ident.name;
-            if let CheckLintNameResult::Tool(Err((None, _))) = lint_store.check_lint_name(
-                &name.as_str(),
-                Some(tool_name.name),
-            );
+            let lint_name = meta_item.path.segments.last().unwrap().ident.name;
             then {
+                return Some(lint_name.as_str());
+            }
+        }
+        None
+    }
+
+    let lint_store = cx.lints();
+    for lint in items {
+        if let Some(lint_name) = extract_name(lint) {
+            if let CheckLintNameResult::Tool(Err((None, _))) =
+                lint_store.check_lint_name(&lint_name, Some(sym!(clippy)))
+            {
                 span_lint_and_then(
                     cx,
                     UNKNOWN_CLIPPY_LINTS,
                     lint.span(),
-                    &format!("unknown clippy lint: clippy::{}", name),
+                    &format!("unknown clippy lint: clippy::{}", lint_name),
                     |diag| {
-                        let name_lower = name.as_str().to_lowercase();
-                        let symbols = lint_store.get_lints().iter().map(
-                            |l| Symbol::intern(&l.name_lower())
-                        ).collect::<Vec<_>>();
-                        let sugg = find_best_match_for_name(
-                            symbols.iter(),
-                            &format!("clippy::{}", name_lower),
-                            None,
-                        );
-                        if name.as_str().chars().any(char::is_uppercase)
-                            && lint_store.find_lints(&format!("clippy::{}", name_lower)).is_ok() {
+                        let name_lower = lint_name.to_lowercase();
+                        let symbols = lint_store
+                            .get_lints()
+                            .iter()
+                            .map(|l| Symbol::intern(&l.name_lower()))
+                            .collect::<Vec<_>>();
+                        let sugg = find_best_match_for_name(symbols.iter(), &format!("clippy::{}", name_lower), None);
+                        if lint_name.chars().any(char::is_uppercase)
+                            && lint_store.find_lints(&format!("clippy::{}", name_lower)).is_ok()
+                        {
                             diag.span_suggestion(
                                 lint.span(),
                                 "lowercase the lint name",
@@ -409,10 +439,19 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, items: &[NestedMetaItem]) {
                                 Applicability::MachineApplicable,
                             );
                         }
-                    }
+                    },
+                );
+            } else if lint_name == "restriction" && ident != "allow" {
+                span_lint_and_help(
+                    cx,
+                    BLANKET_CLIPPY_RESTRICTION_LINTS,
+                    lint.span(),
+                    "restriction lints are not meant to be all enabled",
+                    None,
+                    "try enabling only the lints you really need",
                 );
             }
-        };
+        }
     }
 }
 
@@ -442,15 +481,14 @@ fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> bool {
 }
 
 fn is_relevant_block(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, block: &Block<'_>) -> bool {
-    if let Some(stmt) = block.stmts.first() {
-        match &stmt.kind {
+    block.stmts.first().map_or(
+        block.expr.as_ref().map_or(false, |e| is_relevant_expr(cx, tables, e)),
+        |stmt| match &stmt.kind {
             StmtKind::Local(_) => true,
             StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, tables, expr),
             _ => false,
-        }
-    } else {
-        block.expr.as_ref().map_or(false, |e| is_relevant_expr(cx, tables, e))
-    }
+        },
+    )
 }
 
 fn is_relevant_expr(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, expr: &Expr<'_>) -> bool {
@@ -460,11 +498,10 @@ fn is_relevant_expr(cx: &LateContext<'_>, tables: &ty::TypeckTables<'_>, expr: &
         ExprKind::Ret(None) | ExprKind::Break(_, None) => false,
         ExprKind::Call(path_expr, _) => {
             if let ExprKind::Path(qpath) = &path_expr.kind {
-                if let Some(fun_id) = tables.qpath_res(qpath, path_expr.hir_id).opt_def_id() {
-                    !match_def_path(cx, fun_id, &paths::BEGIN_PANIC)
-                } else {
-                    true
-                }
+                tables
+                    .qpath_res(qpath, path_expr.hir_id)
+                    .opt_def_id()
+                    .map_or(true, |fun_id| !match_def_path(cx, fun_id, &paths::BEGIN_PANIC))
             } else {
                 true
             }
diff --git a/src/tools/clippy/clippy_lints/src/await_holding_lock.rs b/src/tools/clippy/clippy_lints/src/await_holding_lock.rs
index 20b91bc0f1baf..d337262dfa6e2 100644
--- a/src/tools/clippy/clippy_lints/src/await_holding_lock.rs
+++ b/src/tools/clippy/clippy_lints/src/await_holding_lock.rs
@@ -11,7 +11,7 @@ declare_clippy_lint! {
     /// non-async-aware MutexGuard.
     ///
     /// **Why is this bad?** The Mutex types found in syd::sync and parking_lot
-    /// are not designed to operator in an async context across await points.
+    /// are not designed to operate in an async context across await points.
     ///
     /// There are two potential solutions. One is to use an asynx-aware Mutex
     /// type. Many asynchronous foundation crates provide such a Mutex type. The
diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs
index 8090f4673aae0..42bff564de03d 100644
--- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs
+++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs
@@ -115,7 +115,7 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) {
                 COLLAPSIBLE_IF,
                 block.span,
                 "this `else { if .. }` block can be collapsed",
-                "try",
+                "collapse nested if block",
                 snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability).into_owned(),
                 applicability,
             );
@@ -142,7 +142,7 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &
                 let rhs = Sugg::ast(cx, check_inner, "..");
                 diag.span_suggestion(
                     expr.span,
-                    "try",
+                    "collapse nested if block",
                     format!(
                         "if {} {}",
                         lhs.and(&rhs),
diff --git a/src/tools/clippy/clippy_lints/src/comparison_chain.rs b/src/tools/clippy/clippy_lints/src/comparison_chain.rs
index 26476af4cb629..25ccabc1c883e 100644
--- a/src/tools/clippy/clippy_lints/src/comparison_chain.rs
+++ b/src/tools/clippy/clippy_lints/src/comparison_chain.rs
@@ -122,8 +122,5 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain {
 }
 
 fn kind_is_cmp(kind: BinOpKind) -> bool {
-    match kind {
-        BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq => true,
-        _ => false,
-    }
+    matches!(kind, BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq)
 }
diff --git a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs
index 6e8ca647dd7ae..818d8188a787a 100644
--- a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs
@@ -153,5 +153,13 @@ declare_deprecated_lint! {
     ///
     /// **Deprecation reason:** Associated-constants are now preferred.
     pub REPLACE_CONSTS,
-    "associated-constants `MIN`/`MAX` of integers are prefer to `{min,max}_value()` and module constants"
+    "associated-constants `MIN`/`MAX` of integers are prefered to `{min,max}_value()` and module constants"
+}
+
+declare_deprecated_lint! {
+    /// **What it does:** Nothing. This lint has been deprecated.
+    ///
+    /// **Deprecation reason:** The regex! macro does not exist anymore.
+    pub REGEX_MACRO,
+    "the regex! macro has been removed from the regex crate in 2018"
 }
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index d740d88a77d6c..323cad7fa1a8c 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -73,9 +73,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
 fn lint_deref(cx: &LateContext<'_>, method_name: &str, call_expr: &Expr<'_>, var_span: Span, expr_span: Span) {
     match method_name {
         "deref" => {
-            if cx.tcx.lang_items().deref_trait().map_or(false, |id| {
+            let impls_deref_trait = cx.tcx.lang_items().deref_trait().map_or(false, |id| {
                 implements_trait(cx, cx.tables().expr_ty(&call_expr), id, &[])
-            }) {
+            });
+            if impls_deref_trait {
                 span_lint_and_sugg(
                     cx,
                     EXPLICIT_DEREF_METHODS,
@@ -88,9 +89,10 @@ fn lint_deref(cx: &LateContext<'_>, method_name: &str, call_expr: &Expr<'_>, var
             }
         },
         "deref_mut" => {
-            if cx.tcx.lang_items().deref_mut_trait().map_or(false, |id| {
+            let impls_deref_mut_trait = cx.tcx.lang_items().deref_mut_trait().map_or(false, |id| {
                 implements_trait(cx, cx.tables().expr_ty(&call_expr), id, &[])
-            }) {
+            });
+            if impls_deref_mut_trait {
                 span_lint_and_sugg(
                     cx,
                     EXPLICIT_DEREF_METHODS,
diff --git a/src/tools/clippy/clippy_lints/src/eq_op.rs b/src/tools/clippy/clippy_lints/src/eq_op.rs
index cbc93d772dd91..01eff28cb195a 100644
--- a/src/tools/clippy/clippy_lints/src/eq_op.rs
+++ b/src/tools/clippy/clippy_lints/src/eq_op.rs
@@ -214,20 +214,20 @@ impl<'tcx> LateLintPass<'tcx> for EqOp {
 }
 
 fn is_valid_operator(op: BinOp) -> bool {
-    match op.node {
+    matches!(
+        op.node,
         BinOpKind::Sub
-        | BinOpKind::Div
-        | BinOpKind::Eq
-        | BinOpKind::Lt
-        | BinOpKind::Le
-        | BinOpKind::Gt
-        | BinOpKind::Ge
-        | BinOpKind::Ne
-        | BinOpKind::And
-        | BinOpKind::Or
-        | BinOpKind::BitXor
-        | BinOpKind::BitAnd
-        | BinOpKind::BitOr => true,
-        _ => false,
-    }
+            | BinOpKind::Div
+            | BinOpKind::Eq
+            | BinOpKind::Lt
+            | BinOpKind::Le
+            | BinOpKind::Gt
+            | BinOpKind::Ge
+            | BinOpKind::Ne
+            | BinOpKind::And
+            | BinOpKind::Or
+            | BinOpKind::BitXor
+            | BinOpKind::BitAnd
+            | BinOpKind::BitOr
+    )
 }
diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs
index b10181062ff10..ceb3c40d869a1 100644
--- a/src/tools/clippy/clippy_lints/src/escape.rs
+++ b/src/tools/clippy/clippy_lints/src/escape.rs
@@ -105,10 +105,7 @@ fn is_argument(map: rustc_middle::hir::map::Map<'_>, id: HirId) -> bool {
         _ => return false,
     }
 
-    match map.find(map.get_parent_node(id)) {
-        Some(Node::Param(_)) => true,
-        _ => false,
-    }
+    matches!(map.find(map.get_parent_node(id)), Some(Node::Param(_)))
 }
 
 impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
index ceed6a74c4fcc..fb26b9fc27d25 100644
--- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs
+++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
@@ -175,10 +175,7 @@ fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: def_id::DefId, self_a
 fn match_borrow_depth(lhs: Ty<'_>, rhs: Ty<'_>) -> bool {
     match (&lhs.kind, &rhs.kind) {
         (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(&t1, &t2),
-        (l, r) => match (l, r) {
-            (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _)) => false,
-            (_, _) => true,
-        },
+        (l, r) => !matches!((l, r), (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _))),
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
index 4efd068926796..3087d6a940a86 100644
--- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
+++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
@@ -1,11 +1,11 @@
 use crate::consts::{
     constant, constant_simple, Constant,
-    Constant::{F32, F64},
+    Constant::{Int, F32, F64},
 };
-use crate::utils::{higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq};
+use crate::utils::{get_parent_expr, higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
+use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -293,6 +293,121 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
     }
 }
 
+fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
+    if let Some((value, _)) = constant(cx, cx.tables(), &args[1]) {
+        if value == Int(2) {
+            if let Some(parent) = get_parent_expr(cx, expr) {
+                if let Some(grandparent) = get_parent_expr(cx, parent) {
+                    if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args, _) = grandparent.kind {
+                        if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() {
+                            return;
+                        }
+                    }
+                }
+
+                if let ExprKind::Binary(
+                    Spanned {
+                        node: BinOpKind::Add, ..
+                    },
+                    ref lhs,
+                    ref rhs,
+                ) = parent.kind
+                {
+                    let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs };
+
+                    span_lint_and_sugg(
+                        cx,
+                        SUBOPTIMAL_FLOPS,
+                        parent.span,
+                        "square can be computed more efficiently",
+                        "consider using",
+                        format!(
+                            "{}.mul_add({}, {})",
+                            Sugg::hir(cx, &args[0], ".."),
+                            Sugg::hir(cx, &args[0], ".."),
+                            Sugg::hir(cx, &other_addend, ".."),
+                        ),
+                        Applicability::MachineApplicable,
+                    );
+
+                    return;
+                }
+            }
+
+            span_lint_and_sugg(
+                cx,
+                SUBOPTIMAL_FLOPS,
+                expr.span,
+                "square can be computed more efficiently",
+                "consider using",
+                format!("{} * {}", Sugg::hir(cx, &args[0], ".."), Sugg::hir(cx, &args[0], "..")),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
+
+fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option<String> {
+    if let ExprKind::Binary(
+        Spanned {
+            node: BinOpKind::Add, ..
+        },
+        ref add_lhs,
+        ref add_rhs,
+    ) = args[0].kind
+    {
+        // check if expression of the form x * x + y * y
+        if_chain! {
+            if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref lmul_lhs, ref lmul_rhs) = add_lhs.kind;
+            if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref rmul_lhs, ref rmul_rhs) = add_rhs.kind;
+            if are_exprs_equal(cx, lmul_lhs, lmul_rhs);
+            if are_exprs_equal(cx, rmul_lhs, rmul_rhs);
+            then {
+                return Some(format!("{}.hypot({})", Sugg::hir(cx, &lmul_lhs, ".."), Sugg::hir(cx, &rmul_lhs, "..")));
+            }
+        }
+
+        // check if expression of the form x.powi(2) + y.powi(2)
+        if_chain! {
+            if let ExprKind::MethodCall(
+                PathSegment { ident: lmethod_name, .. },
+                ref _lspan,
+                ref largs,
+                _
+            ) = add_lhs.kind;
+            if let ExprKind::MethodCall(
+                PathSegment { ident: rmethod_name, .. },
+                ref _rspan,
+                ref rargs,
+                _
+            ) = add_rhs.kind;
+            if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi";
+            if let Some((lvalue, _)) = constant(cx, cx.tables(), &largs[1]);
+            if let Some((rvalue, _)) = constant(cx, cx.tables(), &rargs[1]);
+            if Int(2) == lvalue && Int(2) == rvalue;
+            then {
+                return Some(format!("{}.hypot({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], "..")));
+            }
+        }
+    }
+
+    None
+}
+
+fn check_hypot(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
+    if let Some(message) = detect_hypot(cx, args) {
+        span_lint_and_sugg(
+            cx,
+            IMPRECISE_FLOPS,
+            expr.span,
+            "hypotenuse can be computed more accurately",
+            "consider using",
+            message,
+            Applicability::MachineApplicable,
+        );
+    }
+}
+
 // TODO: Lint expressions of the form `x.exp() - y` where y > 1
 // and suggest usage of `x.exp_m1() - (y - 1)` instead
 fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
@@ -344,6 +459,14 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
         rhs,
     ) = &expr.kind
     {
+        if let Some(parent) = get_parent_expr(cx, expr) {
+            if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args, _) = parent.kind {
+                if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() {
+                    return;
+                }
+            }
+        }
+
         let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) {
             (inner_lhs, inner_rhs, rhs)
         } else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) {
@@ -479,6 +602,100 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
     }
 }
 
+fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool {
+    if_chain! {
+        if let ExprKind::MethodCall(PathSegment { ident: method_name_a, .. }, _, ref args_a, _) = expr_a.kind;
+        if let ExprKind::MethodCall(PathSegment { ident: method_name_b, .. }, _, ref args_b, _) = expr_b.kind;
+        then {
+            return method_name_a.as_str() == method_name_b.as_str() &&
+                args_a.len() == args_b.len() &&
+                (
+                    ["ln", "log2", "log10"].contains(&&*method_name_a.as_str()) ||
+                    method_name_a.as_str() == "log" && args_a.len() == 2 && are_exprs_equal(cx, &args_a[1], &args_b[1])
+                );
+        }
+    }
+
+    false
+}
+
+fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) {
+    // check if expression of the form x.logN() / y.logN()
+    if_chain! {
+        if let ExprKind::Binary(
+            Spanned {
+                node: BinOpKind::Div, ..
+            },
+            lhs,
+            rhs,
+        ) = &expr.kind;
+        if are_same_base_logs(cx, lhs, rhs);
+        if let ExprKind::MethodCall(_, _, ref largs, _) = lhs.kind;
+        if let ExprKind::MethodCall(_, _, ref rargs, _) = rhs.kind;
+        then {
+            span_lint_and_sugg(
+                cx,
+                SUBOPTIMAL_FLOPS,
+                expr.span,
+                "log base can be expressed more clearly",
+                "consider using",
+                format!("{}.log({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], ".."),),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
+
+fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
+    if_chain! {
+        if let ExprKind::Binary(
+            Spanned {
+                node: BinOpKind::Div, ..
+            },
+            div_lhs,
+            div_rhs,
+        ) = &expr.kind;
+        if let ExprKind::Binary(
+            Spanned {
+                node: BinOpKind::Mul, ..
+            },
+            mul_lhs,
+            mul_rhs,
+        ) = &div_lhs.kind;
+        if let Some((rvalue, _)) = constant(cx, cx.tables(), div_rhs);
+        if let Some((lvalue, _)) = constant(cx, cx.tables(), mul_rhs);
+        then {
+            // TODO: also check for constant values near PI/180 or 180/PI
+            if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) &&
+               (F32(180_f32) == lvalue || F64(180_f64) == lvalue)
+            {
+                span_lint_and_sugg(
+                    cx,
+                    SUBOPTIMAL_FLOPS,
+                    expr.span,
+                    "conversion to degrees can be done more accurately",
+                    "consider using",
+                    format!("{}.to_degrees()", Sugg::hir(cx, &mul_lhs, "..")),
+                    Applicability::MachineApplicable,
+                );
+            } else if
+                (F32(180_f32) == rvalue || F64(180_f64) == rvalue) &&
+                (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue)
+            {
+                span_lint_and_sugg(
+                    cx,
+                    SUBOPTIMAL_FLOPS,
+                    expr.span,
+                    "conversion to radians can be done more accurately",
+                    "consider using",
+                    format!("{}.to_radians()", Sugg::hir(cx, &mul_lhs, "..")),
+                    Applicability::MachineApplicable,
+                );
+            }
+        }
+    }
+}
+
 impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if let ExprKind::MethodCall(ref path, _, args, _) = &expr.kind {
@@ -489,6 +706,8 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
                     "ln" => check_ln1p(cx, expr, args),
                     "log" => check_log_base(cx, expr, args),
                     "powf" => check_powf(cx, expr, args),
+                    "powi" => check_powi(cx, expr, args),
+                    "sqrt" => check_hypot(cx, expr, args),
                     _ => {},
                 }
             }
@@ -496,6 +715,8 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
             check_expm1(cx, expr);
             check_mul_add(cx, expr);
             check_custom_abs(cx, expr);
+            check_log_division(cx, expr);
+            check_radians(cx, expr);
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/formatting.rs b/src/tools/clippy/clippy_lints/src/formatting.rs
index 156246fb8bbb0..1bd16e6cce53a 100644
--- a/src/tools/clippy/clippy_lints/src/formatting.rs
+++ b/src/tools/clippy/clippy_lints/src/formatting.rs
@@ -305,18 +305,10 @@ fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) {
 }
 
 fn is_block(expr: &Expr) -> bool {
-    if let ExprKind::Block(..) = expr.kind {
-        true
-    } else {
-        false
-    }
+    matches!(expr.kind, ExprKind::Block(..))
 }
 
 /// Check if the expression is an `if` or `if let`
 fn is_if(expr: &Expr) -> bool {
-    if let ExprKind::If(..) = expr.kind {
-        true
-    } else {
-        false
-    }
+    matches!(expr.kind, ExprKind::If(..))
 }
diff --git a/src/tools/clippy/clippy_lints/src/functions.rs b/src/tools/clippy/clippy_lints/src/functions.rs
index 3f030dd84225b..63133a4872a3e 100644
--- a/src/tools/clippy/clippy_lints/src/functions.rs
+++ b/src/tools/clippy/clippy_lints/src/functions.rs
@@ -645,13 +645,7 @@ fn is_mutated_static(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> bool {
     use hir::ExprKind::{Field, Index, Path};
 
     match e.kind {
-        Path(ref qpath) => {
-            if let Res::Local(_) = qpath_res(cx, qpath, e.hir_id) {
-                false
-            } else {
-                true
-            }
-        },
+        Path(ref qpath) => !matches!(qpath_res(cx, qpath, e.hir_id), Res::Local(_)),
         Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(cx, inner),
         _ => false,
     }
diff --git a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
index f911cb68ea579..fbd2eeacc6ef5 100644
--- a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
+++ b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
@@ -135,13 +135,10 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> {
     }
 }
 
-impl<'tcx> ArmVisitor<'_, 'tcx> {
+impl<'tcx, 'l> ArmVisitor<'tcx, 'l> {
     fn same_mutex(&self, cx: &LateContext<'_>, op_mutex: &Expr<'_>) -> bool {
-        if let Some(arm_mutex) = self.found_mutex {
-            SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex)
-        } else {
-            false
-        }
+        self.found_mutex
+            .map_or(false, |arm_mutex| SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex))
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs
index 26d96428771d6..1b09328ceabb0 100644
--- a/src/tools/clippy/clippy_lints/src/len_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/len_zero.rs
@@ -302,16 +302,12 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 
     let ty = &walk_ptrs_ty(cx.tables().expr_ty(expr));
     match ty.kind {
-        ty::Dynamic(ref tt, ..) => {
-            if let Some(principal) = tt.principal() {
-                cx.tcx
-                    .associated_items(principal.def_id())
-                    .in_definition_order()
-                    .any(|item| is_is_empty(cx, &item))
-            } else {
-                false
-            }
-        },
+        ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| {
+            cx.tcx
+                .associated_items(principal.def_id())
+                .in_definition_order()
+                .any(|item| is_is_empty(cx, &item))
+        }),
         ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id),
         ty::Adt(id, _) => has_is_empty_impl(cx, id.did),
         ty::Array(..) | ty::Slice(..) | ty::Str => true,
diff --git a/src/tools/clippy/clippy_lints/src/let_and_return.rs b/src/tools/clippy/clippy_lints/src/let_and_return.rs
index ddc41f89f8dec..fa560ffb980c8 100644
--- a/src/tools/clippy/clippy_lints/src/let_and_return.rs
+++ b/src/tools/clippy/clippy_lints/src/let_and_return.rs
@@ -1,6 +1,5 @@
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
 use rustc_hir::{Block, Expr, ExprKind, PatKind, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -9,7 +8,7 @@ use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::subst::GenericArgKind;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
-use crate::utils::{in_macro, match_qpath, snippet_opt, span_lint_and_then};
+use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_then};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for `let`-bindings, which are subsequently
@@ -97,22 +96,6 @@ struct BorrowVisitor<'a, 'tcx> {
     borrows: bool,
 }
 
-impl BorrowVisitor<'_, '_> {
-    fn fn_def_id(&self, expr: &Expr<'_>) -> Option<DefId> {
-        match &expr.kind {
-            ExprKind::MethodCall(..) => self.cx.tables().type_dependent_def_id(expr.hir_id),
-            ExprKind::Call(
-                Expr {
-                    kind: ExprKind::Path(qpath),
-                    ..
-                },
-                ..,
-            ) => self.cx.qpath_res(qpath, expr.hir_id).opt_def_id(),
-            _ => None,
-        }
-    }
-}
-
 impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> {
     type Map = Map<'tcx>;
 
@@ -121,7 +104,7 @@ impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> {
             return;
         }
 
-        if let Some(def_id) = self.fn_def_id(expr) {
+        if let Some(def_id) = fn_def_id(self.cx, expr) {
             self.borrows = self
                 .cx
                 .tcx
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 501220f28e5db..32e79317f8225 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -229,6 +229,7 @@ mod main_recursion;
 mod manual_async_fn;
 mod manual_non_exhaustive;
 mod map_clone;
+mod map_identity;
 mod map_unit_fn;
 mod match_on_vec_items;
 mod matches;
@@ -263,10 +264,12 @@ mod non_copy_const;
 mod non_expressive_names;
 mod open_options;
 mod option_env_unwrap;
+mod option_if_let_else;
 mod overflow_check_conditional;
 mod panic_unimplemented;
 mod partialeq_ne_impl;
 mod path_buf_push_overwrite;
+mod pattern_type_mismatch;
 mod precedence;
 mod ptr;
 mod ptr_offset_with_cast;
@@ -274,11 +277,11 @@ mod question_mark;
 mod ranges;
 mod redundant_clone;
 mod redundant_field_names;
-mod redundant_pattern_matching;
 mod redundant_pub_crate;
 mod redundant_static_lifetimes;
 mod reference;
 mod regex;
+mod repeat_once;
 mod returns;
 mod serde_api;
 mod shadow;
@@ -459,7 +462,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     );
     store.register_removed(
         "clippy::replace_consts",
-        "associated-constants `MIN`/`MAX` of integers are prefer to `{min,max}_value()` and module constants",
+        "associated-constants `MIN`/`MAX` of integers are prefered to `{min,max}_value()` and module constants",
+    );
+    store.register_removed(
+        "clippy::regex_macro",
+        "the regex! macro has been removed from the regex crate in 2018",
     );
     // end deprecated lints, do not remove this comment, it’s used in `update_lints`
 
@@ -473,6 +480,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &assign_ops::ASSIGN_OP_PATTERN,
         &assign_ops::MISREFACTORED_ASSIGN_OP,
         &atomic_ordering::INVALID_ATOMIC_ORDERING,
+        &attrs::BLANKET_CLIPPY_RESTRICTION_LINTS,
         &attrs::DEPRECATED_CFG_ATTR,
         &attrs::DEPRECATED_SEMVER,
         &attrs::EMPTY_LINE_AFTER_OUTER_ATTR,
@@ -608,17 +616,20 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &manual_async_fn::MANUAL_ASYNC_FN,
         &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
         &map_clone::MAP_CLONE,
+        &map_identity::MAP_IDENTITY,
         &map_unit_fn::OPTION_MAP_UNIT_FN,
         &map_unit_fn::RESULT_MAP_UNIT_FN,
         &match_on_vec_items::MATCH_ON_VEC_ITEMS,
         &matches::INFALLIBLE_DESTRUCTURING_MATCH,
         &matches::MATCH_AS_REF,
         &matches::MATCH_BOOL,
+        &matches::MATCH_LIKE_MATCHES_MACRO,
         &matches::MATCH_OVERLAPPING_ARM,
         &matches::MATCH_REF_PATS,
         &matches::MATCH_SINGLE_BINDING,
         &matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
         &matches::MATCH_WILD_ERR_ARM,
+        &matches::REDUNDANT_PATTERN_MATCHING,
         &matches::REST_PAT_IN_FULLY_BOUND_STRUCTS,
         &matches::SINGLE_MATCH,
         &matches::SINGLE_MATCH_ELSE,
@@ -726,6 +737,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &non_expressive_names::SIMILAR_NAMES,
         &open_options::NONSENSICAL_OPEN_OPTIONS,
         &option_env_unwrap::OPTION_ENV_UNWRAP,
+        &option_if_let_else::OPTION_IF_LET_ELSE,
         &overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
         &panic_unimplemented::PANIC,
         &panic_unimplemented::PANIC_PARAMS,
@@ -734,6 +746,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &panic_unimplemented::UNREACHABLE,
         &partialeq_ne_impl::PARTIALEQ_NE_IMPL,
         &path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
+        &pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
         &precedence::PRECEDENCE,
         &ptr::CMP_NULL,
         &ptr::MUT_FROM_REF,
@@ -746,14 +759,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &ranges::REVERSED_EMPTY_RANGES,
         &redundant_clone::REDUNDANT_CLONE,
         &redundant_field_names::REDUNDANT_FIELD_NAMES,
-        &redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING,
         &redundant_pub_crate::REDUNDANT_PUB_CRATE,
         &redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
         &reference::DEREF_ADDROF,
         &reference::REF_IN_DEREF,
         &regex::INVALID_REGEX,
-        &regex::REGEX_MACRO,
         &regex::TRIVIAL_REGEX,
+        &repeat_once::REPEAT_ONCE,
         &returns::NEEDLESS_RETURN,
         &returns::UNUSED_UNIT,
         &serde_api::SERDE_API_MISUSE,
@@ -946,7 +958,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box missing_doc::MissingDoc::new());
     store.register_late_pass(|| box missing_inline::MissingInline);
     store.register_late_pass(|| box if_let_some_result::OkIfLet);
-    store.register_late_pass(|| box redundant_pattern_matching::RedundantPatternMatching);
     store.register_late_pass(|| box partialeq_ne_impl::PartialEqNeImpl);
     store.register_late_pass(|| box unused_io_amount::UnusedIoAmount);
     let enum_variant_size_threshold = conf.enum_variant_size_threshold;
@@ -990,7 +1001,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     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);
-    store.register_late_pass(|| box trait_bounds::TraitBounds);
+    let max_trait_bounds = conf.max_trait_bounds;
+    store.register_late_pass(move || box trait_bounds::TraitBounds::new(max_trait_bounds));
     store.register_late_pass(|| box comparison_chain::ComparisonChain);
     store.register_late_pass(|| box mut_key::MutableKeyType);
     store.register_late_pass(|| box modulo_arithmetic::ModuloArithmetic);
@@ -1027,7 +1039,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     let array_size_threshold = conf.array_size_threshold;
     store.register_late_pass(move || box large_stack_arrays::LargeStackArrays::new(array_size_threshold));
     store.register_late_pass(move || box large_const_arrays::LargeConstArrays::new(array_size_threshold));
-    store.register_late_pass(move || box floating_point_arithmetic::FloatingPointArithmetic);
+    store.register_late_pass(|| box floating_point_arithmetic::FloatingPointArithmetic);
     store.register_early_pass(|| box as_conversions::AsConversions);
     store.register_early_pass(|| box utils::internal_lints::ProduceIce);
     store.register_late_pass(|| box let_underscore::LetUnderscore);
@@ -1043,6 +1055,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default());
     store.register_late_pass(|| box unnamed_address::UnnamedAddress);
     store.register_late_pass(|| box dereference::Dereferencing);
+    store.register_late_pass(|| box option_if_let_else::OptionIfLetElse);
     store.register_late_pass(|| box future_not_send::FutureNotSend);
     store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls);
     store.register_late_pass(|| box if_let_mutex::IfLetMutex);
@@ -1057,6 +1070,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     });
     store.register_early_pass(|| box unnested_or_patterns::UnnestedOrPatterns);
     store.register_late_pass(|| box macro_use::MacroUseImports::default());
+    store.register_late_pass(|| box map_identity::MapIdentity);
+    store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch);
+    store.register_late_pass(|| box repeat_once::RepeatOnce);
 
     store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
         LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@@ -1090,6 +1106,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&panic_unimplemented::TODO),
         LintId::of(&panic_unimplemented::UNIMPLEMENTED),
         LintId::of(&panic_unimplemented::UNREACHABLE),
+        LintId::of(&pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
         LintId::of(&shadow::SHADOW_REUSE),
         LintId::of(&shadow::SHADOW_SAME),
         LintId::of(&strings::STRING_ADD),
@@ -1146,6 +1163,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&needless_continue::NEEDLESS_CONTINUE),
         LintId::of(&needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
         LintId::of(&non_expressive_names::SIMILAR_NAMES),
+        LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE),
+        LintId::of(&ranges::RANGE_MINUS_ONE),
         LintId::of(&ranges::RANGE_PLUS_ONE),
         LintId::of(&shadow::SHADOW_UNRELATED),
         LintId::of(&strings::STRING_ADD_ASSIGN),
@@ -1186,6 +1205,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&assign_ops::ASSIGN_OP_PATTERN),
         LintId::of(&assign_ops::MISREFACTORED_ASSIGN_OP),
         LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING),
+        LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
         LintId::of(&attrs::DEPRECATED_CFG_ATTR),
         LintId::of(&attrs::DEPRECATED_SEMVER),
         LintId::of(&attrs::MISMATCHED_TARGET_OS),
@@ -1273,13 +1293,16 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&manual_async_fn::MANUAL_ASYNC_FN),
         LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
         LintId::of(&map_clone::MAP_CLONE),
+        LintId::of(&map_identity::MAP_IDENTITY),
         LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN),
         LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN),
         LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH),
         LintId::of(&matches::MATCH_AS_REF),
+        LintId::of(&matches::MATCH_LIKE_MATCHES_MACRO),
         LintId::of(&matches::MATCH_OVERLAPPING_ARM),
         LintId::of(&matches::MATCH_REF_PATS),
         LintId::of(&matches::MATCH_SINGLE_BINDING),
+        LintId::of(&matches::REDUNDANT_PATTERN_MATCHING),
         LintId::of(&matches::SINGLE_MATCH),
         LintId::of(&matches::WILDCARD_IN_OR_PATTERNS),
         LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM),
@@ -1364,18 +1387,16 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&ptr::PTR_ARG),
         LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
         LintId::of(&question_mark::QUESTION_MARK),
-        LintId::of(&ranges::RANGE_MINUS_ONE),
         LintId::of(&ranges::RANGE_ZIP_WITH_LEN),
         LintId::of(&ranges::REVERSED_EMPTY_RANGES),
         LintId::of(&redundant_clone::REDUNDANT_CLONE),
         LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
-        LintId::of(&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING),
         LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
         LintId::of(&reference::DEREF_ADDROF),
         LintId::of(&reference::REF_IN_DEREF),
         LintId::of(&regex::INVALID_REGEX),
-        LintId::of(&regex::REGEX_MACRO),
         LintId::of(&regex::TRIVIAL_REGEX),
+        LintId::of(&repeat_once::REPEAT_ONCE),
         LintId::of(&returns::NEEDLESS_RETURN),
         LintId::of(&returns::UNUSED_UNIT),
         LintId::of(&serde_api::SERDE_API_MISUSE),
@@ -1437,6 +1458,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_group(true, "clippy::style", Some("clippy_style"), vec![
         LintId::of(&assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
         LintId::of(&assign_ops::ASSIGN_OP_PATTERN),
+        LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
         LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS),
         LintId::of(&bit_mask::VERBOSE_BIT_MASK),
         LintId::of(&blacklisted_name::BLACKLISTED_NAME),
@@ -1470,8 +1492,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
         LintId::of(&map_clone::MAP_CLONE),
         LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH),
+        LintId::of(&matches::MATCH_LIKE_MATCHES_MACRO),
         LintId::of(&matches::MATCH_OVERLAPPING_ARM),
         LintId::of(&matches::MATCH_REF_PATS),
+        LintId::of(&matches::REDUNDANT_PATTERN_MATCHING),
         LintId::of(&matches::SINGLE_MATCH),
         LintId::of(&mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
         LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT),
@@ -1508,9 +1532,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&ptr::PTR_ARG),
         LintId::of(&question_mark::QUESTION_MARK),
         LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
-        LintId::of(&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING),
         LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
-        LintId::of(&regex::REGEX_MACRO),
         LintId::of(&regex::TRIVIAL_REGEX),
         LintId::of(&returns::NEEDLESS_RETURN),
         LintId::of(&returns::UNUSED_UNIT),
@@ -1550,6 +1572,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&loops::EXPLICIT_COUNTER_LOOP),
         LintId::of(&loops::MUT_RANGE_BOUND),
         LintId::of(&loops::WHILE_LET_LOOP),
+        LintId::of(&map_identity::MAP_IDENTITY),
         LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN),
         LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN),
         LintId::of(&matches::MATCH_AS_REF),
@@ -1580,10 +1603,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&partialeq_ne_impl::PARTIALEQ_NE_IMPL),
         LintId::of(&precedence::PRECEDENCE),
         LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
-        LintId::of(&ranges::RANGE_MINUS_ONE),
         LintId::of(&ranges::RANGE_ZIP_WITH_LEN),
         LintId::of(&reference::DEREF_ADDROF),
         LintId::of(&reference::REF_IN_DEREF),
+        LintId::of(&repeat_once::REPEAT_ONCE),
         LintId::of(&swap::MANUAL_SWAP),
         LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT),
         LintId::of(&transmute::CROSSPOINTER_TRANSMUTE),
diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs
index a79f94855bdab..168f9f953e4d8 100644
--- a/src/tools/clippy/clippy_lints/src/lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs
@@ -129,10 +129,10 @@ fn check_fn_inner<'tcx>(
     }
 
     let mut bounds_lts = Vec::new();
-    let types = generics.params.iter().filter(|param| match param.kind {
-        GenericParamKind::Type { .. } => true,
-        _ => false,
-    });
+    let types = generics
+        .params
+        .iter()
+        .filter(|param| matches!(param.kind, GenericParamKind::Type { .. }));
     for typ in types {
         for bound in typ.bounds {
             let mut visitor = RefVisitor::new(cx);
@@ -337,10 +337,10 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
     fn collect_anonymous_lifetimes(&mut self, qpath: &QPath<'_>, ty: &Ty<'_>) {
         if let Some(ref last_path_segment) = last_path_segment(qpath).args {
             if !last_path_segment.parenthesized
-                && !last_path_segment.args.iter().any(|arg| match arg {
-                    GenericArg::Lifetime(_) => true,
-                    _ => false,
-                })
+                && !last_path_segment
+                    .args
+                    .iter()
+                    .any(|arg| matches!(arg, GenericArg::Lifetime(_)))
             {
                 let hir_id = ty.hir_id;
                 match self.cx.qpath_res(qpath, hir_id) {
diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs
index 7ba43562d7d44..a36fdca5d5de6 100644
--- a/src/tools/clippy/clippy_lints/src/literal_representation.rs
+++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs
@@ -264,10 +264,13 @@ impl LiteralDigitGrouping {
 
         let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent {
             (exponent, &["32", "64"][..], 'f')
-        } else if let Some(fraction) = &mut num_lit.fraction {
-            (fraction, &["32", "64"][..], 'f')
         } else {
-            (&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i')
+            num_lit
+                .fraction
+                .as_mut()
+                .map_or((&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i'), |fraction| {
+                    (fraction, &["32", "64"][..], 'f')
+                })
         };
 
         let mut split = part.rsplit('_');
diff --git a/src/tools/clippy/clippy_lints/src/loops.rs b/src/tools/clippy/clippy_lints/src/loops.rs
index d821b5134841e..396bb65910903 100644
--- a/src/tools/clippy/clippy_lints/src/loops.rs
+++ b/src/tools/clippy/clippy_lints/src/loops.rs
@@ -686,13 +686,9 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
                 NeverLoopResult::AlwaysBreak
             }
         },
-        ExprKind::Break(_, ref e) | ExprKind::Ret(ref e) => {
-            if let Some(ref e) = *e {
-                combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak)
-            } else {
-                NeverLoopResult::AlwaysBreak
-            }
-        },
+        ExprKind::Break(_, ref e) | ExprKind::Ret(ref e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| {
+            combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak)
+        }),
         ExprKind::InlineAsm(ref asm) => asm
             .operands
             .iter()
@@ -1881,13 +1877,9 @@ fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool {
     // IntoIterator is currently only implemented for array sizes <= 32 in rustc
     match ty.kind {
-        ty::Array(_, n) => {
-            if let Some(val) = n.try_eval_usize(cx.tcx, cx.param_env) {
-                (0..=32).contains(&val)
-            } else {
-                false
-            }
-        },
+        ty::Array(_, n) => n
+            .try_eval_usize(cx.tcx, cx.param_env)
+            .map_or(false, |val| (0..=32).contains(&val)),
         _ => false,
     }
 }
@@ -1899,11 +1891,7 @@ fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<
         return None;
     }
     if let StmtKind::Local(ref local) = block.stmts[0].kind {
-        if let Some(expr) = local.init {
-            Some(expr)
-        } else {
-            None
-        }
+        local.init //.map(|expr| expr)
     } else {
         None
     }
@@ -2023,15 +2011,13 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
                 if let PatKind::Binding(.., ident, _) = local.pat.kind {
                     self.name = Some(ident.name);
 
-                    self.state = if let Some(ref init) = local.init {
+                    self.state = local.init.as_ref().map_or(VarState::Declared, |init| {
                         if is_integer_const(&self.cx, init, 0) {
                             VarState::Warn
                         } else {
                             VarState::Declared
                         }
-                    } else {
-                        VarState::Declared
-                    }
+                    })
                 }
             }
         }
@@ -2105,17 +2091,11 @@ fn var_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<HirId> {
 }
 
 fn is_loop(expr: &Expr<'_>) -> bool {
-    match expr.kind {
-        ExprKind::Loop(..) => true,
-        _ => false,
-    }
+    matches!(expr.kind, ExprKind::Loop(..))
 }
 
 fn is_conditional(expr: &Expr<'_>) -> bool {
-    match expr.kind {
-        ExprKind::Match(..) => true,
-        _ => false,
-    }
+    matches!(expr.kind, ExprKind::Match(..))
 }
 
 fn is_nested(cx: &LateContext<'_>, match_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool {
diff --git a/src/tools/clippy/clippy_lints/src/map_identity.rs b/src/tools/clippy/clippy_lints/src/map_identity.rs
new file mode 100644
index 0000000000000..24ec78c884647
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/map_identity.rs
@@ -0,0 +1,126 @@
+use crate::utils::{
+    is_adjusted, is_type_diagnostic_item, match_path, match_trait_method, match_var, paths, remove_blocks,
+    span_lint_and_sugg,
+};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for instances of `map(f)` where `f` is the identity function.
+    ///
+    /// **Why is this bad?** It can be written more concisely without the call to `map`.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// let x = [1, 2, 3];
+    /// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let x = [1, 2, 3];
+    /// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
+    /// ```
+    pub MAP_IDENTITY,
+    complexity,
+    "using iterator.map(|x| x)"
+}
+
+declare_lint_pass!(MapIdentity => [MAP_IDENTITY]);
+
+impl<'tcx> LateLintPass<'tcx> for MapIdentity {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        if expr.span.from_expansion() {
+            return;
+        }
+
+        if_chain! {
+            if let Some([caller, func]) = get_map_argument(cx, expr);
+            if is_expr_identity_function(cx, func);
+            then {
+                span_lint_and_sugg(
+                    cx,
+                    MAP_IDENTITY,
+                    expr.span.trim_start(caller.span).unwrap(),
+                    "unnecessary map of the identity function",
+                    "remove the call to `map`",
+                    String::new(),
+                    Applicability::MachineApplicable
+                )
+            }
+        }
+    }
+}
+
+/// Returns the arguments passed into map() if the expression is a method call to
+/// map(). Otherwise, returns None.
+fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a [Expr<'a>]> {
+    if_chain! {
+        if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind;
+        if args.len() == 2 && method.ident.as_str() == "map";
+        let caller_ty = cx.tables().expr_ty(&args[0]);
+        if match_trait_method(cx, expr, &paths::ITERATOR)
+            || is_type_diagnostic_item(cx, caller_ty, sym!(result_type))
+            || is_type_diagnostic_item(cx, caller_ty, sym!(option_type));
+        then {
+            Some(args)
+        } else {
+            None
+        }
+    }
+}
+
+/// Checks if an expression represents the identity function
+/// Only examines closures and `std::convert::identity`
+fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    match expr.kind {
+        ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
+        ExprKind::Path(QPath::Resolved(_, ref path)) => match_path(path, &paths::STD_CONVERT_IDENTITY),
+        _ => false,
+    }
+}
+
+/// Checks if a function's body represents the identity function
+/// Looks for bodies of the form `|x| x`, `|x| return x`, `|x| { return x }` or `|x| {
+/// return x; }`
+fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
+    let params = func.params;
+    let body = remove_blocks(&func.value);
+
+    // if there's less/more than one parameter, then it is not the identity function
+    if params.len() != 1 {
+        return false;
+    }
+
+    match body.kind {
+        ExprKind::Path(QPath::Resolved(None, _)) => match_expr_param(cx, body, params[0].pat),
+        ExprKind::Ret(Some(ref ret_val)) => match_expr_param(cx, ret_val, params[0].pat),
+        ExprKind::Block(ref block, _) => {
+            if_chain! {
+                if block.stmts.len() == 1;
+                if let StmtKind::Semi(ref expr) | StmtKind::Expr(ref expr) = block.stmts[0].kind;
+                if let ExprKind::Ret(Some(ref ret_val)) = expr.kind;
+                then {
+                    match_expr_param(cx, ret_val, params[0].pat)
+                } else {
+                    false
+                }
+            }
+        },
+        _ => false,
+    }
+}
+
+/// Returns true iff an expression returns the same thing as a parameter's pattern
+fn match_expr_param(cx: &LateContext<'_>, expr: &Expr<'_>, pat: &Pat<'_>) -> bool {
+    if let PatKind::Binding(_, _, ident, _) = pat.kind {
+        match_var(expr, ident.name) && !(cx.tables().hir_owner == expr.hir_id.owner && is_adjusted(cx, expr))
+    } else {
+        false
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/matches.rs b/src/tools/clippy/clippy_lints/src/matches.rs
index b754a45aa404f..bd474c208070c 100644
--- a/src/tools/clippy/clippy_lints/src/matches.rs
+++ b/src/tools/clippy/clippy_lints/src/matches.rs
@@ -13,14 +13,14 @@ use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::def::CtorKind;
 use rustc_hir::{
-    Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Local, MatchSource, Mutability, Node, Pat, PatKind,
-    QPath, RangeEnd,
+    Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, Local, MatchSource, Mutability, Node, Pat,
+    PatKind, QPath, RangeEnd,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, Ty};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::source_map::Span;
+use rustc_span::source_map::{Span, Spanned};
 use std::cmp::Ordering;
 use std::collections::Bound;
 
@@ -409,6 +409,74 @@ declare_clippy_lint! {
     "a match on a struct that binds all fields but still uses the wildcard pattern"
 }
 
+declare_clippy_lint! {
+    /// **What it does:** Lint for redundant pattern matching over `Result` or
+    /// `Option`
+    ///
+    /// **Why is this bad?** It's more concise and clear to just use the proper
+    /// utility function
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// if let Ok(_) = Ok::<i32, i32>(42) {}
+    /// if let Err(_) = Err::<i32, i32>(42) {}
+    /// if let None = None::<()> {}
+    /// if let Some(_) = Some(42) {}
+    /// match Ok::<i32, i32>(42) {
+    ///     Ok(_) => true,
+    ///     Err(_) => false,
+    /// };
+    /// ```
+    ///
+    /// The more idiomatic use would be:
+    ///
+    /// ```rust
+    /// if Ok::<i32, i32>(42).is_ok() {}
+    /// if Err::<i32, i32>(42).is_err() {}
+    /// if None::<()>.is_none() {}
+    /// if Some(42).is_some() {}
+    /// Ok::<i32, i32>(42).is_ok();
+    /// ```
+    pub REDUNDANT_PATTERN_MATCHING,
+    style,
+    "use the proper utility function avoiding an `if let`"
+}
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for `match`  or `if let` expressions producing a
+    /// `bool` that could be written using `matches!`
+    ///
+    /// **Why is this bad?** Readability and needless complexity.
+    ///
+    /// **Known problems:** None
+    ///
+    /// **Example:**
+    /// ```rust
+    /// let x = Some(5);
+    ///
+    /// // Bad
+    /// let a = match x {
+    ///     Some(0) => true,
+    ///     _ => false,
+    /// };
+    ///
+    /// let a = if let Some(0) = x {
+    ///     true
+    /// } else {
+    ///     false
+    /// };
+    ///
+    /// // Good
+    /// let a = matches!(x, Some(0));
+    /// ```
+    pub MATCH_LIKE_MATCHES_MACRO,
+    style,
+    "a match that could be written with the matches! macro"
+}
+
 #[derive(Default)]
 pub struct Matches {
     infallible_destructuring_match_linted: bool,
@@ -427,7 +495,9 @@ impl_lint_pass!(Matches => [
     WILDCARD_IN_OR_PATTERNS,
     MATCH_SINGLE_BINDING,
     INFALLIBLE_DESTRUCTURING_MATCH,
-    REST_PAT_IN_FULLY_BOUND_STRUCTS
+    REST_PAT_IN_FULLY_BOUND_STRUCTS,
+    REDUNDANT_PATTERN_MATCHING,
+    MATCH_LIKE_MATCHES_MACRO
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Matches {
@@ -435,6 +505,10 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
         if in_external_macro(cx.sess(), expr.span) {
             return;
         }
+
+        redundant_pattern_match::check(cx, expr);
+        check_match_like_matches(cx, expr);
+
         if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind {
             check_single_match(cx, ex, arms, expr);
             check_match_bool(cx, ex, arms, expr);
@@ -530,16 +604,22 @@ fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp
             // the lint noisy in unnecessary situations
             return;
         }
-        let els = remove_blocks(&arms[1].body);
-        let els = if is_unit_expr(els) {
+        let els = arms[1].body;
+        let els = if is_unit_expr(remove_blocks(els)) {
             None
-        } else if let ExprKind::Block(_, _) = els.kind {
-            // matches with blocks that contain statements are prettier as `if let + else`
-            Some(els)
+        } else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind {
+            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)
+            }
         } else {
-            // allow match arms with just expressions
-            return;
+            // not a block, don't lint
+            return; 
         };
+
         let ty = cx.tables().expr_ty(ex);
         if ty.kind != ty::Bool || is_allowed(cx, MATCH_BOOL, ex.hir_id) {
             check_single_match_single_pattern(cx, ex, arms, expr, els);
@@ -802,13 +882,8 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
                     // Some simple checks for exhaustive patterns.
                     // There is a room for improvements to detect more cases,
                     // but it can be more expensive to do so.
-                    let is_pattern_exhaustive = |pat: &&Pat<'_>| {
-                        if let PatKind::Wild | PatKind::Binding(.., None) = pat.kind {
-                            true
-                        } else {
-                            false
-                        }
-                    };
+                    let is_pattern_exhaustive =
+                        |pat: &&Pat<'_>| matches!(pat.kind, PatKind::Wild | PatKind::Binding(.., None));
                     if patterns.iter().all(is_pattern_exhaustive) {
                         missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
                     }
@@ -989,6 +1064,79 @@ fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
     }
 }
 
+/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
+fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+    if let ExprKind::Match(ex, arms, ref match_source) = &expr.kind {
+        match match_source {
+            MatchSource::Normal => find_matches_sugg(cx, ex, arms, expr, false),
+            MatchSource::IfLetDesugar { .. } => find_matches_sugg(cx, ex, arms, expr, true),
+            _ => return,
+        }
+    }
+}
+
+/// Lint a `match` or desugared `if let` for replacement by `matches!`
+fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>, desugared: bool) {
+    if_chain! {
+        if arms.len() == 2;
+        if cx.tables().expr_ty(expr).is_bool();
+        if is_wild(&arms[1].pat);
+        if let Some(first) = find_bool_lit(&arms[0].body.kind, desugared);
+        if let Some(second) = find_bool_lit(&arms[1].body.kind, desugared);
+        if first != second;
+        then {
+            let mut applicability = Applicability::MachineApplicable;
+
+            let pat_and_guard = if let Some(Guard::If(g)) = arms[0].guard {
+                format!("{} if {}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability), snippet_with_applicability(cx, g.span, "..", &mut applicability))
+            } else {
+                format!("{}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability))
+            };
+            span_lint_and_sugg(
+                cx,
+                MATCH_LIKE_MATCHES_MACRO,
+                expr.span,
+                &format!("{} expression looks like `matches!` macro", if desugared { "if let .. else" } else { "match" }),
+                "try this",
+                format!(
+                    "{}matches!({}, {})",
+                    if first { "" } else { "!" },
+                    snippet_with_applicability(cx, ex.span, "..", &mut applicability),
+                    pat_and_guard,
+                ),
+                applicability,
+            )
+        }
+    }
+}
+
+/// Extract a `bool` or `{ bool }`
+fn find_bool_lit(ex: &ExprKind<'_>, desugared: bool) -> Option<bool> {
+    match ex {
+        ExprKind::Lit(Spanned {
+            node: LitKind::Bool(b), ..
+        }) => Some(*b),
+        ExprKind::Block(
+            rustc_hir::Block {
+                stmts: &[],
+                expr: Some(exp),
+                ..
+            },
+            _,
+        ) if desugared => {
+            if let ExprKind::Lit(Spanned {
+                node: LitKind::Bool(b), ..
+            }) = exp.kind
+            {
+                Some(b)
+            } else {
+                None
+            }
+        },
+        _ => None,
+    }
+}
+
 fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) {
     if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
         return;
@@ -1179,10 +1327,7 @@ fn is_unit_expr(expr: &Expr<'_>) -> bool {
 
 // Checks if arm has the form `None => None`
 fn is_none_arm(arm: &Arm<'_>) -> bool {
-    match arm.pat.kind {
-        PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => true,
-        _ => false,
-    }
+    matches!(arm.pat.kind, PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE))
 }
 
 // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
@@ -1293,6 +1438,229 @@ where
     None
 }
 
+mod redundant_pattern_match {
+    use super::REDUNDANT_PATTERN_MATCHING;
+    use crate::utils::{in_constant, match_qpath, match_trait_method, paths, snippet, span_lint_and_then};
+    use if_chain::if_chain;
+    use rustc_ast::ast::LitKind;
+    use rustc_errors::Applicability;
+    use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, PatKind, QPath};
+    use rustc_lint::LateContext;
+    use rustc_middle::ty;
+    use rustc_mir::const_eval::is_const_fn;
+    use rustc_span::source_map::Symbol;
+
+    pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if let ExprKind::Match(op, arms, ref match_source) = &expr.kind {
+            match match_source {
+                MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms),
+                MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"),
+                MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"),
+                _ => {},
+            }
+        }
+    }
+
+    fn find_sugg_for_if_let<'tcx>(
+        cx: &LateContext<'tcx>,
+        expr: &'tcx Expr<'_>,
+        op: &Expr<'_>,
+        arms: &[Arm<'_>],
+        keyword: &'static str,
+    ) {
+        fn find_suggestion(cx: &LateContext<'_>, hir_id: HirId, path: &QPath<'_>) -> Option<&'static str> {
+            if match_qpath(path, &paths::RESULT_OK) && can_suggest(cx, hir_id, sym!(result_type), "is_ok") {
+                return Some("is_ok()");
+            }
+            if match_qpath(path, &paths::RESULT_ERR) && can_suggest(cx, hir_id, sym!(result_type), "is_err") {
+                return Some("is_err()");
+            }
+            if match_qpath(path, &paths::OPTION_SOME) && can_suggest(cx, hir_id, sym!(option_type), "is_some") {
+                return Some("is_some()");
+            }
+            if match_qpath(path, &paths::OPTION_NONE) && can_suggest(cx, hir_id, sym!(option_type), "is_none") {
+                return Some("is_none()");
+            }
+            None
+        }
+
+        let hir_id = expr.hir_id;
+        let good_method = match arms[0].pat.kind {
+            PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => {
+                if let PatKind::Wild = patterns[0].kind {
+                    find_suggestion(cx, hir_id, path)
+                } else {
+                    None
+                }
+            },
+            PatKind::Path(ref path) => find_suggestion(cx, hir_id, path),
+            _ => None,
+        };
+        let good_method = match good_method {
+            Some(method) => method,
+            None => return,
+        };
+
+        // check that `while_let_on_iterator` lint does not trigger
+        if_chain! {
+            if keyword == "while";
+            if let ExprKind::MethodCall(method_path, _, _, _) = op.kind;
+            if method_path.ident.name == sym!(next);
+            if match_trait_method(cx, op, &paths::ITERATOR);
+            then {
+                return;
+            }
+        }
+
+        span_lint_and_then(
+            cx,
+            REDUNDANT_PATTERN_MATCHING,
+            arms[0].pat.span,
+            &format!("redundant pattern matching, consider using `{}`", good_method),
+            |diag| {
+                // while let ... = ... { ... }
+                // ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+                let expr_span = expr.span;
+
+                // while let ... = ... { ... }
+                //                 ^^^
+                let op_span = op.span.source_callsite();
+
+                // while let ... = ... { ... }
+                // ^^^^^^^^^^^^^^^^^^^
+                let span = expr_span.until(op_span.shrink_to_hi());
+                diag.span_suggestion(
+                    span,
+                    "try this",
+                    format!("{} {}.{}", keyword, snippet(cx, op_span, "_"), good_method),
+                    Applicability::MachineApplicable, // snippet
+                );
+            },
+        );
+    }
+
+    fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) {
+        if arms.len() == 2 {
+            let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind);
+
+            let hir_id = expr.hir_id;
+            let found_good_method = match node_pair {
+                (
+                    PatKind::TupleStruct(ref path_left, ref patterns_left, _),
+                    PatKind::TupleStruct(ref path_right, ref patterns_right, _),
+                ) if patterns_left.len() == 1 && patterns_right.len() == 1 => {
+                    if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) {
+                        find_good_method_for_match(
+                            arms,
+                            path_left,
+                            path_right,
+                            &paths::RESULT_OK,
+                            &paths::RESULT_ERR,
+                            "is_ok()",
+                            "is_err()",
+                            || can_suggest(cx, hir_id, sym!(result_type), "is_ok"),
+                            || can_suggest(cx, hir_id, sym!(result_type), "is_err"),
+                        )
+                    } else {
+                        None
+                    }
+                },
+                (PatKind::TupleStruct(ref path_left, ref patterns, _), PatKind::Path(ref path_right))
+                | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, ref patterns, _))
+                    if patterns.len() == 1 =>
+                {
+                    if let PatKind::Wild = patterns[0].kind {
+                        find_good_method_for_match(
+                            arms,
+                            path_left,
+                            path_right,
+                            &paths::OPTION_SOME,
+                            &paths::OPTION_NONE,
+                            "is_some()",
+                            "is_none()",
+                            || can_suggest(cx, hir_id, sym!(option_type), "is_some"),
+                            || can_suggest(cx, hir_id, sym!(option_type), "is_none"),
+                        )
+                    } else {
+                        None
+                    }
+                },
+                _ => None,
+            };
+
+            if let Some(good_method) = found_good_method {
+                span_lint_and_then(
+                    cx,
+                    REDUNDANT_PATTERN_MATCHING,
+                    expr.span,
+                    &format!("redundant pattern matching, consider using `{}`", good_method),
+                    |diag| {
+                        let span = expr.span.to(op.span);
+                        diag.span_suggestion(
+                            span,
+                            "try this",
+                            format!("{}.{}", snippet(cx, op.span, "_"), good_method),
+                            Applicability::MaybeIncorrect, // snippet
+                        );
+                    },
+                );
+            }
+        }
+    }
+
+    #[allow(clippy::too_many_arguments)]
+    fn find_good_method_for_match<'a>(
+        arms: &[Arm<'_>],
+        path_left: &QPath<'_>,
+        path_right: &QPath<'_>,
+        expected_left: &[&str],
+        expected_right: &[&str],
+        should_be_left: &'a str,
+        should_be_right: &'a str,
+        can_suggest_left: impl Fn() -> bool,
+        can_suggest_right: impl Fn() -> bool,
+    ) -> Option<&'a str> {
+        let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) {
+            (&(*arms[0].body).kind, &(*arms[1].body).kind)
+        } else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) {
+            (&(*arms[1].body).kind, &(*arms[0].body).kind)
+        } else {
+            return None;
+        };
+
+        match body_node_pair {
+            (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) {
+                (LitKind::Bool(true), LitKind::Bool(false)) if can_suggest_left() => Some(should_be_left),
+                (LitKind::Bool(false), LitKind::Bool(true)) if can_suggest_right() => Some(should_be_right),
+                _ => None,
+            },
+            _ => None,
+        }
+    }
+
+    fn can_suggest(cx: &LateContext<'_>, hir_id: HirId, diag_item: Symbol, name: &str) -> bool {
+        if !in_constant(cx, hir_id) {
+            return true;
+        }
+
+        // Avoid suggesting calls to non-`const fn`s in const contexts, see #5697.
+        cx.tcx
+            .get_diagnostic_item(diag_item)
+            .and_then(|def_id| {
+                cx.tcx.inherent_impls(def_id).iter().find_map(|imp| {
+                    cx.tcx
+                        .associated_items(*imp)
+                        .in_definition_order()
+                        .find_map(|item| match item.kind {
+                            ty::AssocKind::Fn if item.ident.name.as_str() == name => Some(item.def_id),
+                            _ => None,
+                        })
+                })
+            })
+            .map_or(false, |def_id| is_const_fn(cx.tcx, def_id))
+    }
+}
+
 #[test]
 fn test_overlapping() {
     use rustc_span::source_map::DUMMY_SP;
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 216db12f0115f..4c595029ff7bc 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -1844,10 +1844,10 @@ fn lint_expect_fun_call(
                         ty::Ref(ty::ReStatic, ..)
                     )
                 }),
-            hir::ExprKind::Path(ref p) => match cx.qpath_res(p, arg.hir_id) {
-                hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static, _) => true,
-                _ => false,
-            },
+            hir::ExprKind::Path(ref p) => matches!(
+                cx.qpath_res(p, arg.hir_id),
+                hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static, _)
+            ),
             _ => false,
         }
     }
@@ -2028,13 +2028,7 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp
                     .tables()
                     .expr_adjustments(arg)
                     .iter()
-                    .filter(|adj| {
-                        if let ty::adjustment::Adjust::Deref(_) = adj.kind {
-                            true
-                        } else {
-                            false
-                        }
-                    })
+                    .filter(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(_)))
                     .count();
                 let derefs: String = iter::repeat('*').take(deref_count).collect();
                 snip = Some(("try dereferencing it", format!("{}{}", derefs, snippet)));
@@ -2044,7 +2038,7 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp
         }
         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::Unspecified);
+                diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable);
             }
         });
     }
@@ -2460,13 +2454,9 @@ fn derefs_to_slice<'tcx>(
             ty::Slice(_) => true,
             ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
             ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym!(vec_type)),
-            ty::Array(_, size) => {
-                if let Some(size) = size.try_eval_usize(cx.tcx, cx.param_env) {
-                    size < 32
-                } else {
-                    false
-                }
-            },
+            ty::Array(_, size) => size
+                .try_eval_usize(cx.tcx, cx.param_env)
+                .map_or(false, |size| size < 32),
             ty::Ref(_, inner, _) => may_slice(cx, inner),
             _ => false,
         }
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 fdcba11054288..75e123eb5939d 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
@@ -77,13 +77,10 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc
             }
             (true, true)
         },
-        hir::ExprKind::Block(ref block, _) => {
-            if let Some(expr) = &block.expr {
-                check_expression(cx, arg_id, &expr)
-            } else {
-                (false, false)
-            }
-        },
+        hir::ExprKind::Block(ref block, _) => block
+            .expr
+            .as_ref()
+            .map_or((false, false), |expr| check_expression(cx, arg_id, &expr)),
         hir::ExprKind::Match(_, arms, _) => {
             let mut found_mapping = false;
             let mut found_filtering = false;
diff --git a/src/tools/clippy/clippy_lints/src/minmax.rs b/src/tools/clippy/clippy_lints/src/minmax.rs
index 0a2d577396a5f..c8aa98d348927 100644
--- a/src/tools/clippy/clippy_lints/src/minmax.rs
+++ b/src/tools/clippy/clippy_lints/src/minmax.rs
@@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for MinMaxPass {
     }
 }
 
-#[derive(PartialEq, Eq, Debug)]
+#[derive(PartialEq, Eq, Debug, Clone, Copy)]
 enum MinMax {
     Min,
     Max,
@@ -86,16 +86,15 @@ fn fetch_const<'a>(cx: &LateContext<'_>, args: &'a [Expr<'a>], m: MinMax) -> Opt
     if args.len() != 2 {
         return None;
     }
-    if let Some(c) = constant_simple(cx, cx.tables(), &args[0]) {
-        if constant_simple(cx, cx.tables(), &args[1]).is_none() {
-            // otherwise ignore
-            Some((m, c, &args[1]))
-        } else {
-            None
-        }
-    } else if let Some(c) = constant_simple(cx, cx.tables(), &args[1]) {
-        Some((m, c, &args[0]))
-    } else {
-        None
-    }
+    constant_simple(cx, cx.tables(), &args[0]).map_or_else(
+        || constant_simple(cx, cx.tables(), &args[1]).map(|c| (m, c, &args[0])),
+        |c| {
+            if constant_simple(cx, cx.tables(), &args[1]).is_none() {
+                // otherwise ignore
+                Some((m, c, &args[1]))
+            } else {
+                None
+            }
+        },
+    )
 }
diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs
index d7e1a62a19d52..400f4b609af7f 100644
--- a/src/tools/clippy/clippy_lints/src/misc.rs
+++ b/src/tools/clippy/clippy_lints/src/misc.rs
@@ -3,11 +3,11 @@ use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
-    def, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, HirId, Mutability, PatKind, Stmt, StmtKind, Ty,
-    TyKind, UnOp,
+    self as hir, def, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, HirId, Mutability, PatKind, Stmt,
+    StmtKind, TyKind, UnOp,
 };
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
+use rustc_middle::ty::{self, Ty};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::source_map::{ExpnKind, Span};
@@ -371,8 +371,8 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
                 if op.is_comparison() {
                     check_nan(cx, left, expr);
                     check_nan(cx, right, expr);
-                    check_to_owned(cx, left, right);
-                    check_to_owned(cx, right, left);
+                    check_to_owned(cx, left, right, true);
+                    check_to_owned(cx, right, left, false);
                 }
                 if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) {
                     if is_allowed(cx, left) || is_allowed(cx, right) {
@@ -570,11 +570,30 @@ fn is_array(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     matches!(&walk_ptrs_ty(cx.tables().expr_ty(expr)).kind, ty::Array(_, _))
 }
 
-fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>) {
+fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) {
+    #[derive(Default)]
+    struct EqImpl {
+        ty_eq_other: bool,
+        other_eq_ty: bool,
+    }
+
+    impl EqImpl {
+        fn is_implemented(&self) -> bool {
+            self.ty_eq_other || self.other_eq_ty
+        }
+    }
+
+    fn symmetric_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> Option<EqImpl> {
+        cx.tcx.lang_items().eq_trait().map(|def_id| EqImpl {
+            ty_eq_other: implements_trait(cx, ty, def_id, &[other.into()]),
+            other_eq_ty: implements_trait(cx, other, def_id, &[ty.into()]),
+        })
+    }
+
     let (arg_ty, snip) = match expr.kind {
         ExprKind::MethodCall(.., ref args, _) if args.len() == 1 => {
             if match_trait_method(cx, expr, &paths::TO_STRING) || match_trait_method(cx, expr, &paths::TO_OWNED) {
-                (cx.tables().expr_ty_adjusted(&args[0]), snippet(cx, args[0].span, ".."))
+                (cx.tables().expr_ty(&args[0]), snippet(cx, args[0].span, ".."))
             } else {
                 return;
             }
@@ -582,7 +601,7 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>) {
         ExprKind::Call(ref path, ref v) if v.len() == 1 => {
             if let ExprKind::Path(ref path) = path.kind {
                 if match_qpath(path, &["String", "from_str"]) || match_qpath(path, &["String", "from"]) {
-                    (cx.tables().expr_ty_adjusted(&v[0]), snippet(cx, v[0].span, ".."))
+                    (cx.tables().expr_ty(&v[0]), snippet(cx, v[0].span, ".."))
                 } else {
                     return;
                 }
@@ -593,28 +612,19 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>) {
         _ => return,
     };
 
-    let other_ty = cx.tables().expr_ty_adjusted(other);
-    let partial_eq_trait_id = match cx.tcx.lang_items().eq_trait() {
-        Some(id) => id,
-        None => return,
-    };
+    let other_ty = cx.tables().expr_ty(other);
 
-    let deref_arg_impl_partial_eq_other = arg_ty.builtin_deref(true).map_or(false, |tam| {
-        implements_trait(cx, tam.ty, partial_eq_trait_id, &[other_ty.into()])
-    });
-    let arg_impl_partial_eq_deref_other = other_ty.builtin_deref(true).map_or(false, |tam| {
-        implements_trait(cx, arg_ty, partial_eq_trait_id, &[tam.ty.into()])
-    });
-    let arg_impl_partial_eq_other = implements_trait(cx, arg_ty, partial_eq_trait_id, &[other_ty.into()]);
+    let without_deref = symmetric_partial_eq(cx, arg_ty, other_ty).unwrap_or_default();
+    let with_deref = arg_ty
+        .builtin_deref(true)
+        .and_then(|tam| symmetric_partial_eq(cx, tam.ty, other_ty))
+        .unwrap_or_default();
 
-    if !deref_arg_impl_partial_eq_other && !arg_impl_partial_eq_deref_other && !arg_impl_partial_eq_other {
+    if !with_deref.is_implemented() && !without_deref.is_implemented() {
         return;
     }
 
-    let other_gets_derefed = match other.kind {
-        ExprKind::Unary(UnOp::UnDeref, _) => true,
-        _ => false,
-    };
+    let other_gets_derefed = matches!(other.kind, ExprKind::Unary(UnOp::UnDeref, _));
 
     let lint_span = if other_gets_derefed {
         expr.span.to(other.span)
@@ -634,18 +644,34 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>) {
                 return;
             }
 
-            let try_hint = if deref_arg_impl_partial_eq_other {
-                // suggest deref on the left
-                format!("*{}", snip)
+            let expr_snip;
+            let eq_impl;
+            if with_deref.is_implemented() {
+                expr_snip = format!("*{}", snip);
+                eq_impl = with_deref;
             } else {
-                // suggest dropping the to_owned on the left
-                snip.to_string()
+                expr_snip = snip.to_string();
+                eq_impl = without_deref;
             };
 
+            let span;
+            let hint;
+            if (eq_impl.ty_eq_other && left) || (eq_impl.other_eq_ty && !left) {
+                span = expr.span;
+                hint = expr_snip;
+            } else {
+                span = expr.span.to(other.span);
+                if eq_impl.ty_eq_other {
+                    hint = format!("{} == {}", expr_snip, snippet(cx, other.span, ".."));
+                } else {
+                    hint = format!("{} == {}", snippet(cx, other.span, ".."), expr_snip);
+                }
+            }
+
             diag.span_suggestion(
-                lint_span,
+                span,
                 "try",
-                try_hint,
+                hint,
                 Applicability::MachineApplicable, // snippet
             );
         },
@@ -656,16 +682,10 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>) {
 /// `unused_variables`'s idea
 /// of what it means for an expression to be "used".
 fn is_used(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    if let Some(parent) = get_parent_expr(cx, expr) {
-        match parent.kind {
-            ExprKind::Assign(_, ref rhs, _) | ExprKind::AssignOp(_, _, ref rhs) => {
-                SpanlessEq::new(cx).eq_expr(rhs, expr)
-            },
-            _ => is_used(cx, parent),
-        }
-    } else {
-        true
-    }
+    get_parent_expr(cx, expr).map_or(true, |parent| match parent.kind {
+        ExprKind::Assign(_, ref rhs, _) | ExprKind::AssignOp(_, _, ref rhs) => SpanlessEq::new(cx).eq_expr(rhs, expr),
+        _ => is_used(cx, parent),
+    })
 }
 
 /// Tests whether an expression is in a macro expansion (e.g., something
@@ -674,12 +694,7 @@ fn in_attributes_expansion(expr: &Expr<'_>) -> bool {
     use rustc_span::hygiene::MacroKind;
     if expr.span.from_expansion() {
         let data = expr.span.ctxt().outer_expn_data();
-
-        if let ExpnKind::Macro(MacroKind::Attr, _) = data.kind {
-            true
-        } else {
-            false
-        }
+        matches!(data.kind, ExpnKind::Macro(MacroKind::Attr, _))
     } else {
         false
     }
@@ -694,7 +709,7 @@ fn non_macro_local(cx: &LateContext<'_>, res: def::Res) -> bool {
     }
 }
 
-fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &Ty<'_>) {
+fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) {
     if_chain! {
         if let TyKind::Ptr(ref mut_ty) = ty.kind;
         if let ExprKind::Lit(ref lit) = e.kind;
diff --git a/src/tools/clippy/clippy_lints/src/misc_early.rs b/src/tools/clippy/clippy_lints/src/misc_early.rs
index ad39e59d0678a..b84a1a3fe2494 100644
--- a/src/tools/clippy/clippy_lints/src/misc_early.rs
+++ b/src/tools/clippy/clippy_lints/src/misc_early.rs
@@ -641,28 +641,22 @@ fn check_unneeded_wildcard_pattern(cx: &EarlyContext<'_>, pat: &Pat) {
             );
         }
 
-        #[allow(clippy::trivially_copy_pass_by_ref)]
-        fn is_wild<P: std::ops::Deref<Target = Pat>>(pat: &&P) -> bool {
-            if let PatKind::Wild = pat.kind {
-                true
-            } else {
-                false
-            }
-        }
-
         if let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) {
             if let Some((left_index, left_pat)) = patterns[..rest_index]
                 .iter()
                 .rev()
-                .take_while(is_wild)
+                .take_while(|pat| matches!(pat.kind, PatKind::Wild))
                 .enumerate()
                 .last()
             {
                 span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0);
             }
 
-            if let Some((right_index, right_pat)) =
-                patterns[rest_index + 1..].iter().take_while(is_wild).enumerate().last()
+            if let Some((right_index, right_pat)) = patterns[rest_index + 1..]
+                .iter()
+                .take_while(|pat| matches!(pat.kind, PatKind::Wild))
+                .enumerate()
+                .last()
             {
                 span_lint(
                     cx,
diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs
index bf80b62afe6e0..9c96267353701 100644
--- a/src/tools/clippy/clippy_lints/src/missing_inline.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs
@@ -71,10 +71,11 @@ fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[ast::Attribute], sp
 fn is_executable(cx: &LateContext<'_>) -> bool {
     use rustc_session::config::CrateType;
 
-    cx.tcx.sess.crate_types().iter().any(|t: &CrateType| match t {
-        CrateType::Executable => true,
-        _ => false,
-    })
+    cx.tcx
+        .sess
+        .crate_types()
+        .iter()
+        .any(|t: &CrateType| matches!(t, CrateType::Executable))
 }
 
 declare_lint_pass!(MissingInline => [MISSING_INLINE_IN_PUBLIC_ITEMS]);
diff --git a/src/tools/clippy/clippy_lints/src/new_without_default.rs b/src/tools/clippy/clippy_lints/src/new_without_default.rs
index 2597f5f6f17e2..621ebdef2f0b1 100644
--- a/src/tools/clippy/clippy_lints/src/new_without_default.rs
+++ b/src/tools/clippy/clippy_lints/src/new_without_default.rs
@@ -80,10 +80,12 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
                             // can't be implemented for unsafe new
                             return;
                         }
-                        if impl_item.generics.params.iter().any(|gen| match gen.kind {
-                            hir::GenericParamKind::Type { .. } => true,
-                            _ => false,
-                        }) {
+                        if impl_item
+                            .generics
+                            .params
+                            .iter()
+                            .any(|gen| matches!(gen.kind, hir::GenericParamKind::Type { .. }))
+                        {
                             // when the result of `new()` depends on a type parameter we should not require
                             // an
                             // impl of `Default`
diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
index c11a2ff9ee07e..a3521c31a6be6 100644
--- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs
+++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
@@ -238,10 +238,10 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
 
             let ty = if needs_check_adjustment {
                 let adjustments = cx.tables().expr_adjustments(dereferenced_expr);
-                if let Some(i) = adjustments.iter().position(|adj| match adj.kind {
-                    Adjust::Borrow(_) | Adjust::Deref(_) => true,
-                    _ => false,
-                }) {
+                if let Some(i) = adjustments
+                    .iter()
+                    .position(|adj| matches!(adj.kind, Adjust::Borrow(_) | Adjust::Deref(_)))
+                {
                     if i == 0 {
                         cx.tables().expr_ty(dereferenced_expr)
                     } else {
diff --git a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
new file mode 100644
index 0000000000000..8dbe58763bfb2
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
@@ -0,0 +1,267 @@
+use crate::utils;
+use crate::utils::sugg::Sugg;
+use crate::utils::{match_type, paths, span_lint_and_sugg};
+use if_chain::if_chain;
+
+use rustc_errors::Applicability;
+use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
+use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::hir::map::Map;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// **What it does:**
+    /// Lints usage of  `if let Some(v) = ... { y } else { x }` which is more
+    /// idiomatically done with `Option::map_or` (if the else bit is a simple
+    /// expression) or `Option::map_or_else` (if the else bit is a longer
+    /// block).
+    ///
+    /// **Why is this bad?**
+    /// Using the dedicated functions of the Option type is clearer and
+    /// more concise than an if let expression.
+    ///
+    /// **Known problems:**
+    /// This lint uses whether the block is just an expression or if it has
+    /// more statements to decide whether to use `Option::map_or` or
+    /// `Option::map_or_else`. If you have a single expression which calls
+    /// an expensive function, then it would be more efficient to use
+    /// `Option::map_or_else`, but this lint would suggest `Option::map_or`.
+    ///
+    /// Also, this lint uses a deliberately conservative metric for checking
+    /// if the inside of either body contains breaks or continues which will
+    /// cause it to not suggest a fix if either block contains a loop with
+    /// continues or breaks contained within the loop.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// # let optional: Option<u32> = Some(0);
+    /// # fn do_complicated_function() -> u32 { 5 };
+    /// let _ = if let Some(foo) = optional {
+    ///     foo
+    /// } else {
+    ///     5
+    /// };
+    /// let _ = if let Some(foo) = optional {
+    ///     foo
+    /// } else {
+    ///     let y = do_complicated_function();
+    ///     y*y
+    /// };
+    /// ```
+    ///
+    /// should be
+    ///
+    /// ```rust
+    /// # let optional: Option<u32> = Some(0);
+    /// # fn do_complicated_function() -> u32 { 5 };
+    /// let _ = optional.map_or(5, |foo| foo);
+    /// let _ = optional.map_or_else(||{
+    ///     let y = do_complicated_function();
+    ///     y*y
+    /// }, |foo| foo);
+    /// ```
+    pub OPTION_IF_LET_ELSE,
+    pedantic,
+    "reimplementation of Option::map_or"
+}
+
+declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]);
+
+/// Returns true iff the given expression is the result of calling `Result::ok`
+fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
+    if let ExprKind::MethodCall(ref path, _, &[ref receiver], _) = &expr.kind {
+        path.ident.name.to_ident_string() == "ok" && match_type(cx, &cx.tables().expr_ty(&receiver), &paths::RESULT)
+    } else {
+        false
+    }
+}
+
+/// A struct containing information about occurences of the
+/// `if let Some(..) = .. else` construct that this lint detects.
+struct OptionIfLetElseOccurence {
+    option: String,
+    method_sugg: String,
+    some_expr: String,
+    none_expr: String,
+    wrap_braces: bool,
+}
+
+struct ReturnBreakContinueMacroVisitor {
+    seen_return_break_continue: bool,
+}
+impl ReturnBreakContinueMacroVisitor {
+    fn new() -> ReturnBreakContinueMacroVisitor {
+        ReturnBreakContinueMacroVisitor {
+            seen_return_break_continue: false,
+        }
+    }
+}
+impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor {
+    type Map = Map<'tcx>;
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
+
+    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
+        if self.seen_return_break_continue {
+            // No need to look farther if we've already seen one of them
+            return;
+        }
+        match &ex.kind {
+            ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => {
+                self.seen_return_break_continue = true;
+            },
+            // Something special could be done here to handle while or for loop
+            // desugaring, as this will detect a break if there's a while loop
+            // or a for loop inside the expression.
+            _ => {
+                if utils::in_macro(ex.span) {
+                    self.seen_return_break_continue = true;
+                } else {
+                    rustc_hir::intravisit::walk_expr(self, ex);
+                }
+            },
+        }
+    }
+}
+
+fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
+    let mut recursive_visitor = ReturnBreakContinueMacroVisitor::new();
+    recursive_visitor.visit_expr(expression);
+    recursive_visitor.seen_return_break_continue
+}
+
+/// Extracts the body of a given arm. If the arm contains only an expression,
+/// then it returns the expression. Otherwise, it returns the entire block
+fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> {
+    if let ExprKind::Block(
+        Block {
+            stmts: statements,
+            expr: Some(expr),
+            ..
+        },
+        _,
+    ) = &arm.body.kind
+    {
+        if let [] = statements {
+            Some(&expr)
+        } else {
+            Some(&arm.body)
+        }
+    } else {
+        None
+    }
+}
+
+/// If this is the else body of an if/else expression, then we need to wrap
+/// it in curcly braces. Otherwise, we don't.
+fn should_wrap_in_braces(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| {
+        if let Some(Expr {
+            kind:
+                ExprKind::Match(
+                    _,
+                    arms,
+                    MatchSource::IfDesugar {
+                        contains_else_clause: true,
+                    }
+                    | MatchSource::IfLetDesugar {
+                        contains_else_clause: true,
+                    },
+                ),
+            ..
+        }) = parent.expr
+        {
+            expr.hir_id == arms[1].body.hir_id
+        } else {
+            false
+        }
+    })
+}
+
+fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String {
+    format!(
+        "{}{}",
+        Sugg::hir(cx, cond_expr, "..").maybe_par(),
+        if as_mut {
+            ".as_mut()"
+        } else if as_ref {
+            ".as_ref()"
+        } else {
+            ""
+        }
+    )
+}
+
+/// If this expression is the option if let/else construct we're detecting, then
+/// this function returns an `OptionIfLetElseOccurence` struct with details if
+/// this construct is found, or None if this construct is not found.
+fn detect_option_if_let_else(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<OptionIfLetElseOccurence> {
+    if_chain! {
+        if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly
+        if let ExprKind::Match(cond_expr, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind;
+        if arms.len() == 2;
+        if !is_result_ok(cx, cond_expr); // Don't lint on Result::ok because a different lint does it already
+        if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind;
+        if utils::match_qpath(struct_qpath, &paths::OPTION_SOME);
+        if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind;
+        if !contains_return_break_continue_macro(arms[0].body);
+        if !contains_return_break_continue_macro(arms[1].body);
+        then {
+            let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" };
+            let some_body = extract_body_from_arm(&arms[0])?;
+            let none_body = extract_body_from_arm(&arms[1])?;
+            let method_sugg = match &none_body.kind {
+                ExprKind::Block(..) => "map_or_else",
+                _ => "map_or",
+            };
+            let capture_name = id.name.to_ident_string();
+            let wrap_braces = should_wrap_in_braces(cx, expr);
+            let (as_ref, as_mut) = match &cond_expr.kind {
+                ExprKind::AddrOf(_, Mutability::Not, _) => (true, false),
+                ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true),
+                _ => (bind_annotation == &BindingAnnotation::Ref, bind_annotation == &BindingAnnotation::RefMut),
+            };
+            let cond_expr = match &cond_expr.kind {
+                // Pointer dereferencing happens automatically, so we can omit it in the suggestion
+                ExprKind::Unary(UnOp::UnDeref, expr) | ExprKind::AddrOf(_, _, expr) => expr,
+                _ => cond_expr,
+            };
+            Some(OptionIfLetElseOccurence {
+                option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut),
+                method_sugg: method_sugg.to_string(),
+                some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir(cx, some_body, "..")),
+                none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")),
+                wrap_braces,
+            })
+        } else {
+            None
+        }
+    }
+}
+
+impl<'a> LateLintPass<'a> for OptionIfLetElse {
+    fn check_expr(&mut self, cx: &LateContext<'a>, expr: &Expr<'_>) {
+        if let Some(detection) = detect_option_if_let_else(cx, expr) {
+            span_lint_and_sugg(
+                cx,
+                OPTION_IF_LET_ELSE,
+                expr.span,
+                format!("use Option::{} instead of an if let/else", detection.method_sugg).as_str(),
+                "try",
+                format!(
+                    "{}{}.{}({}, {}){}",
+                    if detection.wrap_braces { "{ " } else { "" },
+                    detection.option,
+                    detection.method_sugg,
+                    detection.none_expr,
+                    detection.some_expr,
+                    if detection.wrap_braces { " }" } else { "" },
+                ),
+                Applicability::MaybeIncorrect,
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
new file mode 100644
index 0000000000000..a49dc87c0b47f
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
@@ -0,0 +1,311 @@
+use crate::utils::{last_path_segment, span_lint_and_help};
+use rustc_hir::{
+    intravisit, Body, Expr, ExprKind, FieldPat, FnDecl, HirId, LocalSource, MatchSource, Mutability, Pat, PatKind,
+    QPath, Stmt, StmtKind,
+};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::subst::SubstsRef;
+use rustc_middle::ty::{AdtDef, FieldDef, Ty, TyKind, VariantDef};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Span;
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for patterns that aren't exact representations of the types
+    /// they are applied to.
+    ///
+    /// To satisfy this lint, you will have to adjust either the expression that is matched
+    /// against or the pattern itself, as well as the bindings that are introduced by the
+    /// adjusted patterns. For matching you will have to either dereference the expression
+    /// with the `*` operator, or amend the patterns to explicitly match against `&<pattern>`
+    /// or `&mut <pattern>` depending on the reference mutability. For the bindings you need
+    /// to use the inverse. You can leave them as plain bindings if you wish for the value
+    /// to be copied, but you must use `ref mut <variable>` or `ref <variable>` to construct
+    /// a reference into the matched structure.
+    ///
+    /// If you are looking for a way to learn about ownership semantics in more detail, it
+    /// is recommended to look at IDE options available to you to highlight types, lifetimes
+    /// and reference semantics in your code. The available tooling would expose these things
+    /// in a general way even outside of the various pattern matching mechanics. Of course
+    /// this lint can still be used to highlight areas of interest and ensure a good understanding
+    /// of ownership semantics.
+    ///
+    /// **Why is this bad?** It isn't bad in general. But in some contexts it can be desirable
+    /// because it increases ownership hints in the code, and will guard against some changes
+    /// in ownership.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// This example shows the basic adjustments necessary to satisfy the lint. Note how
+    /// the matched expression is explicitly dereferenced with `*` and the `inner` variable
+    /// is bound to a shared borrow via `ref inner`.
+    ///
+    /// ```rust,ignore
+    /// // Bad
+    /// let value = &Some(Box::new(23));
+    /// match value {
+    ///     Some(inner) => println!("{}", inner),
+    ///     None => println!("none"),
+    /// }
+    ///
+    /// // Good
+    /// let value = &Some(Box::new(23));
+    /// match *value {
+    ///     Some(ref inner) => println!("{}", inner),
+    ///     None => println!("none"),
+    /// }
+    /// ```
+    ///
+    /// The following example demonstrates one of the advantages of the more verbose style.
+    /// Note how the second version uses `ref mut a` to explicitly declare `a` a shared mutable
+    /// borrow, while `b` is simply taken by value. This ensures that the loop body cannot
+    /// accidentally modify the wrong part of the structure.
+    ///
+    /// ```rust,ignore
+    /// // Bad
+    /// let mut values = vec![(2, 3), (3, 4)];
+    /// for (a, b) in &mut values {
+    ///     *a += *b;
+    /// }
+    ///
+    /// // Good
+    /// let mut values = vec![(2, 3), (3, 4)];
+    /// for &mut (ref mut a, b) in &mut values {
+    ///     *a += b;
+    /// }
+    /// ```
+    pub PATTERN_TYPE_MISMATCH,
+    restriction,
+    "type of pattern does not match the expression type"
+}
+
+declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]);
+
+impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch {
+    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
+        if let StmtKind::Local(ref local) = stmt.kind {
+            if let Some(init) = &local.init {
+                if let Some(init_ty) = cx.tables().node_type_opt(init.hir_id) {
+                    let pat = &local.pat;
+                    if in_external_macro(cx.sess(), pat.span) {
+                        return;
+                    }
+                    let deref_possible = match local.source {
+                        LocalSource::Normal => DerefPossible::Possible,
+                        _ => DerefPossible::Impossible,
+                    };
+                    apply_lint(cx, pat, init_ty, deref_possible);
+                }
+            }
+        }
+    }
+
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if let ExprKind::Match(ref expr, arms, source) = expr.kind {
+            match source {
+                MatchSource::Normal | MatchSource::IfLetDesugar { .. } | MatchSource::WhileLetDesugar => {
+                    if let Some(expr_ty) = cx.tables().node_type_opt(expr.hir_id) {
+                        'pattern_checks: for arm in arms {
+                            let pat = &arm.pat;
+                            if in_external_macro(cx.sess(), pat.span) {
+                                continue 'pattern_checks;
+                            }
+                            if apply_lint(cx, pat, expr_ty, DerefPossible::Possible) {
+                                break 'pattern_checks;
+                            }
+                        }
+                    }
+                },
+                _ => (),
+            }
+        }
+    }
+
+    fn check_fn(
+        &mut self,
+        cx: &LateContext<'tcx>,
+        _: intravisit::FnKind<'tcx>,
+        _: &'tcx FnDecl<'_>,
+        body: &'tcx Body<'_>,
+        _: Span,
+        hir_id: HirId,
+    ) {
+        if let Some(fn_sig) = cx.tables().liberated_fn_sigs().get(hir_id) {
+            for (param, ty) in body.params.iter().zip(fn_sig.inputs().iter()) {
+                apply_lint(cx, &param.pat, ty, DerefPossible::Impossible);
+            }
+        }
+    }
+}
+
+#[derive(Debug, Clone, Copy)]
+enum DerefPossible {
+    Possible,
+    Impossible,
+}
+
+fn apply_lint<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, expr_ty: Ty<'tcx>, deref_possible: DerefPossible) -> bool {
+    let maybe_mismatch = find_first_mismatch(cx, pat, expr_ty, Level::Top);
+    if let Some((span, mutability, level)) = maybe_mismatch {
+        span_lint_and_help(
+            cx,
+            PATTERN_TYPE_MISMATCH,
+            span,
+            "type of pattern does not match the expression type",
+            None,
+            &format!(
+                "{}explicitly match against a `{}` pattern and adjust the enclosed variable bindings",
+                match (deref_possible, level) {
+                    (DerefPossible::Possible, Level::Top) => "use `*` to dereference the match expression or ",
+                    _ => "",
+                },
+                match mutability {
+                    Mutability::Mut => "&mut _",
+                    Mutability::Not => "&_",
+                },
+            ),
+        );
+        true
+    } else {
+        false
+    }
+}
+
+#[derive(Debug, Copy, Clone)]
+enum Level {
+    Top,
+    Lower,
+}
+
+#[allow(rustc::usage_of_ty_tykind)]
+fn find_first_mismatch<'tcx>(
+    cx: &LateContext<'tcx>,
+    pat: &Pat<'_>,
+    ty: Ty<'tcx>,
+    level: Level,
+) -> Option<(Span, Mutability, Level)> {
+    if let PatKind::Ref(ref sub_pat, _) = pat.kind {
+        if let TyKind::Ref(_, sub_ty, _) = ty.kind {
+            return find_first_mismatch(cx, sub_pat, sub_ty, Level::Lower);
+        }
+    }
+
+    if let TyKind::Ref(_, _, mutability) = ty.kind {
+        if is_non_ref_pattern(&pat.kind) {
+            return Some((pat.span, mutability, level));
+        }
+    }
+
+    if let PatKind::Struct(ref qpath, ref field_pats, _) = pat.kind {
+        if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind {
+            if let Some(variant) = get_variant(adt_def, qpath) {
+                let field_defs = &variant.fields;
+                return find_first_mismatch_in_struct(cx, field_pats, field_defs, substs_ref);
+            }
+        }
+    }
+
+    if let PatKind::TupleStruct(ref qpath, ref pats, _) = pat.kind {
+        if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind {
+            if let Some(variant) = get_variant(adt_def, qpath) {
+                let field_defs = &variant.fields;
+                let ty_iter = field_defs.iter().map(|field_def| field_def.ty(cx.tcx, substs_ref));
+                return find_first_mismatch_in_tuple(cx, pats, ty_iter);
+            }
+        }
+    }
+
+    if let PatKind::Tuple(ref pats, _) = pat.kind {
+        if let TyKind::Tuple(..) = ty.kind {
+            return find_first_mismatch_in_tuple(cx, pats, ty.tuple_fields());
+        }
+    }
+
+    if let PatKind::Or(sub_pats) = pat.kind {
+        for pat in sub_pats {
+            let maybe_mismatch = find_first_mismatch(cx, pat, ty, level);
+            if let Some(mismatch) = maybe_mismatch {
+                return Some(mismatch);
+            }
+        }
+    }
+
+    None
+}
+
+fn get_variant<'a>(adt_def: &'a AdtDef, qpath: &QPath<'_>) -> Option<&'a VariantDef> {
+    if adt_def.is_struct() {
+        if let Some(variant) = adt_def.variants.iter().next() {
+            return Some(variant);
+        }
+    }
+
+    if adt_def.is_enum() {
+        let pat_ident = last_path_segment(qpath).ident;
+        for variant in &adt_def.variants {
+            if variant.ident == pat_ident {
+                return Some(variant);
+            }
+        }
+    }
+
+    None
+}
+
+fn find_first_mismatch_in_tuple<'tcx, I>(
+    cx: &LateContext<'tcx>,
+    pats: &[&Pat<'_>],
+    ty_iter_src: I,
+) -> Option<(Span, Mutability, Level)>
+where
+    I: IntoIterator<Item = Ty<'tcx>>,
+{
+    let mut field_tys = ty_iter_src.into_iter();
+    'fields: for pat in pats {
+        let field_ty = if let Some(ty) = field_tys.next() {
+            ty
+        } else {
+            break 'fields;
+        };
+
+        let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower);
+        if let Some(mismatch) = maybe_mismatch {
+            return Some(mismatch);
+        }
+    }
+
+    None
+}
+
+fn find_first_mismatch_in_struct<'tcx>(
+    cx: &LateContext<'tcx>,
+    field_pats: &[FieldPat<'_>],
+    field_defs: &[FieldDef],
+    substs_ref: SubstsRef<'tcx>,
+) -> Option<(Span, Mutability, Level)> {
+    for field_pat in field_pats {
+        'definitions: for field_def in field_defs {
+            if field_pat.ident == field_def.ident {
+                let field_ty = field_def.ty(cx.tcx, substs_ref);
+                let pat = &field_pat.pat;
+                let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower);
+                if let Some(mismatch) = maybe_mismatch {
+                    return Some(mismatch);
+                }
+                break 'definitions;
+            }
+        }
+    }
+
+    None
+}
+
+fn is_non_ref_pattern(pat_kind: &PatKind<'_>) -> bool {
+    match pat_kind {
+        PatKind::Struct(..) | PatKind::Tuple(..) | PatKind::TupleStruct(..) | PatKind::Path(..) => true,
+        PatKind::Or(sub_pats) => sub_pats.iter().any(|pat| is_non_ref_pattern(&pat.kind)),
+        _ => false,
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/precedence.rs b/src/tools/clippy/clippy_lints/src/precedence.rs
index 23793678fa0e2..4797771e7bdbb 100644
--- a/src/tools/clippy/clippy_lints/src/precedence.rs
+++ b/src/tools/clippy/clippy_lints/src/precedence.rs
@@ -148,17 +148,11 @@ fn is_arith_expr(expr: &Expr) -> bool {
 #[must_use]
 fn is_bit_op(op: BinOpKind) -> bool {
     use rustc_ast::ast::BinOpKind::{BitAnd, BitOr, BitXor, Shl, Shr};
-    match op {
-        BitXor | BitAnd | BitOr | Shl | Shr => true,
-        _ => false,
-    }
+    matches!(op, BitXor | BitAnd | BitOr | Shl | Shr)
 }
 
 #[must_use]
 fn is_arith_op(op: BinOpKind) -> bool {
     use rustc_ast::ast::BinOpKind::{Add, Div, Mul, Rem, Sub};
-    match op {
-        Add | Sub | Mul | Div | Rem => true,
-        _ => false,
-    }
+    matches!(op, Add | Sub | Mul | Div | Rem)
 }
diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs
index c164ec9aaf173..dd608de5723e2 100644
--- a/src/tools/clippy/clippy_lints/src/ranges.rs
+++ b/src/tools/clippy/clippy_lints/src/ranges.rs
@@ -52,6 +52,11 @@ declare_clippy_lint! {
     /// exclusive ranges, because they essentially add an extra branch that
     /// LLVM may fail to hoist out of the loop.
     ///
+    /// This will cause a warning that cannot be fixed if the consumer of the
+    /// range only accepts a specific range type, instead of the generic
+    /// `RangeBounds` trait
+    /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).
+    ///
     /// **Example:**
     /// ```rust,ignore
     /// for x..(y+1) { .. }
@@ -72,7 +77,10 @@ declare_clippy_lint! {
     /// **Why is this bad?** The code is more readable with an exclusive range
     /// like `x..y`.
     ///
-    /// **Known problems:** None.
+    /// **Known problems:** This will cause a warning that cannot be fixed if
+    /// the consumer of the range only accepts a specific range type, instead of
+    /// the generic `RangeBounds` trait
+    /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).
     ///
     /// **Example:**
     /// ```rust,ignore
@@ -83,7 +91,7 @@ declare_clippy_lint! {
     /// for x..y { .. }
     /// ```
     pub RANGE_MINUS_ONE,
-    complexity,
+    pedantic,
     "`x..=(y-1)` reads better as `x..y`"
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/redundant_pattern_matching.rs b/src/tools/clippy/clippy_lints/src/redundant_pattern_matching.rs
deleted file mode 100644
index d8d16efb978a5..0000000000000
--- a/src/tools/clippy/clippy_lints/src/redundant_pattern_matching.rs
+++ /dev/null
@@ -1,260 +0,0 @@
-use crate::utils::{in_constant, match_qpath, match_trait_method, paths, snippet, span_lint_and_then};
-use if_chain::if_chain;
-use rustc_ast::ast::LitKind;
-use rustc_errors::Applicability;
-use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, PatKind, QPath};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
-use rustc_mir::const_eval::is_const_fn;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Symbol;
-
-declare_clippy_lint! {
-    /// **What it does:** Lint for redundant pattern matching over `Result` or
-    /// `Option`
-    ///
-    /// **Why is this bad?** It's more concise and clear to just use the proper
-    /// utility function
-    ///
-    /// **Known problems:** None.
-    ///
-    /// **Example:**
-    ///
-    /// ```rust
-    /// if let Ok(_) = Ok::<i32, i32>(42) {}
-    /// if let Err(_) = Err::<i32, i32>(42) {}
-    /// if let None = None::<()> {}
-    /// if let Some(_) = Some(42) {}
-    /// match Ok::<i32, i32>(42) {
-    ///     Ok(_) => true,
-    ///     Err(_) => false,
-    /// };
-    /// ```
-    ///
-    /// The more idiomatic use would be:
-    ///
-    /// ```rust
-    /// if Ok::<i32, i32>(42).is_ok() {}
-    /// if Err::<i32, i32>(42).is_err() {}
-    /// if None::<()>.is_none() {}
-    /// if Some(42).is_some() {}
-    /// Ok::<i32, i32>(42).is_ok();
-    /// ```
-    pub REDUNDANT_PATTERN_MATCHING,
-    style,
-    "use the proper utility function avoiding an `if let`"
-}
-
-declare_lint_pass!(RedundantPatternMatching => [REDUNDANT_PATTERN_MATCHING]);
-
-impl<'tcx> LateLintPass<'tcx> for RedundantPatternMatching {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if let ExprKind::Match(op, arms, ref match_source) = &expr.kind {
-            match match_source {
-                MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms),
-                MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"),
-                MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"),
-                _ => return,
-            }
-        }
-    }
-}
-
-fn find_sugg_for_if_let<'tcx>(
-    cx: &LateContext<'tcx>,
-    expr: &'tcx Expr<'_>,
-    op: &Expr<'_>,
-    arms: &[Arm<'_>],
-    keyword: &'static str,
-) {
-    fn find_suggestion(cx: &LateContext<'_>, hir_id: HirId, path: &QPath<'_>) -> Option<&'static str> {
-        if match_qpath(path, &paths::RESULT_OK) && can_suggest(cx, hir_id, sym!(result_type), "is_ok") {
-            return Some("is_ok()");
-        }
-        if match_qpath(path, &paths::RESULT_ERR) && can_suggest(cx, hir_id, sym!(result_type), "is_err") {
-            return Some("is_err()");
-        }
-        if match_qpath(path, &paths::OPTION_SOME) && can_suggest(cx, hir_id, sym!(option_type), "is_some") {
-            return Some("is_some()");
-        }
-        if match_qpath(path, &paths::OPTION_NONE) && can_suggest(cx, hir_id, sym!(option_type), "is_none") {
-            return Some("is_none()");
-        }
-        None
-    }
-
-    let hir_id = expr.hir_id;
-    let good_method = match arms[0].pat.kind {
-        PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => {
-            if let PatKind::Wild = patterns[0].kind {
-                find_suggestion(cx, hir_id, path)
-            } else {
-                None
-            }
-        },
-        PatKind::Path(ref path) => find_suggestion(cx, hir_id, path),
-        _ => None,
-    };
-    let good_method = match good_method {
-        Some(method) => method,
-        None => return,
-    };
-
-    // check that `while_let_on_iterator` lint does not trigger
-    if_chain! {
-        if keyword == "while";
-        if let ExprKind::MethodCall(method_path, _, _, _) = op.kind;
-        if method_path.ident.name == sym!(next);
-        if match_trait_method(cx, op, &paths::ITERATOR);
-        then {
-            return;
-        }
-    }
-
-    span_lint_and_then(
-        cx,
-        REDUNDANT_PATTERN_MATCHING,
-        arms[0].pat.span,
-        &format!("redundant pattern matching, consider using `{}`", good_method),
-        |diag| {
-            // while let ... = ... { ... }
-            // ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-            let expr_span = expr.span;
-
-            // while let ... = ... { ... }
-            //                 ^^^
-            let op_span = op.span.source_callsite();
-
-            // while let ... = ... { ... }
-            // ^^^^^^^^^^^^^^^^^^^
-            let span = expr_span.until(op_span.shrink_to_hi());
-            diag.span_suggestion(
-                span,
-                "try this",
-                format!("{} {}.{}", keyword, snippet(cx, op_span, "_"), good_method),
-                Applicability::MachineApplicable, // snippet
-            );
-        },
-    );
-}
-
-fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) {
-    if arms.len() == 2 {
-        let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind);
-
-        let hir_id = expr.hir_id;
-        let found_good_method = match node_pair {
-            (
-                PatKind::TupleStruct(ref path_left, ref patterns_left, _),
-                PatKind::TupleStruct(ref path_right, ref patterns_right, _),
-            ) if patterns_left.len() == 1 && patterns_right.len() == 1 => {
-                if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) {
-                    find_good_method_for_match(
-                        arms,
-                        path_left,
-                        path_right,
-                        &paths::RESULT_OK,
-                        &paths::RESULT_ERR,
-                        "is_ok()",
-                        "is_err()",
-                        || can_suggest(cx, hir_id, sym!(result_type), "is_ok"),
-                        || can_suggest(cx, hir_id, sym!(result_type), "is_err"),
-                    )
-                } else {
-                    None
-                }
-            },
-            (PatKind::TupleStruct(ref path_left, ref patterns, _), PatKind::Path(ref path_right))
-            | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, ref patterns, _))
-                if patterns.len() == 1 =>
-            {
-                if let PatKind::Wild = patterns[0].kind {
-                    find_good_method_for_match(
-                        arms,
-                        path_left,
-                        path_right,
-                        &paths::OPTION_SOME,
-                        &paths::OPTION_NONE,
-                        "is_some()",
-                        "is_none()",
-                        || can_suggest(cx, hir_id, sym!(option_type), "is_some"),
-                        || can_suggest(cx, hir_id, sym!(option_type), "is_none"),
-                    )
-                } else {
-                    None
-                }
-            },
-            _ => None,
-        };
-
-        if let Some(good_method) = found_good_method {
-            span_lint_and_then(
-                cx,
-                REDUNDANT_PATTERN_MATCHING,
-                expr.span,
-                &format!("redundant pattern matching, consider using `{}`", good_method),
-                |diag| {
-                    let span = expr.span.to(op.span);
-                    diag.span_suggestion(
-                        span,
-                        "try this",
-                        format!("{}.{}", snippet(cx, op.span, "_"), good_method),
-                        Applicability::MaybeIncorrect, // snippet
-                    );
-                },
-            );
-        }
-    }
-}
-
-#[allow(clippy::too_many_arguments)]
-fn find_good_method_for_match<'a>(
-    arms: &[Arm<'_>],
-    path_left: &QPath<'_>,
-    path_right: &QPath<'_>,
-    expected_left: &[&str],
-    expected_right: &[&str],
-    should_be_left: &'a str,
-    should_be_right: &'a str,
-    can_suggest_left: impl Fn() -> bool,
-    can_suggest_right: impl Fn() -> bool,
-) -> Option<&'a str> {
-    let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) {
-        (&(*arms[0].body).kind, &(*arms[1].body).kind)
-    } else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) {
-        (&(*arms[1].body).kind, &(*arms[0].body).kind)
-    } else {
-        return None;
-    };
-
-    match body_node_pair {
-        (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) {
-            (LitKind::Bool(true), LitKind::Bool(false)) if can_suggest_left() => Some(should_be_left),
-            (LitKind::Bool(false), LitKind::Bool(true)) if can_suggest_right() => Some(should_be_right),
-            _ => None,
-        },
-        _ => None,
-    }
-}
-
-fn can_suggest(cx: &LateContext<'_>, hir_id: HirId, diag_item: Symbol, name: &str) -> bool {
-    if !in_constant(cx, hir_id) {
-        return true;
-    }
-
-    // Avoid suggesting calls to non-`const fn`s in const contexts, see #5697.
-    cx.tcx
-        .get_diagnostic_item(diag_item)
-        .and_then(|def_id| {
-            cx.tcx.inherent_impls(def_id).iter().find_map(|imp| {
-                cx.tcx
-                    .associated_items(*imp)
-                    .in_definition_order()
-                    .find_map(|item| match item.kind {
-                        ty::AssocKind::Fn if item.ident.name.as_str() == name => Some(item.def_id),
-                        _ => None,
-                    })
-            })
-        })
-        .map_or(false, |def_id| is_const_fn(cx.tcx, def_id))
-}
diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs
index b67abad6ccb8f..f204a0ffb2c7b 100644
--- a/src/tools/clippy/clippy_lints/src/regex.rs
+++ b/src/tools/clippy/clippy_lints/src/regex.rs
@@ -1,9 +1,9 @@
 use crate::consts::{constant, Constant};
-use crate::utils::{is_expn_of, match_def_path, match_type, paths, span_lint, span_lint_and_help};
+use crate::utils::{match_def_path, paths, span_lint, span_lint_and_help};
 use if_chain::if_chain;
 use rustc_ast::ast::{LitKind, StrStyle};
 use rustc_data_structures::fx::FxHashSet;
-use rustc_hir::{Block, BorrowKind, Crate, Expr, ExprKind, HirId};
+use rustc_hir::{BorrowKind, Expr, ExprKind, HirId};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::{BytePos, Span};
@@ -46,66 +46,15 @@ declare_clippy_lint! {
     "trivial regular expressions"
 }
 
-declare_clippy_lint! {
-    /// **What it does:** Checks for usage of `regex!(_)` which (as of now) is
-    /// usually slower than `Regex::new(_)` unless called in a loop (which is a bad
-    /// idea anyway).
-    ///
-    /// **Why is this bad?** Performance, at least for now. The macro version is
-    /// likely to catch up long-term, but for now the dynamic version is faster.
-    ///
-    /// **Known problems:** None.
-    ///
-    /// **Example:**
-    /// ```ignore
-    /// regex!("foo|bar")
-    /// ```
-    pub REGEX_MACRO,
-    style,
-    "use of `regex!(_)` instead of `Regex::new(_)`"
-}
-
 #[derive(Clone, Default)]
 pub struct Regex {
     spans: FxHashSet<Span>,
     last: Option<HirId>,
 }
 
-impl_lint_pass!(Regex => [INVALID_REGEX, REGEX_MACRO, TRIVIAL_REGEX]);
+impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]);
 
 impl<'tcx> LateLintPass<'tcx> for Regex {
-    fn check_crate(&mut self, _: &LateContext<'tcx>, _: &'tcx Crate<'_>) {
-        self.spans.clear();
-    }
-
-    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
-        if_chain! {
-            if self.last.is_none();
-            if let Some(ref expr) = block.expr;
-            if match_type(cx, cx.tables().expr_ty(expr), &paths::REGEX);
-            if let Some(span) = is_expn_of(expr.span, "regex");
-            then {
-                if !self.spans.contains(&span) {
-                    span_lint(
-                        cx,
-                        REGEX_MACRO,
-                        span,
-                        "`regex!(_)` found. \
-                        Please use `Regex::new(_)`, which is faster for now."
-                    );
-                    self.spans.insert(span);
-                }
-                self.last = Some(block.hir_id);
-            }
-        }
-    }
-
-    fn check_block_post(&mut self, _: &LateContext<'tcx>, block: &'tcx Block<'_>) {
-        if self.last.map_or(false, |id| block.hir_id == id) {
-            self.last = None;
-        }
-    }
-
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if_chain! {
             if let ExprKind::Call(ref fun, ref args) = expr.kind;
@@ -150,12 +99,7 @@ fn is_trivial_regex(s: &regex_syntax::hir::Hir) -> Option<&'static str> {
     use regex_syntax::hir::Anchor::{EndText, StartText};
     use regex_syntax::hir::HirKind::{Alternation, Anchor, Concat, Empty, Literal};
 
-    let is_literal = |e: &[regex_syntax::hir::Hir]| {
-        e.iter().all(|e| match *e.kind() {
-            Literal(_) => true,
-            _ => false,
-        })
-    };
+    let is_literal = |e: &[regex_syntax::hir::Hir]| e.iter().all(|e| matches!(*e.kind(), Literal(_)));
 
     match *s.kind() {
         Empty | Anchor(_) => Some("the regex is unlikely to be useful as it is"),
diff --git a/src/tools/clippy/clippy_lints/src/repeat_once.rs b/src/tools/clippy/clippy_lints/src/repeat_once.rs
new file mode 100644
index 0000000000000..a3af369e41e5a
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/repeat_once.rs
@@ -0,0 +1,82 @@
+use crate::consts::{constant_context, Constant};
+use crate::utils::{in_macro, is_type_diagnostic_item, snippet, span_lint_and_sugg, walk_ptrs_ty};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for usage of `.repeat(1)` and suggest the following method for each types.
+    /// - `.to_string()` for `str`
+    /// - `.clone()` for `String`
+    /// - `.to_vec()` for `slice`
+    ///
+    /// **Why is this bad?** For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning the string is the intention behind this, `clone()` should be used.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// fn main() {
+    ///     let x = String::from("hello world").repeat(1);
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// fn main() {
+    ///     let x = String::from("hello world").clone();
+    /// }
+    /// ```
+    pub REPEAT_ONCE,
+    complexity,
+    "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` "
+}
+
+declare_lint_pass!(RepeatOnce => [REPEAT_ONCE]);
+
+impl<'tcx> LateLintPass<'tcx> for RepeatOnce {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
+        if_chain! {
+            if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind;
+            if path.ident.name == sym!(repeat);
+            if let Some(Constant::Int(1)) = constant_context(cx, cx.tables()).expr(&args[1]);
+            if !in_macro(args[0].span);
+            then {
+                let ty = walk_ptrs_ty(cx.tables().expr_ty(&args[0]));
+                if ty.is_str() {
+                    span_lint_and_sugg(
+                        cx,
+                        REPEAT_ONCE,
+                        expr.span,
+                        "calling `repeat(1)` on str",
+                        "consider using `.to_string()` instead",
+                        format!("{}.to_string()", snippet(cx, args[0].span, r#""...""#)),
+                        Applicability::MachineApplicable,
+                    );
+                } else if ty.builtin_index().is_some() {
+                    span_lint_and_sugg(
+                        cx,
+                        REPEAT_ONCE,
+                        expr.span,
+                        "calling `repeat(1)` on slice",
+                        "consider using `.to_vec()` instead",
+                        format!("{}.to_vec()", snippet(cx, args[0].span, r#""...""#)),
+                        Applicability::MachineApplicable,
+                    );
+                } else if is_type_diagnostic_item(cx, ty, sym!(string_type)) {
+                    span_lint_and_sugg(
+                        cx,
+                        REPEAT_ONCE,
+                        expr.span,
+                        "calling `repeat(1)` on a string literal",
+                        "consider using `.clone()` instead",
+                        format!("{}.clone()", snippet(cx, args[0].span, r#""...""#)),
+                        Applicability::MachineApplicable,
+                    );
+                }
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs
index 3c93974417356..faef7e724dd05 100644
--- a/src/tools/clippy/clippy_lints/src/returns.rs
+++ b/src/tools/clippy/clippy_lints/src/returns.rs
@@ -259,15 +259,15 @@ fn is_unit_expr(expr: &ast::Expr) -> bool {
 
 fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) {
     let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) {
-        if let Some(rpos) = fn_source.rfind("->") {
-            #[allow(clippy::cast_possible_truncation)]
-            (
-                ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
-                Applicability::MachineApplicable,
-            )
-        } else {
-            (ty.span, Applicability::MaybeIncorrect)
-        }
+        fn_source
+            .rfind("->")
+            .map_or((ty.span, Applicability::MaybeIncorrect), |rpos| {
+                (
+                    #[allow(clippy::cast_possible_truncation)]
+                    ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
+                    Applicability::MachineApplicable,
+                )
+            })
     } else {
         (ty.span, Applicability::MaybeIncorrect)
     };
diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs
index 7da47ee4ff94b..194786c5c4145 100644
--- a/src/tools/clippy/clippy_lints/src/shadow.rs
+++ b/src/tools/clippy/clippy_lints/src/shadow.rs
@@ -165,14 +165,7 @@ fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: &
 
 fn is_binding(cx: &LateContext<'_>, pat_id: HirId) -> bool {
     let var_ty = cx.tables().node_type_opt(pat_id);
-    if let Some(var_ty) = var_ty {
-        match var_ty.kind {
-            ty::Adt(..) => false,
-            _ => true,
-        }
-    } else {
-        false
-    }
+    var_ty.map_or(false, |var_ty| !matches!(var_ty.kind, ty::Adt(..)))
 }
 
 fn check_pat<'tcx>(
diff --git a/src/tools/clippy/clippy_lints/src/temporary_assignment.rs b/src/tools/clippy/clippy_lints/src/temporary_assignment.rs
index 509bbfd27c1a5..1aeff1baa362e 100644
--- a/src/tools/clippy/clippy_lints/src/temporary_assignment.rs
+++ b/src/tools/clippy/clippy_lints/src/temporary_assignment.rs
@@ -25,13 +25,7 @@ declare_clippy_lint! {
 fn is_temporary(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     match &expr.kind {
         ExprKind::Struct(..) | ExprKind::Tup(..) => true,
-        ExprKind::Path(qpath) => {
-            if let Res::Def(DefKind::Const, ..) = cx.qpath_res(qpath, expr.hir_id) {
-                true
-            } else {
-                false
-            }
-        },
+        ExprKind::Path(qpath) => matches!(cx.qpath_res(qpath, expr.hir_id), Res::Def(DefKind::Const, ..)),
         _ => false,
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
index 9eb2079c3ca2d..0ef70311fb1cd 100644
--- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs
+++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
@@ -1,19 +1,19 @@
 use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_help, SpanlessHash};
+use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::Applicability;
 use rustc_hir::{GenericBound, Generics, WherePredicate};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
-#[derive(Copy, Clone)]
-pub struct TraitBounds;
-
 declare_clippy_lint! {
     /// **What it does:** This lint warns about unnecessary type repetitions in trait bounds
     ///
     /// **Why is this bad?** Repeating the type for every bound makes the code
     /// less readable than combining the bounds
     ///
+    /// **Known problems:** None.
+    ///
     /// **Example:**
     /// ```rust
     /// pub fn foo<T>(t: T) where T: Copy, T: Clone {}
@@ -29,6 +29,18 @@ declare_clippy_lint! {
     "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
 }
 
+#[derive(Copy, Clone)]
+pub struct TraitBounds {
+    max_trait_bounds: u64,
+}
+
+impl TraitBounds {
+    #[must_use]
+    pub fn new(max_trait_bounds: u64) -> Self {
+        Self { max_trait_bounds }
+    }
+}
+
 impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS]);
 
 impl<'tcx> LateLintPass<'tcx> for TraitBounds {
@@ -44,9 +56,14 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
         let mut map = FxHashMap::default();
         let mut applicability = Applicability::MaybeIncorrect;
         for bound in gen.where_clause.predicates {
-            if let WherePredicate::BoundPredicate(ref p) = bound {
+            if_chain! {
+                if let WherePredicate::BoundPredicate(ref p) = bound;
+                if p.bounds.len() as u64 <= self.max_trait_bounds;
+                if !in_macro(p.span);
                 let h = hash(&p.bounded_ty);
-                if let Some(ref v) = map.insert(h, p.bounds.iter().collect::<Vec<_>>()) {
+                if let Some(ref v) = map.insert(h, p.bounds.iter().collect::<Vec<_>>());
+
+                then {
                     let mut hint_string = format!(
                         "consider combining the bounds: `{}:",
                         snippet(cx, p.bounded_ty.span, "_")
diff --git a/src/tools/clippy/clippy_lints/src/types.rs b/src/tools/clippy/clippy_lints/src/types.rs
index 68f51f0afdccd..bca388ecdcc38 100644
--- a/src/tools/clippy/clippy_lints/src/types.rs
+++ b/src/tools/clippy/clippy_lints/src/types.rs
@@ -775,11 +775,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitArg {
                     .iter()
                     .filter(|arg| {
                         if is_unit(cx.tables().expr_ty(arg)) && !is_unit_literal(arg) {
-                            if let ExprKind::Match(.., MatchSource::TryDesugar) = &arg.kind {
-                                false
-                            } else {
-                                true
-                            }
+                            !matches!(&arg.kind, ExprKind::Match(.., MatchSource::TryDesugar))
                         } else {
                             false
                         }
@@ -899,17 +895,11 @@ fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool {
 }
 
 fn is_unit(ty: Ty<'_>) -> bool {
-    match ty.kind {
-        ty::Tuple(slice) if slice.is_empty() => true,
-        _ => false,
-    }
+    matches!(ty.kind, ty::Tuple(slice) if slice.is_empty())
 }
 
 fn is_unit_literal(expr: &Expr<'_>) -> bool {
-    match expr.kind {
-        ExprKind::Tup(ref slice) if slice.is_empty() => true,
-        _ => false,
-    }
+    matches!(expr.kind, ExprKind::Tup(ref slice) if slice.is_empty())
 }
 
 declare_clippy_lint! {
@@ -1154,10 +1144,7 @@ fn int_ty_to_nbits(typ: Ty<'_>, tcx: TyCtxt<'_>) -> u64 {
 }
 
 fn is_isize_or_usize(typ: Ty<'_>) -> bool {
-    match typ.kind {
-        ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => true,
-        _ => false,
-    }
+    matches!(typ.kind, ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
 }
 
 fn span_precision_loss_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to_f64: bool) {
@@ -1205,16 +1192,19 @@ fn span_lossless_lint(cx: &LateContext<'_>, expr: &Expr<'_>, op: &Expr<'_>, cast
     // has parens on the outside, they are no longer needed.
     let mut applicability = Applicability::MachineApplicable;
     let opt = snippet_opt(cx, op.span);
-    let sugg = if let Some(ref snip) = opt {
-        if should_strip_parens(op, snip) {
-            &snip[1..snip.len() - 1]
-        } else {
-            snip.as_str()
-        }
-    } else {
-        applicability = Applicability::HasPlaceholders;
-        ".."
-    };
+    let sugg = opt.as_ref().map_or_else(
+        || {
+            applicability = Applicability::HasPlaceholders;
+            ".."
+        },
+        |snip| {
+            if should_strip_parens(op, snip) {
+                &snip[1..snip.len() - 1]
+            } else {
+                snip.as_str()
+            }
+        },
+    );
 
     span_lint_and_sugg(
         cx,
@@ -1734,10 +1724,10 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor {
 
             TyKind::TraitObject(ref param_bounds, _) => {
                 let has_lifetime_parameters = param_bounds.iter().any(|bound| {
-                    bound.bound_generic_params.iter().any(|gen| match gen.kind {
-                        GenericParamKind::Lifetime { .. } => true,
-                        _ => false,
-                    })
+                    bound
+                        .bound_generic_params
+                        .iter()
+                        .any(|gen| matches!(gen.kind, GenericParamKind::Lifetime { .. }))
                 });
                 if has_lifetime_parameters {
                     // complex trait bounds like A<'a, 'b>
diff --git a/src/tools/clippy/clippy_lints/src/unnamed_address.rs b/src/tools/clippy/clippy_lints/src/unnamed_address.rs
index b9aa202b328f6..25d136e564d3e 100644
--- a/src/tools/clippy/clippy_lints/src/unnamed_address.rs
+++ b/src/tools/clippy/clippy_lints/src/unnamed_address.rs
@@ -58,10 +58,10 @@ declare_lint_pass!(UnnamedAddress => [FN_ADDRESS_COMPARISONS, VTABLE_ADDRESS_COM
 impl LateLintPass<'_> for UnnamedAddress {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
         fn is_comparison(binop: BinOpKind) -> bool {
-            match binop {
-                BinOpKind::Eq | BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ne | BinOpKind::Ge | BinOpKind::Gt => true,
-                _ => false,
-            }
+            matches!(
+                binop,
+                BinOpKind::Eq | BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ne | BinOpKind::Ge | BinOpKind::Gt
+            )
         }
 
         fn is_trait_ptr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
@@ -72,11 +72,7 @@ impl LateLintPass<'_> for UnnamedAddress {
         }
 
         fn is_fn_def(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-            if let ty::FnDef(..) = cx.tables().expr_ty(expr).kind {
-                true
-            } else {
-                false
-            }
+            matches!(cx.tables().expr_ty(expr).kind, ty::FnDef(..))
         }
 
         if_chain! {
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs
index d940776817ca0..91c1789a2ffb1 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs
@@ -5,24 +5,23 @@ use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
 use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::{self, subst::GenericArgKind};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::symbol::Ident;
 
 declare_clippy_lint! {
     /// **What it does:**
-    /// Detects when people use `Vec::sort_by` and pass in a function
+    /// Detects uses of `Vec::sort_by` passing in a closure
     /// which compares the two arguments, either directly or indirectly.
     ///
     /// **Why is this bad?**
     /// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
-    /// possible) than to use `Vec::sort_by` and and a more complicated
+    /// possible) than to use `Vec::sort_by` and a more complicated
     /// closure.
     ///
     /// **Known problems:**
-    /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't
-    /// imported by a use statement in the current frame, then a `use`
-    /// statement that imports it will need to be added (which this lint
-    /// can't do).
+    /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already
+    /// imported by a use statement, then it will need to be added manually.
     ///
     /// **Example:**
     ///
@@ -201,28 +200,41 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
             };
             let vec_name = Sugg::hir(cx, &args[0], "..").to_string();
             let unstable = name == "sort_unstable_by";
+
             if_chain! {
                 if let ExprKind::Path(QPath::Resolved(_, Path {
                     segments: [PathSegment { ident: left_name, .. }], ..
                 })) = &left_expr.kind;
                 if left_name == left_ident;
                 then {
-                    Some(LintTrigger::Sort(SortDetection { vec_name, unstable }))
-                }
-                else {
-                    Some(LintTrigger::SortByKey(SortByKeyDetection {
-                        vec_name,
-                        unstable,
-                        closure_arg,
-                        closure_body,
-                        reverse
-                    }))
+                    return Some(LintTrigger::Sort(SortDetection { vec_name, unstable }))
+                } else {
+                    if !key_returns_borrow(cx, left_expr) {
+                        return Some(LintTrigger::SortByKey(SortByKeyDetection {
+                            vec_name,
+                            unstable,
+                            closure_arg,
+                            closure_body,
+                            reverse
+                        }))
+                    }
                 }
             }
-        } else {
-            None
         }
     }
+
+    None
+}
+
+fn key_returns_borrow(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    if let Some(def_id) = utils::fn_def_id(cx, expr) {
+        let output = cx.tcx.fn_sig(def_id).output();
+        let ty = output.skip_binder();
+        return matches!(ty.kind, ty::Ref(..))
+            || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
+    }
+
+    false
 }
 
 impl LateLintPass<'_> for UnnecessarySortBy {
diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
index 4d3682263f14f..502bf0c427954 100644
--- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
@@ -72,8 +72,8 @@ impl EarlyLintPass for UnnestedOrPatterns {
 }
 
 fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) {
-    if !cx.sess.opts.unstable_features.is_nightly_build() {
-        // User cannot do `#![feature(or_patterns)]`, so bail.
+    if !cx.sess.features_untracked().or_patterns {
+        // Do not suggest nesting the patterns if the feature `or_patterns` is not enabled.
         return;
     }
 
@@ -400,8 +400,8 @@ fn extend_with_matching(
 
 /// Are the patterns in `ps1` and `ps2` equal save for `ps1[idx]` compared to `ps2[idx]`?
 fn eq_pre_post(ps1: &[P<Pat>], ps2: &[P<Pat>], idx: usize) -> bool {
-    ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`.
-        && ps1.len() == ps2.len()
+    ps1.len() == ps2.len()
+        && ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`.
         && over(&ps1[..idx], &ps2[..idx], |l, r| eq_pat(l, r))
         && over(&ps1[idx + 1..], &ps2[idx + 1..], |l, r| eq_pat(l, r))
 }
diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs
index f85db1e2006e8..776c6bc57ca6f 100644
--- a/src/tools/clippy/clippy_lints/src/use_self.rs
+++ b/src/tools/clippy/clippy_lints/src/use_self.rs
@@ -167,14 +167,11 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
             if let TyKind::Path(QPath::Resolved(_, ref item_path)) = item_type.kind;
             then {
                 let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
-                let should_check = if let Some(ref params) = *parameters {
-                    !params.parenthesized && !params.args.iter().any(|arg| match arg {
-                        GenericArg::Lifetime(_) => true,
-                        _ => false,
-                    })
-                } else {
-                    true
-                };
+                let should_check = parameters.as_ref().map_or(
+                    true,
+                    |params| !params.parenthesized
+                        &&!params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
+                );
 
                 if should_check {
                     let visitor = &mut UseSelfVisitor {
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 e19a79dd8dad1..58c1103da9f7d 100755
--- a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs
@@ -387,10 +387,7 @@ pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool {
 }
 
 pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool {
-    match (l, r) {
-        (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_)) => true,
-        _ => false,
-    }
+    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/attrs.rs b/src/tools/clippy/clippy_lints/src/utils/attrs.rs
index 4dcf6c105ec63..4bb4b087c5566 100644
--- a/src/tools/clippy/clippy_lints/src/utils/attrs.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/attrs.rs
@@ -65,42 +65,45 @@ pub fn get_attr<'a>(
         };
         let attr_segments = &attr.path.segments;
         if attr_segments.len() == 2 && attr_segments[0].ident.to_string() == "clippy" {
-            if let Some(deprecation_status) =
-                BUILTIN_ATTRIBUTES
-                    .iter()
-                    .find_map(|(builtin_name, deprecation_status)| {
-                        if *builtin_name == attr_segments[1].ident.to_string() {
-                            Some(deprecation_status)
-                        } else {
-                            None
-                        }
-                    })
-            {
-                let mut diag = sess.struct_span_err(attr_segments[1].ident.span, "Usage of deprecated attribute");
-                match *deprecation_status {
-                    DeprecationStatus::Deprecated => {
-                        diag.emit();
-                        false
-                    },
-                    DeprecationStatus::Replaced(new_name) => {
-                        diag.span_suggestion(
-                            attr_segments[1].ident.span,
-                            "consider using",
-                            new_name.to_string(),
-                            Applicability::MachineApplicable,
-                        );
-                        diag.emit();
+            BUILTIN_ATTRIBUTES
+                .iter()
+                .find_map(|(builtin_name, deprecation_status)| {
+                    if *builtin_name == attr_segments[1].ident.to_string() {
+                        Some(deprecation_status)
+                    } else {
+                        None
+                    }
+                })
+                .map_or_else(
+                    || {
+                        sess.span_err(attr_segments[1].ident.span, "Usage of unknown attribute");
                         false
                     },
-                    DeprecationStatus::None => {
-                        diag.cancel();
-                        attr_segments[1].ident.to_string() == name
+                    |deprecation_status| {
+                        let mut diag =
+                            sess.struct_span_err(attr_segments[1].ident.span, "Usage of deprecated attribute");
+                        match *deprecation_status {
+                            DeprecationStatus::Deprecated => {
+                                diag.emit();
+                                false
+                            },
+                            DeprecationStatus::Replaced(new_name) => {
+                                diag.span_suggestion(
+                                    attr_segments[1].ident.span,
+                                    "consider using",
+                                    new_name.to_string(),
+                                    Applicability::MachineApplicable,
+                                );
+                                diag.emit();
+                                false
+                            },
+                            DeprecationStatus::None => {
+                                diag.cancel();
+                                attr_segments[1].ident.to_string() == name
+                            },
+                        }
                     },
-                }
-            } else {
-                sess.span_err(attr_segments[1].ident.span, "Usage of unknown attribute");
-                false
-            }
+                )
         } else {
             false
         }
diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs
index c41befbf147b8..de425211e38ef 100644
--- a/src/tools/clippy/clippy_lints/src/utils/conf.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs
@@ -156,6 +156,8 @@ define_Conf! {
     (array_size_threshold, "array_size_threshold": u64, 512_000),
     /// Lint: VEC_BOX. The size of the boxed type in bytes, where boxing in a `Vec` is allowed
     (vec_box_size_threshold, "vec_box_size_threshold": u64, 4096),
+    /// Lint: TYPE_REPETITION_IN_BOUNDS. The maximum number of bounds a trait can have to be linted
+    (max_trait_bounds, "max_trait_bounds": u64, 3),
     /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have
     (max_struct_bools, "max_struct_bools": u64, 3),
     /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have
diff --git a/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs b/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs
index ae58f0a1521e6..34341594c1985 100644
--- a/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs
@@ -703,6 +703,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                     }
                     for segment in path.segments {
                         segment.ident.name.hash(&mut self.s);
+                        self.hash_generic_args(segment.generic_args().args);
                     }
                 },
                 QPath::TypeRelative(ref ty, ref segment) => {
@@ -711,13 +712,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                 },
             },
             TyKind::OpaqueDef(_, arg_list) => {
-                for arg in *arg_list {
-                    match arg {
-                        GenericArg::Lifetime(ref l) => self.hash_lifetime(l),
-                        GenericArg::Type(ref ty) => self.hash_ty(&ty),
-                        GenericArg::Const(ref ca) => self.hash_body(ca.value.body),
-                    }
-                }
+                self.hash_generic_args(arg_list);
             },
             TyKind::TraitObject(_, lifetime) => {
                 self.hash_lifetime(lifetime);
@@ -735,4 +730,14 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
         self.hash_expr(&self.cx.tcx.hir().body(body_id).value);
         self.maybe_typeck_tables = old_maybe_typeck_tables;
     }
+
+    fn hash_generic_args(&mut self, arg_list: &[GenericArg<'_>]) {
+        for arg in arg_list {
+            match arg {
+                GenericArg::Lifetime(ref l) => self.hash_lifetime(l),
+                GenericArg::Type(ref ty) => self.hash_ty(&ty),
+                GenericArg::Const(ref ca) => self.hash_body(ca.value.body),
+            }
+        }
+    }
 }
diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs
index 99ba7d64331cd..4b7a1c2b537f3 100644
--- a/src/tools/clippy/clippy_lints/src/utils/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs
@@ -102,11 +102,7 @@ pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool {
 #[must_use]
 pub fn in_macro(span: Span) -> bool {
     if span.from_expansion() {
-        if let ExpnKind::Desugaring(..) = span.ctxt().outer_expn_data().kind {
-            false
-        } else {
-            true
-        }
+        !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..))
     } else {
         false
     }
@@ -127,10 +123,7 @@ pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool {
 
 /// Checks if given pattern is a wildcard (`_`)
 pub fn is_wild<'tcx>(pat: &impl std::ops::Deref<Target = Pat<'tcx>>) -> bool {
-    match pat.kind {
-        PatKind::Wild => true,
-        _ => false,
-    }
+    matches!(pat.kind, PatKind::Wild)
 }
 
 /// Checks if type is struct, enum or union type with the given def path.
@@ -153,11 +146,7 @@ pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symb
 pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
     let def_id = cx.tables().type_dependent_def_id(expr.hir_id).unwrap();
     let trt_id = cx.tcx.trait_of_item(def_id);
-    if let Some(trt_id) = trt_id {
-        match_def_path(cx, trt_id, path)
-    } else {
-        false
-    }
+    trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
 }
 
 /// Checks if an expression references a variable of the given name.
@@ -600,21 +589,15 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>(
 /// //  ^^^^^^^^^^
 /// ```
 pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span {
-    if let Some(first_char_pos) = first_char_in_first_line(cx, span) {
-        span.with_lo(first_char_pos)
-    } else {
-        span
-    }
+    first_char_in_first_line(cx, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos))
 }
 
 fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> {
     let line_span = line_span(cx, span);
-    if let Some(snip) = snippet_opt(cx, line_span) {
+    snippet_opt(cx, line_span).and_then(|snip| {
         snip.find(|c: char| !c.is_whitespace())
             .map(|pos| line_span.lo() + BytePos::from_usize(pos))
-    } else {
-        None
-    }
+    })
 }
 
 /// Returns the indentation of the line of a span
@@ -626,11 +609,7 @@ fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePo
 /// //          ^^ -- will return 4
 /// ```
 pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
-    if let Some(snip) = snippet_opt(cx, line_span(cx, span)) {
-        snip.find(|c: char| !c.is_whitespace())
-    } else {
-        None
-    }
+    snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
 }
 
 /// Extends the span to the beginning of the spans line, incl. whitespaces.
@@ -738,25 +717,21 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
     let enclosing_node = map
         .get_enclosing_scope(hir_id)
         .and_then(|enclosing_id| map.find(enclosing_id));
-    if let Some(node) = enclosing_node {
-        match node {
-            Node::Block(block) => Some(block),
-            Node::Item(&Item {
-                kind: ItemKind::Fn(_, _, eid),
-                ..
-            })
-            | Node::ImplItem(&ImplItem {
-                kind: ImplItemKind::Fn(_, eid),
-                ..
-            }) => match cx.tcx.hir().body(eid).value.kind {
-                ExprKind::Block(ref block, _) => Some(block),
-                _ => None,
-            },
+    enclosing_node.and_then(|node| match node {
+        Node::Block(block) => Some(block),
+        Node::Item(&Item {
+            kind: ItemKind::Fn(_, _, eid),
+            ..
+        })
+        | Node::ImplItem(&ImplItem {
+            kind: ImplItemKind::Fn(_, eid),
+            ..
+        }) => match cx.tcx.hir().body(eid).value.kind {
+            ExprKind::Block(ref block, _) => Some(block),
             _ => None,
-        }
-    } else {
-        None
-    }
+        },
+        _ => None,
+    })
 }
 
 /// Returns the base type for HIR references and pointers.
@@ -1328,11 +1303,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
         _ => None,
     };
 
-    if let Some(did) = did {
-        must_use_attr(&cx.tcx.get_attrs(did)).is_some()
-    } else {
-        false
-    }
+    did.map_or(false, |did| must_use_attr(&cx.tcx.get_attrs(did)).is_some())
 }
 
 pub fn is_no_std_crate(krate: &Crate<'_>) -> bool {
@@ -1385,6 +1356,21 @@ pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
     )
 }
 
+/// Returns the `DefId` of the callee if the given expression is a function or method call.
+pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
+    match &expr.kind {
+        ExprKind::MethodCall(..) => cx.tables().type_dependent_def_id(expr.hir_id),
+        ExprKind::Call(
+            Expr {
+                kind: ExprKind::Path(qpath),
+                ..
+            },
+            ..,
+        ) => cx.tables().qpath_res(qpath, expr.hir_id).opt_def_id(),
+        _ => None,
+    }
+}
+
 pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bool {
     lints.iter().any(|lint| {
         matches!(
diff --git a/src/tools/clippy/clippy_lints/src/utils/numeric_literal.rs b/src/tools/clippy/clippy_lints/src/utils/numeric_literal.rs
index 99413153d49bb..87cb454f654bc 100644
--- a/src/tools/clippy/clippy_lints/src/utils/numeric_literal.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/numeric_literal.rs
@@ -51,7 +51,7 @@ impl<'a> NumericLiteral<'a> {
     pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option<NumericLiteral<'a>> {
         if lit_kind.is_numeric() && src.chars().next().map_or(false, |c| c.is_digit(10)) {
             let (unsuffixed, suffix) = split_suffix(&src, lit_kind);
-            let float = if let LitKind::Float(..) = lit_kind { true } else { false };
+            let float = matches!(lit_kind, LitKind::Float(..));
             Some(NumericLiteral::new(unsuffixed, suffix, float))
         } else {
             None
@@ -200,12 +200,10 @@ impl<'a> NumericLiteral<'a> {
 
 fn split_suffix<'a>(src: &'a str, lit_kind: &LitKind) -> (&'a str, Option<&'a str>) {
     debug_assert!(lit_kind.is_numeric());
-    if let Some(suffix_length) = lit_suffix_length(lit_kind) {
+    lit_suffix_length(lit_kind).map_or((src, None), |suffix_length| {
         let (unsuffixed, suffix) = src.split_at(src.len() - suffix_length);
         (unsuffixed, Some(suffix))
-    } else {
-        (src, None)
-    }
+    })
 }
 
 fn lit_suffix_length(lit_kind: &LitKind) -> Option<usize> {
diff --git a/src/tools/clippy/clippy_lints/src/utils/paths.rs b/src/tools/clippy/clippy_lints/src/utils/paths.rs
index 3b7e9739211b0..4c3462802e921 100644
--- a/src/tools/clippy/clippy_lints/src/utils/paths.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/paths.rs
@@ -98,7 +98,6 @@ pub const RANGE_TO_STD: [&str; 3] = ["std", "ops", "RangeTo"];
 pub const RC: [&str; 3] = ["alloc", "rc", "Rc"];
 pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
 pub const RECEIVER: [&str; 4] = ["std", "sync", "mpsc", "Receiver"];
-pub const REGEX: [&str; 3] = ["regex", "re_unicode", "Regex"];
 pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
 pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
 pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
diff --git a/src/tools/clippy/clippy_lints/src/utils/sugg.rs b/src/tools/clippy/clippy_lints/src/utils/sugg.rs
index d05e81b950579..0ac7714fbeb79 100644
--- a/src/tools/clippy/clippy_lints/src/utils/sugg.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/sugg.rs
@@ -325,22 +325,22 @@ pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> {
 /// parenthesis will always be added for a mix of these.
 pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> {
     /// Returns `true` if the operator is a shift operator `<<` or `>>`.
-    fn is_shift(op: &AssocOp) -> bool {
-        matches!(*op, AssocOp::ShiftLeft | AssocOp::ShiftRight)
+    fn is_shift(op: AssocOp) -> bool {
+        matches!(op, AssocOp::ShiftLeft | AssocOp::ShiftRight)
     }
 
     /// Returns `true` if the operator is a arithmetic operator
     /// (i.e., `+`, `-`, `*`, `/`, `%`).
-    fn is_arith(op: &AssocOp) -> bool {
+    fn is_arith(op: AssocOp) -> bool {
         matches!(
-            *op,
+            op,
             AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide | AssocOp::Modulus
         )
     }
 
     /// Returns `true` if the operator `op` needs parenthesis with the operator
     /// `other` in the direction `dir`.
-    fn needs_paren(op: &AssocOp, other: &AssocOp, dir: Associativity) -> bool {
+    fn needs_paren(op: AssocOp, other: AssocOp, dir: Associativity) -> bool {
         other.precedence() < op.precedence()
             || (other.precedence() == op.precedence()
                 && ((op != other && associativity(op) != dir)
@@ -349,14 +349,14 @@ pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static>
             || is_shift(other) && is_arith(op)
     }
 
-    let lhs_paren = if let Sugg::BinOp(ref lop, _) = *lhs {
-        needs_paren(&op, lop, Associativity::Left)
+    let lhs_paren = if let Sugg::BinOp(lop, _) = *lhs {
+        needs_paren(op, lop, Associativity::Left)
     } else {
         false
     };
 
-    let rhs_paren = if let Sugg::BinOp(ref rop, _) = *rhs {
-        needs_paren(&op, rop, Associativity::Right)
+    let rhs_paren = if let Sugg::BinOp(rop, _) = *rhs {
+        needs_paren(op, rop, Associativity::Right)
     } else {
         false
     };
@@ -424,13 +424,13 @@ enum Associativity {
 /// they are considered
 /// associative.
 #[must_use]
-fn associativity(op: &AssocOp) -> Associativity {
+fn associativity(op: AssocOp) -> Associativity {
     use rustc_ast::util::parser::AssocOp::{
         Add, As, Assign, AssignOp, BitAnd, BitOr, BitXor, Colon, Divide, DotDot, DotDotEq, Equal, Greater,
         GreaterEqual, LAnd, LOr, Less, LessEqual, Modulus, Multiply, NotEqual, ShiftLeft, ShiftRight, Subtract,
     };
 
-    match *op {
+    match op {
         Assign | AssignOp(_) => Associativity::Right,
         Add | BitAnd | BitOr | BitXor | LAnd | LOr | Multiply | As | Colon => Associativity::Both,
         Divide | Equal | Greater | GreaterEqual | Less | LessEqual | Modulus | NotEqual | ShiftLeft | ShiftRight
@@ -492,20 +492,20 @@ fn astbinop2assignop(op: ast::BinOp) -> AssocOp {
 /// before it on its line.
 fn indentation<T: LintContext>(cx: &T, span: Span) -> Option<String> {
     let lo = cx.sess().source_map().lookup_char_pos(span.lo());
-    if let Some(line) = lo.file.get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */) {
-        if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') {
-            // We can mix char and byte positions here because we only consider `[ \t]`.
-            if lo.col == CharPos(pos) {
-                Some(line[..pos].into())
+    lo.file
+        .get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */)
+        .and_then(|line| {
+            if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') {
+                // We can mix char and byte positions here because we only consider `[ \t]`.
+                if lo.col == CharPos(pos) {
+                    Some(line[..pos].into())
+                } else {
+                    None
+                }
             } else {
                 None
             }
-        } else {
-            None
-        }
-    } else {
-        None
-    }
+        })
 }
 
 /// Convenience extension trait for `DiagnosticBuilder`.
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index cb769b5a2ce95..063f94582b9d1 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -23,7 +23,11 @@ declare_clippy_lint! {
     ///
     /// **Example:**
     /// ```rust
+    /// // Bad
     /// println!("");
+    ///
+    /// // Good
+    /// println!();
     /// ```
     pub PRINTLN_EMPTY_STRING,
     style,
@@ -32,8 +36,7 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// **What it does:** This lint warns when you use `print!()` with a format
-    /// string that
-    /// ends in a newline.
+    /// string that ends in a newline.
     ///
     /// **Why is this bad?** You should use `println!()` instead, which appends the
     /// newline.
@@ -125,7 +128,12 @@ declare_clippy_lint! {
     /// ```rust
     /// # use std::fmt::Write;
     /// # let mut buf = String::new();
+    ///
+    /// // Bad
     /// writeln!(buf, "");
+    ///
+    /// // Good
+    /// writeln!(buf);
     /// ```
     pub WRITELN_EMPTY_STRING,
     style,
@@ -147,7 +155,12 @@ declare_clippy_lint! {
     /// # use std::fmt::Write;
     /// # let mut buf = String::new();
     /// # let name = "World";
+    ///
+    /// // Bad
     /// write!(buf, "Hello {}!\n", name);
+    ///
+    /// // Good
+    /// writeln!(buf, "Hello {}!", name);
     /// ```
     pub WRITE_WITH_NEWLINE,
     style,
@@ -168,7 +181,12 @@ declare_clippy_lint! {
     /// ```rust
     /// # use std::fmt::Write;
     /// # let mut buf = String::new();
+    ///
+    /// // Bad
     /// writeln!(buf, "{}", "foo");
+    ///
+    /// // Good
+    /// writeln!(buf, "foo");
     /// ```
     pub WRITE_LITERAL,
     style,
@@ -279,13 +297,13 @@ impl EarlyLintPass for Write {
             if let (Some(fmt_str), expr) = self.check_tts(cx, &mac.args.inner_tokens(), true) {
                 if fmt_str.symbol == Symbol::intern("") {
                     let mut applicability = Applicability::MachineApplicable;
-                    let suggestion = match expr {
-                        Some(expr) => snippet_with_applicability(cx, expr.span, "v", &mut applicability),
-                        None => {
+                    let suggestion = expr.map_or_else(
+                        || {
                             applicability = Applicability::HasPlaceholders;
                             Cow::Borrowed("v")
                         },
-                    };
+                        |e| snippet_with_applicability(cx, e.span, "v", &mut Applicability::MachineApplicable),
+                    );
 
                     span_lint_and_sugg(
                         cx,
diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs
index decd3a79cce18..47315fa64cd80 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -382,13 +382,8 @@ pub fn main() {
 
         let should_describe_lints = || {
             let args: Vec<_> = env::args().collect();
-            args.windows(2).any(|args| {
-                args[1] == "help"
-                    && match args[0].as_str() {
-                        "-W" | "-A" | "-D" | "-F" => true,
-                        _ => false,
-                    }
-            })
+            args.windows(2)
+                .any(|args| args[1] == "help" && matches!(args[0].as_str(), "-W" | "-A" | "-D" | "-F"))
         };
 
         if !wrapper_mode && should_describe_lints() {
diff --git a/src/tools/clippy/src/lintlist/mod.rs b/src/tools/clippy/src/lintlist/mod.rs
index edceb75518008..b89a87128626b 100644
--- a/src/tools/clippy/src/lintlist/mod.rs
+++ b/src/tools/clippy/src/lintlist/mod.rs
@@ -80,6 +80,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
         deprecation: None,
         module: "blacklisted_name",
     },
+    Lint {
+        name: "blanket_clippy_restriction_lints",
+        group: "style",
+        desc: "enabling the complete restriction group",
+        deprecation: None,
+        module: "attrs",
+    },
     Lint {
         name: "blocks_in_if_conditions",
         group: "style",
@@ -1144,6 +1151,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
         deprecation: None,
         module: "methods",
     },
+    Lint {
+        name: "map_identity",
+        group: "complexity",
+        desc: "using iterator.map(|x| x)",
+        deprecation: None,
+        module: "map_identity",
+    },
     Lint {
         name: "map_unwrap_or",
         group: "pedantic",
@@ -1165,6 +1179,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
         deprecation: None,
         module: "matches",
     },
+    Lint {
+        name: "match_like_matches_macro",
+        group: "style",
+        desc: "a match that could be written with the matches! macro",
+        deprecation: None,
+        module: "matches",
+    },
     Lint {
         name: "match_on_vec_items",
         group: "pedantic",
@@ -1606,6 +1627,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
         deprecation: None,
         module: "option_env_unwrap",
     },
+    Lint {
+        name: "option_if_let_else",
+        group: "pedantic",
+        desc: "reimplementation of Option::map_or",
+        deprecation: None,
+        module: "option_if_let_else",
+    },
     Lint {
         name: "option_map_or_none",
         group: "style",
@@ -1683,6 +1711,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
         deprecation: None,
         module: "path_buf_push_overwrite",
     },
+    Lint {
+        name: "pattern_type_mismatch",
+        group: "restriction",
+        desc: "type of pattern does not match the expression type",
+        deprecation: None,
+        module: "pattern_type_mismatch",
+    },
     Lint {
         name: "possible_missing_comma",
         group: "correctness",
@@ -1755,7 +1790,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
     },
     Lint {
         name: "range_minus_one",
-        group: "complexity",
+        group: "pedantic",
         desc: "`x..=(y-1)` reads better as `x..y`",
         deprecation: None,
         module: "ranges",
@@ -1828,7 +1863,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
         group: "style",
         desc: "use the proper utility function avoiding an `if let`",
         deprecation: None,
-        module: "redundant_pattern_matching",
+        module: "matches",
     },
     Lint {
         name: "redundant_pub_crate",
@@ -1852,11 +1887,11 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
         module: "reference",
     },
     Lint {
-        name: "regex_macro",
-        group: "style",
-        desc: "use of `regex!(_)` instead of `Regex::new(_)`",
+        name: "repeat_once",
+        group: "complexity",
+        desc: "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` ",
         deprecation: None,
-        module: "regex",
+        module: "repeat_once",
     },
     Lint {
         name: "rest_pat_in_fully_bound_structs",
diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs
index 368fa6a98c5d6..26a47d237065a 100644
--- a/src/tools/clippy/tests/compile-test.rs
+++ b/src/tools/clippy/tests/compile-test.rs
@@ -12,19 +12,11 @@ use std::path::{Path, PathBuf};
 mod cargo;
 
 fn host_lib() -> PathBuf {
-    if let Some(path) = option_env!("HOST_LIBS") {
-        PathBuf::from(path)
-    } else {
-        cargo::CARGO_TARGET_DIR.join(env!("PROFILE"))
-    }
+    option_env!("HOST_LIBS").map_or(cargo::CARGO_TARGET_DIR.join(env!("PROFILE")), PathBuf::from)
 }
 
 fn clippy_driver_path() -> PathBuf {
-    if let Some(path) = option_env!("CLIPPY_DRIVER_PATH") {
-        PathBuf::from(path)
-    } else {
-        cargo::TARGET_LIB.join("clippy-driver")
-    }
+    option_env!("CLIPPY_DRIVER_PATH").map_or(cargo::TARGET_LIB.join("clippy-driver"), PathBuf::from)
 }
 
 // When we'll want to use `extern crate ..` for a dependency that is used
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.lock b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.lock
new file mode 100644
index 0000000000000..7e96aa36feb45
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.lock
@@ -0,0 +1,109 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "ansi_term"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
+dependencies = [
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "bitflags"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
+
+[[package]]
+name = "cfg-if"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+
+[[package]]
+name = "ctrlc"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "653abc99aa905f693d89df4797fadc08085baee379db92be9f2496cefe8a6f2c"
+dependencies = [
+ "kernel32-sys",
+ "nix",
+ "winapi 0.2.8",
+]
+
+[[package]]
+name = "kernel32-sys"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
+dependencies = [
+ "winapi 0.2.8",
+ "winapi-build",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.71"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
+
+[[package]]
+name = "multiple_crate_versions"
+version = "0.1.0"
+dependencies = [
+ "ansi_term",
+ "ctrlc",
+]
+
+[[package]]
+name = "nix"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2c5afeb0198ec7be8569d666644b574345aad2e95a53baf3a532da3e0f3fb32"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "libc",
+ "void",
+]
+
+[[package]]
+name = "void"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
+
+[[package]]
+name = "winapi"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-build"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr
index 4f668599be950..f3113e0936502 100644
--- a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr
+++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr
@@ -1,4 +1,4 @@
-error: multiple versions for dependency `winapi`: 0.2.8, 0.3.8
+error: multiple versions for dependency `winapi`: 0.2.8, 0.3.9
    |
    = note: `-D clippy::multiple-crate-versions` implied by `-D warnings`
 
diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
index 53970af41079d..6fbba01416a8d 100644
--- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
+++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
@@ -1,4 +1,4 @@
-error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `third-party` at line 5 column 1
+error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `third-party` at line 5 column 1
 
 error: aborting due to previous error
 
diff --git a/src/tools/clippy/tests/ui/attrs.rs b/src/tools/clippy/tests/ui/attrs.rs
index 91b65a43be77f..908d063729f45 100644
--- a/src/tools/clippy/tests/ui/attrs.rs
+++ b/src/tools/clippy/tests/ui/attrs.rs
@@ -1,5 +1,11 @@
 #![warn(clippy::inline_always, clippy::deprecated_semver)]
 #![allow(clippy::assertions_on_constants)]
+// Test that the whole restriction group is not enabled
+#![warn(clippy::restriction)]
+#![deny(clippy::restriction)]
+#![forbid(clippy::restriction)]
+#![allow(clippy::missing_docs_in_private_items, clippy::panic, clippy::unreachable)]
+
 #[inline(always)]
 fn test_attr_lint() {
     assert!(true)
diff --git a/src/tools/clippy/tests/ui/attrs.stderr b/src/tools/clippy/tests/ui/attrs.stderr
index 39ddf6f226d95..ef4b89eaa6dee 100644
--- a/src/tools/clippy/tests/ui/attrs.stderr
+++ b/src/tools/clippy/tests/ui/attrs.stderr
@@ -1,5 +1,5 @@
 error: you have declared `#[inline(always)]` on `test_attr_lint`. This is usually a bad idea
-  --> $DIR/attrs.rs:3:1
+  --> $DIR/attrs.rs:9:1
    |
 LL | #[inline(always)]
    | ^^^^^^^^^^^^^^^^^
@@ -7,7 +7,7 @@ LL | #[inline(always)]
    = note: `-D clippy::inline-always` implied by `-D warnings`
 
 error: the since field must contain a semver-compliant version
-  --> $DIR/attrs.rs:23:14
+  --> $DIR/attrs.rs:29:14
    |
 LL | #[deprecated(since = "forever")]
    |              ^^^^^^^^^^^^^^^^^
@@ -15,10 +15,35 @@ LL | #[deprecated(since = "forever")]
    = note: `-D clippy::deprecated-semver` implied by `-D warnings`
 
 error: the since field must contain a semver-compliant version
-  --> $DIR/attrs.rs:26:14
+  --> $DIR/attrs.rs:32:14
    |
 LL | #[deprecated(since = "1")]
    |              ^^^^^^^^^^^
 
-error: aborting due to 3 previous errors
+error: restriction lints are not meant to be all enabled
+  --> $DIR/attrs.rs:4:9
+   |
+LL | #![warn(clippy::restriction)]
+   |         ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings`
+   = help: try enabling only the lints you really need
+
+error: restriction lints are not meant to be all enabled
+  --> $DIR/attrs.rs:5:9
+   |
+LL | #![deny(clippy::restriction)]
+   |         ^^^^^^^^^^^^^^^^^^^
+   |
+   = help: try enabling only the lints you really need
+
+error: restriction lints are not meant to be all enabled
+  --> $DIR/attrs.rs:6:11
+   |
+LL | #![forbid(clippy::restriction)]
+   |           ^^^^^^^^^^^^^^^^^^^
+   |
+   = help: try enabling only the lints you really need
+
+error: aborting due to 6 previous errors
 
diff --git a/src/tools/clippy/tests/ui/clone_on_copy.fixed b/src/tools/clippy/tests/ui/clone_on_copy.fixed
new file mode 100644
index 0000000000000..1f0ca101757ec
--- /dev/null
+++ b/src/tools/clippy/tests/ui/clone_on_copy.fixed
@@ -0,0 +1,40 @@
+// run-rustfix
+
+#![allow(
+    unused,
+    clippy::redundant_clone,
+    clippy::deref_addrof,
+    clippy::no_effect,
+    clippy::unnecessary_operation
+)]
+
+use std::cell::RefCell;
+use std::rc::{self, Rc};
+use std::sync::{self, Arc};
+
+fn main() {}
+
+fn is_ascii(ch: char) -> bool {
+    ch.is_ascii()
+}
+
+fn clone_on_copy() {
+    42;
+
+    vec![1].clone(); // ok, not a Copy type
+    Some(vec![1]).clone(); // ok, not a Copy type
+    *(&42);
+
+    let rc = RefCell::new(0);
+    *rc.borrow();
+
+    // Issue #4348
+    let mut x = 43;
+    let _ = &x.clone(); // ok, getting a ref
+    'a'.clone().make_ascii_uppercase(); // ok, clone and then mutate
+    is_ascii('z');
+
+    // Issue #5436
+    let mut vec = Vec::new();
+    vec.push(42);
+}
diff --git a/src/tools/clippy/tests/ui/clone_on_copy.rs b/src/tools/clippy/tests/ui/clone_on_copy.rs
new file mode 100644
index 0000000000000..ca39a654b4fce
--- /dev/null
+++ b/src/tools/clippy/tests/ui/clone_on_copy.rs
@@ -0,0 +1,40 @@
+// run-rustfix
+
+#![allow(
+    unused,
+    clippy::redundant_clone,
+    clippy::deref_addrof,
+    clippy::no_effect,
+    clippy::unnecessary_operation
+)]
+
+use std::cell::RefCell;
+use std::rc::{self, Rc};
+use std::sync::{self, Arc};
+
+fn main() {}
+
+fn is_ascii(ch: char) -> bool {
+    ch.is_ascii()
+}
+
+fn clone_on_copy() {
+    42.clone();
+
+    vec![1].clone(); // ok, not a Copy type
+    Some(vec![1]).clone(); // ok, not a Copy type
+    (&42).clone();
+
+    let rc = RefCell::new(0);
+    rc.borrow().clone();
+
+    // Issue #4348
+    let mut x = 43;
+    let _ = &x.clone(); // ok, getting a ref
+    'a'.clone().make_ascii_uppercase(); // ok, clone and then mutate
+    is_ascii('z'.clone());
+
+    // Issue #5436
+    let mut vec = Vec::new();
+    vec.push(42.clone());
+}
diff --git a/src/tools/clippy/tests/ui/clone_on_copy.stderr b/src/tools/clippy/tests/ui/clone_on_copy.stderr
new file mode 100644
index 0000000000000..ec2faf4ab40d2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/clone_on_copy.stderr
@@ -0,0 +1,34 @@
+error: using `clone` on a `Copy` type
+  --> $DIR/clone_on_copy.rs:22:5
+   |
+LL |     42.clone();
+   |     ^^^^^^^^^^ help: try removing the `clone` call: `42`
+   |
+   = note: `-D clippy::clone-on-copy` implied by `-D warnings`
+
+error: using `clone` on a `Copy` type
+  --> $DIR/clone_on_copy.rs:26:5
+   |
+LL |     (&42).clone();
+   |     ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)`
+
+error: using `clone` on a `Copy` type
+  --> $DIR/clone_on_copy.rs:29:5
+   |
+LL |     rc.borrow().clone();
+   |     ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()`
+
+error: using `clone` on a `Copy` type
+  --> $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
+  --> $DIR/clone_on_copy.rs:39:14
+   |
+LL |     vec.push(42.clone());
+   |              ^^^^^^^^^^ help: try removing the `clone` call: `42`
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.fixed b/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.fixed
new file mode 100644
index 0000000000000..3305ac9bf8b6c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.fixed
@@ -0,0 +1,93 @@
+// run-rustfix
+#![allow(unused, clippy::redundant_clone)] // See #5700
+
+// Define the types in each module to avoid trait impls leaking between modules.
+macro_rules! impl_types {
+    () => {
+        #[derive(PartialEq)]
+        pub struct Owned;
+
+        pub struct Borrowed;
+
+        impl ToOwned for Borrowed {
+            type Owned = Owned;
+            fn to_owned(&self) -> Owned {
+                Owned {}
+            }
+        }
+
+        impl std::borrow::Borrow<Borrowed> for Owned {
+            fn borrow(&self) -> &Borrowed {
+                static VALUE: Borrowed = Borrowed {};
+                &VALUE
+            }
+        }
+    };
+}
+
+// Only Borrowed == Owned is implemented
+mod borrowed_eq_owned {
+    impl_types!();
+
+    impl PartialEq<Owned> for Borrowed {
+        fn eq(&self, _: &Owned) -> bool {
+            true
+        }
+    }
+
+    pub fn compare() {
+        let owned = Owned {};
+        let borrowed = Borrowed {};
+
+        if borrowed == owned {}
+        if borrowed == owned {}
+    }
+}
+
+// Only Owned == Borrowed is implemented
+mod owned_eq_borrowed {
+    impl_types!();
+
+    impl PartialEq<Borrowed> for Owned {
+        fn eq(&self, _: &Borrowed) -> bool {
+            true
+        }
+    }
+
+    fn compare() {
+        let owned = Owned {};
+        let borrowed = Borrowed {};
+
+        if owned == borrowed {}
+        if owned == borrowed {}
+    }
+}
+
+mod issue_4874 {
+    impl_types!();
+
+    // NOTE: PartialEq<Borrowed> for T can't be implemented due to the orphan rules
+    impl<T> PartialEq<T> for Borrowed
+    where
+        T: AsRef<str> + ?Sized,
+    {
+        fn eq(&self, _: &T) -> bool {
+            true
+        }
+    }
+
+    impl std::fmt::Display for Borrowed {
+        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+            write!(f, "borrowed")
+        }
+    }
+
+    fn compare() {
+        let borrowed = Borrowed {};
+
+        if borrowed == "Hi" {}
+        if borrowed == "Hi" {}
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.rs b/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.rs
new file mode 100644
index 0000000000000..88bc2f51dd662
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.rs
@@ -0,0 +1,93 @@
+// run-rustfix
+#![allow(unused, clippy::redundant_clone)] // See #5700
+
+// Define the types in each module to avoid trait impls leaking between modules.
+macro_rules! impl_types {
+    () => {
+        #[derive(PartialEq)]
+        pub struct Owned;
+
+        pub struct Borrowed;
+
+        impl ToOwned for Borrowed {
+            type Owned = Owned;
+            fn to_owned(&self) -> Owned {
+                Owned {}
+            }
+        }
+
+        impl std::borrow::Borrow<Borrowed> for Owned {
+            fn borrow(&self) -> &Borrowed {
+                static VALUE: Borrowed = Borrowed {};
+                &VALUE
+            }
+        }
+    };
+}
+
+// Only Borrowed == Owned is implemented
+mod borrowed_eq_owned {
+    impl_types!();
+
+    impl PartialEq<Owned> for Borrowed {
+        fn eq(&self, _: &Owned) -> bool {
+            true
+        }
+    }
+
+    pub fn compare() {
+        let owned = Owned {};
+        let borrowed = Borrowed {};
+
+        if borrowed.to_owned() == owned {}
+        if owned == borrowed.to_owned() {}
+    }
+}
+
+// Only Owned == Borrowed is implemented
+mod owned_eq_borrowed {
+    impl_types!();
+
+    impl PartialEq<Borrowed> for Owned {
+        fn eq(&self, _: &Borrowed) -> bool {
+            true
+        }
+    }
+
+    fn compare() {
+        let owned = Owned {};
+        let borrowed = Borrowed {};
+
+        if owned == borrowed.to_owned() {}
+        if borrowed.to_owned() == owned {}
+    }
+}
+
+mod issue_4874 {
+    impl_types!();
+
+    // NOTE: PartialEq<Borrowed> for T can't be implemented due to the orphan rules
+    impl<T> PartialEq<T> for Borrowed
+    where
+        T: AsRef<str> + ?Sized,
+    {
+        fn eq(&self, _: &T) -> bool {
+            true
+        }
+    }
+
+    impl std::fmt::Display for Borrowed {
+        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+            write!(f, "borrowed")
+        }
+    }
+
+    fn compare() {
+        let borrowed = Borrowed {};
+
+        if "Hi" == borrowed.to_string() {}
+        if borrowed.to_string() == "Hi" {}
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.stderr b/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.stderr
new file mode 100644
index 0000000000000..43bf8851fc620
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cmp_owned/asymmetric_partial_eq.stderr
@@ -0,0 +1,46 @@
+error: this creates an owned instance just for comparison
+  --> $DIR/asymmetric_partial_eq.rs:42:12
+   |
+LL |         if borrowed.to_owned() == owned {}
+   |            ^^^^^^^^^^^^^^^^^^^ help: try: `borrowed`
+   |
+   = note: `-D clippy::cmp-owned` implied by `-D warnings`
+
+error: this creates an owned instance just for comparison
+  --> $DIR/asymmetric_partial_eq.rs:43:21
+   |
+LL |         if owned == borrowed.to_owned() {}
+   |            ---------^^^^^^^^^^^^^^^^^^^
+   |            |
+   |            help: try: `borrowed == owned`
+
+error: this creates an owned instance just for comparison
+  --> $DIR/asymmetric_partial_eq.rs:61:21
+   |
+LL |         if owned == borrowed.to_owned() {}
+   |                     ^^^^^^^^^^^^^^^^^^^ help: try: `borrowed`
+
+error: this creates an owned instance just for comparison
+  --> $DIR/asymmetric_partial_eq.rs:62:12
+   |
+LL |         if borrowed.to_owned() == owned {}
+   |            ^^^^^^^^^^^^^^^^^^^---------
+   |            |
+   |            help: try: `owned == borrowed`
+
+error: this creates an owned instance just for comparison
+  --> $DIR/asymmetric_partial_eq.rs:88:20
+   |
+LL |         if "Hi" == borrowed.to_string() {}
+   |            --------^^^^^^^^^^^^^^^^^^^^
+   |            |
+   |            help: try: `borrowed == "Hi"`
+
+error: this creates an owned instance just for comparison
+  --> $DIR/asymmetric_partial_eq.rs:89:12
+   |
+LL |         if borrowed.to_string() == "Hi" {}
+   |            ^^^^^^^^^^^^^^^^^^^^ help: try: `borrowed`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.stderr b/src/tools/clippy/tests/ui/collapsible_else_if.stderr
index 28048999e8ec9..3d1c458879e58 100644
--- a/src/tools/clippy/tests/ui/collapsible_else_if.stderr
+++ b/src/tools/clippy/tests/ui/collapsible_else_if.stderr
@@ -10,7 +10,7 @@ LL | |     }
    | |_____^
    |
    = note: `-D clippy::collapsible-if` implied by `-D warnings`
-help: try
+help: collapse nested if block
    |
 LL |     } else if y == "world" {
 LL |         println!("world!")
@@ -28,7 +28,7 @@ LL | |         }
 LL | |     }
    | |_____^
    |
-help: try
+help: collapse nested if block
    |
 LL |     } else if let Some(42) = Some(42) {
 LL |         println!("world!")
@@ -48,7 +48,7 @@ LL | |         }
 LL | |     }
    | |_____^
    |
-help: try
+help: collapse nested if block
    |
 LL |     } else if y == "world" {
 LL |         println!("world")
@@ -71,7 +71,7 @@ LL | |         }
 LL | |     }
    | |_____^
    |
-help: try
+help: collapse nested if block
    |
 LL |     } else if let Some(42) = Some(42) {
 LL |         println!("world")
@@ -94,7 +94,7 @@ LL | |         }
 LL | |     }
    | |_____^
    |
-help: try
+help: collapse nested if block
    |
 LL |     } else if let Some(42) = Some(42) {
 LL |         println!("world")
@@ -117,7 +117,7 @@ LL | |         }
 LL | |     }
    | |_____^
    |
-help: try
+help: collapse nested if block
    |
 LL |     } else if x == "hello" {
 LL |         println!("world")
@@ -140,7 +140,7 @@ LL | |         }
 LL | |     }
    | |_____^
    |
-help: try
+help: collapse nested if block
    |
 LL |     } else if let Some(42) = Some(42) {
 LL |         println!("world")
diff --git a/src/tools/clippy/tests/ui/collapsible_if.stderr b/src/tools/clippy/tests/ui/collapsible_if.stderr
index 6440ff41be81e..f56dd65b9dd26 100644
--- a/src/tools/clippy/tests/ui/collapsible_if.stderr
+++ b/src/tools/clippy/tests/ui/collapsible_if.stderr
@@ -9,7 +9,7 @@ LL | |     }
    | |_____^
    |
    = note: `-D clippy::collapsible-if` implied by `-D warnings`
-help: try
+help: collapse nested if block
    |
 LL |     if x == "hello" && y == "world" {
 LL |         println!("Hello world!");
@@ -26,7 +26,7 @@ LL | |         }
 LL | |     }
    | |_____^
    |
-help: try
+help: collapse nested if block
    |
 LL |     if (x == "hello" || x == "world") && (y == "world" || y == "hello") {
 LL |         println!("Hello world!");
@@ -43,7 +43,7 @@ LL | |         }
 LL | |     }
    | |_____^
    |
-help: try
+help: collapse nested if block
    |
 LL |     if x == "hello" && x == "world" && (y == "world" || y == "hello") {
 LL |         println!("Hello world!");
@@ -60,7 +60,7 @@ LL | |         }
 LL | |     }
    | |_____^
    |
-help: try
+help: collapse nested if block
    |
 LL |     if (x == "hello" || x == "world") && y == "world" && y == "hello" {
 LL |         println!("Hello world!");
@@ -77,7 +77,7 @@ LL | |         }
 LL | |     }
    | |_____^
    |
-help: try
+help: collapse nested if block
    |
 LL |     if x == "hello" && x == "world" && y == "world" && y == "hello" {
 LL |         println!("Hello world!");
@@ -94,7 +94,7 @@ LL | |         }
 LL | |     }
    | |_____^
    |
-help: try
+help: collapse nested if block
    |
 LL |     if 42 == 1337 && 'a' != 'A' {
 LL |         println!("world!")
@@ -111,7 +111,7 @@ LL | |         }
 LL | |     }
    | |_____^
    |
-help: try
+help: collapse nested if block
    |
 LL |     if x == "hello" && y == "world" { // Collapsible
 LL |         println!("Hello world!");
diff --git a/src/tools/clippy/tests/ui/deprecated.rs b/src/tools/clippy/tests/ui/deprecated.rs
index 188a641aa1af2..3eefb232780f1 100644
--- a/src/tools/clippy/tests/ui/deprecated.rs
+++ b/src/tools/clippy/tests/ui/deprecated.rs
@@ -7,5 +7,6 @@
 #[warn(clippy::invalid_ref)]
 #[warn(clippy::into_iter_on_array)]
 #[warn(clippy::unused_label)]
+#[warn(clippy::regex_macro)]
 
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/deprecated.stderr b/src/tools/clippy/tests/ui/deprecated.stderr
index a4efe3d15a952..a80e2bf31feb6 100644
--- a/src/tools/clippy/tests/ui/deprecated.stderr
+++ b/src/tools/clippy/tests/ui/deprecated.stderr
@@ -54,11 +54,17 @@ error: lint `clippy::unused_label` has been removed: `this lint has been uplifte
 LL | #[warn(clippy::unused_label)]
    |        ^^^^^^^^^^^^^^^^^^^^
 
+error: lint `clippy::regex_macro` has been removed: `the regex! macro has been removed from the regex crate in 2018`
+  --> $DIR/deprecated.rs:10:8
+   |
+LL | #[warn(clippy::regex_macro)]
+   |        ^^^^^^^^^^^^^^^^^^^
+
 error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon`
   --> $DIR/deprecated.rs:1:8
    |
 LL | #[warn(clippy::str_to_string)]
    |        ^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 10 previous errors
+error: aborting due to 11 previous errors
 
diff --git a/src/tools/clippy/tests/ui/find_map.rs b/src/tools/clippy/tests/ui/find_map.rs
index c28cca144ca3f..88d3b0e749001 100644
--- a/src/tools/clippy/tests/ui/find_map.rs
+++ b/src/tools/clippy/tests/ui/find_map.rs
@@ -19,6 +19,7 @@ fn main() {
 
     let _: Option<i32> = a.iter().find(|s| s.parse::<i32>().is_ok()).map(|s| s.parse().unwrap());
 
+    #[allow(clippy::match_like_matches_macro)]
     let _: Option<Flavor> = desserts_of_the_week
         .iter()
         .find(|dessert| match *dessert {
diff --git a/src/tools/clippy/tests/ui/find_map.stderr b/src/tools/clippy/tests/ui/find_map.stderr
index 92f40fe6f1fb2..f279850fef8af 100644
--- a/src/tools/clippy/tests/ui/find_map.stderr
+++ b/src/tools/clippy/tests/ui/find_map.stderr
@@ -8,7 +8,7 @@ LL |     let _: Option<i32> = a.iter().find(|s| s.parse::<i32>().is_ok()).map(|s
    = help: this is more succinctly expressed by calling `.find_map(..)` instead
 
 error: called `find(p).map(q)` on an `Iterator`
-  --> $DIR/find_map.rs:22:29
+  --> $DIR/find_map.rs:23:29
    |
 LL |       let _: Option<Flavor> = desserts_of_the_week
    |  _____________________________^
diff --git a/src/tools/clippy/tests/ui/floating_point_hypot.fixed b/src/tools/clippy/tests/ui/floating_point_hypot.fixed
new file mode 100644
index 0000000000000..bbe411b3f4884
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_hypot.fixed
@@ -0,0 +1,14 @@
+// run-rustfix
+#![warn(clippy::imprecise_flops)]
+
+fn main() {
+    let x = 3f32;
+    let y = 4f32;
+    let _ = x.hypot(y);
+    let _ = (x + 1f32).hypot(y);
+    let _ = x.hypot(y);
+    // Cases where the lint shouldn't be applied
+    // TODO: linting this adds some complexity, but could be done
+    let _ = x.mul_add(x, y * y).sqrt();
+    let _ = (x * 4f32 + y * y).sqrt();
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_hypot.rs b/src/tools/clippy/tests/ui/floating_point_hypot.rs
new file mode 100644
index 0000000000000..586fd170ea145
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_hypot.rs
@@ -0,0 +1,14 @@
+// run-rustfix
+#![warn(clippy::imprecise_flops)]
+
+fn main() {
+    let x = 3f32;
+    let y = 4f32;
+    let _ = (x * x + y * y).sqrt();
+    let _ = ((x + 1f32) * (x + 1f32) + y * y).sqrt();
+    let _ = (x.powi(2) + y.powi(2)).sqrt();
+    // Cases where the lint shouldn't be applied
+    // TODO: linting this adds some complexity, but could be done
+    let _ = x.mul_add(x, y * y).sqrt();
+    let _ = (x * 4f32 + y * y).sqrt();
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_hypot.stderr b/src/tools/clippy/tests/ui/floating_point_hypot.stderr
new file mode 100644
index 0000000000000..42069d9ee9efb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_hypot.stderr
@@ -0,0 +1,22 @@
+error: hypotenuse can be computed more accurately
+  --> $DIR/floating_point_hypot.rs:7:13
+   |
+LL |     let _ = (x * x + y * y).sqrt();
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)`
+   |
+   = note: `-D clippy::imprecise-flops` implied by `-D warnings`
+
+error: hypotenuse can be computed more accurately
+  --> $DIR/floating_point_hypot.rs:8:13
+   |
+LL |     let _ = ((x + 1f32) * (x + 1f32) + y * y).sqrt();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 1f32).hypot(y)`
+
+error: hypotenuse can be computed more accurately
+  --> $DIR/floating_point_hypot.rs:9:13
+   |
+LL |     let _ = (x.powi(2) + y.powi(2)).sqrt();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/floating_point_log.fixed b/src/tools/clippy/tests/ui/floating_point_log.fixed
index 42c5e5d2bae24..7dc7ee94affc0 100644
--- a/src/tools/clippy/tests/ui/floating_point_log.fixed
+++ b/src/tools/clippy/tests/ui/floating_point_log.fixed
@@ -25,11 +25,11 @@ fn check_ln1p() {
     let _ = 2.0f32.ln_1p();
     let _ = x.ln_1p();
     let _ = (x / 2.0).ln_1p();
-    let _ = x.powi(2).ln_1p();
-    let _ = (x.powi(2) / 2.0).ln_1p();
+    let _ = x.powi(3).ln_1p();
+    let _ = (x.powi(3) / 2.0).ln_1p();
     let _ = ((std::f32::consts::E - 1.0)).ln_1p();
     let _ = x.ln_1p();
-    let _ = x.powi(2).ln_1p();
+    let _ = x.powi(3).ln_1p();
     let _ = (x + 2.0).ln_1p();
     let _ = (x / 2.0).ln_1p();
     // Cases where the lint shouldn't be applied
@@ -43,9 +43,9 @@ fn check_ln1p() {
     let _ = 2.0f64.ln_1p();
     let _ = x.ln_1p();
     let _ = (x / 2.0).ln_1p();
-    let _ = x.powi(2).ln_1p();
+    let _ = x.powi(3).ln_1p();
     let _ = x.ln_1p();
-    let _ = x.powi(2).ln_1p();
+    let _ = x.powi(3).ln_1p();
     let _ = (x + 2.0).ln_1p();
     let _ = (x / 2.0).ln_1p();
     // Cases where the lint shouldn't be applied
diff --git a/src/tools/clippy/tests/ui/floating_point_log.rs b/src/tools/clippy/tests/ui/floating_point_log.rs
index 8be0d9ad56fc3..01181484e7dee 100644
--- a/src/tools/clippy/tests/ui/floating_point_log.rs
+++ b/src/tools/clippy/tests/ui/floating_point_log.rs
@@ -25,11 +25,11 @@ fn check_ln1p() {
     let _ = (1f32 + 2.0).ln();
     let _ = (1.0 + x).ln();
     let _ = (1.0 + x / 2.0).ln();
-    let _ = (1.0 + x.powi(2)).ln();
-    let _ = (1.0 + x.powi(2) / 2.0).ln();
+    let _ = (1.0 + x.powi(3)).ln();
+    let _ = (1.0 + x.powi(3) / 2.0).ln();
     let _ = (1.0 + (std::f32::consts::E - 1.0)).ln();
     let _ = (x + 1.0).ln();
-    let _ = (x.powi(2) + 1.0).ln();
+    let _ = (x.powi(3) + 1.0).ln();
     let _ = (x + 2.0 + 1.0).ln();
     let _ = (x / 2.0 + 1.0).ln();
     // Cases where the lint shouldn't be applied
@@ -43,9 +43,9 @@ fn check_ln1p() {
     let _ = (1f64 + 2.0).ln();
     let _ = (1.0 + x).ln();
     let _ = (1.0 + x / 2.0).ln();
-    let _ = (1.0 + x.powi(2)).ln();
+    let _ = (1.0 + x.powi(3)).ln();
     let _ = (x + 1.0).ln();
-    let _ = (x.powi(2) + 1.0).ln();
+    let _ = (x.powi(3) + 1.0).ln();
     let _ = (x + 2.0 + 1.0).ln();
     let _ = (x / 2.0 + 1.0).ln();
     // Cases where the lint shouldn't be applied
diff --git a/src/tools/clippy/tests/ui/floating_point_log.stderr b/src/tools/clippy/tests/ui/floating_point_log.stderr
index 943fbdb0b8323..900dc2b79336a 100644
--- a/src/tools/clippy/tests/ui/floating_point_log.stderr
+++ b/src/tools/clippy/tests/ui/floating_point_log.stderr
@@ -77,14 +77,14 @@ LL |     let _ = (1.0 + x / 2.0).ln();
 error: ln(1 + x) can be computed more accurately
   --> $DIR/floating_point_log.rs:28:13
    |
-LL |     let _ = (1.0 + x.powi(2)).ln();
-   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()`
+LL |     let _ = (1.0 + x.powi(3)).ln();
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
   --> $DIR/floating_point_log.rs:29:13
    |
-LL |     let _ = (1.0 + x.powi(2) / 2.0).ln();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(2) / 2.0).ln_1p()`
+LL |     let _ = (1.0 + x.powi(3) / 2.0).ln();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(3) / 2.0).ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
   --> $DIR/floating_point_log.rs:30:13
@@ -101,8 +101,8 @@ LL |     let _ = (x + 1.0).ln();
 error: ln(1 + x) can be computed more accurately
   --> $DIR/floating_point_log.rs:32:13
    |
-LL |     let _ = (x.powi(2) + 1.0).ln();
-   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()`
+LL |     let _ = (x.powi(3) + 1.0).ln();
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
   --> $DIR/floating_point_log.rs:33:13
@@ -143,8 +143,8 @@ LL |     let _ = (1.0 + x / 2.0).ln();
 error: ln(1 + x) can be computed more accurately
   --> $DIR/floating_point_log.rs:46:13
    |
-LL |     let _ = (1.0 + x.powi(2)).ln();
-   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()`
+LL |     let _ = (1.0 + x.powi(3)).ln();
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
   --> $DIR/floating_point_log.rs:47:13
@@ -155,8 +155,8 @@ LL |     let _ = (x + 1.0).ln();
 error: ln(1 + x) can be computed more accurately
   --> $DIR/floating_point_log.rs:48:13
    |
-LL |     let _ = (x.powi(2) + 1.0).ln();
-   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()`
+LL |     let _ = (x.powi(3) + 1.0).ln();
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
 
 error: ln(1 + x) can be computed more accurately
   --> $DIR/floating_point_log.rs:49:13
diff --git a/src/tools/clippy/tests/ui/floating_point_logbase.fixed b/src/tools/clippy/tests/ui/floating_point_logbase.fixed
new file mode 100644
index 0000000000000..13962a272d455
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_logbase.fixed
@@ -0,0 +1,16 @@
+// run-rustfix
+#![warn(clippy::suboptimal_flops)]
+
+fn main() {
+    let x = 3f32;
+    let y = 5f32;
+    let _ = x.log(y);
+    let _ = x.log(y);
+    let _ = x.log(y);
+    let _ = x.log(y);
+    // Cases where the lint shouldn't be applied
+    let _ = x.ln() / y.powf(3.2);
+    let _ = x.powf(3.2) / y.powf(3.2);
+    let _ = x.powf(3.2) / y.ln();
+    let _ = x.log(5f32) / y.log(7f32);
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_logbase.rs b/src/tools/clippy/tests/ui/floating_point_logbase.rs
new file mode 100644
index 0000000000000..26bc20d5370b1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_logbase.rs
@@ -0,0 +1,16 @@
+// run-rustfix
+#![warn(clippy::suboptimal_flops)]
+
+fn main() {
+    let x = 3f32;
+    let y = 5f32;
+    let _ = x.ln() / y.ln();
+    let _ = x.log2() / y.log2();
+    let _ = x.log10() / y.log10();
+    let _ = x.log(5f32) / y.log(5f32);
+    // Cases where the lint shouldn't be applied
+    let _ = x.ln() / y.powf(3.2);
+    let _ = x.powf(3.2) / y.powf(3.2);
+    let _ = x.powf(3.2) / y.ln();
+    let _ = x.log(5f32) / y.log(7f32);
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_logbase.stderr b/src/tools/clippy/tests/ui/floating_point_logbase.stderr
new file mode 100644
index 0000000000000..78354c2f62d43
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_logbase.stderr
@@ -0,0 +1,28 @@
+error: log base can be expressed more clearly
+  --> $DIR/floating_point_logbase.rs:7:13
+   |
+LL |     let _ = x.ln() / y.ln();
+   |             ^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
+   |
+   = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
+
+error: log base can be expressed more clearly
+  --> $DIR/floating_point_logbase.rs:8:13
+   |
+LL |     let _ = x.log2() / y.log2();
+   |             ^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
+
+error: log base can be expressed more clearly
+  --> $DIR/floating_point_logbase.rs:9:13
+   |
+LL |     let _ = x.log10() / y.log10();
+   |             ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
+
+error: log base can be expressed more clearly
+  --> $DIR/floating_point_logbase.rs:10:13
+   |
+LL |     let _ = x.log(5f32) / y.log(5f32);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.fixed b/src/tools/clippy/tests/ui/floating_point_mul_add.fixed
index e343c37740da5..911700bab0040 100644
--- a/src/tools/clippy/tests/ui/floating_point_mul_add.fixed
+++ b/src/tools/clippy/tests/ui/floating_point_mul_add.fixed
@@ -18,4 +18,9 @@ fn main() {
 
     let _ = a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c)) + c;
     let _ = 1234.567_f64.mul_add(45.67834_f64, 0.0004_f64);
+
+    let _ = a.mul_add(a, b).sqrt();
+
+    // Cases where the lint shouldn't be applied
+    let _ = (a * a + b * b).sqrt();
 }
diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.rs b/src/tools/clippy/tests/ui/floating_point_mul_add.rs
index 810f929c8568b..d202385fc8ae7 100644
--- a/src/tools/clippy/tests/ui/floating_point_mul_add.rs
+++ b/src/tools/clippy/tests/ui/floating_point_mul_add.rs
@@ -18,4 +18,9 @@ fn main() {
 
     let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c;
     let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64;
+
+    let _ = (a * a + b).sqrt();
+
+    // Cases where the lint shouldn't be applied
+    let _ = (a * a + b * b).sqrt();
 }
diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.stderr b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr
index 2dfbf562d15fc..ac8d0c0cae068 100644
--- a/src/tools/clippy/tests/ui/floating_point_mul_add.stderr
+++ b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr
@@ -54,5 +54,11 @@ error: multiply and add expressions can be calculated more efficiently and accur
 LL |     let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)`
 
-error: aborting due to 9 previous errors
+error: multiply and add expressions can be calculated more efficiently and accurately
+  --> $DIR/floating_point_mul_add.rs:22:13
+   |
+LL |     let _ = (a * a + b).sqrt();
+   |             ^^^^^^^^^^^ help: consider using: `a.mul_add(a, b)`
+
+error: aborting due to 10 previous errors
 
diff --git a/src/tools/clippy/tests/ui/floating_point_powf.fixed b/src/tools/clippy/tests/ui/floating_point_powf.fixed
index 78a9d44829bb1..b0641a100cdc8 100644
--- a/src/tools/clippy/tests/ui/floating_point_powf.fixed
+++ b/src/tools/clippy/tests/ui/floating_point_powf.fixed
@@ -11,7 +11,7 @@ fn main() {
     let _ = (-3.1f32).exp();
     let _ = x.sqrt();
     let _ = x.cbrt();
-    let _ = x.powi(2);
+    let _ = x.powi(3);
     let _ = x.powi(-2);
     let _ = x.powi(16_777_215);
     let _ = x.powi(-16_777_215);
@@ -30,7 +30,7 @@ fn main() {
     let _ = (-3.1f64).exp();
     let _ = x.sqrt();
     let _ = x.cbrt();
-    let _ = x.powi(2);
+    let _ = x.powi(3);
     let _ = x.powi(-2);
     let _ = x.powi(-2_147_483_648);
     let _ = x.powi(2_147_483_647);
diff --git a/src/tools/clippy/tests/ui/floating_point_powf.rs b/src/tools/clippy/tests/ui/floating_point_powf.rs
index dbc1cac5cb431..a0a2c973900f4 100644
--- a/src/tools/clippy/tests/ui/floating_point_powf.rs
+++ b/src/tools/clippy/tests/ui/floating_point_powf.rs
@@ -11,7 +11,7 @@ fn main() {
     let _ = std::f32::consts::E.powf(-3.1);
     let _ = x.powf(1.0 / 2.0);
     let _ = x.powf(1.0 / 3.0);
-    let _ = x.powf(2.0);
+    let _ = x.powf(3.0);
     let _ = x.powf(-2.0);
     let _ = x.powf(16_777_215.0);
     let _ = x.powf(-16_777_215.0);
@@ -30,7 +30,7 @@ fn main() {
     let _ = std::f64::consts::E.powf(-3.1);
     let _ = x.powf(1.0 / 2.0);
     let _ = x.powf(1.0 / 3.0);
-    let _ = x.powf(2.0);
+    let _ = x.powf(3.0);
     let _ = x.powf(-2.0);
     let _ = x.powf(-2_147_483_648.0);
     let _ = x.powf(2_147_483_647.0);
diff --git a/src/tools/clippy/tests/ui/floating_point_powf.stderr b/src/tools/clippy/tests/ui/floating_point_powf.stderr
index ad5163f0079be..2422eb911e90a 100644
--- a/src/tools/clippy/tests/ui/floating_point_powf.stderr
+++ b/src/tools/clippy/tests/ui/floating_point_powf.stderr
@@ -53,8 +53,8 @@ LL |     let _ = x.powf(1.0 / 3.0);
 error: exponentiation with integer powers can be computed more efficiently
   --> $DIR/floating_point_powf.rs:14:13
    |
-LL |     let _ = x.powf(2.0);
-   |             ^^^^^^^^^^^ help: consider using: `x.powi(2)`
+LL |     let _ = x.powf(3.0);
+   |             ^^^^^^^^^^^ help: consider using: `x.powi(3)`
 
 error: exponentiation with integer powers can be computed more efficiently
   --> $DIR/floating_point_powf.rs:15:13
@@ -125,8 +125,8 @@ LL |     let _ = x.powf(1.0 / 3.0);
 error: exponentiation with integer powers can be computed more efficiently
   --> $DIR/floating_point_powf.rs:33:13
    |
-LL |     let _ = x.powf(2.0);
-   |             ^^^^^^^^^^^ help: consider using: `x.powi(2)`
+LL |     let _ = x.powf(3.0);
+   |             ^^^^^^^^^^^ help: consider using: `x.powi(3)`
 
 error: exponentiation with integer powers can be computed more efficiently
   --> $DIR/floating_point_powf.rs:34:13
diff --git a/src/tools/clippy/tests/ui/floating_point_powi.fixed b/src/tools/clippy/tests/ui/floating_point_powi.fixed
new file mode 100644
index 0000000000000..56762400593b5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_powi.fixed
@@ -0,0 +1,19 @@
+// run-rustfix
+#![warn(clippy::suboptimal_flops)]
+
+fn main() {
+    let one = 1;
+    let x = 3f32;
+    let _ = x * x;
+    let _ = x * x;
+
+    let y = 4f32;
+    let _ = x.mul_add(x, y);
+    let _ = y.mul_add(y, x);
+    let _ = x.mul_add(x, y).sqrt();
+    let _ = y.mul_add(y, x).sqrt();
+    // Cases where the lint shouldn't be applied
+    let _ = x.powi(3);
+    let _ = x.powi(one + 1);
+    let _ = (x.powi(2) + y.powi(2)).sqrt();
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_powi.rs b/src/tools/clippy/tests/ui/floating_point_powi.rs
new file mode 100644
index 0000000000000..1f800e4628dca
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_powi.rs
@@ -0,0 +1,19 @@
+// run-rustfix
+#![warn(clippy::suboptimal_flops)]
+
+fn main() {
+    let one = 1;
+    let x = 3f32;
+    let _ = x.powi(2);
+    let _ = x.powi(1 + 1);
+
+    let y = 4f32;
+    let _ = x.powi(2) + y;
+    let _ = x + y.powi(2);
+    let _ = (x.powi(2) + y).sqrt();
+    let _ = (x + y.powi(2)).sqrt();
+    // Cases where the lint shouldn't be applied
+    let _ = x.powi(3);
+    let _ = x.powi(one + 1);
+    let _ = (x.powi(2) + y.powi(2)).sqrt();
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_powi.stderr b/src/tools/clippy/tests/ui/floating_point_powi.stderr
new file mode 100644
index 0000000000000..d5a5f1bcca101
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_powi.stderr
@@ -0,0 +1,40 @@
+error: square can be computed more efficiently
+  --> $DIR/floating_point_powi.rs:7:13
+   |
+LL |     let _ = x.powi(2);
+   |             ^^^^^^^^^ help: consider using: `x * x`
+   |
+   = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
+
+error: square can be computed more efficiently
+  --> $DIR/floating_point_powi.rs:8:13
+   |
+LL |     let _ = x.powi(1 + 1);
+   |             ^^^^^^^^^^^^^ help: consider using: `x * x`
+
+error: square can be computed more efficiently
+  --> $DIR/floating_point_powi.rs:11:13
+   |
+LL |     let _ = x.powi(2) + y;
+   |             ^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)`
+
+error: square can be computed more efficiently
+  --> $DIR/floating_point_powi.rs:12:13
+   |
+LL |     let _ = x + y.powi(2);
+   |             ^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)`
+
+error: square can be computed more efficiently
+  --> $DIR/floating_point_powi.rs:13:13
+   |
+LL |     let _ = (x.powi(2) + y).sqrt();
+   |             ^^^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)`
+
+error: square can be computed more efficiently
+  --> $DIR/floating_point_powi.rs:14:13
+   |
+LL |     let _ = (x + y.powi(2)).sqrt();
+   |             ^^^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/floating_point_rad.fixed b/src/tools/clippy/tests/ui/floating_point_rad.fixed
new file mode 100644
index 0000000000000..92480c5db8be4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_rad.fixed
@@ -0,0 +1,13 @@
+// run-rustfix
+#![warn(clippy::suboptimal_flops)]
+
+fn main() {
+    let x = 3f32;
+    let _ = x.to_degrees();
+    let _ = x.to_radians();
+    // Cases where the lint shouldn't be applied
+    let _ = x * 90f32 / std::f32::consts::PI;
+    let _ = x * std::f32::consts::PI / 90f32;
+    let _ = x * 180f32 / std::f32::consts::E;
+    let _ = x * std::f32::consts::E / 180f32;
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_rad.rs b/src/tools/clippy/tests/ui/floating_point_rad.rs
new file mode 100644
index 0000000000000..062e7c3fdc17a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_rad.rs
@@ -0,0 +1,13 @@
+// run-rustfix
+#![warn(clippy::suboptimal_flops)]
+
+fn main() {
+    let x = 3f32;
+    let _ = x * 180f32 / std::f32::consts::PI;
+    let _ = x * std::f32::consts::PI / 180f32;
+    // Cases where the lint shouldn't be applied
+    let _ = x * 90f32 / std::f32::consts::PI;
+    let _ = x * std::f32::consts::PI / 90f32;
+    let _ = x * 180f32 / std::f32::consts::E;
+    let _ = x * std::f32::consts::E / 180f32;
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_rad.stderr b/src/tools/clippy/tests/ui/floating_point_rad.stderr
new file mode 100644
index 0000000000000..a6ffdca64eefe
--- /dev/null
+++ b/src/tools/clippy/tests/ui/floating_point_rad.stderr
@@ -0,0 +1,16 @@
+error: conversion to degrees can be done more accurately
+  --> $DIR/floating_point_rad.rs:6:13
+   |
+LL |     let _ = x * 180f32 / std::f32::consts::PI;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_degrees()`
+   |
+   = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
+
+error: conversion to radians can be done more accurately
+  --> $DIR/floating_point_rad.rs:7:13
+   |
+LL |     let _ = x * std::f32::consts::PI / 180f32;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_radians()`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/map_flatten.fixed b/src/tools/clippy/tests/ui/map_flatten.fixed
index 7ac368878ab7e..4171d80f48a3f 100644
--- a/src/tools/clippy/tests/ui/map_flatten.fixed
+++ b/src/tools/clippy/tests/ui/map_flatten.fixed
@@ -2,6 +2,7 @@
 
 #![warn(clippy::all, clippy::pedantic)]
 #![allow(clippy::missing_docs_in_private_items)]
+#![allow(clippy::map_identity)]
 
 fn main() {
     let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect();
diff --git a/src/tools/clippy/tests/ui/map_flatten.rs b/src/tools/clippy/tests/ui/map_flatten.rs
index a608601039ce7..16a0fd090ad04 100644
--- a/src/tools/clippy/tests/ui/map_flatten.rs
+++ b/src/tools/clippy/tests/ui/map_flatten.rs
@@ -2,6 +2,7 @@
 
 #![warn(clippy::all, clippy::pedantic)]
 #![allow(clippy::missing_docs_in_private_items)]
+#![allow(clippy::map_identity)]
 
 fn main() {
     let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
diff --git a/src/tools/clippy/tests/ui/map_flatten.stderr b/src/tools/clippy/tests/ui/map_flatten.stderr
index 3cf2abd5b6d85..00bc41c15e9b8 100644
--- a/src/tools/clippy/tests/ui/map_flatten.stderr
+++ b/src/tools/clippy/tests/ui/map_flatten.stderr
@@ -1,5 +1,5 @@
 error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.flat_map(..)`
-  --> $DIR/map_flatten.rs:7:21
+  --> $DIR/map_flatten.rs:8:21
    |
 LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `vec![5_i8; 6].into_iter().flat_map(|x| 0..x)`
@@ -7,7 +7,7 @@ LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().colle
    = note: `-D clippy::map-flatten` implied by `-D warnings`
 
 error: called `map(..).flatten()` on an `Option`. This is more succinctly expressed by calling `.and_then(..)`
-  --> $DIR/map_flatten.rs:8:24
+  --> $DIR/map_flatten.rs:9:24
    |
 LL |     let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `(Some(Some(1))).and_then(|x| x)`
diff --git a/src/tools/clippy/tests/ui/map_identity.fixed b/src/tools/clippy/tests/ui/map_identity.fixed
new file mode 100644
index 0000000000000..4a1452b25f343
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_identity.fixed
@@ -0,0 +1,23 @@
+// run-rustfix
+#![warn(clippy::map_identity)]
+#![allow(clippy::needless_return)]
+
+fn main() {
+    let x: [u16; 3] = [1, 2, 3];
+    // should lint
+    let _: Vec<_> = x.iter().map(not_identity).collect();
+    let _: Vec<_> = x.iter().collect();
+    let _: Option<u8> = Some(3);
+    let _: Result<i8, f32> = Ok(-3);
+    // should not lint
+    let _: Vec<_> = x.iter().map(|x| 2 * x).collect();
+    let _: Vec<_> = x.iter().map(not_identity).map(|x| return x - 4).collect();
+    let _: Option<u8> = None.map(|x: u8| x - 1);
+    let _: Result<i8, f32> = Err(2.3).map(|x: i8| {
+        return x + 3;
+    });
+}
+
+fn not_identity(x: &u16) -> u16 {
+    *x
+}
diff --git a/src/tools/clippy/tests/ui/map_identity.rs b/src/tools/clippy/tests/ui/map_identity.rs
new file mode 100644
index 0000000000000..65c7e6e1ea554
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_identity.rs
@@ -0,0 +1,25 @@
+// run-rustfix
+#![warn(clippy::map_identity)]
+#![allow(clippy::needless_return)]
+
+fn main() {
+    let x: [u16; 3] = [1, 2, 3];
+    // should lint
+    let _: Vec<_> = x.iter().map(not_identity).map(|x| return x).collect();
+    let _: Vec<_> = x.iter().map(std::convert::identity).map(|y| y).collect();
+    let _: Option<u8> = Some(3).map(|x| x);
+    let _: Result<i8, f32> = Ok(-3).map(|x| {
+        return x;
+    });
+    // should not lint
+    let _: Vec<_> = x.iter().map(|x| 2 * x).collect();
+    let _: Vec<_> = x.iter().map(not_identity).map(|x| return x - 4).collect();
+    let _: Option<u8> = None.map(|x: u8| x - 1);
+    let _: Result<i8, f32> = Err(2.3).map(|x: i8| {
+        return x + 3;
+    });
+}
+
+fn not_identity(x: &u16) -> u16 {
+    *x
+}
diff --git a/src/tools/clippy/tests/ui/map_identity.stderr b/src/tools/clippy/tests/ui/map_identity.stderr
new file mode 100644
index 0000000000000..e4a0320cbda55
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_identity.stderr
@@ -0,0 +1,37 @@
+error: unnecessary map of the identity function
+  --> $DIR/map_identity.rs:8:47
+   |
+LL |     let _: Vec<_> = x.iter().map(not_identity).map(|x| return x).collect();
+   |                                               ^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
+   |
+   = note: `-D clippy::map-identity` implied by `-D warnings`
+
+error: unnecessary map of the identity function
+  --> $DIR/map_identity.rs:9:57
+   |
+LL |     let _: Vec<_> = x.iter().map(std::convert::identity).map(|y| y).collect();
+   |                                                         ^^^^^^^^^^^ help: remove the call to `map`
+
+error: unnecessary map of the identity function
+  --> $DIR/map_identity.rs:9:29
+   |
+LL |     let _: Vec<_> = x.iter().map(std::convert::identity).map(|y| y).collect();
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
+
+error: unnecessary map of the identity function
+  --> $DIR/map_identity.rs:10:32
+   |
+LL |     let _: Option<u8> = Some(3).map(|x| x);
+   |                                ^^^^^^^^^^^ help: remove the call to `map`
+
+error: unnecessary map of the identity function
+  --> $DIR/map_identity.rs:11:36
+   |
+LL |       let _: Result<i8, f32> = Ok(-3).map(|x| {
+   |  ____________________________________^
+LL | |         return x;
+LL | |     });
+   | |______^ help: remove the call to `map`
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed
new file mode 100644
index 0000000000000..f3e19092480ad
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed
@@ -0,0 +1,36 @@
+// run-rustfix
+
+#![warn(clippy::match_like_matches_macro)]
+#![allow(unreachable_patterns)]
+
+fn main() {
+    let x = Some(5);
+
+    // Lint
+    let _y = matches!(x, Some(0));
+
+    // Lint
+    let _w = matches!(x, Some(_));
+
+    // Turn into is_none
+    let _z = x.is_none();
+
+    // Lint
+    let _zz = !matches!(x, Some(r) if r == 0);
+
+    // Lint
+    let _zzz = matches!(x, Some(5));
+
+    // No lint
+    let _a = match x {
+        Some(_) => false,
+        _ => false,
+    };
+
+    // No lint
+    let _ab = match x {
+        Some(0) => false,
+        _ => true,
+        None => false,
+    };
+}
diff --git a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs
new file mode 100644
index 0000000000000..fbae7c18b9239
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs
@@ -0,0 +1,48 @@
+// run-rustfix
+
+#![warn(clippy::match_like_matches_macro)]
+#![allow(unreachable_patterns)]
+
+fn main() {
+    let x = Some(5);
+
+    // Lint
+    let _y = match x {
+        Some(0) => true,
+        _ => false,
+    };
+
+    // Lint
+    let _w = match x {
+        Some(_) => true,
+        _ => false,
+    };
+
+    // Turn into is_none
+    let _z = match x {
+        Some(_) => false,
+        None => true,
+    };
+
+    // Lint
+    let _zz = match x {
+        Some(r) if r == 0 => false,
+        _ => true,
+    };
+
+    // Lint
+    let _zzz = if let Some(5) = x { true } else { false };
+
+    // No lint
+    let _a = match x {
+        Some(_) => false,
+        _ => false,
+    };
+
+    // No lint
+    let _ab = match x {
+        Some(0) => false,
+        _ => true,
+        None => false,
+    };
+}
diff --git a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr
new file mode 100644
index 0000000000000..4668f8565a656
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr
@@ -0,0 +1,52 @@
+error: match expression looks like `matches!` macro
+  --> $DIR/match_expr_like_matches_macro.rs:10:14
+   |
+LL |       let _y = match x {
+   |  ______________^
+LL | |         Some(0) => true,
+LL | |         _ => false,
+LL | |     };
+   | |_____^ help: try this: `matches!(x, Some(0))`
+   |
+   = note: `-D clippy::match-like-matches-macro` implied by `-D warnings`
+
+error: match expression looks like `matches!` macro
+  --> $DIR/match_expr_like_matches_macro.rs:16:14
+   |
+LL |       let _w = match x {
+   |  ______________^
+LL | |         Some(_) => true,
+LL | |         _ => false,
+LL | |     };
+   | |_____^ help: try this: `matches!(x, Some(_))`
+
+error: redundant pattern matching, consider using `is_none()`
+  --> $DIR/match_expr_like_matches_macro.rs:22:14
+   |
+LL |       let _z = match x {
+   |  ______________^
+LL | |         Some(_) => false,
+LL | |         None => true,
+LL | |     };
+   | |_____^ help: try this: `x.is_none()`
+   |
+   = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
+
+error: match expression looks like `matches!` macro
+  --> $DIR/match_expr_like_matches_macro.rs:28:15
+   |
+LL |       let _zz = match x {
+   |  _______________^
+LL | |         Some(r) if r == 0 => false,
+LL | |         _ => true,
+LL | |     };
+   | |_____^ help: try this: `!matches!(x, Some(r) if r == 0)`
+
+error: if let .. else expression looks like `matches!` macro
+  --> $DIR/match_expr_like_matches_macro.rs:34:16
+   |
+LL |     let _zzz = if let Some(5) = x { true } else { false };
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))`
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.rs b/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.rs
index 0b47119527247..2d392c593b3e7 100644
--- a/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.rs
+++ b/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.rs
@@ -4,7 +4,7 @@
 
 use std::cmp::Ordering;
 
-#[allow(clippy::unnested_or_patterns)]
+#[allow(clippy::unnested_or_patterns, clippy::match_like_matches_macro)]
 #[warn(clippy::neg_cmp_op_on_partial_ord)]
 fn main() {
     let a_value = 1.0;
diff --git a/src/tools/clippy/tests/ui/option_if_let_else.fixed b/src/tools/clippy/tests/ui/option_if_let_else.fixed
new file mode 100644
index 0000000000000..695a460cc4edf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_if_let_else.fixed
@@ -0,0 +1,74 @@
+// run-rustfix
+#![warn(clippy::option_if_let_else)]
+
+fn bad1(string: Option<&str>) -> (bool, &str) {
+    string.map_or((false, "hello"), |x| (true, x))
+}
+
+fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> {
+    if string.is_none() {
+        None
+    } else { string.map_or(Some((false, "")), |x| Some((true, x))) }
+}
+
+fn unop_bad(string: &Option<&str>, mut num: Option<i32>) {
+    let _ = string.map_or(0, |s| s.len());
+    let _ = num.as_ref().map_or(&0, |s| s);
+    let _ = num.as_mut().map_or(&mut 0, |s| {
+        *s += 1;
+        s
+    });
+    let _ = num.as_ref().map_or(&0, |s| s);
+    let _ = num.map_or(0, |mut s| {
+        s += 1;
+        s
+    });
+    let _ = num.as_mut().map_or(&mut 0, |s| {
+        *s += 1;
+        s
+    });
+}
+
+fn longer_body(arg: Option<u32>) -> u32 {
+    arg.map_or(13, |x| {
+        let y = x * x;
+        y * y
+    })
+}
+
+fn test_map_or_else(arg: Option<u32>) {
+    let _ = arg.map_or_else(|| {
+        let mut y = 1;
+        y = (y + 2 / y) / 2;
+        y = (y + 2 / y) / 2;
+        y
+    }, |x| x * x * x * x);
+}
+
+fn negative_tests(arg: Option<u32>) -> u32 {
+    let _ = if let Some(13) = arg { "unlucky" } else { "lucky" };
+    for _ in 0..10 {
+        let _ = if let Some(x) = arg {
+            x
+        } else {
+            continue;
+        };
+    }
+    let _ = if let Some(x) = arg {
+        return x;
+    } else {
+        5
+    };
+    7
+}
+
+fn main() {
+    let optional = Some(5);
+    let _ = optional.map_or(5, |x| x + 2);
+    let _ = bad1(None);
+    let _ = else_if_option(None);
+    unop_bad(&None, None);
+    let _ = longer_body(None);
+    test_map_or_else(None);
+    let _ = negative_tests(None);
+}
diff --git a/src/tools/clippy/tests/ui/option_if_let_else.rs b/src/tools/clippy/tests/ui/option_if_let_else.rs
new file mode 100644
index 0000000000000..dee80d26bd976
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_if_let_else.rs
@@ -0,0 +1,92 @@
+// run-rustfix
+#![warn(clippy::option_if_let_else)]
+
+fn bad1(string: Option<&str>) -> (bool, &str) {
+    if let Some(x) = string {
+        (true, x)
+    } else {
+        (false, "hello")
+    }
+}
+
+fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> {
+    if string.is_none() {
+        None
+    } else if let Some(x) = string {
+        Some((true, x))
+    } else {
+        Some((false, ""))
+    }
+}
+
+fn unop_bad(string: &Option<&str>, mut num: Option<i32>) {
+    let _ = if let Some(s) = *string { s.len() } else { 0 };
+    let _ = if let Some(s) = &num { s } else { &0 };
+    let _ = if let Some(s) = &mut num {
+        *s += 1;
+        s
+    } else {
+        &mut 0
+    };
+    let _ = if let Some(ref s) = num { s } else { &0 };
+    let _ = if let Some(mut s) = num {
+        s += 1;
+        s
+    } else {
+        0
+    };
+    let _ = if let Some(ref mut s) = num {
+        *s += 1;
+        s
+    } else {
+        &mut 0
+    };
+}
+
+fn longer_body(arg: Option<u32>) -> u32 {
+    if let Some(x) = arg {
+        let y = x * x;
+        y * y
+    } else {
+        13
+    }
+}
+
+fn test_map_or_else(arg: Option<u32>) {
+    let _ = if let Some(x) = arg {
+        x * x * x * x
+    } else {
+        let mut y = 1;
+        y = (y + 2 / y) / 2;
+        y = (y + 2 / y) / 2;
+        y
+    };
+}
+
+fn negative_tests(arg: Option<u32>) -> u32 {
+    let _ = if let Some(13) = arg { "unlucky" } else { "lucky" };
+    for _ in 0..10 {
+        let _ = if let Some(x) = arg {
+            x
+        } else {
+            continue;
+        };
+    }
+    let _ = if let Some(x) = arg {
+        return x;
+    } else {
+        5
+    };
+    7
+}
+
+fn main() {
+    let optional = Some(5);
+    let _ = if let Some(x) = optional { x + 2 } else { 5 };
+    let _ = bad1(None);
+    let _ = else_if_option(None);
+    unop_bad(&None, None);
+    let _ = longer_body(None);
+    test_map_or_else(None);
+    let _ = negative_tests(None);
+}
diff --git a/src/tools/clippy/tests/ui/option_if_let_else.stderr b/src/tools/clippy/tests/ui/option_if_let_else.stderr
new file mode 100644
index 0000000000000..7005850efaf83
--- /dev/null
+++ b/src/tools/clippy/tests/ui/option_if_let_else.stderr
@@ -0,0 +1,151 @@
+error: use Option::map_or instead of an if let/else
+  --> $DIR/option_if_let_else.rs:5:5
+   |
+LL | /     if let Some(x) = string {
+LL | |         (true, x)
+LL | |     } else {
+LL | |         (false, "hello")
+LL | |     }
+   | |_____^ help: try: `string.map_or((false, "hello"), |x| (true, x))`
+   |
+   = note: `-D clippy::option-if-let-else` implied by `-D warnings`
+
+error: use Option::map_or instead of an if let/else
+  --> $DIR/option_if_let_else.rs:15:12
+   |
+LL |       } else if let Some(x) = string {
+   |  ____________^
+LL | |         Some((true, x))
+LL | |     } else {
+LL | |         Some((false, ""))
+LL | |     }
+   | |_____^ help: try: `{ string.map_or(Some((false, "")), |x| Some((true, x))) }`
+
+error: use Option::map_or instead of an if let/else
+  --> $DIR/option_if_let_else.rs:23:13
+   |
+LL |     let _ = if let Some(s) = *string { s.len() } else { 0 };
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())`
+
+error: use Option::map_or instead of an if let/else
+  --> $DIR/option_if_let_else.rs:24:13
+   |
+LL |     let _ = if let Some(s) = &num { s } else { &0 };
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
+
+error: use Option::map_or instead of an if let/else
+  --> $DIR/option_if_let_else.rs:25:13
+   |
+LL |       let _ = if let Some(s) = &mut num {
+   |  _____________^
+LL | |         *s += 1;
+LL | |         s
+LL | |     } else {
+LL | |         &mut 0
+LL | |     };
+   | |_____^
+   |
+help: try
+   |
+LL |     let _ = num.as_mut().map_or(&mut 0, |s| {
+LL |         *s += 1;
+LL |         s
+LL |     });
+   |
+
+error: use Option::map_or instead of an if let/else
+  --> $DIR/option_if_let_else.rs:31:13
+   |
+LL |     let _ = if let Some(ref s) = num { s } else { &0 };
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
+
+error: use Option::map_or instead of an if let/else
+  --> $DIR/option_if_let_else.rs:32:13
+   |
+LL |       let _ = if let Some(mut s) = num {
+   |  _____________^
+LL | |         s += 1;
+LL | |         s
+LL | |     } else {
+LL | |         0
+LL | |     };
+   | |_____^
+   |
+help: try
+   |
+LL |     let _ = num.map_or(0, |mut s| {
+LL |         s += 1;
+LL |         s
+LL |     });
+   |
+
+error: use Option::map_or instead of an if let/else
+  --> $DIR/option_if_let_else.rs:38:13
+   |
+LL |       let _ = if let Some(ref mut s) = num {
+   |  _____________^
+LL | |         *s += 1;
+LL | |         s
+LL | |     } else {
+LL | |         &mut 0
+LL | |     };
+   | |_____^
+   |
+help: try
+   |
+LL |     let _ = num.as_mut().map_or(&mut 0, |s| {
+LL |         *s += 1;
+LL |         s
+LL |     });
+   |
+
+error: use Option::map_or instead of an if let/else
+  --> $DIR/option_if_let_else.rs:47:5
+   |
+LL | /     if let Some(x) = arg {
+LL | |         let y = x * x;
+LL | |         y * y
+LL | |     } else {
+LL | |         13
+LL | |     }
+   | |_____^
+   |
+help: try
+   |
+LL |     arg.map_or(13, |x| {
+LL |         let y = x * x;
+LL |         y * y
+LL |     })
+   |
+
+error: use Option::map_or_else instead of an if let/else
+  --> $DIR/option_if_let_else.rs:56:13
+   |
+LL |       let _ = if let Some(x) = arg {
+   |  _____________^
+LL | |         x * x * x * x
+LL | |     } else {
+LL | |         let mut y = 1;
+...  |
+LL | |         y
+LL | |     };
+   | |_____^
+   |
+help: try
+   |
+LL |     let _ = arg.map_or_else(|| {
+LL |         let mut y = 1;
+LL |         y = (y + 2 / y) / 2;
+LL |         y = (y + 2 / y) / 2;
+LL |         y
+LL |     }, |x| x * x * x * x);
+   |
+
+error: use Option::map_or instead of an if let/else
+  --> $DIR/option_if_let_else.rs:85:13
+   |
+LL |     let _ = if let Some(x) = optional { x + 2 } else { 5 };
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)`
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.rs b/src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.rs
new file mode 100644
index 0000000000000..9b4f2f1f57934
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.rs
@@ -0,0 +1,40 @@
+#![allow(clippy::all)]
+#![warn(clippy::pattern_type_mismatch)]
+
+fn main() {}
+
+fn should_lint() {
+    let value = &Some(23);
+    match value {
+        Some(_) => (),
+        _ => (),
+    }
+
+    let value = &mut Some(23);
+    match value {
+        Some(_) => (),
+        _ => (),
+    }
+}
+
+fn should_not_lint() {
+    let value = &Some(23);
+    match value {
+        &Some(_) => (),
+        _ => (),
+    }
+    match *value {
+        Some(_) => (),
+        _ => (),
+    }
+
+    let value = &mut Some(23);
+    match value {
+        &mut Some(_) => (),
+        _ => (),
+    }
+    match *value {
+        Some(_) => (),
+        _ => (),
+    }
+}
diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.stderr b/src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.stderr
new file mode 100644
index 0000000000000..3421d568365cc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.stderr
@@ -0,0 +1,19 @@
+error: type of pattern does not match the expression type
+  --> $DIR/mutability.rs:9:9
+   |
+LL |         Some(_) => (),
+   |         ^^^^^^^
+   |
+   = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings`
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/mutability.rs:15:9
+   |
+LL |         Some(_) => (),
+   |         ^^^^^^^
+   |
+   = help: use `*` to dereference the match expression or explicitly match against a `&mut _` pattern and adjust the enclosed variable bindings
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.rs b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.rs
new file mode 100644
index 0000000000000..065ea9fb9b5a4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.rs
@@ -0,0 +1,24 @@
+#![allow(clippy::all)]
+#![warn(clippy::pattern_type_mismatch)]
+
+fn main() {}
+
+fn alternatives() {
+    enum Value<'a> {
+        Unused,
+        A(&'a Option<i32>),
+        B,
+    }
+    let ref_value = &Value::A(&Some(23));
+
+    // not ok
+    if let Value::B | Value::A(_) = ref_value {}
+    if let &Value::B | &Value::A(Some(_)) = ref_value {}
+    if let Value::B | Value::A(Some(_)) = *ref_value {}
+
+    // ok
+    if let &Value::B | &Value::A(_) = ref_value {}
+    if let Value::B | Value::A(_) = *ref_value {}
+    if let &Value::B | &Value::A(&Some(_)) = ref_value {}
+    if let Value::B | Value::A(&Some(_)) = *ref_value {}
+}
diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr
new file mode 100644
index 0000000000000..d285c93782c67
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr
@@ -0,0 +1,27 @@
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_alternatives.rs:15:12
+   |
+LL |     if let Value::B | Value::A(_) = ref_value {}
+   |            ^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings`
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_alternatives.rs:16:34
+   |
+LL |     if let &Value::B | &Value::A(Some(_)) = ref_value {}
+   |                                  ^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_alternatives.rs:17:32
+   |
+LL |     if let Value::B | Value::A(Some(_)) = *ref_value {}
+   |                                ^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.rs b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.rs
new file mode 100644
index 0000000000000..417b1c107c55b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.rs
@@ -0,0 +1,45 @@
+#![allow(clippy::all)]
+#![warn(clippy::pattern_type_mismatch)]
+
+fn main() {}
+
+fn struct_types() {
+    struct Struct<'a> {
+        ref_inner: &'a Option<i32>,
+    }
+    let ref_value = &Struct { ref_inner: &Some(42) };
+
+    // not ok
+    let Struct { .. } = ref_value;
+    if let &Struct { ref_inner: Some(_) } = ref_value {}
+    if let Struct { ref_inner: Some(_) } = *ref_value {}
+
+    // ok
+    let &Struct { .. } = ref_value;
+    let Struct { .. } = *ref_value;
+    if let &Struct { ref_inner: &Some(_) } = ref_value {}
+    if let Struct { ref_inner: &Some(_) } = *ref_value {}
+}
+
+fn struct_enum_variants() {
+    enum StructEnum<'a> {
+        Empty,
+        Var { inner_ref: &'a Option<i32> },
+    }
+    let ref_value = &StructEnum::Var { inner_ref: &Some(42) };
+
+    // not ok
+    if let StructEnum::Var { .. } = ref_value {}
+    if let StructEnum::Var { inner_ref: Some(_) } = ref_value {}
+    if let &StructEnum::Var { inner_ref: Some(_) } = ref_value {}
+    if let StructEnum::Var { inner_ref: Some(_) } = *ref_value {}
+    if let StructEnum::Empty = ref_value {}
+
+    // ok
+    if let &StructEnum::Var { .. } = ref_value {}
+    if let StructEnum::Var { .. } = *ref_value {}
+    if let &StructEnum::Var { inner_ref: &Some(_) } = ref_value {}
+    if let StructEnum::Var { inner_ref: &Some(_) } = *ref_value {}
+    if let &StructEnum::Empty = ref_value {}
+    if let StructEnum::Empty = *ref_value {}
+}
diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.stderr b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.stderr
new file mode 100644
index 0000000000000..d428e85b0c914
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_structs.stderr
@@ -0,0 +1,67 @@
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_structs.rs:13:9
+   |
+LL |     let Struct { .. } = ref_value;
+   |         ^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings`
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_structs.rs:14:33
+   |
+LL |     if let &Struct { ref_inner: Some(_) } = ref_value {}
+   |                                 ^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_structs.rs:15:32
+   |
+LL |     if let Struct { ref_inner: Some(_) } = *ref_value {}
+   |                                ^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_structs.rs:32:12
+   |
+LL |     if let StructEnum::Var { .. } = ref_value {}
+   |            ^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_structs.rs:33:12
+   |
+LL |     if let StructEnum::Var { inner_ref: Some(_) } = ref_value {}
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_structs.rs:34:42
+   |
+LL |     if let &StructEnum::Var { inner_ref: Some(_) } = ref_value {}
+   |                                          ^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_structs.rs:35:41
+   |
+LL |     if let StructEnum::Var { inner_ref: Some(_) } = *ref_value {}
+   |                                         ^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_structs.rs:36:12
+   |
+LL |     if let StructEnum::Empty = ref_value {}
+   |            ^^^^^^^^^^^^^^^^^
+   |
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.rs b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.rs
new file mode 100644
index 0000000000000..19504a051d8b1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.rs
@@ -0,0 +1,57 @@
+#![allow(clippy::all)]
+#![warn(clippy::pattern_type_mismatch)]
+
+fn main() {}
+
+fn tuple_types() {
+    struct TupleStruct<'a>(&'a Option<i32>);
+    let ref_value = &TupleStruct(&Some(42));
+
+    // not ok
+    let TupleStruct(_) = ref_value;
+    if let &TupleStruct(Some(_)) = ref_value {}
+    if let TupleStruct(Some(_)) = *ref_value {}
+
+    // ok
+    let &TupleStruct(_) = ref_value;
+    let TupleStruct(_) = *ref_value;
+    if let &TupleStruct(&Some(_)) = ref_value {}
+    if let TupleStruct(&Some(_)) = *ref_value {}
+}
+
+fn tuple_enum_variants() {
+    enum TupleEnum<'a> {
+        Empty,
+        Var(&'a Option<i32>),
+    }
+    let ref_value = &TupleEnum::Var(&Some(42));
+
+    // not ok
+    if let TupleEnum::Var(_) = ref_value {}
+    if let &TupleEnum::Var(Some(_)) = ref_value {}
+    if let TupleEnum::Var(Some(_)) = *ref_value {}
+    if let TupleEnum::Empty = ref_value {}
+
+    // ok
+    if let &TupleEnum::Var(_) = ref_value {}
+    if let TupleEnum::Var(_) = *ref_value {}
+    if let &TupleEnum::Var(&Some(_)) = ref_value {}
+    if let TupleEnum::Var(&Some(_)) = *ref_value {}
+    if let &TupleEnum::Empty = ref_value {}
+    if let TupleEnum::Empty = *ref_value {}
+}
+
+fn plain_tuples() {
+    let ref_value = &(&Some(23), &Some(42));
+
+    // not ok
+    let (_a, _b) = ref_value;
+    if let &(_a, Some(_)) = ref_value {}
+    if let (_a, Some(_)) = *ref_value {}
+
+    // ok
+    let &(_a, _b) = ref_value;
+    let (_a, _b) = *ref_value;
+    if let &(_a, &Some(_)) = ref_value {}
+    if let (_a, &Some(_)) = *ref_value {}
+}
diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.stderr b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.stderr
new file mode 100644
index 0000000000000..edd0074d00d3b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/pattern_tuples.stderr
@@ -0,0 +1,83 @@
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_tuples.rs:11:9
+   |
+LL |     let TupleStruct(_) = ref_value;
+   |         ^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings`
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_tuples.rs:12:25
+   |
+LL |     if let &TupleStruct(Some(_)) = ref_value {}
+   |                         ^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_tuples.rs:13:24
+   |
+LL |     if let TupleStruct(Some(_)) = *ref_value {}
+   |                        ^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_tuples.rs:30:12
+   |
+LL |     if let TupleEnum::Var(_) = ref_value {}
+   |            ^^^^^^^^^^^^^^^^^
+   |
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_tuples.rs:31:28
+   |
+LL |     if let &TupleEnum::Var(Some(_)) = ref_value {}
+   |                            ^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_tuples.rs:32:27
+   |
+LL |     if let TupleEnum::Var(Some(_)) = *ref_value {}
+   |                           ^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_tuples.rs:33:12
+   |
+LL |     if let TupleEnum::Empty = ref_value {}
+   |            ^^^^^^^^^^^^^^^^
+   |
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_tuples.rs:48:9
+   |
+LL |     let (_a, _b) = ref_value;
+   |         ^^^^^^^^
+   |
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_tuples.rs:49:18
+   |
+LL |     if let &(_a, Some(_)) = ref_value {}
+   |                  ^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_tuples.rs:50:17
+   |
+LL |     if let (_a, Some(_)) = *ref_value {}
+   |                 ^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.rs b/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.rs
new file mode 100644
index 0000000000000..e89917c41e8c1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.rs
@@ -0,0 +1,146 @@
+#![allow(clippy::all)]
+#![warn(clippy::pattern_type_mismatch)]
+
+fn main() {}
+
+fn syntax_match() {
+    let ref_value = &Some(&Some(42));
+
+    // not ok
+    match ref_value {
+        Some(_) => (),
+        None => (),
+    }
+
+    // ok
+    match ref_value {
+        &Some(_) => (),
+        &None => (),
+    }
+    match *ref_value {
+        Some(_) => (),
+        None => (),
+    }
+}
+
+fn syntax_if_let() {
+    let ref_value = &Some(42);
+
+    // not ok
+    if let Some(_) = ref_value {}
+
+    // ok
+    if let &Some(_) = ref_value {}
+    if let Some(_) = *ref_value {}
+}
+
+fn syntax_while_let() {
+    let ref_value = &Some(42);
+
+    // not ok
+    while let Some(_) = ref_value {
+        break;
+    }
+
+    // ok
+    while let &Some(_) = ref_value {
+        break;
+    }
+    while let Some(_) = *ref_value {
+        break;
+    }
+}
+
+fn syntax_for() {
+    let ref_value = &Some(23);
+    let slice = &[(2, 3), (4, 2)];
+
+    // not ok
+    for (_a, _b) in slice.iter() {}
+
+    // ok
+    for &(_a, _b) in slice.iter() {}
+}
+
+fn syntax_let() {
+    let ref_value = &(2, 3);
+
+    // not ok
+    let (_n, _m) = ref_value;
+
+    // ok
+    let &(_n, _m) = ref_value;
+    let (_n, _m) = *ref_value;
+}
+
+fn syntax_fn() {
+    // not ok
+    fn foo((_a, _b): &(i32, i32)) {}
+
+    // ok
+    fn foo_ok_1(&(_a, _b): &(i32, i32)) {}
+}
+
+fn syntax_closure() {
+    fn foo<F>(f: F)
+    where
+        F: FnOnce(&(i32, i32)),
+    {
+    }
+
+    // not ok
+    foo(|(_a, _b)| ());
+
+    // ok
+    foo(|&(_a, _b)| ());
+}
+
+fn macro_with_expression() {
+    macro_rules! matching_macro {
+        ($e:expr) => {
+            $e
+        };
+    }
+    let value = &Some(23);
+
+    // not ok
+    matching_macro!(match value {
+        Some(_) => (),
+        _ => (),
+    });
+
+    // ok
+    matching_macro!(match value {
+        &Some(_) => (),
+        _ => (),
+    });
+    matching_macro!(match *value {
+        Some(_) => (),
+        _ => (),
+    });
+}
+
+fn macro_expansion() {
+    macro_rules! matching_macro {
+        ($e:expr) => {
+            // not ok
+            match $e {
+                Some(_) => (),
+                _ => (),
+            }
+
+            // ok
+            match $e {
+                &Some(_) => (),
+                _ => (),
+            }
+            match *$e {
+                Some(_) => (),
+                _ => (),
+            }
+        };
+    }
+
+    let value = &Some(23);
+    matching_macro!(value);
+}
diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.stderr b/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.stderr
new file mode 100644
index 0000000000000..5a5186bd4fcb3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/syntax.stderr
@@ -0,0 +1,79 @@
+error: type of pattern does not match the expression type
+  --> $DIR/syntax.rs:11:9
+   |
+LL |         Some(_) => (),
+   |         ^^^^^^^
+   |
+   = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings`
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/syntax.rs:30:12
+   |
+LL |     if let Some(_) = ref_value {}
+   |            ^^^^^^^
+   |
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/syntax.rs:41:15
+   |
+LL |     while let Some(_) = ref_value {
+   |               ^^^^^^^
+   |
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/syntax.rs:59:9
+   |
+LL |     for (_a, _b) in slice.iter() {}
+   |         ^^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/syntax.rs:69:9
+   |
+LL |     let (_n, _m) = ref_value;
+   |         ^^^^^^^^
+   |
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/syntax.rs:78:12
+   |
+LL |     fn foo((_a, _b): &(i32, i32)) {}
+   |            ^^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/syntax.rs:92:10
+   |
+LL |     foo(|(_a, _b)| ());
+   |          ^^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/syntax.rs:108:9
+   |
+LL |         Some(_) => (),
+   |         ^^^^^^^
+   |
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/syntax.rs:128:17
+   |
+LL |                 Some(_) => (),
+   |                 ^^^^^^^
+...
+LL |     matching_macro!(value);
+   |     ----------------------- in this macro invocation
+   |
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 9 previous errors
+
diff --git a/src/tools/clippy/tests/ui/range_plus_minus_one.fixed b/src/tools/clippy/tests/ui/range_plus_minus_one.fixed
index 6b40211409974..19b253b0fe2c6 100644
--- a/src/tools/clippy/tests/ui/range_plus_minus_one.fixed
+++ b/src/tools/clippy/tests/ui/range_plus_minus_one.fixed
@@ -7,6 +7,7 @@ fn f() -> usize {
 }
 
 #[warn(clippy::range_plus_one)]
+#[warn(clippy::range_minus_one)]
 fn main() {
     for _ in 0..2 {}
     for _ in 0..=2 {}
diff --git a/src/tools/clippy/tests/ui/range_plus_minus_one.rs b/src/tools/clippy/tests/ui/range_plus_minus_one.rs
index 3cfed4125b35c..7d034117547ca 100644
--- a/src/tools/clippy/tests/ui/range_plus_minus_one.rs
+++ b/src/tools/clippy/tests/ui/range_plus_minus_one.rs
@@ -7,6 +7,7 @@ fn f() -> usize {
 }
 
 #[warn(clippy::range_plus_one)]
+#[warn(clippy::range_minus_one)]
 fn main() {
     for _ in 0..2 {}
     for _ in 0..=2 {}
diff --git a/src/tools/clippy/tests/ui/range_plus_minus_one.stderr b/src/tools/clippy/tests/ui/range_plus_minus_one.stderr
index f72943a04f252..fb4f1658597a5 100644
--- a/src/tools/clippy/tests/ui/range_plus_minus_one.stderr
+++ b/src/tools/clippy/tests/ui/range_plus_minus_one.stderr
@@ -1,5 +1,5 @@
 error: an inclusive range would be more readable
-  --> $DIR/range_plus_minus_one.rs:14:14
+  --> $DIR/range_plus_minus_one.rs:15:14
    |
 LL |     for _ in 0..3 + 1 {}
    |              ^^^^^^^^ help: use: `0..=3`
@@ -7,25 +7,25 @@ LL |     for _ in 0..3 + 1 {}
    = note: `-D clippy::range-plus-one` implied by `-D warnings`
 
 error: an inclusive range would be more readable
-  --> $DIR/range_plus_minus_one.rs:17:14
+  --> $DIR/range_plus_minus_one.rs:18:14
    |
 LL |     for _ in 0..1 + 5 {}
    |              ^^^^^^^^ help: use: `0..=5`
 
 error: an inclusive range would be more readable
-  --> $DIR/range_plus_minus_one.rs:20:14
+  --> $DIR/range_plus_minus_one.rs:21:14
    |
 LL |     for _ in 1..1 + 1 {}
    |              ^^^^^^^^ help: use: `1..=1`
 
 error: an inclusive range would be more readable
-  --> $DIR/range_plus_minus_one.rs:26:14
+  --> $DIR/range_plus_minus_one.rs:27:14
    |
 LL |     for _ in 0..(1 + f()) {}
    |              ^^^^^^^^^^^^ help: use: `0..=f()`
 
 error: an exclusive range would be more readable
-  --> $DIR/range_plus_minus_one.rs:30:13
+  --> $DIR/range_plus_minus_one.rs:31:13
    |
 LL |     let _ = ..=11 - 1;
    |             ^^^^^^^^^ help: use: `..11`
@@ -33,25 +33,25 @@ LL |     let _ = ..=11 - 1;
    = note: `-D clippy::range-minus-one` implied by `-D warnings`
 
 error: an exclusive range would be more readable
-  --> $DIR/range_plus_minus_one.rs:31:13
+  --> $DIR/range_plus_minus_one.rs:32:13
    |
 LL |     let _ = ..=(11 - 1);
    |             ^^^^^^^^^^^ help: use: `..11`
 
 error: an inclusive range would be more readable
-  --> $DIR/range_plus_minus_one.rs:32:13
+  --> $DIR/range_plus_minus_one.rs:33:13
    |
 LL |     let _ = (1..11 + 1);
    |             ^^^^^^^^^^^ help: use: `(1..=11)`
 
 error: an inclusive range would be more readable
-  --> $DIR/range_plus_minus_one.rs:33:13
+  --> $DIR/range_plus_minus_one.rs:34:13
    |
 LL |     let _ = (f() + 1)..(f() + 1);
    |             ^^^^^^^^^^^^^^^^^^^^ help: use: `((f() + 1)..=f())`
 
 error: an inclusive range would be more readable
-  --> $DIR/range_plus_minus_one.rs:37:14
+  --> $DIR/range_plus_minus_one.rs:38:14
    |
 LL |     for _ in 1..ONE + ONE {}
    |              ^^^^^^^^^^^^ help: use: `1..=ONE`
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching.fixed
index 8b4e2d21331cd..ce8582d2b221c 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching.fixed
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching.fixed
@@ -2,7 +2,13 @@
 
 #![warn(clippy::all)]
 #![warn(clippy::redundant_pattern_matching)]
-#![allow(clippy::unit_arg, unused_must_use, clippy::needless_bool, deprecated)]
+#![allow(
+    clippy::unit_arg,
+    unused_must_use,
+    clippy::needless_bool,
+    clippy::match_like_matches_macro,
+    deprecated
+)]
 
 fn main() {
     if Ok::<i32, i32>(42).is_ok() {}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching.rs
index b0904e41b6f43..a3a9aa40e3b9c 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching.rs
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching.rs
@@ -2,7 +2,13 @@
 
 #![warn(clippy::all)]
 #![warn(clippy::redundant_pattern_matching)]
-#![allow(clippy::unit_arg, unused_must_use, clippy::needless_bool, deprecated)]
+#![allow(
+    clippy::unit_arg,
+    unused_must_use,
+    clippy::needless_bool,
+    clippy::match_like_matches_macro,
+    deprecated
+)]
 
 fn main() {
     if let Ok(_) = Ok::<i32, i32>(42) {}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching.stderr
index 51a6f4350d32c..25d1476062e7f 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching.stderr
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching.stderr
@@ -1,5 +1,5 @@
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching.rs:8:12
+  --> $DIR/redundant_pattern_matching.rs:14:12
    |
 LL |     if let Ok(_) = Ok::<i32, i32>(42) {}
    |     -------^^^^^--------------------- help: try this: `if Ok::<i32, i32>(42).is_ok()`
@@ -7,67 +7,67 @@ LL |     if let Ok(_) = Ok::<i32, i32>(42) {}
    = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching.rs:10:12
+  --> $DIR/redundant_pattern_matching.rs:16:12
    |
 LL |     if let Err(_) = Err::<i32, i32>(42) {}
    |     -------^^^^^^---------------------- help: try this: `if Err::<i32, i32>(42).is_err()`
 
 error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching.rs:12:12
+  --> $DIR/redundant_pattern_matching.rs:18:12
    |
 LL |     if let None = None::<()> {}
    |     -------^^^^------------- help: try this: `if None::<()>.is_none()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:14:12
+  --> $DIR/redundant_pattern_matching.rs:20:12
    |
 LL |     if let Some(_) = Some(42) {}
    |     -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:16:12
+  --> $DIR/redundant_pattern_matching.rs:22:12
    |
 LL |     if let Some(_) = Some(42) {
    |     -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:22:15
+  --> $DIR/redundant_pattern_matching.rs:28:15
    |
 LL |     while let Some(_) = Some(42) {}
    |     ----------^^^^^^^----------- help: try this: `while Some(42).is_some()`
 
 error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching.rs:24:15
+  --> $DIR/redundant_pattern_matching.rs:30:15
    |
 LL |     while let None = Some(42) {}
    |     ----------^^^^----------- help: try this: `while Some(42).is_none()`
 
 error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching.rs:26:15
+  --> $DIR/redundant_pattern_matching.rs:32:15
    |
 LL |     while let None = None::<()> {}
    |     ----------^^^^------------- help: try this: `while None::<()>.is_none()`
 
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching.rs:28:15
+  --> $DIR/redundant_pattern_matching.rs:34:15
    |
 LL |     while let Ok(_) = Ok::<i32, i32>(10) {}
    |     ----------^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_ok()`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching.rs:30:15
+  --> $DIR/redundant_pattern_matching.rs:36:15
    |
 LL |     while let Err(_) = Ok::<i32, i32>(10) {}
    |     ----------^^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_err()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:33:15
+  --> $DIR/redundant_pattern_matching.rs:39:15
    |
 LL |     while let Some(_) = v.pop() {
    |     ----------^^^^^^^---------- help: try this: `while v.pop().is_some()`
 
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching.rs:49:5
+  --> $DIR/redundant_pattern_matching.rs:55:5
    |
 LL | /     match Ok::<i32, i32>(42) {
 LL | |         Ok(_) => true,
@@ -76,7 +76,7 @@ LL | |     };
    | |_____^ help: try this: `Ok::<i32, i32>(42).is_ok()`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching.rs:54:5
+  --> $DIR/redundant_pattern_matching.rs:60:5
    |
 LL | /     match Ok::<i32, i32>(42) {
 LL | |         Ok(_) => false,
@@ -85,7 +85,7 @@ LL | |     };
    | |_____^ help: try this: `Ok::<i32, i32>(42).is_err()`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching.rs:59:5
+  --> $DIR/redundant_pattern_matching.rs:65:5
    |
 LL | /     match Err::<i32, i32>(42) {
 LL | |         Ok(_) => false,
@@ -94,7 +94,7 @@ LL | |     };
    | |_____^ help: try this: `Err::<i32, i32>(42).is_err()`
 
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching.rs:64:5
+  --> $DIR/redundant_pattern_matching.rs:70:5
    |
 LL | /     match Err::<i32, i32>(42) {
 LL | |         Ok(_) => true,
@@ -103,7 +103,7 @@ LL | |     };
    | |_____^ help: try this: `Err::<i32, i32>(42).is_ok()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:69:5
+  --> $DIR/redundant_pattern_matching.rs:75:5
    |
 LL | /     match Some(42) {
 LL | |         Some(_) => true,
@@ -112,7 +112,7 @@ LL | |     };
    | |_____^ help: try this: `Some(42).is_some()`
 
 error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching.rs:74:5
+  --> $DIR/redundant_pattern_matching.rs:80:5
    |
 LL | /     match None::<()> {
 LL | |         Some(_) => false,
@@ -121,7 +121,7 @@ LL | |     };
    | |_____^ help: try this: `None::<()>.is_none()`
 
 error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching.rs:79:13
+  --> $DIR/redundant_pattern_matching.rs:85:13
    |
 LL |       let _ = match None::<()> {
    |  _____________^
@@ -131,61 +131,61 @@ LL | |     };
    | |_____^ help: try this: `None::<()>.is_none()`
 
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching.rs:84:20
+  --> $DIR/redundant_pattern_matching.rs:90:20
    |
 LL |     let _ = if let Ok(_) = Ok::<usize, ()>(4) { true } else { false };
    |             -------^^^^^--------------------- help: try this: `if Ok::<usize, ()>(4).is_ok()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:87:20
+  --> $DIR/redundant_pattern_matching.rs:93:20
    |
 LL |     let x = if let Some(_) = opt { true } else { false };
    |             -------^^^^^^^------ help: try this: `if opt.is_some()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:93:20
+  --> $DIR/redundant_pattern_matching.rs:99:20
    |
 LL |     let _ = if let Some(_) = gen_opt() {
    |             -------^^^^^^^------------ help: try this: `if gen_opt().is_some()`
 
 error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching.rs:95:19
+  --> $DIR/redundant_pattern_matching.rs:101:19
    |
 LL |     } else if let None = gen_opt() {
    |            -------^^^^------------ help: try this: `if gen_opt().is_none()`
 
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching.rs:97:19
+  --> $DIR/redundant_pattern_matching.rs:103:19
    |
 LL |     } else if let Ok(_) = gen_res() {
    |            -------^^^^^------------ help: try this: `if gen_res().is_ok()`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching.rs:99:19
+  --> $DIR/redundant_pattern_matching.rs:105:19
    |
 LL |     } else if let Err(_) = gen_res() {
    |            -------^^^^^^------------ help: try this: `if gen_res().is_err()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:132:19
+  --> $DIR/redundant_pattern_matching.rs:138:19
    |
 LL |         while let Some(_) = r#try!(result_opt()) {}
    |         ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:133:16
+  --> $DIR/redundant_pattern_matching.rs:139:16
    |
 LL |         if let Some(_) = r#try!(result_opt()) {}
    |         -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:139:12
+  --> $DIR/redundant_pattern_matching.rs:145:12
    |
 LL |     if let Some(_) = m!() {}
    |     -------^^^^^^^------- help: try this: `if m!().is_some()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:140:15
+  --> $DIR/redundant_pattern_matching.rs:146:15
    |
 LL |     while let Some(_) = m!() {}
    |     ----------^^^^^^^------- help: try this: `while m!().is_some()`
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_const_result.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_const_result.fixed
index 8a81e92f04a73..de3fe00d5fa68 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_const_result.fixed
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_const_result.fixed
@@ -2,7 +2,7 @@
 
 #![feature(const_result)]
 #![warn(clippy::redundant_pattern_matching)]
-#![allow(unused)]
+#![allow(clippy::match_like_matches_macro, unused)]
 
 // Test that results are linted with the feature enabled.
 
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_const_result.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_const_result.rs
index 1cd515441d13a..b77969d53d92d 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_const_result.rs
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_const_result.rs
@@ -2,7 +2,7 @@
 
 #![feature(const_result)]
 #![warn(clippy::redundant_pattern_matching)]
-#![allow(unused)]
+#![allow(clippy::match_like_matches_macro, unused)]
 
 // Test that results are linted with the feature enabled.
 
diff --git a/src/tools/clippy/tests/ui/regex.rs b/src/tools/clippy/tests/ui/regex.rs
index b523fa5b711ae..9767e5bf76a85 100644
--- a/src/tools/clippy/tests/ui/regex.rs
+++ b/src/tools/clippy/tests/ui/regex.rs
@@ -1,5 +1,5 @@
 #![allow(unused)]
-#![warn(clippy::invalid_regex, clippy::trivial_regex, clippy::regex_macro)]
+#![warn(clippy::invalid_regex, clippy::trivial_regex)]
 
 extern crate regex;
 
diff --git a/src/tools/clippy/tests/ui/repeat_once.fixed b/src/tools/clippy/tests/ui/repeat_once.fixed
new file mode 100644
index 0000000000000..a637c22fbcd26
--- /dev/null
+++ b/src/tools/clippy/tests/ui/repeat_once.fixed
@@ -0,0 +1,16 @@
+// run-rustfix
+#![warn(clippy::repeat_once)]
+#[allow(unused, clippy::many_single_char_names, clippy::redundant_clone)]
+fn main() {
+    const N: usize = 1;
+    let s = "str";
+    let string = "String".to_string();
+    let slice = [1; 5];
+
+    let a = [1; 5].to_vec();
+    let b = slice.to_vec();
+    let c = "hello".to_string();
+    let d = "hi".to_string();
+    let e = s.to_string();
+    let f = string.clone();
+}
diff --git a/src/tools/clippy/tests/ui/repeat_once.rs b/src/tools/clippy/tests/ui/repeat_once.rs
new file mode 100644
index 0000000000000..d99ca1b5b55d4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/repeat_once.rs
@@ -0,0 +1,16 @@
+// run-rustfix
+#![warn(clippy::repeat_once)]
+#[allow(unused, clippy::many_single_char_names, clippy::redundant_clone)]
+fn main() {
+    const N: usize = 1;
+    let s = "str";
+    let string = "String".to_string();
+    let slice = [1; 5];
+
+    let a = [1; 5].repeat(1);
+    let b = slice.repeat(1);
+    let c = "hello".repeat(N);
+    let d = "hi".repeat(1);
+    let e = s.repeat(1);
+    let f = string.repeat(1);
+}
diff --git a/src/tools/clippy/tests/ui/repeat_once.stderr b/src/tools/clippy/tests/ui/repeat_once.stderr
new file mode 100644
index 0000000000000..915eea3bfc6b8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/repeat_once.stderr
@@ -0,0 +1,40 @@
+error: calling `repeat(1)` on slice
+  --> $DIR/repeat_once.rs:10:13
+   |
+LL |     let a = [1; 5].repeat(1);
+   |             ^^^^^^^^^^^^^^^^ help: consider using `.to_vec()` instead: `[1; 5].to_vec()`
+   |
+   = note: `-D clippy::repeat-once` implied by `-D warnings`
+
+error: calling `repeat(1)` on slice
+  --> $DIR/repeat_once.rs:11:13
+   |
+LL |     let b = slice.repeat(1);
+   |             ^^^^^^^^^^^^^^^ help: consider using `.to_vec()` instead: `slice.to_vec()`
+
+error: calling `repeat(1)` on str
+  --> $DIR/repeat_once.rs:12:13
+   |
+LL |     let c = "hello".repeat(N);
+   |             ^^^^^^^^^^^^^^^^^ help: consider using `.to_string()` instead: `"hello".to_string()`
+
+error: calling `repeat(1)` on str
+  --> $DIR/repeat_once.rs:13:13
+   |
+LL |     let d = "hi".repeat(1);
+   |             ^^^^^^^^^^^^^^ help: consider using `.to_string()` instead: `"hi".to_string()`
+
+error: calling `repeat(1)` on str
+  --> $DIR/repeat_once.rs:14:13
+   |
+LL |     let e = s.repeat(1);
+   |             ^^^^^^^^^^^ help: consider using `.to_string()` instead: `s.to_string()`
+
+error: calling `repeat(1)` on a string literal
+  --> $DIR/repeat_once.rs:15:13
+   |
+LL |     let f = string.repeat(1);
+   |             ^^^^^^^^^^^^^^^^ help: consider using `.clone()` instead: `string.clone()`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/single_match_else.rs b/src/tools/clippy/tests/ui/single_match_else.rs
index 34193be0b75e4..b624a41a29b2d 100644
--- a/src/tools/clippy/tests/ui/single_match_else.rs
+++ b/src/tools/clippy/tests/ui/single_match_else.rs
@@ -1,4 +1,6 @@
 #![warn(clippy::single_match_else)]
+#![allow(clippy::needless_return)]
+#![allow(clippy::no_effect)]
 
 enum ExprNode {
     ExprAddrOf,
@@ -30,6 +32,55 @@ macro_rules! unwrap_addr {
     };
 }
 
+#[rustfmt::skip]
 fn main() {
     unwrap_addr!(ExprNode::Unicorns);
+
+    //
+    // don't lint single exprs/statements
+    //
+
+    // don't lint here
+    match Some(1) {
+        Some(a) => println!("${:?}", a),
+        None => return,
+    }
+
+    // don't lint here
+    match Some(1) {
+        Some(a) => println!("${:?}", a),
+        None => {
+            return
+        },
+    }
+
+    // don't lint here
+    match Some(1) {
+        Some(a) => println!("${:?}", a),
+        None => {
+            return;
+        },
+    }
+
+    //
+    // lint multiple exprs/statements "else" blocks
+    //
+
+    // lint here
+    match Some(1) {
+        Some(a) => println!("${:?}", a),
+        None => {
+            println!("else block");
+            return
+        },
+    }
+
+    // lint here
+    match Some(1) {
+        Some(a) => println!("${:?}", a),
+        None => {
+            println!("else block");
+            return;
+        },
+    }
 }
diff --git a/src/tools/clippy/tests/ui/single_match_else.stderr b/src/tools/clippy/tests/ui/single_match_else.stderr
index 59861d46eb34c..3a07c2ec54262 100644
--- a/src/tools/clippy/tests/ui/single_match_else.stderr
+++ b/src/tools/clippy/tests/ui/single_match_else.stderr
@@ -1,5 +1,5 @@
 error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let`
-  --> $DIR/single_match_else.rs:12:5
+  --> $DIR/single_match_else.rs:14:5
    |
 LL | /     match ExprNode::Butterflies {
 LL | |         ExprNode::ExprAddrOf => Some(&NODE),
@@ -19,5 +19,45 @@ LL |         None
 LL |     }
    |
 
-error: aborting due to previous error
+error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let`
+  --> $DIR/single_match_else.rs:70:5
+   |
+LL | /     match Some(1) {
+LL | |         Some(a) => println!("${:?}", a),
+LL | |         None => {
+LL | |             println!("else block");
+LL | |             return
+LL | |         },
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     if let Some(a) = Some(1) { println!("${:?}", a) } else {
+LL |         println!("else block");
+LL |         return
+LL |     }
+   |
+
+error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let`
+  --> $DIR/single_match_else.rs:79:5
+   |
+LL | /     match Some(1) {
+LL | |         Some(a) => println!("${:?}", a),
+LL | |         None => {
+LL | |             println!("else block");
+LL | |             return;
+LL | |         },
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     if let Some(a) = Some(1) { println!("${:?}", a) } else {
+LL |         println!("else block");
+LL |         return;
+LL |     }
+   |
+
+error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs
index 8b538be762b0c..766190f209977 100644
--- a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs
+++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs
@@ -1,4 +1,6 @@
-#[deny(clippy::type_repetition_in_bounds)]
+#![deny(clippy::type_repetition_in_bounds)]
+
+use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
 
 pub fn foo<T>(_t: T)
 where
@@ -16,4 +18,55 @@ where
     unimplemented!();
 }
 
+// Threshold test (see #4380)
+trait LintBounds
+where
+    Self: Clone,
+    Self: Copy + Default + Ord,
+    Self: Add<Output = Self> + AddAssign + Sub<Output = Self> + SubAssign,
+    Self: Mul<Output = Self> + MulAssign + Div<Output = Self> + DivAssign,
+{
+}
+
+trait LotsOfBounds
+where
+    Self: Clone + Copy + Default + Ord,
+    Self: Add<Output = Self> + AddAssign + Sub<Output = Self> + SubAssign,
+    Self: Mul<Output = Self> + MulAssign + Div<Output = Self> + DivAssign,
+{
+}
+
+// Generic distinction (see #4323)
+mod issue4323 {
+    pub struct Foo<A>(A);
+    pub struct Bar<A, B> {
+        a: Foo<A>,
+        b: Foo<B>,
+    }
+
+    impl<A, B> Unpin for Bar<A, B>
+    where
+        Foo<A>: Unpin,
+        Foo<B>: Unpin,
+    {
+    }
+}
+
+// Extern macros shouldn't lint (see #4326)
+extern crate serde;
+mod issue4326 {
+    use serde::{Deserialize, Serialize};
+
+    trait Foo {}
+    impl Foo for String {}
+
+    #[derive(Debug, Serialize, Deserialize)]
+    struct Bar<S>
+    where
+        S: Foo,
+    {
+        foo: S,
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr b/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr
index 4264e2e10bf17..148c19c7d0701 100644
--- a/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr
+++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr
@@ -1,15 +1,23 @@
 error: this type has already been used as a bound predicate
-  --> $DIR/type_repetition_in_bounds.rs:6:5
+  --> $DIR/type_repetition_in_bounds.rs:8:5
    |
 LL |     T: Clone,
    |     ^^^^^^^^
    |
 note: the lint level is defined here
-  --> $DIR/type_repetition_in_bounds.rs:1:8
+  --> $DIR/type_repetition_in_bounds.rs:1:9
    |
-LL | #[deny(clippy::type_repetition_in_bounds)]
-   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | #![deny(clippy::type_repetition_in_bounds)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: consider combining the bounds: `T: Copy + Clone`
 
-error: aborting due to previous error
+error: this type has already been used as a bound predicate
+  --> $DIR/type_repetition_in_bounds.rs:25:5
+   |
+LL |     Self: Copy + Default + Ord,
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord`
+
+error: aborting due to 2 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_clone.rs b/src/tools/clippy/tests/ui/unnecessary_clone.rs
index f1cc5b564c1dc..2c9d4d39e6c7d 100644
--- a/src/tools/clippy/tests/ui/unnecessary_clone.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_clone.rs
@@ -13,31 +13,6 @@ impl SomeTrait for SomeImpl {}
 
 fn main() {}
 
-fn is_ascii(ch: char) -> bool {
-    ch.is_ascii()
-}
-
-fn clone_on_copy() {
-    42.clone();
-
-    vec![1].clone(); // ok, not a Copy type
-    Some(vec![1]).clone(); // ok, not a Copy type
-    (&42).clone();
-
-    let rc = RefCell::new(0);
-    rc.borrow().clone();
-
-    // Issue #4348
-    let mut x = 43;
-    let _ = &x.clone(); // ok, getting a ref
-    'a'.clone().make_ascii_uppercase(); // ok, clone and then mutate
-    is_ascii('z'.clone());
-
-    // Issue #5436
-    let mut vec = Vec::new();
-    vec.push(42.clone());
-}
-
 fn clone_on_ref_ptr() {
     let rc = Rc::new(true);
     let arc = Arc::new(true);
diff --git a/src/tools/clippy/tests/ui/unnecessary_clone.stderr b/src/tools/clippy/tests/ui/unnecessary_clone.stderr
index 6176a2bc46479..113fab6900954 100644
--- a/src/tools/clippy/tests/ui/unnecessary_clone.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_clone.stderr
@@ -1,37 +1,5 @@
-error: using `clone` on a `Copy` type
-  --> $DIR/unnecessary_clone.rs:21:5
-   |
-LL |     42.clone();
-   |     ^^^^^^^^^^ help: try removing the `clone` call: `42`
-   |
-   = note: `-D clippy::clone-on-copy` implied by `-D warnings`
-
-error: using `clone` on a `Copy` type
-  --> $DIR/unnecessary_clone.rs:25:5
-   |
-LL |     (&42).clone();
-   |     ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)`
-
-error: using `clone` on a `Copy` type
-  --> $DIR/unnecessary_clone.rs:28:5
-   |
-LL |     rc.borrow().clone();
-   |     ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()`
-
-error: using `clone` on a `Copy` type
-  --> $DIR/unnecessary_clone.rs:34:14
-   |
-LL |     is_ascii('z'.clone());
-   |              ^^^^^^^^^^^ help: try removing the `clone` call: `'z'`
-
-error: using `clone` on a `Copy` type
-  --> $DIR/unnecessary_clone.rs:38:14
-   |
-LL |     vec.push(42.clone());
-   |              ^^^^^^^^^^ help: try removing the `clone` call: `42`
-
 error: using `.clone()` on a ref-counted pointer
-  --> $DIR/unnecessary_clone.rs:48:5
+  --> $DIR/unnecessary_clone.rs:23:5
    |
 LL |     rc.clone();
    |     ^^^^^^^^^^ help: try this: `Rc::<bool>::clone(&rc)`
@@ -39,43 +7,45 @@ LL |     rc.clone();
    = note: `-D clippy::clone-on-ref-ptr` implied by `-D warnings`
 
 error: using `.clone()` on a ref-counted pointer
-  --> $DIR/unnecessary_clone.rs:51:5
+  --> $DIR/unnecessary_clone.rs:26:5
    |
 LL |     arc.clone();
    |     ^^^^^^^^^^^ help: try this: `Arc::<bool>::clone(&arc)`
 
 error: using `.clone()` on a ref-counted pointer
-  --> $DIR/unnecessary_clone.rs:54:5
+  --> $DIR/unnecessary_clone.rs:29:5
    |
 LL |     rcweak.clone();
    |     ^^^^^^^^^^^^^^ help: try this: `Weak::<bool>::clone(&rcweak)`
 
 error: using `.clone()` on a ref-counted pointer
-  --> $DIR/unnecessary_clone.rs:57:5
+  --> $DIR/unnecessary_clone.rs:32:5
    |
 LL |     arc_weak.clone();
    |     ^^^^^^^^^^^^^^^^ help: try this: `Weak::<bool>::clone(&arc_weak)`
 
 error: using `.clone()` on a ref-counted pointer
-  --> $DIR/unnecessary_clone.rs:61:33
+  --> $DIR/unnecessary_clone.rs:36:33
    |
 LL |     let _: Arc<dyn SomeTrait> = x.clone();
    |                                 ^^^^^^^^^ help: try this: `Arc::<SomeImpl>::clone(&x)`
 
 error: using `clone` on a `Copy` type
-  --> $DIR/unnecessary_clone.rs:65:5
+  --> $DIR/unnecessary_clone.rs:40:5
    |
 LL |     t.clone();
    |     ^^^^^^^^^ help: try removing the `clone` call: `t`
+   |
+   = note: `-D clippy::clone-on-copy` implied by `-D warnings`
 
 error: using `clone` on a `Copy` type
-  --> $DIR/unnecessary_clone.rs:67:5
+  --> $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
-  --> $DIR/unnecessary_clone.rs:73:22
+  --> $DIR/unnecessary_clone.rs:48:22
    |
 LL |     let z: &Vec<_> = y.clone();
    |                      ^^^^^^^^^
@@ -91,13 +61,13 @@ LL |     let z: &Vec<_> = <&std::vec::Vec<i32>>::clone(y);
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: using `clone` on a `Copy` type
-  --> $DIR/unnecessary_clone.rs:109:20
+  --> $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
-  --> $DIR/unnecessary_clone.rs:114:22
+  --> $DIR/unnecessary_clone.rs:89:22
    |
 LL |         let _ = &mut encoded.clone();
    |                      ^^^^^^^^^^^^^^^
@@ -112,7 +82,7 @@ LL |         let _ = &mut <&[u8]>::clone(encoded);
    |                      ^^^^^^^^^^^^^^^^^^^^^^^
 
 error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type
-  --> $DIR/unnecessary_clone.rs:115:18
+  --> $DIR/unnecessary_clone.rs:90:18
    |
 LL |         let _ = &encoded.clone();
    |                  ^^^^^^^^^^^^^^^
@@ -126,5 +96,5 @@ help: or try being explicit if you are sure, that you want to clone a reference
 LL |         let _ = &<&[u8]>::clone(encoded);
    |                  ^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 16 previous errors
+error: aborting due to 11 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed b/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed
index 779fd57707ad4..c017d1cf9a468 100644
--- a/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed
@@ -2,11 +2,11 @@
 
 use std::cmp::Reverse;
 
-fn id(x: isize) -> isize {
-    x
-}
+fn unnecessary_sort_by() {
+    fn id(x: isize) -> isize {
+        x
+    }
 
-fn main() {
     let mut vec: Vec<isize> = vec![3, 6, 1, 2, 5];
     // Forward examples
     vec.sort();
@@ -24,3 +24,41 @@ fn main() {
     vec.sort_by(|_, b| b.cmp(c));
     vec.sort_unstable_by(|a, _| a.cmp(c));
 }
+
+// Should not be linted to avoid hitting https://github.com/rust-lang/rust/issues/34162
+mod issue_5754 {
+    struct Test(String);
+
+    #[derive(PartialOrd, Ord, PartialEq, Eq)]
+    struct Wrapper<'a>(&'a str);
+
+    impl Test {
+        fn name(&self) -> &str {
+            &self.0
+        }
+
+        fn wrapped(&self) -> Wrapper<'_> {
+            Wrapper(&self.0)
+        }
+    }
+
+    pub fn test() {
+        let mut args: Vec<Test> = vec![];
+
+        // Forward
+        args.sort_by(|a, b| a.name().cmp(b.name()));
+        args.sort_by(|a, b| a.wrapped().cmp(&b.wrapped()));
+        args.sort_unstable_by(|a, b| a.name().cmp(b.name()));
+        args.sort_unstable_by(|a, b| a.wrapped().cmp(&b.wrapped()));
+        // Reverse
+        args.sort_by(|a, b| b.name().cmp(a.name()));
+        args.sort_by(|a, b| b.wrapped().cmp(&a.wrapped()));
+        args.sort_unstable_by(|a, b| b.name().cmp(a.name()));
+        args.sort_unstable_by(|a, b| b.wrapped().cmp(&a.wrapped()));
+    }
+}
+
+fn main() {
+    unnecessary_sort_by();
+    issue_5754::test();
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by.rs b/src/tools/clippy/tests/ui/unnecessary_sort_by.rs
index 0485a5630afef..1929c72b2f2cd 100644
--- a/src/tools/clippy/tests/ui/unnecessary_sort_by.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_sort_by.rs
@@ -2,11 +2,11 @@
 
 use std::cmp::Reverse;
 
-fn id(x: isize) -> isize {
-    x
-}
+fn unnecessary_sort_by() {
+    fn id(x: isize) -> isize {
+        x
+    }
 
-fn main() {
     let mut vec: Vec<isize> = vec![3, 6, 1, 2, 5];
     // Forward examples
     vec.sort_by(|a, b| a.cmp(b));
@@ -24,3 +24,41 @@ fn main() {
     vec.sort_by(|_, b| b.cmp(c));
     vec.sort_unstable_by(|a, _| a.cmp(c));
 }
+
+// Should not be linted to avoid hitting https://github.com/rust-lang/rust/issues/34162
+mod issue_5754 {
+    struct Test(String);
+
+    #[derive(PartialOrd, Ord, PartialEq, Eq)]
+    struct Wrapper<'a>(&'a str);
+
+    impl Test {
+        fn name(&self) -> &str {
+            &self.0
+        }
+
+        fn wrapped(&self) -> Wrapper<'_> {
+            Wrapper(&self.0)
+        }
+    }
+
+    pub fn test() {
+        let mut args: Vec<Test> = vec![];
+
+        // Forward
+        args.sort_by(|a, b| a.name().cmp(b.name()));
+        args.sort_by(|a, b| a.wrapped().cmp(&b.wrapped()));
+        args.sort_unstable_by(|a, b| a.name().cmp(b.name()));
+        args.sort_unstable_by(|a, b| a.wrapped().cmp(&b.wrapped()));
+        // Reverse
+        args.sort_by(|a, b| b.name().cmp(a.name()));
+        args.sort_by(|a, b| b.wrapped().cmp(&a.wrapped()));
+        args.sort_unstable_by(|a, b| b.name().cmp(a.name()));
+        args.sort_unstable_by(|a, b| b.wrapped().cmp(&a.wrapped()));
+    }
+}
+
+fn main() {
+    unnecessary_sort_by();
+    issue_5754::test();
+}
diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns3.rs b/src/tools/clippy/tests/ui/unnested_or_patterns3.rs
new file mode 100644
index 0000000000000..6bd35057bfad1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnested_or_patterns3.rs
@@ -0,0 +1,6 @@
+#![warn(clippy::unnested_or_patterns)]
+
+// Test that `unnested_or_patterns` does not trigger without enabling `or_patterns`
+fn main() {
+    if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {}
+}