diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index a410826d3fda6..eb143e5bac22d 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -633,6 +633,9 @@ declare_features! (
     /// Allows associated types in inherent impls.
     (active, inherent_associated_types, "1.52.0", Some(8995), None),
 
+    // Allows setting the threshold for the `large_assignments` lint.
+    (active, large_assignments, "1.52.0", Some(83518), None),
+
     /// Allows `extern "C-unwind" fn` to enable unwinding across ABI boundaries.
     (active, c_unwind, "1.52.0", Some(74990), None),
 
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 43054f5bf5e70..8dfc4572a848c 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -241,6 +241,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         const_eval_limit, CrateLevel, template!(NameValueStr: "N"), const_eval_limit,
         experimental!(const_eval_limit)
     ),
+    gated!(
+        move_size_limit, CrateLevel, template!(NameValueStr: "N"), large_assignments,
+        experimental!(move_size_limit)
+    ),
 
     // Entry point:
     ungated!(main, Normal, template!(Word)),
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index f15a7cc5ec2ca..04e45e2351b56 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -2877,6 +2877,39 @@ declare_lint! {
     };
 }
 
+declare_lint! {
+    /// The `large_assignments` lint detects when objects of large
+    /// types are being moved around.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,ignore (can crash on some platforms)
+    /// let x = [0; 50000];
+    /// let y = x;
+    /// ```
+    ///
+    /// produces:
+    ///
+    /// ```text
+    /// warning: moving a large value
+    ///   --> $DIR/move-large.rs:1:3
+    ///   let y = x;
+    ///           - Copied large value here
+    /// ```
+    ///
+    /// ### Explanation
+    ///
+    /// When using a large type in a plain assignment or in a function
+    /// argument, idiomatic code can be inefficient.
+    /// Ideally appropriate optimizations would resolve this, but such
+    /// optimizations are only done in a best-effort manner.
+    /// This lint will trigger on all sites of large moves and thus allow the
+    /// user to resolve them in code.
+    pub LARGE_ASSIGNMENTS,
+    Warn,
+    "detects large moves or copies",
+}
+
 declare_lint_pass! {
     /// Does nothing as a lint pass, but registers some `Lint`s
     /// that are used by other parts of the compiler.
@@ -2962,6 +2995,7 @@ declare_lint_pass! {
         LEGACY_DERIVE_HELPERS,
         PROC_MACRO_BACK_COMPAT,
         OR_PATTERNS_BACK_COMPAT,
+        LARGE_ASSIGNMENTS,
     ]
 }
 
diff --git a/compiler/rustc_middle/src/middle/limits.rs b/compiler/rustc_middle/src/middle/limits.rs
index 61f850c2fc166..601198fd0de04 100644
--- a/compiler/rustc_middle/src/middle/limits.rs
+++ b/compiler/rustc_middle/src/middle/limits.rs
@@ -1,4 +1,8 @@
-//! Registering limits, recursion_limit, type_length_limit and const_eval_limit
+//! Registering limits:
+//! * recursion_limit,
+//! * move_size_limit,
+//! * type_length_limit, and
+//! * const_eval_limit
 //!
 //! There are various parts of the compiler that must impose arbitrary limits
 //! on how deeply they recurse to prevent stack overflow. Users can override
@@ -8,13 +12,14 @@
 use crate::bug;
 use rustc_ast as ast;
 use rustc_data_structures::sync::OnceCell;
-use rustc_session::{Limit, Session};
+use rustc_session::Session;
 use rustc_span::symbol::{sym, Symbol};
 
 use std::num::IntErrorKind;
 
 pub fn update_limits(sess: &Session, krate: &ast::Crate) {
     update_limit(sess, krate, &sess.recursion_limit, sym::recursion_limit, 128);
+    update_limit(sess, krate, &sess.move_size_limit, sym::move_size_limit, 0);
     update_limit(sess, krate, &sess.type_length_limit, sym::type_length_limit, 1048576);
     update_limit(sess, krate, &sess.const_eval_limit, sym::const_eval_limit, 1_000_000);
 }
@@ -22,7 +27,7 @@ pub fn update_limits(sess: &Session, krate: &ast::Crate) {
 fn update_limit(
     sess: &Session,
     krate: &ast::Crate,
-    limit: &OnceCell<Limit>,
+    limit: &OnceCell<impl From<usize> + std::fmt::Debug>,
     name: Symbol,
     default: usize,
 ) {
@@ -34,7 +39,7 @@ fn update_limit(
         if let Some(s) = attr.value_str() {
             match s.as_str().parse() {
                 Ok(n) => {
-                    limit.set(Limit::new(n)).unwrap();
+                    limit.set(From::from(n)).unwrap();
                     return;
                 }
                 Err(e) => {
@@ -63,5 +68,5 @@ fn update_limit(
             }
         }
     }
-    limit.set(Limit::new(default)).unwrap();
+    limit.set(From::from(default)).unwrap();
 }
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 998868211401f..e22c0b40d5a53 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -12,10 +12,10 @@ use crate::ty::print::{FmtPrinter, Printer};
 use crate::ty::subst::{Subst, SubstsRef};
 use crate::ty::{self, List, Ty, TyCtxt};
 use crate::ty::{AdtDef, InstanceDef, Region, ScalarInt, UserTypeAnnotationIndex};
-use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, Namespace};
 use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
 use rustc_hir::{self, GeneratorKind};
+use rustc_hir::{self as hir, HirId};
 use rustc_target::abi::{Size, VariantIdx};
 
 use polonius_engine::Atom;
@@ -1948,6 +1948,29 @@ rustc_index::newtype_index! {
     }
 }
 
+impl SourceScope {
+    /// Finds the original HirId this MIR item came from.
+    /// This is necessary after MIR optimizations, as otherwise we get a HirId
+    /// from the function that was inlined instead of the function call site.
+    pub fn lint_root(
+        self,
+        source_scopes: &IndexVec<SourceScope, SourceScopeData<'tcx>>,
+    ) -> Option<HirId> {
+        let mut data = &source_scopes[self];
+        // FIXME(oli-obk): we should be able to just walk the `inlined_parent_scope`, but it
+        // does not work as I thought it would. Needs more investigation and documentation.
+        while data.inlined.is_some() {
+            trace!(?data);
+            data = &source_scopes[data.parent_scope.unwrap()];
+        }
+        trace!(?data);
+        match &data.local_data {
+            ClearCrossCrate::Set(data) => Some(data.lint_root),
+            ClearCrossCrate::Clear => None,
+        }
+    }
+}
+
 #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)]
 pub struct SourceScopeData<'tcx> {
     pub span: Span,
diff --git a/compiler/rustc_mir/src/monomorphize/collector.rs b/compiler/rustc_mir/src/monomorphize/collector.rs
index 1fda71d74bbf5..e621bc9167d80 100644
--- a/compiler/rustc_mir/src/monomorphize/collector.rs
+++ b/compiler/rustc_mir/src/monomorphize/collector.rs
@@ -198,7 +198,9 @@ use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts};
 use rustc_middle::ty::{self, GenericParamDefKind, Instance, Ty, TyCtxt, TypeFoldable};
 use rustc_middle::{middle::codegen_fn_attrs::CodegenFnAttrFlags, mir::visit::TyContext};
 use rustc_session::config::EntryFnType;
+use rustc_session::lint::builtin::LARGE_ASSIGNMENTS;
 use rustc_span::source_map::{dummy_spanned, respan, Span, Spanned, DUMMY_SP};
+use rustc_target::abi::Size;
 use smallvec::SmallVec;
 use std::iter;
 use std::ops::Range;
@@ -753,6 +755,46 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
         self.super_terminator(terminator, location);
     }
 
+    fn visit_operand(&mut self, operand: &mir::Operand<'tcx>, location: Location) {
+        self.super_operand(operand, location);
+        let limit = self.tcx.sess.move_size_limit();
+        if limit == 0 {
+            return;
+        }
+        let limit = Size::from_bytes(limit);
+        let ty = operand.ty(self.body, self.tcx);
+        let ty = self.monomorphize(ty);
+        let layout = self.tcx.layout_of(ty::ParamEnv::reveal_all().and(ty));
+        if let Ok(layout) = layout {
+            if layout.size > limit {
+                debug!(?layout);
+                let source_info = self.body.source_info(location);
+                debug!(?source_info);
+                let lint_root = source_info.scope.lint_root(&self.body.source_scopes);
+                debug!(?lint_root);
+                let lint_root = match lint_root {
+                    Some(lint_root) => lint_root,
+                    // This happens when the issue is in a function from a foreign crate that
+                    // we monomorphized in the current crate. We can't get a `HirId` for things
+                    // in other crates.
+                    // FIXME: Find out where to report the lint on. Maybe simply crate-level lint root
+                    // but correct span? This would make the lint at least accept crate-level lint attributes.
+                    None => return,
+                };
+                self.tcx.struct_span_lint_hir(
+                    LARGE_ASSIGNMENTS,
+                    lint_root,
+                    source_info.span,
+                    |lint| {
+                        let mut err = lint.build(&format!("moving {} bytes", layout.size.bytes()));
+                        err.span_label(source_info.span, "value moved from here");
+                        err.emit()
+                    },
+                );
+            }
+        }
+    }
+
     fn visit_local(
         &mut self,
         _place_local: &Local,
diff --git a/compiler/rustc_mir/src/transform/const_prop.rs b/compiler/rustc_mir/src/transform/const_prop.rs
index 7706316c96516..5968bbbfca7f3 100644
--- a/compiler/rustc_mir/src/transform/const_prop.rs
+++ b/compiler/rustc_mir/src/transform/const_prop.rs
@@ -13,9 +13,9 @@ use rustc_middle::mir::visit::{
     MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor,
 };
 use rustc_middle::mir::{
-    AssertKind, BasicBlock, BinOp, Body, ClearCrossCrate, Constant, ConstantKind, Local, LocalDecl,
-    LocalKind, Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData,
-    Statement, StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE,
+    AssertKind, BasicBlock, BinOp, Body, Constant, ConstantKind, Local, LocalDecl, LocalKind,
+    Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement,
+    StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE,
 };
 use rustc_middle::ty::layout::{HasTyCtxt, LayoutError, TyAndLayout};
 use rustc_middle::ty::subst::{InternalSubsts, Subst};
@@ -440,18 +440,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
     }
 
     fn lint_root(&self, source_info: SourceInfo) -> Option<HirId> {
-        let mut data = &self.source_scopes[source_info.scope];
-        // FIXME(oli-obk): we should be able to just walk the `inlined_parent_scope`, but it
-        // does not work as I thought it would. Needs more investigation and documentation.
-        while data.inlined.is_some() {
-            trace!(?data);
-            data = &self.source_scopes[data.parent_scope.unwrap()];
-        }
-        trace!(?data);
-        match &data.local_data {
-            ClearCrossCrate::Set(data) => Some(data.lint_root),
-            ClearCrossCrate::Clear => None,
-        }
+        source_info.scope.lint_root(&self.source_scopes)
     }
 
     fn use_ecx<F, T>(&mut self, f: F) -> Option<T>
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index cc2583be94474..7bff634fb2dd0 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -83,6 +83,12 @@ impl Limit {
     }
 }
 
+impl From<usize> for Limit {
+    fn from(value: usize) -> Self {
+        Self::new(value)
+    }
+}
+
 impl fmt::Display for Limit {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "{}", self.0)
@@ -143,6 +149,10 @@ pub struct Session {
     /// operations such as auto-dereference and monomorphization.
     pub recursion_limit: OnceCell<Limit>,
 
+    /// The size at which the `large_assignments` lint starts
+    /// being emitted.
+    pub move_size_limit: OnceCell<usize>,
+
     /// The maximum length of types during monomorphization.
     pub type_length_limit: OnceCell<Limit>,
 
@@ -352,6 +362,11 @@ impl Session {
         self.recursion_limit.get().copied().unwrap()
     }
 
+    #[inline]
+    pub fn move_size_limit(&self) -> usize {
+        self.move_size_limit.get().copied().unwrap()
+    }
+
     #[inline]
     pub fn type_length_limit(&self) -> Limit {
         self.type_length_limit.get().copied().unwrap()
@@ -1414,6 +1429,7 @@ pub fn build_session(
         features: OnceCell::new(),
         lint_store: OnceCell::new(),
         recursion_limit: OnceCell::new(),
+        move_size_limit: OnceCell::new(),
         type_length_limit: OnceCell::new(),
         const_eval_limit: OnceCell::new(),
         incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)),
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 1d1471fdeca04..4a1af4a294565 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -669,6 +669,7 @@ symbols! {
         label_break_value,
         lang,
         lang_items,
+        large_assignments,
         lateout,
         lazy_normalization_consts,
         le,
@@ -749,6 +750,7 @@ symbols! {
         more_struct_aliases,
         movbe_target_feature,
         move_ref_pattern,
+        move_size_limit,
         mul,
         mul_assign,
         mul_with_overflow,
diff --git a/library/alloc/src/vec/drain.rs b/library/alloc/src/vec/drain.rs
index fb32d144f872c..ccce7da059088 100644
--- a/library/alloc/src/vec/drain.rs
+++ b/library/alloc/src/vec/drain.rs
@@ -1,6 +1,6 @@
 use crate::alloc::{Allocator, Global};
 use core::fmt;
-use core::iter::{FusedIterator, TrustedLen};
+use core::iter::{FusedIterator, TrustedLen, TrustedRandomAccess};
 use core::mem::{self};
 use core::ptr::{self, NonNull};
 use core::slice::{self};
@@ -89,6 +89,19 @@ impl<T, A: Allocator> Iterator for Drain<'_, T, A> {
     fn size_hint(&self) -> (usize, Option<usize>) {
         self.iter.size_hint()
     }
+
+    #[doc(hidden)]
+    unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> T
+    where
+        Self: TrustedRandomAccess,
+    {
+        // SAFETY: `TrustedRandomAccess` requires that `idx` is in bounds and that
+        // each `idx` is only accessed once. Forwarding to the slice iterator's
+        // implementation is thus safe, and reading the value is safe because
+        // `Self: TrustedRandomAccess` implies `T: Copy` so the `Drop` impl below
+        // won't cause each item to be dropped twice.
+        unsafe { ptr::read(self.iter.__iterator_get_unchecked(idx) as *const _) }
+    }
 }
 
 #[stable(feature = "drain", since = "1.6.0")]
@@ -108,9 +121,11 @@ impl<T, A: Allocator> Drop for Drain<'_, T, A> {
 
         impl<'r, 'a, T, A: Allocator> Drop for DropGuard<'r, 'a, T, A> {
             fn drop(&mut self) {
-                // Continue the same loop we have below. If the loop already finished, this does
-                // nothing.
-                self.0.for_each(drop);
+                if mem::needs_drop::<T>() {
+                    // Continue the same loop we have below. If the loop already finished, this does
+                    // nothing.
+                    self.0.for_each(drop);
+                }
 
                 if self.0.tail_len > 0 {
                     unsafe {
@@ -129,11 +144,13 @@ impl<T, A: Allocator> Drop for Drain<'_, T, A> {
             }
         }
 
-        // exhaust self first
-        while let Some(item) = self.next() {
-            let guard = DropGuard(self);
-            drop(item);
-            mem::forget(guard);
+        // exhaust self first if dropping of the items is required
+        if mem::needs_drop::<T>() {
+            while let Some(item) = self.next() {
+                let guard = DropGuard(self);
+                drop(item);
+                mem::forget(guard);
+            }
         }
 
         // Drop a `DropGuard` to move back the non-drained tail of `self`.
@@ -149,7 +166,26 @@ impl<T, A: Allocator> ExactSizeIterator for Drain<'_, T, A> {
 }
 
 #[unstable(feature = "trusted_len", issue = "37572")]
+// SAFETY: `Drain` simply forwards to the underlying slice iterator, which implements `TrustedLen`
+// so the required properties are all preserved.
 unsafe impl<T, A: Allocator> TrustedLen for Drain<'_, T, A> {}
 
+#[doc(hidden)]
+#[unstable(feature = "trusted_random_access", issue = "none")]
+// SAFETY: `Drain` forwards to the underlying slice iterator, which implements `TrustedRandomAccess`,
+// and then reads the items instead of just returning a reference. As `TrustedRandomAccess`
+// requires each index to be accessed only once, this is safe to do here.
+//
+// T: Copy as approximation for !Drop since get_unchecked does not advance self.iter
+// and as a result the `Drop` impl above would otherwise cause items to be dropped twice.
+unsafe impl<T, A: Allocator> TrustedRandomAccess for Drain<'_, T, A>
+where
+    T: Copy,
+{
+    fn may_have_side_effect() -> bool {
+        false
+    }
+}
+
 #[stable(feature = "fused", since = "1.26.0")]
 impl<T, A: Allocator> FusedIterator for Drain<'_, T, A> {}
diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs
index b6ce825e2477a..5c7a8bdf6bd6d 100644
--- a/library/core/src/array/mod.rs
+++ b/library/core/src/array/mod.rs
@@ -22,14 +22,14 @@ mod iter;
 pub use iter::IntoIter;
 
 /// Converts a reference to `T` into a reference to an array of length 1 (without copying).
-#[unstable(feature = "array_from_ref", issue = "77101")]
+#[stable(feature = "array_from_ref", since = "1.53.0")]
 pub fn from_ref<T>(s: &T) -> &[T; 1] {
     // SAFETY: Converting `&T` to `&[T; 1]` is sound.
     unsafe { &*(s as *const T).cast::<[T; 1]>() }
 }
 
 /// Converts a mutable reference to `T` into a mutable reference to an array of length 1 (without copying).
-#[unstable(feature = "array_from_ref", issue = "77101")]
+#[stable(feature = "array_from_ref", since = "1.53.0")]
 pub fn from_mut<T>(s: &mut T) -> &mut [T; 1] {
     // SAFETY: Converting `&mut T` to `&mut [T; 1]` is sound.
     unsafe { &mut *(s as *mut T).cast::<[T; 1]>() }
diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
index f6bfe67e1b12c..35e4d213dde1e 100644
--- a/library/core/tests/lib.rs
+++ b/library/core/tests/lib.rs
@@ -1,6 +1,5 @@
 #![feature(alloc_layout_extra)]
 #![feature(array_chunks)]
-#![feature(array_from_ref)]
 #![feature(array_methods)]
 #![feature(array_map)]
 #![feature(array_windows)]
diff --git a/library/std/src/sys/hermit/args.rs b/library/std/src/sys/hermit/args.rs
index 4794f89a5aee3..4eb0d8437ba5f 100644
--- a/library/std/src/sys/hermit/args.rs
+++ b/library/std/src/sys/hermit/args.rs
@@ -1,6 +1,5 @@
 use crate::ffi::OsString;
 use crate::fmt;
-use crate::marker::PhantomData;
 use crate::vec;
 
 /// One-time global initialization.
@@ -20,7 +19,6 @@ pub fn args() -> Args {
 
 pub struct Args {
     iter: vec::IntoIter<OsString>,
-    _dont_send_or_sync_me: PhantomData<*mut ()>,
 }
 
 impl fmt::Debug for Args {
@@ -29,6 +27,9 @@ impl fmt::Debug for Args {
     }
 }
 
+impl !Send for Args {}
+impl !Sync for Args {}
+
 impl Iterator for Args {
     type Item = OsString;
     fn next(&mut self) -> Option<OsString> {
@@ -54,7 +55,6 @@ impl DoubleEndedIterator for Args {
 mod imp {
     use super::Args;
     use crate::ffi::{CStr, OsString};
-    use crate::marker::PhantomData;
     use crate::ptr;
     use crate::sys_common::os_str_bytes::*;
 
@@ -77,7 +77,7 @@ mod imp {
     }
 
     pub fn args() -> Args {
-        Args { iter: clone().into_iter(), _dont_send_or_sync_me: PhantomData }
+        Args { iter: clone().into_iter() }
     }
 
     fn clone() -> Vec<OsString> {
diff --git a/library/std/src/sys/hermit/os.rs b/library/std/src/sys/hermit/os.rs
index 4487e9d636cb0..81cd68a74e665 100644
--- a/library/std/src/sys/hermit/os.rs
+++ b/library/std/src/sys/hermit/os.rs
@@ -110,9 +110,11 @@ pub fn init_environment(env: *const *const i8) {
 
 pub struct Env {
     iter: vec::IntoIter<(OsString, OsString)>,
-    _dont_send_or_sync_me: PhantomData<*mut ()>,
 }
 
+impl !Send for Env {}
+impl !Sync for Env {}
+
 impl Iterator for Env {
     type Item = (OsString, OsString);
     fn next(&mut self) -> Option<(OsString, OsString)> {
@@ -134,7 +136,7 @@ pub fn env() -> Env {
             result.push((key.clone(), value.clone()));
         }
 
-        return Env { iter: result.into_iter(), _dont_send_or_sync_me: PhantomData };
+        return Env { iter: result.into_iter() };
     }
 }
 
diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs
index cba0627e93a85..fc423e393d4a4 100644
--- a/library/std/src/sys/unix/args.rs
+++ b/library/std/src/sys/unix/args.rs
@@ -7,7 +7,6 @@
 
 use crate::ffi::OsString;
 use crate::fmt;
-use crate::marker::PhantomData;
 use crate::vec;
 
 /// One-time global initialization.
@@ -27,9 +26,11 @@ pub fn args() -> Args {
 
 pub struct Args {
     iter: vec::IntoIter<OsString>,
-    _dont_send_or_sync_me: PhantomData<*mut ()>,
 }
 
+impl !Send for Args {}
+impl !Sync for Args {}
+
 impl fmt::Debug for Args {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         self.iter.as_slice().fmt(f)
@@ -77,7 +78,6 @@ impl DoubleEndedIterator for Args {
 mod imp {
     use super::Args;
     use crate::ffi::{CStr, OsString};
-    use crate::marker::PhantomData;
     use crate::os::unix::prelude::*;
     use crate::ptr;
     use crate::sync::atomic::{AtomicIsize, AtomicPtr, Ordering};
@@ -134,7 +134,7 @@ mod imp {
     }
 
     pub fn args() -> Args {
-        Args { iter: clone().into_iter(), _dont_send_or_sync_me: PhantomData }
+        Args { iter: clone().into_iter() }
     }
 
     fn clone() -> Vec<OsString> {
@@ -156,7 +156,6 @@ mod imp {
 mod imp {
     use super::Args;
     use crate::ffi::CStr;
-    use crate::marker::PhantomData;
 
     pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
 
@@ -181,7 +180,7 @@ mod imp {
                 })
                 .collect::<Vec<_>>()
         };
-        Args { iter: vec.into_iter(), _dont_send_or_sync_me: PhantomData }
+        Args { iter: vec.into_iter() }
     }
 
     // As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs
@@ -248,6 +247,6 @@ mod imp {
             }
         }
 
-        Args { iter: res.into_iter(), _dont_send_or_sync_me: PhantomData }
+        Args { iter: res.into_iter() }
     }
 }
diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs
index 503ba410097c8..984c08c2ad531 100644
--- a/library/std/src/sys/unix/os.rs
+++ b/library/std/src/sys/unix/os.rs
@@ -12,7 +12,6 @@ use crate::ffi::{CStr, CString, OsStr, OsString};
 use crate::fmt;
 use crate::io;
 use crate::iter;
-use crate::marker::PhantomData;
 use crate::mem;
 use crate::memchr;
 use crate::path::{self, PathBuf};
@@ -460,9 +459,11 @@ pub fn current_exe() -> io::Result<PathBuf> {
 
 pub struct Env {
     iter: vec::IntoIter<(OsString, OsString)>,
-    _dont_send_or_sync_me: PhantomData<*mut ()>,
 }
 
+impl !Send for Env {}
+impl !Sync for Env {}
+
 impl Iterator for Env {
     type Item = (OsString, OsString);
     fn next(&mut self) -> Option<(OsString, OsString)> {
@@ -510,7 +511,7 @@ pub fn env() -> Env {
                 environ = environ.add(1);
             }
         }
-        return Env { iter: result.into_iter(), _dont_send_or_sync_me: PhantomData };
+        return Env { iter: result.into_iter() };
     }
 
     fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
diff --git a/library/std/src/sys/wasi/args.rs b/library/std/src/sys/wasi/args.rs
index 86405dede4277..3c3e66985b3d9 100644
--- a/library/std/src/sys/wasi/args.rs
+++ b/library/std/src/sys/wasi/args.rs
@@ -2,7 +2,6 @@
 
 use crate::ffi::{CStr, OsStr, OsString};
 use crate::fmt;
-use crate::marker::PhantomData;
 use crate::os::wasi::ffi::OsStrExt;
 use crate::vec;
 
@@ -12,15 +11,14 @@ pub unsafe fn cleanup() {}
 
 pub struct Args {
     iter: vec::IntoIter<OsString>,
-    _dont_send_or_sync_me: PhantomData<*mut ()>,
 }
 
+impl !Send for Args {}
+impl !Sync for Args {}
+
 /// Returns the command line arguments
 pub fn args() -> Args {
-    Args {
-        iter: maybe_args().unwrap_or(Vec::new()).into_iter(),
-        _dont_send_or_sync_me: PhantomData,
-    }
+    Args { iter: maybe_args().unwrap_or(Vec::new()).into_iter() }
 }
 
 fn maybe_args() -> Option<Vec<OsString>> {
diff --git a/library/std/src/sys/wasi/os.rs b/library/std/src/sys/wasi/os.rs
index cf17ac0ba5f2b..f129ee55a8391 100644
--- a/library/std/src/sys/wasi/os.rs
+++ b/library/std/src/sys/wasi/os.rs
@@ -129,9 +129,11 @@ pub fn current_exe() -> io::Result<PathBuf> {
 }
 pub struct Env {
     iter: vec::IntoIter<(OsString, OsString)>,
-    _dont_send_or_sync_me: PhantomData<*mut ()>,
 }
 
+impl !Send for Env {}
+impl !Sync for Env {}
+
 impl Iterator for Env {
     type Item = (OsString, OsString);
     fn next(&mut self) -> Option<(OsString, OsString)> {
@@ -155,7 +157,7 @@ pub fn env() -> Env {
                 environ = environ.add(1);
             }
         }
-        return Env { iter: result.into_iter(), _dont_send_or_sync_me: PhantomData };
+        return Env { iter: result.into_iter() };
     }
 
     // See src/libstd/sys/unix/os.rs, same as that
diff --git a/library/std/src/sys/wasm/args.rs b/library/std/src/sys/wasm/args.rs
index 99d300b53b3b9..99161ee056a3e 100644
--- a/library/std/src/sys/wasm/args.rs
+++ b/library/std/src/sys/wasm/args.rs
@@ -1,6 +1,5 @@
 use crate::ffi::OsString;
 use crate::fmt;
-use crate::marker::PhantomData;
 use crate::vec;
 
 pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
@@ -10,14 +9,16 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
 pub unsafe fn cleanup() {}
 
 pub fn args() -> Args {
-    Args { iter: Vec::new().into_iter(), _dont_send_or_sync_me: PhantomData }
+    Args { iter: Vec::new().into_iter() }
 }
 
 pub struct Args {
     iter: vec::IntoIter<OsString>,
-    _dont_send_or_sync_me: PhantomData<*mut ()>,
 }
 
+impl !Send for Args {}
+impl !Sync for Args {}
+
 impl fmt::Debug for Args {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         self.iter.as_slice().fmt(f)
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 277ec91f15ed7..2f6a91641e48e 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -15,7 +15,7 @@ use rustc_span::hygiene::MacroKind;
 use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_span::Span;
 
-use crate::clean::{self, Attributes, GetDefId, ToSource, TypeKind};
+use crate::clean::{self, Attributes, GetDefId, ToSource};
 use crate::core::DocContext;
 use crate::formats::item_type::ItemType;
 
@@ -56,36 +56,36 @@ crate fn try_inline(
 
     let kind = match res {
         Res::Def(DefKind::Trait, did) => {
-            record_extern_fqn(cx, did, clean::TypeKind::Trait);
+            record_extern_fqn(cx, did, ItemType::Trait);
             build_impls(cx, Some(parent_module), did, attrs, &mut ret);
             clean::TraitItem(build_external_trait(cx, did))
         }
         Res::Def(DefKind::Fn, did) => {
-            record_extern_fqn(cx, did, clean::TypeKind::Function);
+            record_extern_fqn(cx, did, ItemType::Function);
             clean::FunctionItem(build_external_function(cx, did))
         }
         Res::Def(DefKind::Struct, did) => {
-            record_extern_fqn(cx, did, clean::TypeKind::Struct);
+            record_extern_fqn(cx, did, ItemType::Struct);
             build_impls(cx, Some(parent_module), did, attrs, &mut ret);
             clean::StructItem(build_struct(cx, did))
         }
         Res::Def(DefKind::Union, did) => {
-            record_extern_fqn(cx, did, clean::TypeKind::Union);
+            record_extern_fqn(cx, did, ItemType::Union);
             build_impls(cx, Some(parent_module), did, attrs, &mut ret);
             clean::UnionItem(build_union(cx, did))
         }
         Res::Def(DefKind::TyAlias, did) => {
-            record_extern_fqn(cx, did, clean::TypeKind::Typedef);
+            record_extern_fqn(cx, did, ItemType::Typedef);
             build_impls(cx, Some(parent_module), did, attrs, &mut ret);
             clean::TypedefItem(build_type_alias(cx, did), false)
         }
         Res::Def(DefKind::Enum, did) => {
-            record_extern_fqn(cx, did, clean::TypeKind::Enum);
+            record_extern_fqn(cx, did, ItemType::Enum);
             build_impls(cx, Some(parent_module), did, attrs, &mut ret);
             clean::EnumItem(build_enum(cx, did))
         }
         Res::Def(DefKind::ForeignTy, did) => {
-            record_extern_fqn(cx, did, clean::TypeKind::Foreign);
+            record_extern_fqn(cx, did, ItemType::ForeignType);
             build_impls(cx, Some(parent_module), did, attrs, &mut ret);
             clean::ForeignTypeItem
         }
@@ -95,24 +95,24 @@ crate fn try_inline(
         // their constructors.
         Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => return Some(Vec::new()),
         Res::Def(DefKind::Mod, did) => {
-            record_extern_fqn(cx, did, clean::TypeKind::Module);
+            record_extern_fqn(cx, did, ItemType::Module);
             clean::ModuleItem(build_module(cx, did, visited))
         }
         Res::Def(DefKind::Static, did) => {
-            record_extern_fqn(cx, did, clean::TypeKind::Static);
+            record_extern_fqn(cx, did, ItemType::Static);
             clean::StaticItem(build_static(cx, did, cx.tcx.is_mutable_static(did)))
         }
         Res::Def(DefKind::Const, did) => {
-            record_extern_fqn(cx, did, clean::TypeKind::Const);
+            record_extern_fqn(cx, did, ItemType::Constant);
             clean::ConstantItem(build_const(cx, did))
         }
         Res::Def(DefKind::Macro(kind), did) => {
             let mac = build_macro(cx, did, name);
 
             let type_kind = match kind {
-                MacroKind::Bang => TypeKind::Macro,
-                MacroKind::Attr => TypeKind::Attr,
-                MacroKind::Derive => TypeKind::Derive,
+                MacroKind::Bang => ItemType::Macro,
+                MacroKind::Attr => ItemType::ProcAttribute,
+                MacroKind::Derive => ItemType::ProcDerive,
             };
             record_extern_fqn(cx, did, type_kind);
             mac
@@ -157,7 +157,7 @@ crate fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> Attrs<'hir> {
 ///
 /// These names are used later on by HTML rendering to generate things like
 /// source links back to the original item.
-crate fn record_extern_fqn(cx: &mut DocContext<'_>, did: DefId, kind: clean::TypeKind) {
+crate fn record_extern_fqn(cx: &mut DocContext<'_>, did: DefId, kind: ItemType) {
     let crate_name = cx.tcx.crate_name(did.krate).to_string();
 
     let relative = cx.tcx.def_path(did).data.into_iter().filter_map(|elem| {
@@ -165,7 +165,7 @@ crate fn record_extern_fqn(cx: &mut DocContext<'_>, did: DefId, kind: clean::Typ
         let s = elem.data.to_string();
         if !s.is_empty() { Some(s) } else { None }
     });
-    let fqn = if let clean::TypeKind::Macro = kind {
+    let fqn = if let ItemType::Macro = kind {
         // Check to see if it is a macro 2.0 or built-in macro
         if matches!(
             cx.enter_resolver(|r| r.cstore().load_macro_untracked(did, cx.sess())),
@@ -487,7 +487,7 @@ fn build_module(
         }
     }
 
-    clean::Module { items, is_crate: false }
+    clean::Module { items }
 }
 
 crate fn print_inlined_const(tcx: TyCtxt<'_>, did: DefId) -> String {
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 72046645e3a97..2dc3f63a2d8e4 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -36,6 +36,7 @@ use std::{mem, vec};
 
 use crate::core::{self, DocContext, ImplTraitParam};
 use crate::doctree;
+use crate::formats::item_type::ItemType;
 
 use utils::*;
 
@@ -112,12 +113,8 @@ impl Clean<Item> for doctree::Module<'_> {
             }
         };
 
-        let what_rustc_thinks = Item::from_hir_id_and_parts(
-            self.id,
-            Some(self.name),
-            ModuleItem(Module { is_crate: self.is_crate, items }),
-            cx,
-        );
+        let what_rustc_thinks =
+            Item::from_hir_id_and_parts(self.id, Some(self.name), ModuleItem(Module { items }), cx);
         Item { span: span.clean(cx), ..what_rustc_thinks }
     }
 }
@@ -158,7 +155,7 @@ impl Clean<GenericBound> for hir::GenericBound<'_> {
 impl Clean<Type> for (ty::TraitRef<'_>, &[TypeBinding]) {
     fn clean(&self, cx: &mut DocContext<'_>) -> Type {
         let (trait_ref, bounds) = *self;
-        inline::record_extern_fqn(cx, trait_ref.def_id, TypeKind::Trait);
+        inline::record_extern_fqn(cx, trait_ref.def_id, ItemType::Trait);
         let path = external_path(
             cx,
             cx.tcx.item_name(trait_ref.def_id),
@@ -913,12 +910,6 @@ impl Clean<PolyTrait> for hir::PolyTraitRef<'_> {
     }
 }
 
-impl Clean<TypeKind> for hir::def::DefKind {
-    fn clean(&self, _: &mut DocContext<'_>) -> TypeKind {
-        (*self).into()
-    }
-}
-
 impl Clean<Item> for hir::TraitItem<'_> {
     fn clean(&self, cx: &mut DocContext<'_>) -> Item {
         let local_did = self.def_id.to_def_id();
@@ -1453,16 +1444,16 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
             ty::Adt(def, substs) => {
                 let did = def.did;
                 let kind = match def.adt_kind() {
-                    AdtKind::Struct => TypeKind::Struct,
-                    AdtKind::Union => TypeKind::Union,
-                    AdtKind::Enum => TypeKind::Enum,
+                    AdtKind::Struct => ItemType::Struct,
+                    AdtKind::Union => ItemType::Union,
+                    AdtKind::Enum => ItemType::Enum,
                 };
                 inline::record_extern_fqn(cx, did, kind);
                 let path = external_path(cx, cx.tcx.item_name(did), None, false, vec![], substs);
                 ResolvedPath { path, param_names: None, did, is_generic: false }
             }
             ty::Foreign(did) => {
-                inline::record_extern_fqn(cx, did, TypeKind::Foreign);
+                inline::record_extern_fqn(cx, did, ItemType::ForeignType);
                 let path = external_path(
                     cx,
                     cx.tcx.item_name(did),
@@ -1487,7 +1478,7 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
                     _ => cx.tcx.intern_substs(&[]),
                 };
 
-                inline::record_extern_fqn(cx, did, TypeKind::Trait);
+                inline::record_extern_fqn(cx, did, ItemType::Trait);
 
                 let mut param_names = vec![];
                 if let Some(b) = reg.clean(cx) {
@@ -1497,7 +1488,7 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
                     let empty = cx.tcx.intern_substs(&[]);
                     let path =
                         external_path(cx, cx.tcx.item_name(did), Some(did), false, vec![], empty);
-                    inline::record_extern_fqn(cx, did, TypeKind::Trait);
+                    inline::record_extern_fqn(cx, did, ItemType::Trait);
                     let bound = GenericBound::TraitBound(
                         PolyTrait {
                             trait_: ResolvedPath {
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 1acde8401b21e..0b55d276bad0e 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -391,12 +391,9 @@ impl Item {
     }
 
     crate fn is_crate(&self) -> bool {
-        matches!(
-            *self.kind,
-            StrippedItem(box ModuleItem(Module { is_crate: true, .. }))
-                | ModuleItem(Module { is_crate: true, .. })
-        )
+        self.is_mod() && self.def_id.index == CRATE_DEF_INDEX
     }
+
     crate fn is_mod(&self) -> bool {
         self.type_() == ItemType::Module
     }
@@ -608,7 +605,6 @@ impl ItemKind {
 #[derive(Clone, Debug)]
 crate struct Module {
     crate items: Vec<Item>,
-    crate is_crate: bool,
 }
 
 crate struct ListAttributesIter<'a> {
@@ -1103,7 +1099,7 @@ impl GenericBound {
         let did = cx.tcx.require_lang_item(LangItem::Sized, None);
         let empty = cx.tcx.intern_substs(&[]);
         let path = external_path(cx, cx.tcx.item_name(did), Some(did), false, vec![], empty);
-        inline::record_extern_fqn(cx, did, TypeKind::Trait);
+        inline::record_extern_fqn(cx, did, ItemType::Trait);
         GenericBound::TraitBound(
             PolyTrait {
                 trait_: ResolvedPath { path, param_names: None, did, is_generic: false },
@@ -1442,62 +1438,6 @@ crate enum PrimitiveType {
     Never,
 }
 
-#[derive(Clone, PartialEq, Eq, Hash, Copy, Debug)]
-crate enum TypeKind {
-    Enum,
-    Function,
-    Module,
-    Const,
-    Static,
-    Struct,
-    Union,
-    Trait,
-    Typedef,
-    Foreign,
-    Macro,
-    Attr,
-    Derive,
-    TraitAlias,
-    Primitive,
-}
-
-impl From<hir::def::DefKind> for TypeKind {
-    fn from(other: hir::def::DefKind) -> Self {
-        match other {
-            hir::def::DefKind::Enum => Self::Enum,
-            hir::def::DefKind::Fn => Self::Function,
-            hir::def::DefKind::Mod => Self::Module,
-            hir::def::DefKind::Const => Self::Const,
-            hir::def::DefKind::Static => Self::Static,
-            hir::def::DefKind::Struct => Self::Struct,
-            hir::def::DefKind::Union => Self::Union,
-            hir::def::DefKind::Trait => Self::Trait,
-            hir::def::DefKind::TyAlias => Self::Typedef,
-            hir::def::DefKind::TraitAlias => Self::TraitAlias,
-            hir::def::DefKind::Macro(_) => Self::Macro,
-            hir::def::DefKind::ForeignTy
-            | hir::def::DefKind::Variant
-            | hir::def::DefKind::AssocTy
-            | hir::def::DefKind::TyParam
-            | hir::def::DefKind::ConstParam
-            | hir::def::DefKind::Ctor(..)
-            | hir::def::DefKind::AssocFn
-            | hir::def::DefKind::AssocConst
-            | hir::def::DefKind::ExternCrate
-            | hir::def::DefKind::Use
-            | hir::def::DefKind::ForeignMod
-            | hir::def::DefKind::AnonConst
-            | hir::def::DefKind::OpaqueTy
-            | hir::def::DefKind::Field
-            | hir::def::DefKind::LifetimeParam
-            | hir::def::DefKind::GlobalAsm
-            | hir::def::DefKind::Impl
-            | hir::def::DefKind::Closure
-            | hir::def::DefKind::Generator => Self::Foreign,
-        }
-    }
-}
-
 crate trait GetDefId {
     /// Use this method to get the [`DefId`] of a [`clean`] AST node.
     /// This will return [`None`] when called on a primitive [`clean::Type`].
@@ -1983,7 +1923,7 @@ crate enum Variant {
 
 /// Small wrapper around [`rustc_span::Span]` that adds helper methods
 /// and enforces calling [`rustc_span::Span::source_callsite()`].
-#[derive(Clone, Debug)]
+#[derive(Copy, Clone, Debug)]
 crate struct Span(rustc_span::Span);
 
 impl Span {
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index 55a0cb42a2081..7df8b442e5acc 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -3,9 +3,9 @@ use crate::clean::blanket_impl::BlanketImplFinder;
 use crate::clean::{
     inline, Clean, Crate, Generic, GenericArg, GenericArgs, ImportSource, Item, ItemKind, Lifetime,
     MacroKind, Path, PathSegment, Primitive, PrimitiveType, ResolvedPath, Type, TypeBinding,
-    TypeKind,
 };
 use crate::core::DocContext;
+use crate::formats::item_type::ItemType;
 
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
@@ -435,29 +435,29 @@ crate fn register_res(cx: &mut DocContext<'_>, res: Res) -> DefId {
     debug!("register_res({:?})", res);
 
     let (did, kind) = match res {
-        Res::Def(DefKind::Fn, i) => (i, TypeKind::Function),
-        Res::Def(DefKind::TyAlias, i) => (i, TypeKind::Typedef),
-        Res::Def(DefKind::Enum, i) => (i, TypeKind::Enum),
-        Res::Def(DefKind::Trait, i) => (i, TypeKind::Trait),
+        Res::Def(DefKind::Fn, i) => (i, ItemType::Function),
+        Res::Def(DefKind::TyAlias, i) => (i, ItemType::Typedef),
+        Res::Def(DefKind::Enum, i) => (i, ItemType::Enum),
+        Res::Def(DefKind::Trait, i) => (i, ItemType::Trait),
         Res::Def(DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst, i) => {
-            (cx.tcx.parent(i).unwrap(), TypeKind::Trait)
+            (cx.tcx.parent(i).unwrap(), ItemType::Trait)
         }
-        Res::Def(DefKind::Struct, i) => (i, TypeKind::Struct),
-        Res::Def(DefKind::Union, i) => (i, TypeKind::Union),
-        Res::Def(DefKind::Mod, i) => (i, TypeKind::Module),
-        Res::Def(DefKind::ForeignTy, i) => (i, TypeKind::Foreign),
-        Res::Def(DefKind::Const, i) => (i, TypeKind::Const),
-        Res::Def(DefKind::Static, i) => (i, TypeKind::Static),
+        Res::Def(DefKind::Struct, i) => (i, ItemType::Struct),
+        Res::Def(DefKind::Union, i) => (i, ItemType::Union),
+        Res::Def(DefKind::Mod, i) => (i, ItemType::Module),
+        Res::Def(DefKind::ForeignTy, i) => (i, ItemType::ForeignType),
+        Res::Def(DefKind::Const, i) => (i, ItemType::Constant),
+        Res::Def(DefKind::Static, i) => (i, ItemType::Static),
         Res::Def(DefKind::Variant, i) => {
-            (cx.tcx.parent(i).expect("cannot get parent def id"), TypeKind::Enum)
+            (cx.tcx.parent(i).expect("cannot get parent def id"), ItemType::Enum)
         }
         Res::Def(DefKind::Macro(mac_kind), i) => match mac_kind {
-            MacroKind::Bang => (i, TypeKind::Macro),
-            MacroKind::Attr => (i, TypeKind::Attr),
-            MacroKind::Derive => (i, TypeKind::Derive),
+            MacroKind::Bang => (i, ItemType::Macro),
+            MacroKind::Attr => (i, ItemType::ProcAttribute),
+            MacroKind::Derive => (i, ItemType::ProcDerive),
         },
-        Res::Def(DefKind::TraitAlias, i) => (i, TypeKind::TraitAlias),
-        Res::SelfTy(Some(def_id), _) => (def_id, TypeKind::Trait),
+        Res::Def(DefKind::TraitAlias, i) => (i, ItemType::TraitAlias),
+        Res::SelfTy(Some(def_id), _) => (def_id, ItemType::Trait),
         Res::SelfTy(_, Some((impl_def_id, _))) => return impl_def_id,
         _ => return res.def_id(),
     };
@@ -465,7 +465,7 @@ crate fn register_res(cx: &mut DocContext<'_>, res: Res) -> DefId {
         return did;
     }
     inline::record_extern_fqn(cx, did, kind);
-    if let TypeKind::Trait = kind {
+    if let ItemType::Trait = kind {
         inline::record_extern_trait(cx, did);
     }
     did
diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs
index 189624c0d809c..d3f4353a58b7b 100644
--- a/src/librustdoc/doctree.rs
+++ b/src/librustdoc/doctree.rs
@@ -14,7 +14,6 @@ crate struct Module<'hir> {
     crate items: Vec<(&'hir hir::Item<'hir>, Option<Symbol>)>,
     crate foreigns: Vec<(&'hir hir::ForeignItem<'hir>, Option<Symbol>)>,
     crate macros: Vec<(&'hir hir::MacroDef<'hir>, Option<Symbol>)>,
-    crate is_crate: bool,
 }
 
 impl Module<'hir> {
@@ -28,7 +27,6 @@ impl Module<'hir> {
             items: Vec::new(),
             foreigns: Vec::new(),
             macros: Vec::new(),
-            is_crate: false,
         }
     }
 }
diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs
index 376fef6568af7..ed91a56057574 100644
--- a/src/librustdoc/fold.rs
+++ b/src/librustdoc/fold.rs
@@ -80,10 +80,7 @@ crate trait DocFolder: Sized {
     }
 
     fn fold_mod(&mut self, m: Module) -> Module {
-        Module {
-            is_crate: m.is_crate,
-            items: m.items.into_iter().filter_map(|i| self.fold_item(i)).collect(),
-        }
+        Module { items: m.items.into_iter().filter_map(|i| self.fold_item(i)).collect() }
     }
 
     fn fold_crate(&mut self, mut c: Crate) -> Crate {
diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs
index 460d4b907c09a..955de57dc0e5f 100644
--- a/src/librustdoc/formats/item_type.rs
+++ b/src/librustdoc/formats/item_type.rs
@@ -4,6 +4,7 @@ use std::fmt;
 
 use serde::{Serialize, Serializer};
 
+use rustc_hir::def::DefKind;
 use rustc_span::hygiene::MacroKind;
 
 use crate::clean;
@@ -19,7 +20,7 @@ use crate::clean;
 /// module headings. If you are adding to this enum and want to ensure that the sidebar also prints
 /// a heading, edit the listing in `html/render.rs`, function `sidebar_module`. This uses an
 /// ordering based on a helper function inside `item_module`, in the same file.
-#[derive(Copy, PartialEq, Eq, Clone, Debug, PartialOrd, Ord)]
+#[derive(Copy, PartialEq, Eq, Hash, Clone, Debug, PartialOrd, Ord)]
 crate enum ItemType {
     Module = 0,
     ExternCrate = 1,
@@ -102,24 +103,43 @@ impl<'a> From<&'a clean::Item> for ItemType {
     }
 }
 
-impl From<clean::TypeKind> for ItemType {
-    fn from(kind: clean::TypeKind) -> ItemType {
-        match kind {
-            clean::TypeKind::Struct => ItemType::Struct,
-            clean::TypeKind::Union => ItemType::Union,
-            clean::TypeKind::Enum => ItemType::Enum,
-            clean::TypeKind::Function => ItemType::Function,
-            clean::TypeKind::Trait => ItemType::Trait,
-            clean::TypeKind::Module => ItemType::Module,
-            clean::TypeKind::Static => ItemType::Static,
-            clean::TypeKind::Const => ItemType::Constant,
-            clean::TypeKind::Typedef => ItemType::Typedef,
-            clean::TypeKind::Foreign => ItemType::ForeignType,
-            clean::TypeKind::Macro => ItemType::Macro,
-            clean::TypeKind::Attr => ItemType::ProcAttribute,
-            clean::TypeKind::Derive => ItemType::ProcDerive,
-            clean::TypeKind::TraitAlias => ItemType::TraitAlias,
-            clean::TypeKind::Primitive => ItemType::Primitive,
+impl From<DefKind> for ItemType {
+    fn from(other: DefKind) -> Self {
+        match other {
+            DefKind::Enum => Self::Enum,
+            DefKind::Fn => Self::Function,
+            DefKind::Mod => Self::Module,
+            DefKind::Const => Self::Constant,
+            DefKind::Static => Self::Static,
+            DefKind::Struct => Self::Struct,
+            DefKind::Union => Self::Union,
+            DefKind::Trait => Self::Trait,
+            DefKind::TyAlias => Self::Typedef,
+            DefKind::TraitAlias => Self::TraitAlias,
+            DefKind::Macro(kind) => match kind {
+                MacroKind::Bang => ItemType::Macro,
+                MacroKind::Attr => ItemType::ProcAttribute,
+                MacroKind::Derive => ItemType::ProcDerive,
+            },
+            DefKind::ForeignTy
+            | DefKind::Variant
+            | DefKind::AssocTy
+            | DefKind::TyParam
+            | DefKind::ConstParam
+            | DefKind::Ctor(..)
+            | DefKind::AssocFn
+            | DefKind::AssocConst
+            | DefKind::ExternCrate
+            | DefKind::Use
+            | DefKind::ForeignMod
+            | DefKind::AnonConst
+            | DefKind::OpaqueTy
+            | DefKind::Field
+            | DefKind::LifetimeParam
+            | DefKind::GlobalAsm
+            | DefKind::Impl
+            | DefKind::Closure
+            | DefKind::Generator => Self::ForeignType,
         }
     }
 }
diff --git a/src/librustdoc/html/render/cache.rs b/src/librustdoc/html/render/cache.rs
index b8544a0f439a7..f93a0d4fc2d72 100644
--- a/src/librustdoc/html/render/cache.rs
+++ b/src/librustdoc/html/render/cache.rs
@@ -7,7 +7,7 @@ use rustc_span::symbol::{sym, Symbol};
 use serde::ser::{Serialize, SerializeStruct, Serializer};
 
 use crate::clean::types::{
-    FnDecl, FnRetTy, GenericBound, Generics, GetDefId, Type, TypeKind, WherePredicate,
+    FnDecl, FnRetTy, GenericBound, Generics, GetDefId, Type, WherePredicate,
 };
 use crate::clean::{self, AttributesExt};
 use crate::formats::cache::Cache;
@@ -316,15 +316,15 @@ crate fn get_real_types<'tcx>(
     arg: &Type,
     tcx: TyCtxt<'tcx>,
     recurse: i32,
-    res: &mut FxHashSet<(Type, TypeKind)>,
+    res: &mut FxHashSet<(Type, ItemType)>,
 ) -> usize {
-    fn insert(res: &mut FxHashSet<(Type, TypeKind)>, tcx: TyCtxt<'_>, ty: Type) -> usize {
+    fn insert(res: &mut FxHashSet<(Type, ItemType)>, tcx: TyCtxt<'_>, ty: Type) -> usize {
         if let Some(kind) = ty.def_id().map(|did| tcx.def_kind(did).into()) {
             res.insert((ty, kind));
             1
         } else if ty.is_primitive() {
             // This is a primitive, let's store it as such.
-            res.insert((ty, TypeKind::Primitive));
+            res.insert((ty, ItemType::Primitive));
             1
         } else {
             0
@@ -394,7 +394,7 @@ crate fn get_all_types<'tcx>(
     generics: &Generics,
     decl: &FnDecl,
     tcx: TyCtxt<'tcx>,
-) -> (Vec<(Type, TypeKind)>, Vec<(Type, TypeKind)>) {
+) -> (Vec<(Type, ItemType)>, Vec<(Type, ItemType)>) {
     let mut all_types = FxHashSet::default();
     for arg in decl.inputs.values.iter() {
         if arg.type_.is_self_type() {
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index f6d6b34f8bef9..67ccf2137bf6e 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -54,7 +54,7 @@ use rustc_span::symbol::{kw, sym, Symbol};
 use serde::ser::SerializeSeq;
 use serde::{Serialize, Serializer};
 
-use crate::clean::{self, GetDefId, RenderedLink, SelfTy, TypeKind};
+use crate::clean::{self, GetDefId, RenderedLink, SelfTy};
 use crate::docfs::PathError;
 use crate::error::Error;
 use crate::formats::cache::Cache;
@@ -182,11 +182,11 @@ impl Serialize for IndexItemFunctionType {
 #[derive(Debug)]
 crate struct TypeWithKind {
     ty: RenderType,
-    kind: TypeKind,
+    kind: ItemType,
 }
 
-impl From<(RenderType, TypeKind)> for TypeWithKind {
-    fn from(x: (RenderType, TypeKind)) -> TypeWithKind {
+impl From<(RenderType, ItemType)> for TypeWithKind {
+    fn from(x: (RenderType, ItemType)) -> TypeWithKind {
         TypeWithKind { ty: x.0, kind: x.1 }
     }
 }
@@ -196,7 +196,7 @@ impl Serialize for TypeWithKind {
     where
         S: Serializer,
     {
-        (&self.ty.name, ItemType::from(self.kind)).serialize(serializer)
+        (&self.ty.name, self.kind).serialize(serializer)
     }
 }
 
@@ -1305,7 +1305,10 @@ fn render_impl(
         if let Some(use_absolute) = use_absolute {
             write!(
                 w,
-                "<details class=\"rustdoc-toggle implementors-toggle\"><summary><h3 id=\"{}\" class=\"impl\"{}><code class=\"in-band\">",
+                "<details class=\"rustdoc-toggle implementors-toggle\">\
+                     <summary>\
+                         <h3 id=\"{}\" class=\"impl\"{}>\
+                             <code class=\"in-band\">",
                 id, aliases
             );
             close_tags.insert_str(0, "</details>");
@@ -1331,7 +1334,10 @@ fn render_impl(
         } else {
             write!(
                 w,
-                "<details class=\"rustdoc-toggle implementors-toggle\"><summary><h3 id=\"{}\" class=\"impl\"{}><code class=\"in-band\">{}</code>",
+                "<details class=\"rustdoc-toggle implementors-toggle\">\
+                     <summary>\
+                         <h3 id=\"{}\" class=\"impl\"{}>\
+                             <code class=\"in-band\">{}</code>",
                 id,
                 aliases,
                 i.inner_impl().print(false, cx)
@@ -1347,8 +1353,7 @@ fn render_impl(
             outer_const_version,
         );
         write_srclink(cx, &i.impl_item, w);
-        w.write_str("</h3>");
-        w.write_str("</summary>");
+        w.write_str("</h3></summary>");
 
         if trait_.is_some() {
             if let Some(portability) = portability(&i.impl_item, Some(parent)) {
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 9d7d8a7cb8aef..5865da9440b16 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -28,8 +28,8 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer)
     // Write the breadcrumb trail header for the top
     buf.write_str("<h1 class=\"fqn\"><span class=\"in-band\">");
     let name = match *item.kind {
-        clean::ModuleItem(ref m) => {
-            if m.is_crate {
+        clean::ModuleItem(_) => {
+            if item.is_crate() {
                 "Crate "
             } else {
                 "Module "
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 10d5b9807b010..80ef0073da8a4 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -10,7 +10,6 @@ use rustc_ast::ast;
 use rustc_hir::def::CtorKind;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::def_id::{DefId, CRATE_DEF_INDEX};
-use rustc_span::symbol::Symbol;
 use rustc_span::Pos;
 
 use rustdoc_json_types::*;
@@ -34,10 +33,17 @@ impl JsonRenderer<'_> {
                 did.map(|did| (link.clone(), from_def_id(did)))
             })
             .collect();
-        let clean::Item { span, name, attrs, kind, visibility, def_id } = item;
-        let inner = match *kind {
+        let docs = item.attrs.collapsed_doc_value();
+        let attrs = item
+            .attrs
+            .other_attrs
+            .iter()
+            .map(rustc_ast_pretty::pprust::attribute_to_string)
+            .collect();
+        let clean::Item { span, name, attrs: _, kind: _, visibility, def_id } = item;
+        let inner = match *item.kind {
             clean::StrippedItem(_) => return None,
-            kind => from_clean_item_kind(kind, self.tcx, &name),
+            _ => from_clean_item(item, self.tcx),
         };
         Some(Item {
             id: from_def_id(def_id),
@@ -45,12 +51,8 @@ impl JsonRenderer<'_> {
             name: name.map(|sym| sym.to_string()),
             span: self.convert_span(span),
             visibility: self.convert_visibility(visibility),
-            docs: attrs.collapsed_doc_value(),
-            attrs: attrs
-                .other_attrs
-                .iter()
-                .map(rustc_ast_pretty::pprust::attribute_to_string)
-                .collect(),
+            docs,
+            attrs,
             deprecation: deprecation.map(from_deprecation),
             inner,
             links,
@@ -172,10 +174,12 @@ crate fn from_def_id(did: DefId) -> Id {
     Id(format!("{}:{}", did.krate.as_u32(), u32::from(did.index)))
 }
 
-fn from_clean_item_kind(item: clean::ItemKind, tcx: TyCtxt<'_>, name: &Option<Symbol>) -> ItemEnum {
+fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
     use clean::ItemKind::*;
-    match item {
-        ModuleItem(m) => ItemEnum::Module(m.into_tcx(tcx)),
+    let name = item.name;
+    let is_crate = item.is_crate();
+    match *item.kind {
+        ModuleItem(m) => ItemEnum::Module(Module { is_crate, items: ids(m.items) }),
         ImportItem(i) => ItemEnum::Import(i.into_tcx(tcx)),
         StructItem(s) => ItemEnum::Struct(s.into_tcx(tcx)),
         UnionItem(u) => ItemEnum::Union(u.into_tcx(tcx)),
@@ -214,12 +218,6 @@ fn from_clean_item_kind(item: clean::ItemKind, tcx: TyCtxt<'_>, name: &Option<Sy
     }
 }
 
-impl FromWithTcx<clean::Module> for Module {
-    fn from_tcx(module: clean::Module, _tcx: TyCtxt<'_>) -> Self {
-        Module { is_crate: module.is_crate, items: ids(module.items) }
-    }
-}
-
 impl FromWithTcx<clean::Struct> for Struct {
     fn from_tcx(struct_: clean::Struct, tcx: TyCtxt<'_>) -> Self {
         let clean::Struct { struct_type, generics, fields, fields_stripped } = struct_;
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index b048e7f919fa4..96ea4b6c3b8c1 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -181,21 +181,8 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
         Ok(())
     }
 
-    fn mod_item_in(&mut self, item: &clean::Item) -> Result<(), Error> {
-        use clean::types::ItemKind::*;
-        if let ModuleItem(m) = &*item.kind {
-            for item in &m.items {
-                match &*item.kind {
-                    // These don't have names so they don't get added to the output by default
-                    ImportItem(_) => self.item(item.clone()).unwrap(),
-                    ExternCrateItem { .. } => self.item(item.clone()).unwrap(),
-                    ImplItem(i) => i.items.iter().for_each(|i| self.item(i.clone()).unwrap()),
-                    _ => {}
-                }
-            }
-        }
-        self.item(item.clone()).unwrap();
-        Ok(())
+    fn mod_item_in(&mut self, _item: &clean::Item) -> Result<(), Error> {
+        unreachable!("RUN_ON_MODULE = false should never call mod_item_in")
     }
 
     fn after_krate(&mut self) -> Result<(), Error> {
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index d852728b74962..3538182427a93 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -79,7 +79,6 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             &krate.item,
             self.cx.tcx.crate_name,
         );
-        top_level_module.is_crate = true;
         // Attach the crate's exported macros to the top-level module.
         // In the case of macros 2.0 (`pub macro`), and for built-in `derive`s or attributes as
         // well (_e.g._, `Copy`), these are wrongly bundled in there too, so we need to fix that by
diff --git a/src/test/ui/async-await/large_moves.rs b/src/test/ui/async-await/large_moves.rs
new file mode 100644
index 0000000000000..4fac046beef62
--- /dev/null
+++ b/src/test/ui/async-await/large_moves.rs
@@ -0,0 +1,24 @@
+#![deny(large_assignments)]
+#![feature(large_assignments)]
+#![move_size_limit = "1000"]
+// build-fail
+// only-x86_64
+
+// edition:2018
+
+fn main() {
+    let x = async { //~ ERROR large_assignments
+        let y = [0; 9999];
+        dbg!(y);
+        thing(&y).await;
+        dbg!(y);
+    };
+    let z = (x, 42); //~ ERROR large_assignments
+    //~^ ERROR large_assignments
+    let a = z.0; //~ ERROR large_assignments
+    let b = z.1;
+}
+
+async fn thing(y: &[u8]) {
+    dbg!(y);
+}
diff --git a/src/test/ui/async-await/large_moves.stderr b/src/test/ui/async-await/large_moves.stderr
new file mode 100644
index 0000000000000..8c47ec0ed9d38
--- /dev/null
+++ b/src/test/ui/async-await/large_moves.stderr
@@ -0,0 +1,38 @@
+error: moving 10024 bytes
+  --> $DIR/large_moves.rs:10:13
+   |
+LL |       let x = async {
+   |  _____________^
+LL | |         let y = [0; 9999];
+LL | |         dbg!(y);
+LL | |         thing(&y).await;
+LL | |         dbg!(y);
+LL | |     };
+   | |_____^ value moved from here
+   |
+note: the lint level is defined here
+  --> $DIR/large_moves.rs:1:9
+   |
+LL | #![deny(large_assignments)]
+   |         ^^^^^^^^^^^^^^^^^
+
+error: moving 10024 bytes
+  --> $DIR/large_moves.rs:16:14
+   |
+LL |     let z = (x, 42);
+   |              ^ value moved from here
+
+error: moving 10024 bytes
+  --> $DIR/large_moves.rs:16:13
+   |
+LL |     let z = (x, 42);
+   |             ^^^^^^^ value moved from here
+
+error: moving 10024 bytes
+  --> $DIR/large_moves.rs:18:13
+   |
+LL |     let a = z.0;
+   |             ^^^ value moved from here
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/ui/feature-gates/feature-gate-large-assignments.rs b/src/test/ui/feature-gates/feature-gate-large-assignments.rs
new file mode 100644
index 0000000000000..7e9e574bfa08f
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-large-assignments.rs
@@ -0,0 +1,5 @@
+// check that `move_size_limit is feature-gated
+
+#![move_size_limit = "42"] //~ ERROR the `#[move_size_limit]` attribute is an experimental feature
+
+fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-large-assignments.stderr b/src/test/ui/feature-gates/feature-gate-large-assignments.stderr
new file mode 100644
index 0000000000000..8ddc3043e966c
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-large-assignments.stderr
@@ -0,0 +1,12 @@
+error[E0658]: the `#[move_size_limit]` attribute is an experimental feature
+  --> $DIR/feature-gate-large-assignments.rs:3:1
+   |
+LL | #![move_size_limit = "42"]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #83518 <https://github.com/rust-lang/rust/issues/83518> for more information
+   = help: add `#![feature(large_assignments)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/tools/clippy/.github/PULL_REQUEST_TEMPLATE.md b/src/tools/clippy/.github/PULL_REQUEST_TEMPLATE.md
index a3f114e0bb34f..780ee9d63dfd2 100644
--- a/src/tools/clippy/.github/PULL_REQUEST_TEMPLATE.md
+++ b/src/tools/clippy/.github/PULL_REQUEST_TEMPLATE.md
@@ -3,7 +3,9 @@ Thank you for making Clippy better!
 We're collecting our changelog from pull request descriptions.
 If your PR only includes internal changes, you can just write
 `changelog: none`. Otherwise, please write a short comment
-explaining your change.
+explaining your change. Also, it's helpful for us that
+the lint name is put into brackets `[]` and backticks `` ` ` ``,
+e.g. ``[`lint_name`]``.
 
 If your PR fixes an issue, you can add "fixes #issue_number" into this
 PR description. This way the issue will be automatically closed when
@@ -29,4 +31,5 @@ Delete this line and everything above before opening your PR.
 ---
 
 *Please write a short comment explaining your change (or "none" for internal only changes)*
+
 changelog:
diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml
index 47253eecc4c4c..ae6f1aa1b30be 100644
--- a/src/tools/clippy/.github/workflows/clippy_bors.yml
+++ b/src/tools/clippy/.github/workflows/clippy_bors.yml
@@ -34,15 +34,16 @@ jobs:
       run: |
         MESSAGE=$(git log --format=%B -n 1)
         PR=$(echo "$MESSAGE" | grep -o "#[0-9]*" | head -1 | sed -e 's/^#//')
-        output=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR" | \
-          python -c "import sys, json; print(json.load(sys.stdin)['body'])" | \
-          grep "^changelog: " | \
-          sed "s/changelog: //g")
-        if [[ -z "$output" ]]; then
+        body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR" | \
+          python -c "import sys, json; print(json.load(sys.stdin)['body'])")
+        output=$(grep "^changelog:\s*\S" <<< "$body" | sed "s/changelog:\s*//g") || {
           echo "ERROR: PR body must contain 'changelog: ...'"
           exit 1
-        elif [[ "$output" = "none" ]]; then
+        }
+        if [[ "$output" = "none" ]]; then
           echo "WARNING: changelog is 'none'"
+        else
+          echo "changelog: $output"
         fi
       env:
         PYTHONIOENCODING: 'utf-8'
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index 73997192ae0d5..204d56e2a9854 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -179,7 +179,7 @@ Current stable, released 2021-03-25
 
 * Replace [`find_map`] with [`manual_find_map`]
   [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
-* [`unknown_clippy_lints`] Now integrated in the `unknown_lints` rustc lint
+* `unknown_clippy_lints` Now integrated in the `unknown_lints` rustc lint
   [#6653](https://github.com/rust-lang/rust-clippy/pull/6653)
 
 ### Enhancements
@@ -280,7 +280,7 @@ Released 2021-02-11
 
 * Previously deprecated [`str_to_string`] and [`string_to_string`] have been un-deprecated
   as `restriction` lints [#6333](https://github.com/rust-lang/rust-clippy/pull/6333)
-* Deprecate [`panic_params`] lint. This is now available in rustc as `panic_fmt`
+* Deprecate `panic_params` lint. This is now available in rustc as `non_fmt_panic`
   [#6351](https://github.com/rust-lang/rust-clippy/pull/6351)
 * Move [`map_err_ignore`] to `restriction`
   [#6416](https://github.com/rust-lang/rust-clippy/pull/6416)
@@ -419,7 +419,7 @@ Released 2020-12-31
   [#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
 * Rename `zero_width_space` to [`invisible_characters`]
   [#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
-* Deprecate [`drop_bounds`] (uplifted)
+* Deprecate `drop_bounds` (uplifted)
   [#6111](https://github.com/rust-lang/rust-clippy/pull/6111)
 * Move [`string_lit_as_bytes`] to `nursery`
   [#6117](https://github.com/rust-lang/rust-clippy/pull/6117)
@@ -1018,7 +1018,7 @@ Released 2020-03-12
   [#5015](https://github.com/rust-lang/rust-clippy/pull/5015)
 * Move [`range_plus_one`] to pedantic group [#5057](https://github.com/rust-lang/rust-clippy/pull/5057)
 * Move [`debug_assert_with_mut_call`] to nursery group [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
-* Deprecate [`unused_label`] [#4930](https://github.com/rust-lang/rust-clippy/pull/4930)
+* Deprecate `unused_label` [#4930](https://github.com/rust-lang/rust-clippy/pull/4930)
 
 ### Enhancements
 
@@ -1046,7 +1046,7 @@ Released 2020-03-12
 * [`wildcard_enum_match_arm`] [#4934](https://github.com/rust-lang/rust-clippy/pull/4934)
 * [`cognitive_complexity`] [#4935](https://github.com/rust-lang/rust-clippy/pull/4935)
 * [`decimal_literal_representation`] [#4956](https://github.com/rust-lang/rust-clippy/pull/4956)
-* [`unknown_clippy_lints`] [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
+* `unknown_clippy_lints` [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
 * [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978)
 * [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022)
 * [`if_let_some_result`] [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
@@ -1080,7 +1080,7 @@ Released 2020-01-30
   [Inside Rust Blog](https://blog.rust-lang.org/inside-rust/2019/11/04/Clippy-removes-plugin-interface.html) for
   details [#4714](https://github.com/rust-lang/rust-clippy/pull/4714)
 * Move [`use_self`] to nursery group [#4863](https://github.com/rust-lang/rust-clippy/pull/4863)
-* Deprecate [`into_iter_on_array`] [#4788](https://github.com/rust-lang/rust-clippy/pull/4788)
+* Deprecate `into_iter_on_array` [#4788](https://github.com/rust-lang/rust-clippy/pull/4788)
 * Expand [`string_lit_as_bytes`] to also trigger when literal has escapes
   [#4808](https://github.com/rust-lang/rust-clippy/pull/4808)
 * Fix false positive in `comparison_chain` [#4842](https://github.com/rust-lang/rust-clippy/pull/4842)
@@ -1282,7 +1282,7 @@ Released 2019-05-20
 
 [1fac380..37f5c1e](https://github.com/rust-lang/rust-clippy/compare/1fac380...37f5c1e)
 
-* New lint: [`drop_bounds`] to detect `T: Drop` bounds
+* New lint: `drop_bounds` to detect `T: Drop` bounds
 * Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
 * Rename `cyclomatic_complexity` to [`cognitive_complexity`], start work on making lint more practical for Rust code
 * Move [`get_unwrap`] to the restriction category
@@ -1375,7 +1375,7 @@ Released 2019-01-17
 
 * New lints: [`slow_vector_initialization`], [`mem_discriminant_non_enum`],
   [`redundant_clone`], [`wildcard_dependencies`],
-  [`into_iter_on_ref`], [`into_iter_on_array`], [`deprecated_cfg_attr`],
+  [`into_iter_on_ref`], `into_iter_on_array`, [`deprecated_cfg_attr`],
   [`mem_discriminant_non_enum`], [`cargo_common_metadata`]
 * Add support for `u128` and `i128` to integer related lints
 * Add float support to `mistyped_literal_suffixes`
@@ -1649,7 +1649,7 @@ Released 2018-09-13
 
 ## 0.0.166
 * Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)*
-* New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], [`invalid_ref`], [`option_map_or_none`],
+* New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], `invalid_ref`, [`option_map_or_none`],
   [`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`],
   [`transmute_int_to_float`]
 
@@ -2037,7 +2037,7 @@ Released 2018-09-13
 
 ## 0.0.64 — 2016-04-26
 * Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)*
-* New lints: [`temporary_cstring_as_ptr`], [`unsafe_removed_from_name`], and [`mem_forget`]
+* New lints: `temporary_cstring_as_ptr`, [`unsafe_removed_from_name`], and [`mem_forget`]
 
 ## 0.0.63 — 2016-04-08
 * Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)*
@@ -2091,7 +2091,7 @@ Released 2018-09-13
 
 ## 0.0.49 — 2016-03-09
 * Update to *rustc 1.9.0-nightly (eabfc160f 2016-03-08)*
-* New lints: [`overflow_check_conditional`], [`unused_label`], [`new_without_default`]
+* New lints: [`overflow_check_conditional`], `unused_label`, [`new_without_default`]
 
 ## 0.0.48 — 2016-03-07
 * Fixed: ICE in [`needless_range_loop`] with globals
@@ -2124,6 +2124,7 @@ Released 2018-09-13
 [`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_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
 [`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
 [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
@@ -2148,6 +2149,7 @@ Released 2018-09-13
 [`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref
 [`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy
 [`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr
+[`cloned_instead_of_copied`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied
 [`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan
 [`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null
 [`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned
@@ -2178,7 +2180,6 @@ Released 2018-09-13
 [`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
 [`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg
 [`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens
-[`drop_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_bounds
 [`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
 [`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref
 [`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
@@ -2216,6 +2217,7 @@ Released 2018-09-13
 [`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next
 [`find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#find_map
 [`flat_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_identity
+[`flat_map_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_option
 [`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic
 [`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp
 [`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const
@@ -2264,10 +2266,9 @@ Released 2018-09-13
 [`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
 [`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic
 [`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division
-[`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array
 [`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
 [`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering
-[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref
+[`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage
 [`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
 [`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
 [`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
@@ -2402,7 +2403,6 @@ Released 2018-09-13
 [`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional
 [`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
 [`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn
-[`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params
 [`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
@@ -2488,7 +2488,6 @@ Released 2018-09-13
 [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
 [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
 [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
-[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
 [`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
 [`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display
 [`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
@@ -2517,13 +2516,13 @@ Released 2018-09-13
 [`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg
 [`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp
 [`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord
-[`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints
 [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
 [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
 [`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
 [`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
 [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
 [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
+[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
 [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
 [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
 [`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
@@ -2541,7 +2540,6 @@ Released 2018-09-13
 [`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice
 [`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
 [`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
-[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
 [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
 [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
 [`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings
diff --git a/src/tools/clippy/CONTRIBUTING.md b/src/tools/clippy/CONTRIBUTING.md
index e0a4d4455e9c6..02d2b63c9e199 100644
--- a/src/tools/clippy/CONTRIBUTING.md
+++ b/src/tools/clippy/CONTRIBUTING.md
@@ -21,10 +21,10 @@ All contributors are expected to follow the [Rust Code of Conduct].
     - [IntelliJ Rust](#intellij-rust)
     - [Rust Analyzer](#rust-analyzer)
   - [How Clippy works](#how-clippy-works)
-  - [Syncing changes between Clippy and [`rust-lang/rust`]](#syncing-changes-between-clippy-and-rust-langrust)
+  - [Syncing changes between Clippy and `rust-lang/rust`](#syncing-changes-between-clippy-and-rust-langrust)
     - [Patching git-subtree to work with big repos](#patching-git-subtree-to-work-with-big-repos)
-    - [Performing the sync from [`rust-lang/rust`] to Clippy](#performing-the-sync-from-rust-langrust-to-clippy)
-    - [Performing the sync from Clippy to [`rust-lang/rust`]](#performing-the-sync-from-clippy-to-rust-langrust)
+    - [Performing the sync from `rust-lang/rust` to Clippy](#performing-the-sync-from-rust-langrust-to-clippy)
+    - [Performing the sync from Clippy to `rust-lang/rust`](#performing-the-sync-from-clippy-to-rust-langrust)
     - [Defining remotes](#defining-remotes)
   - [Issue and PR triage](#issue-and-pr-triage)
   - [Bors and Homu](#bors-and-homu)
diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
index a0993bb6913e7..c565e29d07801 100644
--- a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
+++ b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
@@ -127,10 +127,9 @@ fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>)
             _ => &block.expr,
         };
         // function call
-        if let Some(args) = match_panic_call(cx, begin_panic_call);
-        if args.len() == 1;
+        if let Some(arg) = match_panic_call(cx, begin_panic_call);
         // bind the second argument of the `assert!` macro if it exists
-        if let panic_message = snippet_opt(cx, args[0].span);
+        if let panic_message = snippet_opt(cx, arg.span);
         // second argument of begin_panic is irrelevant
         // as is the second match arm
         then {
diff --git a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
new file mode 100644
index 0000000000000..bee706ed40215
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
@@ -0,0 +1,75 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::{ast_utils, is_direct_expn_of};
+use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// **What it does:** This lint warns about boolean comparisons in assert-like macros.
+    ///
+    /// **Why is this bad?** It is shorter to use the equivalent.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// // Bad
+    /// assert_eq!("a".is_empty(), false);
+    /// assert_ne!("a".is_empty(), true);
+    ///
+    /// // Good
+    /// assert!(!"a".is_empty());
+    /// ```
+    pub BOOL_ASSERT_COMPARISON,
+    style,
+    "Using a boolean as comparison value in an assert_* macro when there is no need"
+}
+
+declare_lint_pass!(BoolAssertComparison => [BOOL_ASSERT_COMPARISON]);
+
+fn is_bool_lit(e: &Expr) -> bool {
+    matches!(
+        e.kind,
+        ExprKind::Lit(Lit {
+            kind: LitKind::Bool(_),
+            ..
+        })
+    ) && !e.span.from_expansion()
+}
+
+impl EarlyLintPass for BoolAssertComparison {
+    fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
+        let macros = ["assert_eq", "debug_assert_eq"];
+        let inverted_macros = ["assert_ne", "debug_assert_ne"];
+
+        for mac in macros.iter().chain(inverted_macros.iter()) {
+            if let Some(span) = is_direct_expn_of(e.span, mac) {
+                if let Some([a, b]) = ast_utils::extract_assert_macro_args(e) {
+                    let nb_bool_args = is_bool_lit(a) as usize + is_bool_lit(b) as usize;
+
+                    if nb_bool_args != 1 {
+                        // If there are two boolean arguments, we definitely don't understand
+                        // what's going on, so better leave things as is...
+                        //
+                        // Or there is simply no boolean and then we can leave things as is!
+                        return;
+                    }
+
+                    let non_eq_mac = &mac[..mac.len() - 3];
+                    span_lint_and_sugg(
+                        cx,
+                        BOOL_ASSERT_COMPARISON,
+                        span,
+                        &format!("used `{}!` with a literal bool", mac),
+                        "replace it with",
+                        format!("{}!(..)", non_eq_mac),
+                        Applicability::MaybeIncorrect,
+                    );
+                    return;
+                }
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs
index 58d9aa9c005c2..67f0e0c78700b 100644
--- a/src/tools/clippy/clippy_lints/src/booleans.rs
+++ b/src/tools/clippy/clippy_lints/src/booleans.rs
@@ -261,7 +261,7 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
             }
             METHODS_WITH_NEGATION
                 .iter()
-                .cloned()
+                .copied()
                 .flat_map(|(a, b)| vec![(a, b), (b, a)])
                 .find(|&(a, _)| {
                     let path: &str = &path.ident.name.as_str();
diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs
index d7136f84cc3af..6a2666bc6c011 100644
--- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs
+++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs
@@ -323,7 +323,7 @@ fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function:
         if let [int] = &*tp.segments;
         then {
             let name = &int.ident.name.as_str();
-            candidates.iter().find(|c| name == *c).cloned()
+            candidates.iter().find(|c| name == *c).copied()
         } else {
             None
         }
@@ -337,7 +337,7 @@ fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> {
         if let [ty] = &*path.segments;
         then {
             let name = &ty.ident.name.as_str();
-            INTS.iter().find(|c| name == *c).cloned()
+            INTS.iter().find(|c| name == *c).copied()
         } else {
             None
         }
diff --git a/src/tools/clippy/clippy_lints/src/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/collapsible_match.rs
index 04fff237bb4cb..ab22578abd674 100644
--- a/src/tools/clippy/clippy_lints/src/collapsible_match.rs
+++ b/src/tools/clippy/clippy_lints/src/collapsible_match.rs
@@ -1,11 +1,11 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::visitors::LocalUsedVisitor;
-use clippy_utils::{path_to_local, SpanlessEq};
+use clippy_utils::{is_lang_ctor, path_to_local, SpanlessEq};
 use if_chain::if_chain;
-use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
-use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind, UnOp};
+use rustc_hir::LangItem::OptionNone;
+use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{DefIdTree, TyCtxt, TypeckResults};
+use rustc_middle::ty::TypeckResults;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::{MultiSpan, Span};
 
@@ -52,7 +52,7 @@ declare_lint_pass!(CollapsibleMatch => [COLLAPSIBLE_MATCH]);
 impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
         if let ExprKind::Match(_expr, arms, _source) = expr.kind {
-            if let Some(wild_arm) = arms.iter().rfind(|arm| arm_is_wild_like(arm, cx.tcx)) {
+            if let Some(wild_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) {
                 for arm in arms {
                     check_arm(arm, wild_arm, cx);
                 }
@@ -75,7 +75,7 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext
         // match <local> { .. }
         if let Some(binding_id) = path_to_local(strip_ref_operators(expr_in, cx.typeck_results()));
         // one of the branches must be "wild-like"
-        if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(arm_inner, cx.tcx));
+        if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(cx, arm_inner));
         let (wild_inner_arm, non_wild_inner_arm) =
             (&arms_inner[wild_inner_arm_idx], &arms_inner[1 - wild_inner_arm_idx]);
         if !pat_contains_or(non_wild_inner_arm.pat);
@@ -126,13 +126,13 @@ fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir>
 /// A "wild-like" pattern is wild ("_") or `None`.
 /// For this lint to apply, both the outer and inner match expressions
 /// must have "wild-like" branches that can be combined.
-fn arm_is_wild_like(arm: &Arm<'_>, tcx: TyCtxt<'_>) -> bool {
+fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
     if arm.guard.is_some() {
         return false;
     }
     match arm.pat.kind {
         PatKind::Binding(..) | PatKind::Wild => true,
-        PatKind::Path(QPath::Resolved(None, path)) if is_none_ctor(path.res, tcx) => true,
+        PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
         _ => false,
     }
 }
@@ -164,17 +164,6 @@ fn pat_contains_or(pat: &Pat<'_>) -> bool {
     result
 }
 
-fn is_none_ctor(res: Res, tcx: TyCtxt<'_>) -> bool {
-    if let Some(none_id) = tcx.lang_items().option_none_variant() {
-        if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = res {
-            if let Some(variant_id) = tcx.parent(id) {
-                return variant_id == none_id;
-            }
-        }
-    }
-    false
-}
-
 /// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
 /// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
 fn strip_ref_operators<'hir>(mut expr: &'hir Expr<'hir>, typeck_results: &TypeckResults<'_>) -> &'hir Expr<'hir> {
diff --git a/src/tools/clippy/clippy_lints/src/comparison_chain.rs b/src/tools/clippy/clippy_lints/src/comparison_chain.rs
index 31ae63b51849c..42e153909ce75 100644
--- a/src/tools/clippy/clippy_lints/src/comparison_chain.rs
+++ b/src/tools/clippy/clippy_lints/src/comparison_chain.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::ty::implements_trait;
-use clippy_utils::{get_trait_def_id, if_sequence, parent_node_is_if_expr, paths, SpanlessEq};
+use clippy_utils::{get_trait_def_id, if_sequence, is_else_clause, paths, SpanlessEq};
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain {
         }
 
         // We only care about the top-most `if` in the chain
-        if parent_node_is_if_expr(expr, cx) {
+        if is_else_clause(cx.tcx, expr) {
             return;
         }
 
diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs
index 8b503c9a0306b..f956d171bfbe0 100644
--- a/src/tools/clippy/clippy_lints/src/copies.rs
+++ b/src/tools/clippy/clippy_lints/src/copies.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
 use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
 use clippy_utils::{
-    both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, in_macro, parent_node_is_if_expr,
+    both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, in_macro, is_else_clause,
     run_lints, search_same, ContainsName, SpanlessEq, SpanlessHash,
 };
 use if_chain::if_chain;
@@ -188,13 +188,18 @@ fn lint_same_then_else<'tcx>(
     expr: &'tcx Expr<'_>,
 ) {
     // We only lint ifs with multiple blocks
-    if blocks.len() < 2 || parent_node_is_if_expr(expr, cx) {
+    if blocks.len() < 2 || is_else_clause(cx.tcx, expr) {
         return;
     }
 
     // Check if each block has shared code
     let has_expr = blocks[0].expr.is_some();
-    let (start_eq, mut end_eq, expr_eq) = scan_block_for_eq(cx, blocks);
+
+    let (start_eq, mut end_eq, expr_eq) = if let Some(block_eq) = scan_block_for_eq(cx, blocks) {
+        (block_eq.start_eq, block_eq.end_eq, block_eq.expr_eq)
+    } else {
+        return;
+    };
 
     // BRANCHES_SHARING_CODE prerequisites
     if has_conditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) {
@@ -290,7 +295,19 @@ fn lint_same_then_else<'tcx>(
     }
 }
 
-fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize, usize, bool) {
+struct BlockEqual {
+    /// The amount statements that are equal from the start
+    start_eq: usize,
+    /// The amount statements that are equal from the end
+    end_eq: usize,
+    ///  An indication if the block expressions are the same. This will also be true if both are
+    /// `None`
+    expr_eq: bool,
+}
+
+/// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `None` to
+/// abort any further processing and avoid duplicate lint triggers.
+fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option<BlockEqual> {
     let mut start_eq = usize::MAX;
     let mut end_eq = usize::MAX;
     let mut expr_eq = true;
@@ -332,7 +349,7 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize,
                     "same as this",
                 );
 
-                return (0, 0, false);
+                return None;
             }
         }
 
@@ -352,7 +369,11 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize,
         end_eq = min_block_size - start_eq;
     }
 
-    (start_eq, end_eq, expr_eq)
+    Some(BlockEqual {
+        start_eq,
+        end_eq,
+        expr_eq,
+    })
 }
 
 fn check_for_warn_of_moved_symbol(
diff --git a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs
index 89088c533ed50..4688b3d51050d 100644
--- a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs
@@ -93,15 +93,6 @@ declare_deprecated_lint! {
     "the replacement suggested by this lint had substantially different behavior"
 }
 
-declare_deprecated_lint! {
-    /// **What it does:** Nothing. This lint has been deprecated.
-    ///
-    /// **Deprecation reason:** This lint has been superseded by the warn-by-default
-    /// `invalid_value` rustc lint.
-    pub INVALID_REF,
-    "superseded by rustc lint `invalid_value`"
-}
-
 declare_deprecated_lint! {
     /// **What it does:** Nothing. This lint has been deprecated.
     ///
@@ -110,24 +101,6 @@ declare_deprecated_lint! {
     "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint"
 }
 
-declare_deprecated_lint! {
-    /// **What it does:** Nothing. This lint has been deprecated.
-    ///
-    /// **Deprecation reason:** This lint has been uplifted to rustc and is now called
-    /// `array_into_iter`.
-    pub INTO_ITER_ON_ARRAY,
-    "this lint has been uplifted to rustc and is now called `array_into_iter`"
-}
-
-declare_deprecated_lint! {
-    /// **What it does:** Nothing. This lint has been deprecated.
-    ///
-    /// **Deprecation reason:** This lint has been uplifted to rustc and is now called
-    /// `unused_labels`.
-    pub UNUSED_LABEL,
-    "this lint has been uplifted to rustc and is now called `unused_labels`"
-}
-
 declare_deprecated_lint! {
     /// **What it does:** Nothing. This lint has been deprecated.
     ///
@@ -147,44 +120,17 @@ declare_deprecated_lint! {
 declare_deprecated_lint! {
     /// **What it does:** Nothing. This lint has been deprecated.
     ///
-    /// **Deprecation reason:** This lint has been uplifted to rustc and is now called
-    /// `drop_bounds`.
-    pub DROP_BOUNDS,
-    "this lint has been uplifted to rustc and is now called `drop_bounds`"
-}
-
-declare_deprecated_lint! {
-    /// **What it does:** Nothing. This lint has been deprecated.
-    ///
-    /// **Deprecation reason:** This lint has been uplifted to rustc and is now called
-    /// `temporary_cstring_as_ptr`.
-    pub TEMPORARY_CSTRING_AS_PTR,
-    "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`"
-}
-
-declare_deprecated_lint! {
-    /// **What it does:** Nothing. This lint has been deprecated.
-    ///
-    /// **Deprecation reason:** This lint has been uplifted to rustc and is now called
-    /// `panic_fmt`.
-    pub PANIC_PARAMS,
-    "this lint has been uplifted to rustc and is now called `panic_fmt`"
-}
-
-declare_deprecated_lint! {
-    /// **What it does:** Nothing. This lint has been deprecated.
-    ///
-    /// **Deprecation reason:** This lint has been integrated into the `unknown_lints`
-    /// rustc lint.
-    pub UNKNOWN_CLIPPY_LINTS,
-    "this lint has been integrated into the `unknown_lints` rustc lint"
+    /// **Deprecation reason:** This lint has been replaced by `manual_find_map`, a
+    /// more specific lint.
+    pub FIND_MAP,
+    "this lint has been replaced by `manual_find_map`, a more specific lint"
 }
 
 declare_deprecated_lint! {
     /// **What it does:** Nothing. This lint has been deprecated.
     ///
-    /// **Deprecation reason:** This lint has been replaced by `manual_find_map`, a
+    /// **Deprecation reason:** This lint has been replaced by `manual_filter_map`, a
     /// more specific lint.
-    pub FIND_MAP,
-    "this lint has been replaced by `manual_find_map`, a more specific lint"
+    pub FILTER_MAP,
+    "this lint has been replaced by `manual_filter_map`, a more specific lint"
 }
diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs
index a815df1691a1c..8db5050a5ac30 100644
--- a/src/tools/clippy/clippy_lints/src/entry.rs
+++ b/src/tools/clippy/clippy_lints/src/entry.rs
@@ -1,17 +1,19 @@
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
-use clippy_utils::ty::{is_type_diagnostic_item, match_type};
-use clippy_utils::SpanlessEq;
-use clippy_utils::{get_item_name, paths};
-use if_chain::if_chain;
+use clippy_utils::{
+    can_move_expr_to_closure_no_visit,
+    diagnostics::span_lint_and_sugg,
+    is_expr_final_block_expr, is_expr_used_or_unified, match_def_path, paths, peel_hir_expr_while,
+    source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context},
+    SpanlessEq,
+};
 use rustc_errors::Applicability;
-use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
-use rustc_hir::{BorrowKind, Expr, ExprKind, UnOp};
+use rustc_hir::{
+    intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
+    Block, Expr, ExprKind, Guard, HirId, Local, Stmt, StmtKind, UnOp,
+};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::hir::map::Map;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
-use rustc_span::sym;
+use rustc_span::{Span, SyntaxContext, DUMMY_SP};
+use std::fmt::Write;
 
 declare_clippy_lint! {
     /// **What it does:** Checks for uses of `contains_key` + `insert` on `HashMap`
@@ -19,15 +21,14 @@ declare_clippy_lint! {
     ///
     /// **Why is this bad?** Using `entry` is more efficient.
     ///
-    /// **Known problems:** Some false negatives, eg.:
+    /// **Known problems:** The suggestion may have type inference errors in some cases. e.g.
     /// ```rust
-    /// # use std::collections::HashMap;
-    /// # let mut map = HashMap::new();
-    /// # let v = 1;
-    /// # let k = 1;
-    /// if !map.contains_key(&k) {
-    ///     map.insert(k.clone(), v);
-    /// }
+    /// let mut map = std::collections::HashMap::new();
+    /// let _ = if !map.contains_key(&0) {
+    ///     map.insert(0, 0)
+    /// } else {
+    ///     None
+    /// };
     /// ```
     ///
     /// **Example:**
@@ -56,132 +57,584 @@ declare_clippy_lint! {
 declare_lint_pass!(HashMapPass => [MAP_ENTRY]);
 
 impl<'tcx> LateLintPass<'tcx> for HashMapPass {
+    #[allow(clippy::too_many_lines)]
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if let ExprKind::If(check, then_block, ref else_block) = expr.kind {
-            if let ExprKind::Unary(UnOp::Not, check) = check.kind {
-                if let Some((ty, map, key)) = check_cond(cx, check) {
-                    // in case of `if !m.contains_key(&k) { m.insert(k, v); }`
-                    // we can give a better error message
-                    let sole_expr = {
-                        else_block.is_none()
-                            && if let ExprKind::Block(then_block, _) = then_block.kind {
-                                (then_block.expr.is_some() as usize) + then_block.stmts.len() == 1
-                            } else {
-                                true
-                            }
-                        // XXXManishearth we can also check for if/else blocks containing `None`.
-                    };
-
-                    let mut visitor = InsertVisitor {
-                        cx,
-                        span: expr.span,
-                        ty,
-                        map,
-                        key,
-                        sole_expr,
-                    };
-
-                    walk_expr(&mut visitor, then_block);
+        let (cond_expr, then_expr, else_expr) = match expr.kind {
+            ExprKind::If(c, t, e) => (c, t, e),
+            _ => return,
+        };
+        let (map_ty, contains_expr) = match try_parse_contains(cx, cond_expr) {
+            Some(x) => x,
+            None => return,
+        };
+
+        let then_search = match find_insert_calls(cx, &contains_expr, then_expr) {
+            Some(x) => x,
+            None => return,
+        };
+
+        let mut app = Applicability::MachineApplicable;
+        let map_str = snippet_with_context(cx, contains_expr.map.span, contains_expr.call_ctxt, "..", &mut app).0;
+        let key_str = snippet_with_context(cx, contains_expr.key.span, contains_expr.call_ctxt, "..", &mut app).0;
+        let sugg = if let Some(else_expr) = else_expr {
+            let else_search = match find_insert_calls(cx, &contains_expr, else_expr) {
+                Some(search) => search,
+                None => return,
+            };
+
+            if then_search.edits.is_empty() && else_search.edits.is_empty() {
+                // No insertions
+                return;
+            } else if then_search.edits.is_empty() || else_search.edits.is_empty() {
+                // if .. { insert } else { .. } or if .. { .. } else { insert }
+                let ((then_str, entry_kind), else_str) = match (else_search.edits.is_empty(), contains_expr.negated) {
+                    (true, true) => (
+                        then_search.snippet_vacant(cx, then_expr.span, &mut app),
+                        snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app),
+                    ),
+                    (true, false) => (
+                        then_search.snippet_occupied(cx, then_expr.span, &mut app),
+                        snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app),
+                    ),
+                    (false, true) => (
+                        else_search.snippet_occupied(cx, else_expr.span, &mut app),
+                        snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app),
+                    ),
+                    (false, false) => (
+                        else_search.snippet_vacant(cx, else_expr.span, &mut app),
+                        snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app),
+                    ),
+                };
+                format!(
+                    "if let {}::{} = {}.entry({}) {} else {}",
+                    map_ty.entry_path(),
+                    entry_kind,
+                    map_str,
+                    key_str,
+                    then_str,
+                    else_str,
+                )
+            } else {
+                // if .. { insert } else { insert }
+                let ((then_str, then_entry), (else_str, else_entry)) = if contains_expr.negated {
+                    (
+                        then_search.snippet_vacant(cx, then_expr.span, &mut app),
+                        else_search.snippet_occupied(cx, else_expr.span, &mut app),
+                    )
+                } else {
+                    (
+                        then_search.snippet_occupied(cx, then_expr.span, &mut app),
+                        else_search.snippet_vacant(cx, else_expr.span, &mut app),
+                    )
+                };
+                let indent_str = snippet_indent(cx, expr.span);
+                let indent_str = indent_str.as_deref().unwrap_or("");
+                format!(
+                    "match {}.entry({}) {{\n{indent}    {entry}::{} => {}\n\
+                        {indent}    {entry}::{} => {}\n{indent}}}",
+                    map_str,
+                    key_str,
+                    then_entry,
+                    reindent_multiline(then_str.into(), true, Some(4 + indent_str.len())),
+                    else_entry,
+                    reindent_multiline(else_str.into(), true, Some(4 + indent_str.len())),
+                    entry = map_ty.entry_path(),
+                    indent = indent_str,
+                )
+            }
+        } else {
+            if then_search.edits.is_empty() {
+                // no insertions
+                return;
+            }
+
+            // if .. { insert }
+            if !then_search.allow_insert_closure {
+                let (body_str, entry_kind) = if contains_expr.negated {
+                    then_search.snippet_vacant(cx, then_expr.span, &mut app)
+                } else {
+                    then_search.snippet_occupied(cx, then_expr.span, &mut app)
+                };
+                format!(
+                    "if let {}::{} = {}.entry({}) {}",
+                    map_ty.entry_path(),
+                    entry_kind,
+                    map_str,
+                    key_str,
+                    body_str,
+                )
+            } else if let Some(insertion) = then_search.as_single_insertion() {
+                let value_str = snippet_with_context(cx, insertion.value.span, then_expr.span.ctxt(), "..", &mut app).0;
+                if contains_expr.negated {
+                    if insertion.value.can_have_side_effects() {
+                        format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, value_str)
+                    } else {
+                        format!("{}.entry({}).or_insert({});", map_str, key_str, value_str)
+                    }
+                } else {
+                    // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here.
+                    // This would need to be a different lint.
+                    return;
                 }
-            } else if let Some(else_block) = *else_block {
-                if let Some((ty, map, key)) = check_cond(cx, check) {
-                    let mut visitor = InsertVisitor {
-                        cx,
-                        span: expr.span,
-                        ty,
-                        map,
-                        key,
-                        sole_expr: false,
-                    };
-
-                    walk_expr(&mut visitor, else_block);
+            } else {
+                let block_str = then_search.snippet_closure(cx, then_expr.span, &mut app);
+                if contains_expr.negated {
+                    format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, block_str)
+                } else {
+                    // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here.
+                    // This would need to be a different lint.
+                    return;
                 }
             }
-        }
+        };
+
+        span_lint_and_sugg(
+            cx,
+            MAP_ENTRY,
+            expr.span,
+            &format!("usage of `contains_key` followed by `insert` on a `{}`", map_ty.name()),
+            "try this",
+            sugg,
+            app,
+        );
     }
 }
 
-fn check_cond<'a>(cx: &LateContext<'_>, check: &'a Expr<'a>) -> Option<(&'static str, &'a Expr<'a>, &'a Expr<'a>)> {
-    if_chain! {
-        if let ExprKind::MethodCall(path, _, params, _) = check.kind;
-        if params.len() >= 2;
-        if path.ident.name == sym!(contains_key);
-        if let ExprKind::AddrOf(BorrowKind::Ref, _, key) = params[1].kind;
-        then {
-            let map = &params[0];
-            let obj_ty = cx.typeck_results().expr_ty(map).peel_refs();
+#[derive(Clone, Copy)]
+enum MapType {
+    Hash,
+    BTree,
+}
+impl MapType {
+    fn name(self) -> &'static str {
+        match self {
+            Self::Hash => "HashMap",
+            Self::BTree => "BTreeMap",
+        }
+    }
+    fn entry_path(self) -> &'static str {
+        match self {
+            Self::Hash => "std::collections::hash_map::Entry",
+            Self::BTree => "std::collections::btree_map::Entry",
+        }
+    }
+}
 
-            return if match_type(cx, obj_ty, &paths::BTREEMAP) {
-                Some(("BTreeMap", map, key))
-            }
-            else if is_type_diagnostic_item(cx, obj_ty, sym::hashmap_type) {
-                Some(("HashMap", map, key))
-            }
-            else {
-                None
+struct ContainsExpr<'tcx> {
+    negated: bool,
+    map: &'tcx Expr<'tcx>,
+    key: &'tcx Expr<'tcx>,
+    call_ctxt: SyntaxContext,
+}
+fn try_parse_contains(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> {
+    let mut negated = false;
+    let expr = peel_hir_expr_while(expr, |e| match e.kind {
+        ExprKind::Unary(UnOp::Not, e) => {
+            negated = !negated;
+            Some(e)
+        },
+        _ => None,
+    });
+    match expr.kind {
+        ExprKind::MethodCall(
+            _,
+            _,
+            [map, Expr {
+                kind: ExprKind::AddrOf(_, _, key),
+                span: key_span,
+                ..
+            }],
+            _,
+        ) if key_span.ctxt() == expr.span.ctxt() => {
+            let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
+            let expr = ContainsExpr {
+                negated,
+                map,
+                key,
+                call_ctxt: expr.span.ctxt(),
             };
+            if match_def_path(cx, id, &paths::BTREEMAP_CONTAINS_KEY) {
+                Some((MapType::BTree, expr))
+            } else if match_def_path(cx, id, &paths::HASHMAP_CONTAINS_KEY) {
+                Some((MapType::Hash, expr))
+            } else {
+                None
+            }
+        },
+        _ => None,
+    }
+}
+
+struct InsertExpr<'tcx> {
+    map: &'tcx Expr<'tcx>,
+    key: &'tcx Expr<'tcx>,
+    value: &'tcx Expr<'tcx>,
+}
+fn try_parse_insert(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> {
+    if let ExprKind::MethodCall(_, _, [map, key, value], _) = expr.kind {
+        let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
+        if match_def_path(cx, id, &paths::BTREEMAP_INSERT) || match_def_path(cx, id, &paths::HASHMAP_INSERT) {
+            Some(InsertExpr { map, key, value })
+        } else {
+            None
         }
+    } else {
+        None
     }
+}
 
-    None
+/// An edit that will need to be made to move the expression to use the entry api
+#[derive(Clone, Copy)]
+enum Edit<'tcx> {
+    /// A semicolon that needs to be removed. Used to create a closure for `insert_with`.
+    RemoveSemi(Span),
+    /// An insertion into the map.
+    Insertion(Insertion<'tcx>),
+}
+impl Edit<'tcx> {
+    fn as_insertion(self) -> Option<Insertion<'tcx>> {
+        if let Self::Insertion(i) = self { Some(i) } else { None }
+    }
+}
+#[derive(Clone, Copy)]
+struct Insertion<'tcx> {
+    call: &'tcx Expr<'tcx>,
+    value: &'tcx Expr<'tcx>,
 }
 
-struct InsertVisitor<'a, 'tcx, 'b> {
-    cx: &'a LateContext<'tcx>,
-    span: Span,
-    ty: &'static str,
-    map: &'b Expr<'b>,
-    key: &'b Expr<'b>,
-    sole_expr: bool,
+/// This visitor needs to do a multiple things:
+/// * Find all usages of the map. An insertion can only be made before any other usages of the map.
+/// * Determine if there's an insertion using the same key. There's no need for the entry api
+///   otherwise.
+/// * Determine if the final statement executed is an insertion. This is needed to use
+///   `or_insert_with`.
+/// * Determine if there's any sub-expression that can't be placed in a closure.
+/// * Determine if there's only a single insert statement. `or_insert` can be used in this case.
+#[allow(clippy::struct_excessive_bools)]
+struct InsertSearcher<'cx, 'tcx> {
+    cx: &'cx LateContext<'tcx>,
+    /// The map expression used in the contains call.
+    map: &'tcx Expr<'tcx>,
+    /// The key expression used in the contains call.
+    key: &'tcx Expr<'tcx>,
+    /// The context of the top level block. All insert calls must be in the same context.
+    ctxt: SyntaxContext,
+    /// Whether this expression can be safely moved into a closure.
+    allow_insert_closure: bool,
+    /// Whether this expression can use the entry api.
+    can_use_entry: bool,
+    /// Whether this expression is the final expression in this code path. This may be a statement.
+    in_tail_pos: bool,
+    // Is this expression a single insert. A slightly better suggestion can be made in this case.
+    is_single_insert: bool,
+    /// If the visitor has seen the map being used.
+    is_map_used: bool,
+    /// The locations where changes need to be made for the suggestion.
+    edits: Vec<Edit<'tcx>>,
+    /// A stack of loops the visitor is currently in.
+    loops: Vec<HirId>,
 }
+impl<'tcx> InsertSearcher<'_, 'tcx> {
+    /// Visit the expression as a branch in control flow. Multiple insert calls can be used, but
+    /// only if they are on separate code paths. This will return whether the map was used in the
+    /// given expression.
+    fn visit_cond_arm(&mut self, e: &'tcx Expr<'_>) -> bool {
+        let is_map_used = self.is_map_used;
+        let in_tail_pos = self.in_tail_pos;
+        self.visit_expr(e);
+        let res = self.is_map_used;
+        self.is_map_used = is_map_used;
+        self.in_tail_pos = in_tail_pos;
+        res
+    }
 
-impl<'a, 'tcx, 'b> Visitor<'tcx> for InsertVisitor<'a, 'tcx, 'b> {
-    type Map = Map<'tcx>;
+    /// Visits an expression which is not itself in a tail position, but other sibling expressions
+    /// may be. e.g. if conditions
+    fn visit_non_tail_expr(&mut self, e: &'tcx Expr<'_>) {
+        let in_tail_pos = self.in_tail_pos;
+        self.in_tail_pos = false;
+        self.visit_expr(e);
+        self.in_tail_pos = in_tail_pos;
+    }
+}
+impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
+    type Map = ErasedMap<'tcx>;
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
+
+    fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
+        match stmt.kind {
+            StmtKind::Semi(e) => {
+                self.visit_expr(e);
+
+                if self.in_tail_pos && self.allow_insert_closure {
+                    // The spans are used to slice the top level expression into multiple parts. This requires that
+                    // they all come from the same part of the source code.
+                    if stmt.span.ctxt() == self.ctxt && e.span.ctxt() == self.ctxt {
+                        self.edits
+                            .push(Edit::RemoveSemi(stmt.span.trim_start(e.span).unwrap_or(DUMMY_SP)));
+                    } else {
+                        self.allow_insert_closure = false;
+                    }
+                }
+            },
+            StmtKind::Expr(e) => self.visit_expr(e),
+            StmtKind::Local(Local { init: Some(e), .. }) => {
+                self.allow_insert_closure &= !self.in_tail_pos;
+                self.in_tail_pos = false;
+                self.is_single_insert = false;
+                self.visit_expr(e);
+            },
+            _ => {
+                self.allow_insert_closure &= !self.in_tail_pos;
+                self.is_single_insert = false;
+            },
+        }
+    }
+
+    fn visit_block(&mut self, block: &'tcx Block<'_>) {
+        // If the block is in a tail position, then the last expression (possibly a statement) is in the
+        // tail position. The rest, however, are not.
+        match (block.stmts, block.expr) {
+            ([], None) => {
+                self.allow_insert_closure &= !self.in_tail_pos;
+            },
+            ([], Some(expr)) => self.visit_expr(expr),
+            (stmts, Some(expr)) => {
+                let in_tail_pos = self.in_tail_pos;
+                self.in_tail_pos = false;
+                for stmt in stmts {
+                    self.visit_stmt(stmt);
+                }
+                self.in_tail_pos = in_tail_pos;
+                self.visit_expr(expr);
+            },
+            ([stmts @ .., stmt], None) => {
+                let in_tail_pos = self.in_tail_pos;
+                self.in_tail_pos = false;
+                for stmt in stmts {
+                    self.visit_stmt(stmt);
+                }
+                self.in_tail_pos = in_tail_pos;
+                self.visit_stmt(stmt);
+            },
+        }
+    }
 
     fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
-        if_chain! {
-            if let ExprKind::MethodCall(path, _, params, _) = expr.kind;
-            if params.len() == 3;
-            if path.ident.name == sym!(insert);
-            if get_item_name(self.cx, self.map) == get_item_name(self.cx, &params[0]);
-            if SpanlessEq::new(self.cx).eq_expr(self.key, &params[1]);
-            if snippet_opt(self.cx, self.map.span) == snippet_opt(self.cx, params[0].span);
-            then {
-                span_lint_and_then(self.cx, MAP_ENTRY, self.span,
-                                   &format!("usage of `contains_key` followed by `insert` on a `{}`", self.ty), |diag| {
-                    if self.sole_expr {
-                        let mut app = Applicability::MachineApplicable;
-                        let help = format!("{}.entry({}).or_insert({});",
-                                           snippet_with_applicability(self.cx, self.map.span, "map", &mut app),
-                                           snippet_with_applicability(self.cx, params[1].span, "..", &mut app),
-                                           snippet_with_applicability(self.cx, params[2].span, "..", &mut app));
-
-                        diag.span_suggestion(
-                            self.span,
-                            "consider using",
-                            help,
-                            Applicability::MachineApplicable, // snippet
-                        );
+        if !self.can_use_entry {
+            return;
+        }
+
+        match try_parse_insert(self.cx, expr) {
+            Some(insert_expr) if SpanlessEq::new(self.cx).eq_expr(self.map, insert_expr.map) => {
+                // Multiple inserts, inserts with a different key, and inserts from a macro can't use the entry api.
+                if self.is_map_used
+                    || !SpanlessEq::new(self.cx).eq_expr(self.key, insert_expr.key)
+                    || expr.span.ctxt() != self.ctxt
+                {
+                    self.can_use_entry = false;
+                    return;
+                }
+
+                self.edits.push(Edit::Insertion(Insertion {
+                    call: expr,
+                    value: insert_expr.value,
+                }));
+                self.is_map_used = true;
+                self.allow_insert_closure &= self.in_tail_pos;
+
+                // The value doesn't affect whether there is only a single insert expression.
+                let is_single_insert = self.is_single_insert;
+                self.visit_non_tail_expr(insert_expr.value);
+                self.is_single_insert = is_single_insert;
+            },
+            _ if SpanlessEq::new(self.cx).eq_expr(self.map, expr) => {
+                self.is_map_used = true;
+            },
+            _ => match expr.kind {
+                ExprKind::If(cond_expr, then_expr, Some(else_expr)) => {
+                    self.is_single_insert = false;
+                    self.visit_non_tail_expr(cond_expr);
+                    // Each branch may contain it's own insert expression.
+                    let mut is_map_used = self.visit_cond_arm(then_expr);
+                    is_map_used |= self.visit_cond_arm(else_expr);
+                    self.is_map_used = is_map_used;
+                },
+                ExprKind::Match(scrutinee_expr, arms, _) => {
+                    self.is_single_insert = false;
+                    self.visit_non_tail_expr(scrutinee_expr);
+                    // Each branch may contain it's own insert expression.
+                    let mut is_map_used = self.is_map_used;
+                    for arm in arms {
+                        if let Some(Guard::If(guard) | Guard::IfLet(_, guard)) = arm.guard {
+                            self.visit_non_tail_expr(guard)
+                        }
+                        is_map_used |= self.visit_cond_arm(arm.body);
                     }
-                    else {
-                        let help = format!("consider using `{}.entry({})`",
-                                           snippet(self.cx, self.map.span, "map"),
-                                           snippet(self.cx, params[1].span, ".."));
-
-                        diag.span_label(
-                            self.span,
-                            &help,
-                        );
+                    self.is_map_used = is_map_used;
+                },
+                ExprKind::Loop(block, ..) => {
+                    self.loops.push(expr.hir_id);
+                    self.is_single_insert = false;
+                    self.allow_insert_closure &= !self.in_tail_pos;
+                    // Don't allow insertions inside of a loop.
+                    let edit_len = self.edits.len();
+                    self.visit_block(block);
+                    if self.edits.len() != edit_len {
+                        self.can_use_entry = false;
                     }
-                });
-            }
+                    self.loops.pop();
+                },
+                ExprKind::Block(block, _) => self.visit_block(block),
+                ExprKind::InlineAsm(_) | ExprKind::LlvmInlineAsm(_) => {
+                    self.can_use_entry = false;
+                },
+                _ => {
+                    self.allow_insert_closure &= !self.in_tail_pos;
+                    self.allow_insert_closure &= can_move_expr_to_closure_no_visit(self.cx, expr, &self.loops);
+                    // Sub expressions are no longer in the tail position.
+                    self.is_single_insert = false;
+                    self.in_tail_pos = false;
+                    walk_expr(self, expr);
+                },
+            },
         }
+    }
+}
+
+struct InsertSearchResults<'tcx> {
+    edits: Vec<Edit<'tcx>>,
+    allow_insert_closure: bool,
+    is_single_insert: bool,
+}
+impl InsertSearchResults<'tcx> {
+    fn as_single_insertion(&self) -> Option<Insertion<'tcx>> {
+        self.is_single_insert.then(|| self.edits[0].as_insertion().unwrap())
+    }
 
-        if !self.sole_expr {
-            walk_expr(self, expr);
+    fn snippet(
+        &self,
+        cx: &LateContext<'_>,
+        mut span: Span,
+        app: &mut Applicability,
+        write_wrapped: impl Fn(&mut String, Insertion<'_>, SyntaxContext, &mut Applicability),
+    ) -> String {
+        let ctxt = span.ctxt();
+        let mut res = String::new();
+        for insertion in self.edits.iter().filter_map(|e| e.as_insertion()) {
+            res.push_str(&snippet_with_applicability(
+                cx,
+                span.until(insertion.call.span),
+                "..",
+                app,
+            ));
+            if is_expr_used_or_unified(cx.tcx, insertion.call) {
+                write_wrapped(&mut res, insertion, ctxt, app);
+            } else {
+                let _ = write!(
+                    res,
+                    "e.insert({})",
+                    snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0
+                );
+            }
+            span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP);
         }
+        res.push_str(&snippet_with_applicability(cx, span, "..", app));
+        res
     }
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::None
+
+    fn snippet_occupied(&self, cx: &LateContext<'_>, span: Span, app: &mut Applicability) -> (String, &'static str) {
+        (
+            self.snippet(cx, span, app, |res, insertion, ctxt, app| {
+                // Insertion into a map would return `Some(&mut value)`, but the entry returns `&mut value`
+                let _ = write!(
+                    res,
+                    "Some(e.insert({}))",
+                    snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0
+                );
+            }),
+            "Occupied(mut e)",
+        )
     }
+
+    fn snippet_vacant(&self, cx: &LateContext<'_>, span: Span, app: &mut Applicability) -> (String, &'static str) {
+        (
+            self.snippet(cx, span, app, |res, insertion, ctxt, app| {
+                // Insertion into a map would return `None`, but the entry returns a mutable reference.
+                let _ = if is_expr_final_block_expr(cx.tcx, insertion.call) {
+                    write!(
+                        res,
+                        "e.insert({});\n{}None",
+                        snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0,
+                        snippet_indent(cx, insertion.call.span).as_deref().unwrap_or(""),
+                    )
+                } else {
+                    write!(
+                        res,
+                        "{{ e.insert({}); None }}",
+                        snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0,
+                    )
+                };
+            }),
+            "Vacant(e)",
+        )
+    }
+
+    fn snippet_closure(&self, cx: &LateContext<'_>, mut span: Span, app: &mut Applicability) -> String {
+        let ctxt = span.ctxt();
+        let mut res = String::new();
+        for edit in &self.edits {
+            match *edit {
+                Edit::Insertion(insertion) => {
+                    // Cut out the value from `map.insert(key, value)`
+                    res.push_str(&snippet_with_applicability(
+                        cx,
+                        span.until(insertion.call.span),
+                        "..",
+                        app,
+                    ));
+                    res.push_str(&snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0);
+                    span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP);
+                },
+                Edit::RemoveSemi(semi_span) => {
+                    // Cut out the semicolon. This allows the value to be returned from the closure.
+                    res.push_str(&snippet_with_applicability(cx, span.until(semi_span), "..", app));
+                    span = span.trim_start(semi_span).unwrap_or(DUMMY_SP);
+                },
+            }
+        }
+        res.push_str(&snippet_with_applicability(cx, span, "..", app));
+        res
+    }
+}
+
+fn find_insert_calls(
+    cx: &LateContext<'tcx>,
+    contains_expr: &ContainsExpr<'tcx>,
+    expr: &'tcx Expr<'_>,
+) -> Option<InsertSearchResults<'tcx>> {
+    let mut s = InsertSearcher {
+        cx,
+        map: contains_expr.map,
+        key: contains_expr.key,
+        ctxt: expr.span.ctxt(),
+        edits: Vec::new(),
+        is_map_used: false,
+        allow_insert_closure: true,
+        can_use_entry: true,
+        in_tail_pos: true,
+        is_single_insert: true,
+        loops: Vec::new(),
+    };
+    s.visit_expr(expr);
+    let allow_insert_closure = s.allow_insert_closure;
+    let is_single_insert = s.is_single_insert;
+    let edits = s.edits;
+    s.can_use_entry.then(|| InsertSearchResults {
+        edits,
+        allow_insert_closure,
+        is_single_insert,
+    })
 }
diff --git a/src/tools/clippy/clippy_lints/src/excessive_bools.rs b/src/tools/clippy/clippy_lints/src/excessive_bools.rs
index 249ee27330bf9..4e2dbf005d51c 100644
--- a/src/tools/clippy/clippy_lints/src/excessive_bools.rs
+++ b/src/tools/clippy/clippy_lints/src/excessive_bools.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::{in_macro, match_path_ast};
+use clippy_utils::in_macro;
 use rustc_ast::ast::{AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind};
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -126,7 +126,9 @@ impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_B
 
 fn is_bool_ty(ty: &Ty) -> bool {
     if let TyKind::Path(None, path) = &ty.kind {
-        return match_path_ast(path, &["bool"]);
+        if let [name] = path.segments.as_slice() {
+            return name.ident.name == sym::bool;
+        }
     }
     false
 }
diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs
index 4729abbd8e3f7..c2b055ed6488e 100644
--- a/src/tools/clippy/clippy_lints/src/format.rs
+++ b/src/tools/clippy/clippy_lints/src/format.rs
@@ -1,6 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::paths;
 use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{is_expn_of, last_path_segment, match_def_path, match_function_call};
 use if_chain::if_chain;
@@ -100,15 +101,15 @@ fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: &
                     return Some(format!("{:?}.to_string()", s.as_str()));
                 }
             } else {
-                let snip = snippet(cx, format_args.span, "<arg>");
+                let sugg = Sugg::hir(cx, format_args, "<arg>");
                 if let ExprKind::MethodCall(path, _, _, _) = format_args.kind {
                     if path.ident.name == sym!(to_string) {
-                        return Some(format!("{}", snip));
+                        return Some(format!("{}", sugg));
                     }
                 } else if let ExprKind::Binary(..) = format_args.kind {
-                    return Some(format!("{}", snip));
+                    return Some(format!("{}", sugg));
                 }
-                return Some(format!("{}.to_string()", snip));
+                return Some(format!("{}.to_string()", sugg.maybe_par()));
             }
         }
     }
@@ -136,7 +137,7 @@ fn on_new_v1<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<Strin
                 if let Some(s_src) = snippet_opt(cx, lit.span) {
                     // Simulate macro expansion, converting {{ and }} to { and }.
                     let s_expand = s_src.replace("{{", "{").replace("}}", "}");
-                    return Some(format!("{}.to_string()", s_expand))
+                    return Some(format!("{}.to_string()", s_expand));
                 }
             } else if s.as_str().is_empty() {
                 return on_argumentv1_new(cx, &tup[0], arms);
diff --git a/src/tools/clippy/clippy_lints/src/formatting.rs b/src/tools/clippy/clippy_lints/src/formatting.rs
index 48612befc68d9..3bd6a09d3653a 100644
--- a/src/tools/clippy/clippy_lints/src/formatting.rs
+++ b/src/tools/clippy/clippy_lints/src/formatting.rs
@@ -215,9 +215,22 @@ fn check_else(cx: &EarlyContext<'_>, expr: &Expr) {
         // the snippet should look like " else \n    " with maybe comments anywhere
         // it’s bad when there is a ‘\n’ after the “else”
         if let Some(else_snippet) = snippet_opt(cx, else_span);
-        if let Some(else_pos) = else_snippet.find("else");
-        if else_snippet[else_pos..].contains('\n');
+        if let Some((pre_else, post_else)) = else_snippet.split_once("else");
+        if let Some((_, post_else_post_eol)) = post_else.split_once('\n');
+
         then {
+            // Allow allman style braces `} \n else \n {`
+            if_chain! {
+                if is_block(else_);
+                if let Some((_, pre_else_post_eol)) = pre_else.split_once('\n');
+                // Exactly one eol before and after the else
+                if !pre_else_post_eol.contains('\n');
+                if !post_else_post_eol.contains('\n');
+                then {
+                    return;
+                }
+            }
+
             let else_desc = if is_if(else_) { "if" } else { "{..}" };
             span_lint_and_note(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs
index e5ec245e5029b..5e2baba894349 100644
--- a/src/tools/clippy/clippy_lints/src/from_over_into.rs
+++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs
@@ -73,7 +73,7 @@ impl LateLintPass<'_> for FromOverInto {
                     cx.tcx.sess.source_map().guess_head_span(item.span),
                     "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true",
                     None,
-                    "consider to implement `From` instead",
+                    &format!("consider to implement `From<{}>` instead", impl_trait_ref.self_ty()),
                 );
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
index ee16519692f9a..85c95f1151f84 100644
--- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
+++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
@@ -1,7 +1,8 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::source::snippet_with_macro_callsite;
-use clippy_utils::{match_qpath, meets_msrv, parent_node_is_if_expr};
+use clippy_utils::{is_else_clause, is_lang_ctor, meets_msrv};
 use if_chain::if_chain;
+use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
@@ -67,7 +68,7 @@ impl LateLintPass<'_> for IfThenSomeElseNone {
         }
 
         // We only care about the top-most `if` in the chain
-        if parent_node_is_if_expr(expr, cx) {
+        if is_else_clause(cx.tcx, expr) {
             return;
         }
 
@@ -77,12 +78,12 @@ impl LateLintPass<'_> for IfThenSomeElseNone {
             if let Some(then_expr) = then_block.expr;
             if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind;
             if let ExprKind::Path(ref then_call_qpath) = then_call.kind;
-            if match_qpath(then_call_qpath, &clippy_utils::paths::OPTION_SOME);
+            if is_lang_ctor(cx, then_call_qpath, OptionSome);
             if let ExprKind::Block(els_block, _) = els.kind;
             if els_block.stmts.is_empty();
             if let Some(els_expr) = els_block.expr;
-            if let ExprKind::Path(ref els_call_qpath) = els_expr.kind;
-            if match_qpath(els_call_qpath, &clippy_utils::paths::OPTION_NONE);
+            if let ExprKind::Path(ref qpath) = els_expr.kind;
+            if is_lang_ctor(cx, qpath, OptionNone);
             then {
                 let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
                 let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
index 77a38544edc87..03fe0d16d480f 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
@@ -22,7 +22,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
 use clippy_utils::paths;
 use clippy_utils::source::{snippet, snippet_opt};
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{differing_macro_contexts, match_path};
+use clippy_utils::{differing_macro_contexts, match_def_path};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for public `impl` or `fn` missing generalization
@@ -333,12 +333,13 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't
             if let ExprKind::Call(fun, args) = e.kind;
             if let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind;
             if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind;
+            if let Some(ty_did) = ty_path.res.opt_def_id();
             then {
                 if !TyS::same_type(self.target.ty(), self.maybe_typeck_results.unwrap().expr_ty(e)) {
                     return;
                 }
 
-                if match_path(ty_path, &paths::HASHMAP) {
+                if match_def_path(self.cx, ty_did, &paths::HASHMAP) {
                     if method.ident.name == sym::new {
                         self.suggestions
                             .insert(e.span, "HashMap::default()".to_string());
@@ -351,7 +352,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't
                             ),
                         );
                     }
-                } else if match_path(ty_path, &paths::HASHSET) {
+                } else if match_def_path(self.cx, ty_did, &paths::HASHSET) {
                     if method.ident.name == sym::new {
                         self.suggestions
                             .insert(e.span, "HashSet::default()".to_string());
diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
index cba3183e86950..4069a685ea0a4 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
@@ -1,9 +1,9 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::{in_macro, match_qpath, SpanlessEq};
+use clippy_utils::{in_macro, SpanlessEq};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, StmtKind};
+use rustc_hir::{lang_items::LangItem, BinOpKind, Expr, ExprKind, QPath, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
@@ -87,7 +87,13 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
 
                 // Get the variable name
                 let var_name = ares_path.segments[0].ident.name.as_str();
-                const INT_TYPES: [&str; 5] = ["i8", "i16", "i32", "i64", "i128"];
+                const INT_TYPES: [LangItem; 5] = [
+                    LangItem::I8,
+                    LangItem::I16,
+                    LangItem::I32,
+                    LangItem::I64,
+                    LangItem::Isize
+                ];
 
                 match cond_num_val.kind {
                     ExprKind::Lit(ref cond_lit) => {
@@ -99,17 +105,30 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
                             };
                         }
                     },
-                    ExprKind::Path(ref cond_num_path) => {
-                        if INT_TYPES.iter().any(|int_type| match_qpath(cond_num_path, &[int_type, "MIN"])) {
-                            print_lint_and_sugg(cx, &var_name, expr);
-                        };
+                    ExprKind::Path(QPath::TypeRelative(_, name)) => {
+                        if_chain! {
+                            if name.ident.as_str() == "MIN";
+                            if let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id);
+                            if let Some(impl_id) = cx.tcx.impl_of_method(const_id);
+                            let mut int_ids = INT_TYPES.iter().filter_map(|&ty| cx.tcx.lang_items().require(ty).ok());
+                            if int_ids.any(|int_id| int_id == impl_id);
+                            then {
+                                print_lint_and_sugg(cx, &var_name, expr)
+                            }
+                        }
                     },
-                    ExprKind::Call(func, _) => {
-                        if let ExprKind::Path(ref cond_num_path) = func.kind {
-                            if INT_TYPES.iter().any(|int_type| match_qpath(cond_num_path, &[int_type, "min_value"])) {
-                                print_lint_and_sugg(cx, &var_name, expr);
+                    ExprKind::Call(func, []) => {
+                        if_chain! {
+                            if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind;
+                            if name.ident.as_str() == "min_value";
+                            if let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id);
+                            if let Some(impl_id) = cx.tcx.impl_of_method(func_id);
+                            let mut int_ids = INT_TYPES.iter().filter_map(|&ty| cx.tcx.lang_items().require(ty).ok());
+                            if int_ids.any(|int_id| int_id == impl_id);
+                            then {
+                                print_lint_and_sugg(cx, &var_name, expr)
                             }
-                        };
+                        }
                     },
                     _ => (),
                 }
diff --git a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
index d7ca24487a884..d138c3a8acfef 100644
--- a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
+++ b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
@@ -1,4 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_macro;
 use clippy_utils::source::snippet;
 use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashMap;
@@ -66,6 +67,7 @@ declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRU
 impl LateLintPass<'_> for InconsistentStructConstructor {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
         if_chain! {
+            if !in_macro(expr.span);
             if let ExprKind::Struct(qpath, fields, base) = expr.kind;
             let ty = cx.typeck_results().expr_ty(expr);
             if let Some(adt_def) = ty.ty_adt_def();
diff --git a/src/tools/clippy/clippy_lints/src/infinite_iter.rs b/src/tools/clippy/clippy_lints/src/infinite_iter.rs
index bbb4ddc613af5..afee20ce43e48 100644
--- a/src/tools/clippy/clippy_lints/src/infinite_iter.rs
+++ b/src/tools/clippy/clippy_lints/src/infinite_iter.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::ty::{implements_trait, match_type};
-use clippy_utils::{get_trait_def_id, higher, match_qpath, paths};
+use clippy_utils::{get_trait_def_id, higher, is_qpath_def_path, paths};
 use rustc_hir::{BorrowKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -163,7 +163,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
         ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e),
         ExprKind::Call(path, _) => {
             if let ExprKind::Path(ref qpath) = path.kind {
-                match_qpath(qpath, &paths::REPEAT).into()
+                is_qpath_def_path(cx, qpath, path.hir_id, &paths::ITER_REPEAT).into()
             } else {
                 Finite
             }
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 11fef30945d78..d6134e178ca17 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -179,6 +179,7 @@ mod await_holding_invalid;
 mod bit_mask;
 mod blacklisted_name;
 mod blocks_in_if_conditions;
+mod bool_assert_comparison;
 mod booleans;
 mod bytecount;
 mod cargo_common_metadata;
@@ -357,6 +358,7 @@ mod unicode;
 mod unit_return_expecting_ord;
 mod unit_types;
 mod unnamed_address;
+mod unnecessary_self_imports;
 mod unnecessary_sort_by;
 mod unnecessary_wraps;
 mod unnested_or_patterns;
@@ -391,6 +393,7 @@ pub use crate::utils::conf::Conf;
 ///
 /// Used in `./src/driver.rs`.
 pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) {
+    // NOTE: Do not add any more pre-expansion passes. These should be removed eventually.
     store.register_pre_expansion_pass(|| box write::Write::default());
     store.register_pre_expansion_pass(|| box attrs::EarlyAttributes);
     store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro);
@@ -494,22 +497,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         "clippy::unsafe_vector_initialization",
         "the replacement suggested by this lint had substantially different behavior",
     );
-    store.register_removed(
-        "clippy::invalid_ref",
-        "superseded by rustc lint `invalid_value`",
-    );
     store.register_removed(
         "clippy::unused_collect",
         "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint",
     );
-    store.register_removed(
-        "clippy::into_iter_on_array",
-        "this lint has been uplifted to rustc and is now called `array_into_iter`",
-    );
-    store.register_removed(
-        "clippy::unused_label",
-        "this lint has been uplifted to rustc and is now called `unused_labels`",
-    );
     store.register_removed(
         "clippy::replace_consts",
         "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants",
@@ -518,26 +509,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         "clippy::regex_macro",
         "the regex! macro has been removed from the regex crate in 2018",
     );
-    store.register_removed(
-        "clippy::drop_bounds",
-        "this lint has been uplifted to rustc and is now called `drop_bounds`",
-    );
-    store.register_removed(
-        "clippy::temporary_cstring_as_ptr",
-        "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`",
-    );
-    store.register_removed(
-        "clippy::panic_params",
-        "this lint has been uplifted to rustc and is now called `panic_fmt`",
-    );
-    store.register_removed(
-        "clippy::unknown_clippy_lints",
-        "this lint has been integrated into the `unknown_lints` rustc lint",
-    );
     store.register_removed(
         "clippy::find_map",
         "this lint has been replaced by `manual_find_map`, a more specific lint",
     );
+    store.register_removed(
+        "clippy::filter_map",
+        "this lint has been replaced by `manual_filter_map`, a more specific lint",
+    );
     // end deprecated lints, do not remove this comment, it’s used in `update_lints`
 
     // begin register lints, do not remove this comment, it’s used in `update_lints`
@@ -592,6 +571,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         bit_mask::VERBOSE_BIT_MASK,
         blacklisted_name::BLACKLISTED_NAME,
         blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS,
+        bool_assert_comparison::BOOL_ASSERT_COMPARISON,
         booleans::LOGIC_BUG,
         booleans::NONMINIMAL_BOOL,
         bytecount::NAIVE_BYTECOUNT,
@@ -783,17 +763,18 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         methods::BYTES_NTH,
         methods::CHARS_LAST_CMP,
         methods::CHARS_NEXT_CMP,
+        methods::CLONED_INSTEAD_OF_COPIED,
         methods::CLONE_DOUBLE_REF,
         methods::CLONE_ON_COPY,
         methods::CLONE_ON_REF_PTR,
         methods::EXPECT_FUN_CALL,
         methods::EXPECT_USED,
         methods::FILETYPE_IS_FILE,
-        methods::FILTER_MAP,
         methods::FILTER_MAP_IDENTITY,
         methods::FILTER_MAP_NEXT,
         methods::FILTER_NEXT,
         methods::FLAT_MAP_IDENTITY,
+        methods::FLAT_MAP_OPTION,
         methods::FROM_ITER_INSTEAD_OF_COLLECT,
         methods::GET_UNWRAP,
         methods::IMPLICIT_CLONE,
@@ -904,6 +885,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
         precedence::PRECEDENCE,
         ptr::CMP_NULL,
+        ptr::INVALID_NULL_PTR_USAGE,
         ptr::MUT_FROM_REF,
         ptr::PTR_ARG,
         ptr_eq::PTR_EQ,
@@ -988,6 +970,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         unit_types::UNIT_CMP,
         unnamed_address::FN_ADDRESS_COMPARISONS,
         unnamed_address::VTABLE_ADDRESS_COMPARISONS,
+        unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
         unnecessary_sort_by::UNNECESSARY_SORT_BY,
         unnecessary_wraps::UNNECESSARY_WRAPS,
         unnested_or_patterns::UNNESTED_OR_PATTERNS,
@@ -1073,6 +1056,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback);
     store.register_late_pass(|| box inconsistent_struct_constructor::InconsistentStructConstructor);
     store.register_late_pass(|| box non_octal_unix_permissions::NonOctalUnixPermissions);
+    store.register_early_pass(|| box unnecessary_self_imports::UnnecessarySelfImports);
 
     let msrv = conf.msrv.as_ref().and_then(|s| {
         parse_msrv(s, None, None).or_else(|| {
@@ -1295,6 +1279,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box from_str_radix_10::FromStrRadix10);
     store.register_late_pass(|| box manual_map::ManualMap);
     store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv));
+    store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison);
 
     store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
         LintId::of(arithmetic::FLOAT_ARITHMETIC),
@@ -1345,6 +1330,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(strings::STRING_TO_STRING),
         LintId::of(strings::STR_TO_STRING),
         LintId::of(types::RC_BUFFER),
+        LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
         LintId::of(unwrap_in_result::UNWRAP_IN_RESULT),
         LintId::of(verbose_file_reads::VERBOSE_FILE_READS),
         LintId::of(write::PRINT_STDERR),
@@ -1404,8 +1390,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
         LintId::of(matches::MATCH_WILD_ERR_ARM),
         LintId::of(matches::SINGLE_MATCH_ELSE),
-        LintId::of(methods::FILTER_MAP),
+        LintId::of(methods::CLONED_INSTEAD_OF_COPIED),
         LintId::of(methods::FILTER_MAP_NEXT),
+        LintId::of(methods::FLAT_MAP_OPTION),
         LintId::of(methods::IMPLICIT_CLONE),
         LintId::of(methods::INEFFICIENT_TO_STRING),
         LintId::of(methods::MAP_FLATTEN),
@@ -1428,6 +1415,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(strings::STRING_ADD_ASSIGN),
         LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
         LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
+        LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
         LintId::of(types::LINKEDLIST),
         LintId::of(types::OPTION_OPTION),
         LintId::of(unicode::NON_ASCII_LITERAL),
@@ -1474,6 +1462,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(bit_mask::INEFFECTIVE_BIT_MASK),
         LintId::of(blacklisted_name::BLACKLISTED_NAME),
         LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
+        LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
         LintId::of(booleans::LOGIC_BUG),
         LintId::of(booleans::NONMINIMAL_BOOL),
         LintId::of(casts::CAST_REF_TO_MUT),
@@ -1673,6 +1662,7 @@ 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::CMP_NULL),
+        LintId::of(ptr::INVALID_NULL_PTR_USAGE),
         LintId::of(ptr::MUT_FROM_REF),
         LintId::of(ptr::PTR_ARG),
         LintId::of(ptr_eq::PTR_EQ),
@@ -1715,7 +1705,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
         LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
         LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
-        LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
         LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
         LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
         LintId::of(transmute::WRONG_TRANSMUTE),
@@ -1759,6 +1748,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
         LintId::of(blacklisted_name::BLACKLISTED_NAME),
         LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
+        LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
         LintId::of(casts::FN_TO_NUMERIC_CAST),
         LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
         LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
@@ -1949,7 +1939,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
         LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
         LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
-        LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
         LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
         LintId::of(types::BORROWED_BOX),
         LintId::of(types::TYPE_COMPLEXITY),
@@ -2012,6 +2001,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
         LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
         LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
+        LintId::of(ptr::INVALID_NULL_PTR_USAGE),
         LintId::of(ptr::MUT_FROM_REF),
         LintId::of(ranges::REVERSED_EMPTY_RANGES),
         LintId::of(regex::INVALID_REGEX),
@@ -2153,6 +2143,15 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
     ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion");
     ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters");
     ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str");
+
+    // uplifted lints
+    ls.register_renamed("clippy::invalid_ref", "invalid_value");
+    ls.register_renamed("clippy::into_iter_on_array", "array_into_iter");
+    ls.register_renamed("clippy::unused_label", "unused_labels");
+    ls.register_renamed("clippy::drop_bounds", "drop_bounds");
+    ls.register_renamed("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr");
+    ls.register_renamed("clippy::panic_params", "non_fmt_panic");
+    ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints");
 }
 
 // only exists to let the dogfood integration test works.
diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs
index 4871a03118739..c7a28f42ea19b 100644
--- a/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/explicit_into_iter_loop.rs
@@ -1,24 +1,25 @@
 use super::EXPLICIT_INTO_ITER_LOOP;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{match_trait_method, paths};
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty::TyS;
 
-pub(super) fn check(cx: &LateContext<'_>, args: &'hir [Expr<'hir>], arg: &Expr<'_>) {
-    let receiver_ty = cx.typeck_results().expr_ty(&args[0]);
-    let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(&args[0]);
-    if !TyS::same_type(receiver_ty, receiver_ty_adjusted) {
+pub(super) fn check(cx: &LateContext<'_>, self_arg: &'hir Expr<'hir>, call_expr: &Expr<'_>) {
+    let self_ty = cx.typeck_results().expr_ty(self_arg);
+    let self_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg);
+    if !(TyS::same_type(self_ty, self_ty_adjusted) && match_trait_method(cx, call_expr, &paths::INTO_ITERATOR)) {
         return;
     }
 
     let mut applicability = Applicability::MachineApplicable;
-    let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
+    let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability);
     span_lint_and_sugg(
         cx,
         EXPLICIT_INTO_ITER_LOOP,
-        arg.span,
+        call_expr.span,
         "it is more concise to loop over containers instead of using explicit \
             iteration methods",
         "to write this more concisely, try",
diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
index 92aa2beb66d45..ce02ad013bef6 100644
--- a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
@@ -9,12 +9,12 @@ use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty, TyS};
 use rustc_span::sym;
 
-pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, method_name: &str) {
+pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, arg: &Expr<'_>, method_name: &str) {
     let should_lint = match method_name {
-        "iter" | "iter_mut" => is_ref_iterable_type(cx, &args[0]),
+        "iter" | "iter_mut" => is_ref_iterable_type(cx, self_arg),
         "into_iter" if match_trait_method(cx, arg, &paths::INTO_ITERATOR) => {
-            let receiver_ty = cx.typeck_results().expr_ty(&args[0]);
-            let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(&args[0]);
+            let receiver_ty = cx.typeck_results().expr_ty(self_arg);
+            let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg);
             let ref_receiver_ty = cx.tcx.mk_ref(
                 cx.tcx.lifetimes.re_erased,
                 ty::TypeAndMut {
@@ -32,7 +32,7 @@ pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, met
     }
 
     let mut applicability = Applicability::MachineApplicable;
-    let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
+    let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability);
     let muta = if method_name == "iter_mut" { "mut " } else { "" };
     span_lint_and_sugg(
         cx,
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
index 94743cfcf4657..64ff7574f86b7 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
@@ -1,10 +1,11 @@
 use super::utils::make_iterator_snippet;
 use super::MANUAL_FLATTEN;
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::{is_ok_ctor, is_some_ctor, path_to_local_id};
+use clippy_utils::{is_lang_ctor, path_to_local_id};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, StmtKind};
+use rustc_hir::LangItem::{OptionSome, ResultOk};
+use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, StmtKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
 use rustc_span::source_map::Span;
@@ -42,9 +43,9 @@ pub(super) fn check<'tcx>(
             if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind;
             if path_to_local_id(match_expr, pat_hir_id);
             // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result`
-            if let PatKind::TupleStruct(QPath::Resolved(None, path), _, _) = match_arms[0].pat.kind;
-            let some_ctor = is_some_ctor(cx, path.res);
-            let ok_ctor = is_ok_ctor(cx, path.res);
+            if let PatKind::TupleStruct(ref qpath, _, _) = match_arms[0].pat.kind;
+            let some_ctor = is_lang_ctor(cx, qpath, OptionSome);
+            let ok_ctor = is_lang_ctor(cx, qpath, ResultOk);
             if some_ctor || ok_ctor;
             then {
                 let if_let_type = if some_ctor { "Some" } else { "Ok" };
diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs
index 28acefd51fef7..a4bc3e6bd100c 100644
--- a/src/tools/clippy/clippy_lints/src/loops/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs
@@ -602,22 +602,19 @@ fn check_for_loop<'tcx>(
 fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, expr: &Expr<'_>) {
     let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used
 
-    if let ExprKind::MethodCall(method, _, args, _) = arg.kind {
-        // just the receiver, no arguments
-        if args.len() == 1 {
-            let method_name = &*method.ident.as_str();
-            // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
-            match method_name {
-                "iter" | "iter_mut" => explicit_iter_loop::check(cx, args, arg, method_name),
-                "into_iter" => {
-                    explicit_iter_loop::check(cx, args, arg, method_name);
-                    explicit_into_iter_loop::check(cx, args, arg);
-                },
-                "next" => {
-                    next_loop_linted = iter_next_loop::check(cx, arg, expr);
-                },
-                _ => {},
-            }
+    if let ExprKind::MethodCall(method, _, [self_arg], _) = arg.kind {
+        let method_name = &*method.ident.as_str();
+        // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
+        match method_name {
+            "iter" | "iter_mut" => explicit_iter_loop::check(cx, self_arg, arg, method_name),
+            "into_iter" => {
+                explicit_iter_loop::check(cx, self_arg, arg, method_name);
+                explicit_into_iter_loop::check(cx, self_arg, arg);
+            },
+            "next" => {
+                next_loop_linted = iter_next_loop::check(cx, arg, expr);
+            },
+            _ => {},
         }
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
index 96720764e1658..e97b7c9417033 100644
--- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
@@ -100,7 +100,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
         ExprKind::Binary(_, e1, e2)
         | ExprKind::Assign(e1, e2, _)
         | ExprKind::AssignOp(_, e1, e2)
-        | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().cloned(), main_loop_id),
+        | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), main_loop_id),
         ExprKind::Loop(b, _, _, _) => {
             // Break can come from the inner loop so remove them.
             absorb_break(&never_loop_block(b, main_loop_id))
diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs
index c506d52e7462a..ec03daff87b07 100644
--- a/src/tools/clippy/clippy_lints/src/macro_use.rs
+++ b/src/tools/clippy/clippy_lints/src/macro_use.rs
@@ -112,6 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
             let attrs = cx.tcx.hir().attrs(item.hir_id());
             if let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use));
             if let Res::Def(DefKind::Mod, id) = path.res;
+            if !id.is_local();
             then {
                 for kid in cx.tcx.item_children(id).iter() {
                     if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res {
diff --git a/src/tools/clippy/clippy_lints/src/manual_map.rs b/src/tools/clippy/clippy_lints/src/manual_map.rs
index 8c9e3af62f482..0b873534f2c8d 100644
--- a/src/tools/clippy/clippy_lints/src/manual_map.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_map.rs
@@ -1,15 +1,14 @@
 use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
-use clippy_utils::ty::{can_partially_move_ty, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
-use clippy_utils::{in_constant, is_allowed, is_else_clause, match_def_path, match_var, paths, peel_hir_expr_refs};
+use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
+use clippy_utils::{
+    can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, match_var, peel_hir_expr_refs,
+};
 use rustc_ast::util::parser::PREC_POSTFIX;
 use rustc_errors::Applicability;
-use rustc_hir::{
-    def::Res,
-    intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
-    Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath,
-};
+use rustc_hir::LangItem::{OptionNone, OptionSome};
+use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -103,12 +102,18 @@ impl LateLintPass<'_> for ManualMap {
                 None => return,
             };
 
+            // These two lints will go back and forth with each other.
             if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit
                 && !is_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
             {
                 return;
             }
 
+            // `map` won't perform any adjustments.
+            if !cx.typeck_results().expr_adjustments(some_expr).is_empty() {
+                return;
+            }
+
             if !can_move_expr_to_closure(cx, some_expr) {
                 return;
             }
@@ -192,51 +197,6 @@ impl LateLintPass<'_> for ManualMap {
     }
 }
 
-// Checks if the expression can be moved into a closure as is.
-fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
-    struct V<'cx, 'tcx> {
-        cx: &'cx LateContext<'tcx>,
-        make_closure: bool,
-    }
-    impl Visitor<'tcx> for V<'_, 'tcx> {
-        type Map = ErasedMap<'tcx>;
-        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-            NestedVisitorMap::None
-        }
-
-        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
-            match e.kind {
-                ExprKind::Break(..)
-                | ExprKind::Continue(_)
-                | ExprKind::Ret(_)
-                | ExprKind::Yield(..)
-                | ExprKind::InlineAsm(_)
-                | ExprKind::LlvmInlineAsm(_) => {
-                    self.make_closure = false;
-                },
-                // Accessing a field of a local value can only be done if the type isn't
-                // partially moved.
-                ExprKind::Field(base_expr, _)
-                    if matches!(
-                        base_expr.kind,
-                        ExprKind::Path(QPath::Resolved(_, Path { res: Res::Local(_), .. }))
-                    ) && can_partially_move_ty(self.cx, self.cx.typeck_results().expr_ty(base_expr)) =>
-                {
-                    // TODO: check if the local has been partially moved. Assume it has for now.
-                    self.make_closure = false;
-                    return;
-                }
-                _ => (),
-            };
-            walk_expr(self, e);
-        }
-    }
-
-    let mut v = V { cx, make_closure: true };
-    v.visit_expr(expr);
-    v.make_closure
-}
-
 // Checks whether the expression could be passed as a function, or whether a closure is needed.
 // Returns the function to be passed to `map` if it exists.
 fn can_pass_as_func(cx: &LateContext<'tcx>, binding: Ident, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
@@ -269,20 +229,9 @@ fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxCon
         match pat.kind {
             PatKind::Wild => Some(OptionPat::Wild),
             PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt),
-            PatKind::Path(QPath::Resolved(None, path))
-                if path
-                    .res
-                    .opt_def_id()
-                    .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_NONE)) =>
-            {
-                Some(OptionPat::None)
-            },
-            PatKind::TupleStruct(QPath::Resolved(None, path), [pattern], _)
-                if path
-                    .res
-                    .opt_def_id()
-                    .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_SOME))
-                    && pat.span.ctxt() == ctxt =>
+            PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None),
+            PatKind::TupleStruct(ref qpath, [pattern], _)
+                if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt =>
             {
                 Some(OptionPat::Some { pattern, ref_count })
             },
@@ -298,17 +247,11 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ctxt: SyntaxConte
     match expr.kind {
         ExprKind::Call(
             Expr {
-                kind: ExprKind::Path(QPath::Resolved(None, path)),
+                kind: ExprKind::Path(ref qpath),
                 ..
             },
             [arg],
-        ) if ctxt == expr.span.ctxt() => {
-            if match_def_path(cx, path.res.opt_def_id()?, &paths::OPTION_SOME) {
-                Some(arg)
-            } else {
-                None
-            }
-        },
+        ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(arg),
         ExprKind::Block(
             Block {
                 stmts: [],
@@ -324,10 +267,7 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ctxt: SyntaxConte
 // Checks for the `None` value.
 fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
     match expr.kind {
-        ExprKind::Path(QPath::Resolved(None, path)) => path
-            .res
-            .opt_def_id()
-            .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_NONE)),
+        ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
         ExprKind::Block(
             Block {
                 stmts: [],
diff --git a/src/tools/clippy/clippy_lints/src/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/manual_ok_or.rs
index 9bfae602c407d..847c8c648b00a 100644
--- a/src/tools/clippy/clippy_lints/src/manual_ok_or.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_ok_or.rs
@@ -1,9 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{match_qpath, path_to_local_id, paths};
+use clippy_utils::{is_lang_ctor, path_to_local_id};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
+use rustc_hir::LangItem::{ResultErr, ResultOk};
 use rustc_hir::{Expr, ExprKind, PatKind};
 use rustc_lint::LintContext;
 use rustc_lint::{LateContext, LateLintPass};
@@ -54,7 +55,7 @@ impl LateLintPass<'_> for ManualOkOr {
             let or_expr = &args[1];
             if is_ok_wrapping(cx, &args[2]);
             if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind;
-            if match_qpath(err_path, &paths::RESULT_ERR);
+            if is_lang_ctor(cx, err_path, ResultErr);
             if let Some(method_receiver_snippet) = snippet_opt(cx, method_receiver.span);
             if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
             if let Some(indent) = indent_of(cx, scrutinee.span);
@@ -81,7 +82,7 @@ impl LateLintPass<'_> for ManualOkOr {
 
 fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
     if let ExprKind::Path(ref qpath) = map_expr.kind {
-        if match_qpath(qpath, &paths::RESULT_OK) {
+        if is_lang_ctor(cx, qpath, ResultOk) {
             return true;
         }
     }
@@ -90,7 +91,7 @@ fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
         let body = cx.tcx.hir().body(body_id);
         if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind;
         if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind;
-        if match_qpath(ok_path, &paths::RESULT_OK);
+        if is_lang_ctor(cx, ok_path, ResultOk);
         then { path_to_local_id(ok_arg, param_id) } else { false }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs
index f296d6a1a15f5..65baa2552ccc6 100644
--- a/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs
@@ -3,10 +3,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::usage::contains_return_break_continue_macro;
-use clippy_utils::{in_constant, match_qpath, path_to_local_id, paths, sugg};
+use clippy_utils::{in_constant, is_lang_ctor, path_to_local_id, sugg};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind};
+use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
+use rustc_hir::{Arm, Expr, ExprKind, PatKind};
 use rustc_lint::LintContext;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
@@ -68,23 +69,21 @@ impl Case {
 }
 
 fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-    fn applicable_or_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> {
+    fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> {
         if_chain! {
             if arms.len() == 2;
             if arms.iter().all(|arm| arm.guard.is_none());
-            if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)|
+            if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| {
                 match arm.pat.kind {
-                    PatKind::Path(ref some_qpath) =>
-                        match_qpath(some_qpath, &paths::OPTION_NONE),
-                    PatKind::TupleStruct(ref err_qpath, &[Pat { kind: PatKind::Wild, .. }], _) =>
-                        match_qpath(err_qpath, &paths::RESULT_ERR),
+                    PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
+                    PatKind::TupleStruct(ref qpath, &[pat], _) =>
+                        matches!(pat.kind, PatKind::Wild) && is_lang_ctor(cx, qpath, ResultErr),
                     _ => false,
                 }
-            );
+            });
             let unwrap_arm = &arms[1 - idx];
-            if let PatKind::TupleStruct(ref unwrap_qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind;
-            if match_qpath(unwrap_qpath, &paths::OPTION_SOME)
-                || match_qpath(unwrap_qpath, &paths::RESULT_OK);
+            if let PatKind::TupleStruct(ref qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind;
+            if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk);
             if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind;
             if path_to_local_id(unwrap_arm.body, binding_hir_id);
             if !contains_return_break_continue_macro(or_arm.body);
@@ -106,7 +105,7 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
         } else {
             None
         };
-        if let Some(or_arm) = applicable_or_arm(match_arms);
+        if let Some(or_arm) = applicable_or_arm(cx, match_arms);
         if let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span);
         if let Some(indent) = indent_of(cx, expr.span);
         if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some();
diff --git a/src/tools/clippy/clippy_lints/src/map_identity.rs b/src/tools/clippy/clippy_lints/src/map_identity.rs
index e7719e7663d64..41cda23510ea2 100644
--- a/src/tools/clippy/clippy_lints/src/map_identity.rs
+++ b/src/tools/clippy/clippy_lints/src/map_identity.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_adjusted, is_trait_method, match_path, match_var, paths, remove_blocks};
+use clippy_utils::{is_adjusted, is_qpath_def_path, is_trait_method, match_var, paths, remove_blocks};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind};
@@ -80,7 +80,7 @@ fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a
 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(_, path)) => match_path(path, &paths::STD_CONVERT_IDENTITY),
+        ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY),
         _ => false,
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/matches.rs b/src/tools/clippy/clippy_lints/src/matches.rs
index c8ee0abd3dfef..44b4eb2903531 100644
--- a/src/tools/clippy/clippy_lints/src/matches.rs
+++ b/src/tools/clippy/clippy_lints/src/matches.rs
@@ -7,7 +7,7 @@ use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
 use clippy_utils::visitors::LocalUsedVisitor;
 use clippy_utils::{
-    get_parent_expr, in_macro, is_allowed, is_expn_of, is_refutable, is_wild, match_qpath, meets_msrv, path_to_local,
+    get_parent_expr, in_macro, is_allowed, is_expn_of, is_lang_ctor, is_refutable, is_wild, meets_msrv, path_to_local,
     path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks, strip_pat_refs,
 };
 use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
@@ -15,6 +15,7 @@ use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::def::{CtorKind, DefKind, Res};
+use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_hir::{
     self as hir, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource,
     Mutability, Node, Pat, PatKind, PathSegment, QPath, RangeEnd, TyKind,
@@ -422,7 +423,12 @@ declare_clippy_lint! {
     /// **Why is this bad?** It's more concise and clear to just use the proper
     /// utility function
     ///
-    /// **Known problems:** None.
+    /// **Known problems:** This will change the drop order for the matched type. Both `if let` and
+    /// `while let` will drop the value at the end of the block, both `if` and `while` will drop the
+    /// value before entering the block. For most types this change will not matter, but for a few
+    /// types this will not be an acceptable change (e.g. locks). See the
+    /// [reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about
+    /// drop order.
     ///
     /// **Example:**
     ///
@@ -737,8 +743,11 @@ fn report_single_match_single_pattern(
     let (msg, sugg) = if_chain! {
         if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind;
         let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex));
-        if let Some(trait_id) = cx.tcx.lang_items().structural_peq_trait();
-        if ty.is_integral() || ty.is_char() || ty.is_str() || implements_trait(cx, ty, trait_id, &[]);
+        if let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait();
+        if let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait();
+        if ty.is_integral() || ty.is_char() || ty.is_str()
+            || (implements_trait(cx, ty, spe_trait_id, &[])
+                && implements_trait(cx, ty, pe_trait_id, &[ty.into()]));
         then {
             // scrutinee derives PartialEq and the pattern is a constant.
             let pat_ref_count = match pat.kind {
@@ -1120,7 +1129,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
             Applicability::MaybeIncorrect,
         ),
         variants => {
-            let mut suggestions: Vec<_> = variants.iter().cloned().map(format_suggestion).collect();
+            let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect();
             let message = if adt_def.is_variant_list_non_exhaustive() {
                 suggestions.push("_".into());
                 "wildcard matches known variants and will also match future added variants"
@@ -1189,10 +1198,10 @@ fn check_match_ref_pats(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], e
 
 fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
     if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
-        let arm_ref: Option<BindingAnnotation> = if is_none_arm(&arms[0]) {
-            is_ref_some_arm(&arms[1])
-        } else if is_none_arm(&arms[1]) {
-            is_ref_some_arm(&arms[0])
+        let arm_ref: Option<BindingAnnotation> = if is_none_arm(cx, &arms[0]) {
+            is_ref_some_arm(cx, &arms[1])
+        } else if is_none_arm(cx, &arms[1]) {
+            is_ref_some_arm(cx, &arms[0])
         } else {
             None
         };
@@ -1500,7 +1509,7 @@ fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'
 /// Gets all arms that are unbounded `PatRange`s.
 fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) -> Vec<SpannedRange<Constant>> {
     arms.iter()
-        .flat_map(|arm| {
+        .filter_map(|arm| {
             if let Arm { pat, guard: None, .. } = *arm {
                 if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
                     let lhs = match lhs {
@@ -1575,20 +1584,20 @@ fn is_unit_expr(expr: &Expr<'_>) -> bool {
 }
 
 // Checks if arm has the form `None => None`
-fn is_none_arm(arm: &Arm<'_>) -> bool {
-    matches!(arm.pat.kind, PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE))
+fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
+    matches!(arm.pat.kind, PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone))
 }
 
 // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
-fn is_ref_some_arm(arm: &Arm<'_>) -> Option<BindingAnnotation> {
+fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<BindingAnnotation> {
     if_chain! {
-        if let PatKind::TupleStruct(ref path, pats, _) = arm.pat.kind;
-        if pats.len() == 1 && match_qpath(path, &paths::OPTION_SOME);
+        if let PatKind::TupleStruct(ref qpath, pats, _) = arm.pat.kind;
+        if is_lang_ctor(cx, qpath, OptionSome);
         if let PatKind::Binding(rb, .., ident, _) = pats[0].kind;
         if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
         if let ExprKind::Call(e, args) = remove_blocks(arm.body).kind;
         if let ExprKind::Path(ref some_path) = e.kind;
-        if match_qpath(some_path, &paths::OPTION_SOME) && args.len() == 1;
+        if is_lang_ctor(cx, some_path, OptionSome) && args.len() == 1;
         if let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind;
         if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
         then {
@@ -1699,54 +1708,206 @@ where
 mod redundant_pattern_match {
     use super::REDUNDANT_PATTERN_MATCHING;
     use clippy_utils::diagnostics::span_lint_and_then;
-    use clippy_utils::source::snippet;
-    use clippy_utils::{is_trait_method, match_qpath, paths};
+    use clippy_utils::source::{snippet, snippet_with_applicability};
+    use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type};
+    use clippy_utils::{is_lang_ctor, is_qpath_def_path, is_trait_method, paths};
     use if_chain::if_chain;
     use rustc_ast::ast::LitKind;
     use rustc_errors::Applicability;
-    use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind, QPath};
+    use rustc_hir::LangItem::{OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
+    use rustc_hir::{
+        intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
+        Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, PatKind, QPath,
+    };
     use rustc_lint::LateContext;
+    use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
     use rustc_span::sym;
 
     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"),
+                MatchSource::IfLetDesugar { contains_else_clause } => {
+                    find_sugg_for_if_let(cx, expr, op, &arms[0], "if", *contains_else_clause)
+                },
+                MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, &arms[0], "while", false),
                 _ => {},
             }
         }
     }
 
+    /// Checks if the drop order for a type matters. Some std types implement drop solely to
+    /// deallocate memory. For these types, and composites containing them, changing the drop order
+    /// won't result in any observable side effects.
+    fn type_needs_ordered_drop(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+        if !ty.needs_drop(cx.tcx, cx.param_env) {
+            false
+        } else if !cx
+            .tcx
+            .lang_items()
+            .drop_trait()
+            .map_or(false, |id| implements_trait(cx, ty, id, &[]))
+        {
+            // This type doesn't implement drop, so no side effects here.
+            // Check if any component type has any.
+            match ty.kind() {
+                ty::Tuple(_) => ty.tuple_fields().any(|ty| type_needs_ordered_drop(cx, ty)),
+                ty::Array(ty, _) => type_needs_ordered_drop(cx, ty),
+                ty::Adt(adt, subs) => adt
+                    .all_fields()
+                    .map(|f| f.ty(cx.tcx, subs))
+                    .any(|ty| type_needs_ordered_drop(cx, ty)),
+                _ => true,
+            }
+        }
+        // Check for std types which implement drop, but only for memory allocation.
+        else if is_type_diagnostic_item(cx, ty, sym::vec_type)
+            || is_type_lang_item(cx, ty, LangItem::OwnedBox)
+            || is_type_diagnostic_item(cx, ty, sym::Rc)
+            || is_type_diagnostic_item(cx, ty, sym::Arc)
+            || is_type_diagnostic_item(cx, ty, sym::cstring_type)
+            || match_type(cx, ty, &paths::BTREEMAP)
+            || match_type(cx, ty, &paths::LINKED_LIST)
+            || match_type(cx, ty, &paths::WEAK_RC)
+            || match_type(cx, ty, &paths::WEAK_ARC)
+        {
+            // Check all of the generic arguments.
+            if let ty::Adt(_, subs) = ty.kind() {
+                subs.types().any(|ty| type_needs_ordered_drop(cx, ty))
+            } else {
+                true
+            }
+        } else {
+            true
+        }
+    }
+
+    // Extract the generic arguments out of a type
+    fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option<Ty<'_>> {
+        if_chain! {
+            if let ty::Adt(_, subs) = ty.kind();
+            if let Some(sub) = subs.get(index);
+            if let GenericArgKind::Type(sub_ty) = sub.unpack();
+            then {
+                Some(sub_ty)
+            } else {
+                None
+            }
+        }
+    }
+
+    // Checks if there are any temporaries created in the given expression for which drop order
+    // matters.
+    fn temporaries_need_ordered_drop(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+        struct V<'a, 'tcx> {
+            cx: &'a LateContext<'tcx>,
+            res: bool,
+        }
+        impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> {
+            type Map = ErasedMap<'tcx>;
+            fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+                NestedVisitorMap::None
+            }
+
+            fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+                match expr.kind {
+                    // Taking the reference of a value leaves a temporary
+                    // e.g. In `&String::new()` the string is a temporary value.
+                    // Remaining fields are temporary values
+                    // e.g. In `(String::new(), 0).1` the string is a temporary value.
+                    ExprKind::AddrOf(_, _, expr) | ExprKind::Field(expr, _) => {
+                        if !matches!(expr.kind, ExprKind::Path(_)) {
+                            if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) {
+                                self.res = true;
+                            } else {
+                                self.visit_expr(expr);
+                            }
+                        }
+                    },
+                    // the base type is alway taken by reference.
+                    // e.g. In `(vec![0])[0]` the vector is a temporary value.
+                    ExprKind::Index(base, index) => {
+                        if !matches!(base.kind, ExprKind::Path(_)) {
+                            if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) {
+                                self.res = true;
+                            } else {
+                                self.visit_expr(base);
+                            }
+                        }
+                        self.visit_expr(index);
+                    },
+                    // Method calls can take self by reference.
+                    // e.g. In `String::new().len()` the string is a temporary value.
+                    ExprKind::MethodCall(_, _, [self_arg, args @ ..], _) => {
+                        if !matches!(self_arg.kind, ExprKind::Path(_)) {
+                            let self_by_ref = self
+                                .cx
+                                .typeck_results()
+                                .type_dependent_def_id(expr.hir_id)
+                                .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref());
+                            if self_by_ref
+                                && type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg))
+                            {
+                                self.res = true;
+                            } else {
+                                self.visit_expr(self_arg)
+                            }
+                        }
+                        args.iter().for_each(|arg| self.visit_expr(arg));
+                    },
+                    // Either explicitly drops values, or changes control flow.
+                    ExprKind::DropTemps(_)
+                    | ExprKind::Ret(_)
+                    | ExprKind::Break(..)
+                    | ExprKind::Yield(..)
+                    | ExprKind::Block(Block { expr: None, .. }, _)
+                    | ExprKind::Loop(..) => (),
+
+                    // Only consider the final expression.
+                    ExprKind::Block(Block { expr: Some(expr), .. }, _) => self.visit_expr(expr),
+
+                    _ => walk_expr(self, expr),
+                }
+            }
+        }
+
+        let mut v = V { cx, res: false };
+        v.visit_expr(expr);
+        v.res
+    }
+
     fn find_sugg_for_if_let<'tcx>(
         cx: &LateContext<'tcx>,
         expr: &'tcx Expr<'_>,
-        op: &Expr<'_>,
-        arms: &[Arm<'_>],
+        op: &'tcx Expr<'tcx>,
+        arm: &Arm<'_>,
         keyword: &'static str,
+        has_else: bool,
     ) {
         // also look inside refs
-        let mut kind = &arms[0].pat.kind;
+        let mut kind = &arm.pat.kind;
         // if we have &None for example, peel it so we can detect "if let None = x"
         if let PatKind::Ref(inner, _mutability) = kind {
             kind = &inner.kind;
         }
-        let good_method = match kind {
-            PatKind::TupleStruct(ref path, patterns, _) if patterns.len() == 1 => {
-                if let PatKind::Wild = patterns[0].kind {
-                    if match_qpath(path, &paths::RESULT_OK) {
-                        "is_ok()"
-                    } else if match_qpath(path, &paths::RESULT_ERR) {
-                        "is_err()"
-                    } else if match_qpath(path, &paths::OPTION_SOME) {
-                        "is_some()"
-                    } else if match_qpath(path, &paths::POLL_READY) {
-                        "is_ready()"
-                    } else if match_qpath(path, &paths::IPADDR_V4) {
-                        "is_ipv4()"
-                    } else if match_qpath(path, &paths::IPADDR_V6) {
-                        "is_ipv6()"
+        let op_ty = cx.typeck_results().expr_ty(op);
+        // Determine which function should be used, and the type contained by the corresponding
+        // variant.
+        let (good_method, inner_ty) = match kind {
+            PatKind::TupleStruct(ref path, [sub_pat], _) => {
+                if let PatKind::Wild = sub_pat.kind {
+                    if is_lang_ctor(cx, path, ResultOk) {
+                        ("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty))
+                    } else if is_lang_ctor(cx, path, ResultErr) {
+                        ("is_err()", try_get_generic_ty(op_ty, 1).unwrap_or(op_ty))
+                    } else if is_lang_ctor(cx, path, OptionSome) {
+                        ("is_some()", op_ty)
+                    } else if is_lang_ctor(cx, path, PollReady) {
+                        ("is_ready()", op_ty)
+                    } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V4) {
+                        ("is_ipv4()", op_ty)
+                    } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V6) {
+                        ("is_ipv6()", op_ty)
                     } else {
                         return;
                     }
@@ -1755,17 +1916,36 @@ mod redundant_pattern_match {
                 }
             },
             PatKind::Path(ref path) => {
-                if match_qpath(path, &paths::OPTION_NONE) {
+                let method = if is_lang_ctor(cx, path, OptionNone) {
                     "is_none()"
-                } else if match_qpath(path, &paths::POLL_PENDING) {
+                } else if is_lang_ctor(cx, path, PollPending) {
                     "is_pending()"
                 } else {
                     return;
-                }
+                };
+                // `None` and `Pending` don't have an inner type.
+                (method, cx.tcx.types.unit)
             },
             _ => return,
         };
 
+        // If this is the last expression in a block or there is an else clause then the whole
+        // type needs to be considered, not just the inner type of the branch being matched on.
+        // Note the last expression in a block is dropped after all local bindings.
+        let check_ty = if has_else
+            || (keyword == "if" && matches!(cx.tcx.hir().parent_iter(expr.hir_id).next(), Some((_, Node::Block(..)))))
+        {
+            op_ty
+        } else {
+            inner_ty
+        };
+
+        // All temporaries created in the scrutinee expression are dropped at the same time as the
+        // scrutinee would be, so they have to be considered as well.
+        // e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held
+        // for the duration if body.
+        let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, op);
+
         // check that `while_let_on_iterator` lint does not trigger
         if_chain! {
             if keyword == "while";
@@ -1784,7 +1964,7 @@ mod redundant_pattern_match {
         span_lint_and_then(
             cx,
             REDUNDANT_PATTERN_MATCHING,
-            arms[0].pat.span,
+            arm.pat.span,
             &format!("redundant pattern matching, consider using `{}`", good_method),
             |diag| {
                 // while let ... = ... { ... }
@@ -1798,12 +1978,20 @@ mod redundant_pattern_match {
                 // 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
-                );
+
+                let mut app = if needs_drop {
+                    Applicability::MaybeIncorrect
+                } else {
+                    Applicability::MachineApplicable
+                };
+                let sugg = snippet_with_applicability(cx, op_span, "_", &mut app);
+
+                diag.span_suggestion(span, "try this", format!("{} {}.{}", keyword, sugg, good_method), app);
+
+                if needs_drop {
+                    diag.note("this will change drop order of the result, as well as all temporaries");
+                    diag.note("add `#[allow(clippy::redundant_pattern_matching)]` if this is important");
+                }
             },
         );
     }
@@ -1819,6 +2007,7 @@ mod redundant_pattern_match {
                 ) 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(
+                            cx,
                             arms,
                             path_left,
                             path_right,
@@ -1829,6 +2018,7 @@ mod redundant_pattern_match {
                         )
                         .or_else(|| {
                             find_good_method_for_match(
+                                cx,
                                 arms,
                                 path_left,
                                 path_right,
@@ -1848,6 +2038,7 @@ mod redundant_pattern_match {
                 {
                     if let PatKind::Wild = patterns[0].kind {
                         find_good_method_for_match(
+                            cx,
                             arms,
                             path_left,
                             path_right,
@@ -1858,6 +2049,7 @@ mod redundant_pattern_match {
                         )
                         .or_else(|| {
                             find_good_method_for_match(
+                                cx,
                                 arms,
                                 path_left,
                                 path_right,
@@ -1898,7 +2090,9 @@ mod redundant_pattern_match {
         }
     }
 
+    #[allow(clippy::too_many_arguments)]
     fn find_good_method_for_match<'a>(
+        cx: &LateContext<'_>,
         arms: &[Arm<'_>],
         path_left: &QPath<'_>,
         path_right: &QPath<'_>,
@@ -1907,9 +2101,13 @@ mod redundant_pattern_match {
         should_be_left: &'a str,
         should_be_right: &'a str,
     ) -> Option<&'a str> {
-        let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) {
+        let body_node_pair = if is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_left)
+            && is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_right)
+        {
             (&(*arms[0].body).kind, &(*arms[1].body).kind)
-        } else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) {
+        } else if is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_left)
+            && is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_right)
+        {
             (&(*arms[1].body).kind, &(*arms[0].body).kind)
         } else {
             return None;
diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs
index e1d351aee4547..ec60bffe95555 100644
--- a/src/tools/clippy/clippy_lints/src/mem_replace.rs
+++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs
@@ -1,10 +1,10 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::is_diagnostic_assoc_item;
 use clippy_utils::source::{snippet, snippet_with_applicability};
-use clippy_utils::{in_macro, match_def_path, match_qpath, meets_msrv, paths};
+use clippy_utils::{in_macro, is_diag_trait_item, is_lang_ctor, match_def_path, meets_msrv, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::def_id::DefId;
+use rustc_hir::LangItem::OptionNone;
 use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
@@ -102,7 +102,7 @@ impl_lint_pass!(MemReplace =>
 fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
     if let ExprKind::Path(ref replacement_qpath) = src.kind {
         // Check that second argument is `Option::None`
-        if match_qpath(replacement_qpath, &paths::OPTION_NONE) {
+        if is_lang_ctor(cx, replacement_qpath, OptionNone) {
             // Since this is a late pass (already type-checked),
             // and we already know that the second argument is an
             // `Option`, we do not need to check the first
@@ -210,17 +210,17 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<
         sym::BinaryHeap,
     ];
 
-    if std_types_symbols
-        .iter()
-        .any(|symbol| is_diagnostic_assoc_item(cx, def_id, *symbol))
-    {
-        if let QPath::TypeRelative(_, method) = path {
-            if method.ident.name == sym::new {
-                return true;
+    if let QPath::TypeRelative(_, method) = path {
+        if method.ident.name == sym::new {
+            if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
+                if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
+                    return std_types_symbols
+                        .iter()
+                        .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did));
+                }
             }
         }
     }
-
     false
 }
 
@@ -230,7 +230,7 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<
         if !in_external_macro(cx.tcx.sess, expr_span);
         if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
         if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
-        if is_diagnostic_assoc_item(cx, repl_def_id, sym::Default)
+        if is_diag_trait_item(cx, repl_def_id, sym::Default)
             || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
 
         then {
diff --git a/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs b/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs
new file mode 100644
index 0000000000000..ba97ab3900ca4
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs
@@ -0,0 +1,38 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_trait_method;
+use clippy_utils::ty::{get_iterator_item_ty, is_copy};
+use rustc_errors::Applicability;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::{sym, Span};
+
+use super::CLONED_INSTEAD_OF_COPIED;
+
+pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span) {
+    let recv_ty = cx.typeck_results().expr_ty_adjusted(recv);
+    let inner_ty = match recv_ty.kind() {
+        // `Option<T>` -> `T`
+        ty::Adt(adt, subst) if cx.tcx.is_diagnostic_item(sym::option_type, adt.did) => subst.type_at(0),
+        _ if is_trait_method(cx, expr, sym::Iterator) => match get_iterator_item_ty(cx, recv_ty) {
+            // <T as Iterator>::Item
+            Some(ty) => ty,
+            _ => return,
+        },
+        _ => return,
+    };
+    match inner_ty.kind() {
+        // &T where T: Copy
+        ty::Ref(_, ty, _) if is_copy(cx, ty) => {},
+        _ => return,
+    };
+    span_lint_and_sugg(
+        cx,
+        CLONED_INSTEAD_OF_COPIED,
+        span,
+        "used `cloned` where `copied` could be used instead",
+        "try",
+        "copied".into(),
+        Applicability::MachineApplicable,
+    )
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_flat_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_flat_map.rs
deleted file mode 100644
index 1588eec88824b..0000000000000
--- a/src/tools/clippy/clippy_lints/src/methods/filter_flat_map.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::is_trait_method;
-use rustc_hir as hir;
-use rustc_lint::LateContext;
-use rustc_span::sym;
-
-use super::FILTER_MAP;
-
-/// lint use of `filter().flat_map()` for `Iterators`
-pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-    // lint if caller of `.filter().flat_map()` is an Iterator
-    if is_trait_method(cx, expr, sym::Iterator) {
-        let msg = "called `filter(..).flat_map(..)` on an `Iterator`";
-        let hint = "this is more succinctly expressed by calling `.flat_map(..)` \
-                    and filtering by returning `iter::empty()`";
-        span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_flat_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_flat_map.rs
deleted file mode 100644
index 741b1e7e36125..0000000000000
--- a/src/tools/clippy/clippy_lints/src/methods/filter_map_flat_map.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::is_trait_method;
-use rustc_hir as hir;
-use rustc_lint::LateContext;
-use rustc_span::sym;
-
-use super::FILTER_MAP;
-
-/// lint use of `filter_map().flat_map()` for `Iterators`
-pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-    // lint if caller of `.filter_map().flat_map()` is an Iterator
-    if is_trait_method(cx, expr, sym::Iterator) {
-        let msg = "called `filter_map(..).flat_map(..)` on an `Iterator`";
-        let hint = "this is more succinctly expressed by calling `.flat_map(..)` \
-                    and filtering by returning `iter::empty()`";
-        span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs
index 3a61f4ccad789..403fe8d354684 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths};
+use clippy_utils::{is_expr_path_def_path, is_trait_method, path_to_local_id, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -33,14 +33,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg:
             }
         }
 
-        if_chain! {
-            if let hir::ExprKind::Path(ref qpath) = filter_map_arg.kind;
-
-            if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY);
-
-            then {
-                apply_lint("called `filter_map(std::convert::identity)` on an `Iterator`");
-            }
+        if is_expr_path_def_path(cx, filter_map_arg, &paths::CONVERT_IDENTITY) {
+            apply_lint("called `filter_map(std::convert::identity)` on an `Iterator`");
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_map.rs
deleted file mode 100644
index 713bbf258370d..0000000000000
--- a/src/tools/clippy/clippy_lints/src/methods/filter_map_map.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::is_trait_method;
-use rustc_hir as hir;
-use rustc_lint::LateContext;
-use rustc_span::sym;
-
-use super::FILTER_MAP;
-
-/// lint use of `filter_map().map()` for `Iterators`
-pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-    // lint if caller of `.filter_map().map()` is an Iterator
-    if is_trait_method(cx, expr, sym::Iterator) {
-        let msg = "called `filter_map(..).map(..)` on an `Iterator`";
-        let hint = "this is more succinctly expressed by only calling `.filter_map(..)` instead";
-        span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs
index dd613d0cd6384..25f8434cb9442 100644
--- a/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::{is_trait_method, match_qpath, paths};
+use clippy_utils::{is_expr_path_def_path, is_trait_method, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -16,8 +16,6 @@ pub(super) fn check<'tcx>(
     flat_map_span: Span,
 ) {
     if is_trait_method(cx, expr, sym::Iterator) {
-        let arg_node = &flat_map_arg.kind;
-
         let apply_lint = |message: &str| {
             span_lint_and_sugg(
                 cx,
@@ -31,8 +29,8 @@ pub(super) fn check<'tcx>(
         };
 
         if_chain! {
-            if let hir::ExprKind::Closure(_, _, body_id, _, _) = arg_node;
-            let body = cx.tcx.hir().body(*body_id);
+            if let hir::ExprKind::Closure(_, _, body_id, _, _) = flat_map_arg.kind;
+            let body = cx.tcx.hir().body(body_id);
 
             if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind;
             if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = body.value.kind;
@@ -45,14 +43,8 @@ pub(super) fn check<'tcx>(
             }
         }
 
-        if_chain! {
-            if let hir::ExprKind::Path(ref qpath) = arg_node;
-
-            if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY);
-
-            then {
-                apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`");
-            }
+        if is_expr_path_def_path(cx, flat_map_arg, &paths::CONVERT_IDENTITY) {
+            apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`");
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs b/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs
new file mode 100644
index 0000000000000..12d560653edf3
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs
@@ -0,0 +1,34 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_trait_method;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::{source_map::Span, sym};
+
+use super::FLAT_MAP_OPTION;
+use clippy_utils::ty::is_type_diagnostic_item;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, span: Span) {
+    if !is_trait_method(cx, expr, sym::Iterator) {
+        return;
+    }
+    let arg_ty = cx.typeck_results().expr_ty_adjusted(arg);
+    let sig = match arg_ty.kind() {
+        ty::Closure(_, substs) => substs.as_closure().sig(),
+        _ if arg_ty.is_fn() => arg_ty.fn_sig(cx.tcx),
+        _ => return,
+    };
+    if !is_type_diagnostic_item(cx, sig.output().skip_binder(), sym::option_type) {
+        return;
+    }
+    span_lint_and_sugg(
+        cx,
+        FLAT_MAP_OPTION,
+        span,
+        "used `flat_map` where `filter_map` could be used instead",
+        "try",
+        "filter_map".into(),
+        Applicability::MachineApplicable,
+    )
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs
index 707c54f7a3cad..28d0e8cd4ae9e 100644
--- a/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs
@@ -1,26 +1,23 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::ty::implements_trait;
-use clippy_utils::{get_trait_def_id, match_qpath, paths, sugg};
+use clippy_utils::{is_expr_path_def_path, paths, sugg};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
-use rustc_hir::ExprKind;
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::ty::Ty;
 use rustc_span::sym;
 
 use super::FROM_ITER_INSTEAD_OF_COLLECT;
 
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func_kind: &ExprKind<'_>) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func: &hir::Expr<'_>) {
     if_chain! {
-        if let hir::ExprKind::Path(path) = func_kind;
-        if match_qpath(path, &["from_iter"]);
+        if is_expr_path_def_path(cx, func, &paths::FROM_ITERATOR_METHOD);
         let ty = cx.typeck_results().expr_ty(expr);
         let arg_ty = cx.typeck_results().expr_ty(&args[0]);
-        if let Some(from_iter_id) = get_trait_def_id(cx, &paths::FROM_ITERATOR);
         if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
 
-        if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]);
+        if implements_trait(cx, arg_ty, iter_id, &[]);
         then {
             // `expr` implements `FromIterator` trait
             let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par();
diff --git a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs
index 1211e2f2bf7cb..81c42de145f6c 100644
--- a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs
@@ -1,28 +1,36 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::{is_diag_item_method, is_diag_trait_item};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
-use rustc_hir::ExprKind;
 use rustc_lint::LateContext;
 use rustc_middle::ty::TyS;
-use rustc_span::symbol::Symbol;
+use rustc_span::{sym, Span};
 
 use super::IMPLICIT_CLONE;
-use clippy_utils::is_diagnostic_assoc_item;
 
-pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, trait_diagnostic: Symbol) {
+pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, span: Span) {
     if_chain! {
-        if let ExprKind::MethodCall(method_path, _, [arg], _) = &expr.kind;
+        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+        if match method_name {
+            "to_os_string" => is_diag_item_method(cx, method_def_id, sym::OsStr),
+            "to_owned" => is_diag_trait_item(cx, method_def_id, sym::ToOwned),
+            "to_path_buf" => is_diag_item_method(cx, method_def_id, sym::Path),
+            "to_vec" => cx.tcx.impl_of_method(method_def_id)
+                .map(|impl_did| Some(impl_did) == cx.tcx.lang_items().slice_alloc_impl())
+                == Some(true),
+            _ => false,
+        };
         let return_type = cx.typeck_results().expr_ty(expr);
-        let input_type = cx.typeck_results().expr_ty(arg).peel_refs();
-        if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+        let input_type = cx.typeck_results().expr_ty(recv).peel_refs();
         if let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did));
         if TyS::same_type(return_type, input_type);
-        if is_diagnostic_assoc_item(cx, expr_def_id, trait_diagnostic);
         then {
             span_lint_and_sugg(
-                cx,IMPLICIT_CLONE,method_path.ident.span,
-                &format!("implicitly cloning a `{}` by calling `{}` on its dereferenced type", ty_name, method_path.ident.name),
+                cx,
+                IMPLICIT_CLONE,
+                span,
+                &format!("implicitly cloning a `{}` by calling `{}` on its dereferenced type", ty_name, method_name),
                 "consider using",
                 "clone".to_string(),
                 Applicability::MachineApplicable
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs
index ecb8b72ef4610..2fddea7068d96 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::match_qpath;
+use clippy_utils::is_qpath_def_path;
 use clippy_utils::source::snippet_with_applicability;
 use if_chain::if_chain;
 use rustc_ast::ast;
@@ -94,11 +94,11 @@ fn is_min_or_max<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option<M
 
     // `std::T::MAX` `std::T::MIN` constants
     if let hir::ExprKind::Path(path) = &expr.kind {
-        if match_qpath(path, &["core", &ty_str, "MAX"][..]) {
+        if is_qpath_def_path(cx, path, expr.hir_id, &["core", &ty_str, "MAX"][..]) {
             return Some(MinMax::Max);
         }
 
-        if match_qpath(path, &["core", &ty_str, "MIN"][..]) {
+        if is_qpath_def_path(cx, path, expr.hir_id, &["core", &ty_str, "MIN"][..]) {
             return Some(MinMax::Min);
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index b1ade5addd6aa..c2cd3011d1493 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -8,17 +8,16 @@ mod chars_next_cmp;
 mod chars_next_cmp_with_unwrap;
 mod clone_on_copy;
 mod clone_on_ref_ptr;
+mod cloned_instead_of_copied;
 mod expect_fun_call;
 mod expect_used;
 mod filetype_is_file;
-mod filter_flat_map;
 mod filter_map;
-mod filter_map_flat_map;
 mod filter_map_identity;
-mod filter_map_map;
 mod filter_map_next;
 mod filter_next;
 mod flat_map_identity;
+mod flat_map_option;
 mod from_iter_instead_of_collect;
 mod get_unwrap;
 mod implicit_clone;
@@ -76,6 +75,52 @@ use rustc_span::symbol::SymbolStr;
 use rustc_span::{sym, Span};
 use rustc_typeck::hir_ty_to_ty;
 
+declare_clippy_lint! {
+    /// **What it does:** Checks for usages of `cloned()` on an `Iterator` or `Option` where
+    /// `copied()` could be used instead.
+    ///
+    /// **Why is this bad?** `copied()` is better because it guarantees that the type being cloned
+    /// implements `Copy`.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// [1, 2, 3].iter().cloned();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// [1, 2, 3].iter().copied();
+    /// ```
+    pub CLONED_INSTEAD_OF_COPIED,
+    pedantic,
+    "used `cloned` where `copied` could be used instead"
+}
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for usages of `Iterator::flat_map()` where `filter_map()` could be
+    /// used instead.
+    ///
+    /// **Why is this bad?** When applicable, `filter_map()` is more clear since it shows that
+    /// `Option` is used to produce 0 or 1 items.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect();
+    /// ```
+    pub FLAT_MAP_OPTION,
+    pedantic,
+    "used `flat_map` where `filter_map` could be used instead"
+}
+
 declare_clippy_lint! {
     /// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s.
     ///
@@ -472,35 +517,6 @@ declare_clippy_lint! {
     "using combinations of `flatten` and `map` which can usually be written as a single method call"
 }
 
-declare_clippy_lint! {
-    /// **What it does:** Checks for usage of `_.filter(_).map(_)`,
-    /// `_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar.
-    ///
-    /// **Why is this bad?** Readability, this can be written more concisely as
-    /// `_.filter_map(_)`.
-    ///
-    /// **Known problems:** Often requires a condition + Option/Iterator creation
-    /// inside the closure.
-    ///
-    /// **Example:**
-    /// ```rust
-    /// let vec = vec![1];
-    ///
-    /// // Bad
-    /// vec.iter().filter(|x| **x == 0).map(|x| *x * 2);
-    ///
-    /// // Good
-    /// vec.iter().filter_map(|x| if *x == 0 {
-    ///     Some(*x * 2)
-    /// } else {
-    ///     None
-    /// });
-    /// ```
-    pub FILTER_MAP,
-    pedantic,
-    "using combinations of `filter`, `map`, `filter_map` and `flat_map` which can usually be written as a single method call"
-}
-
 declare_clippy_lint! {
     /// **What it does:** Checks for usage of `_.filter(_).map(_)` that can be written more simply
     /// as `filter_map(_)`.
@@ -1670,6 +1686,8 @@ impl_lint_pass!(Methods => [
     CLONE_ON_COPY,
     CLONE_ON_REF_PTR,
     CLONE_DOUBLE_REF,
+    CLONED_INSTEAD_OF_COPIED,
+    FLAT_MAP_OPTION,
     INEFFICIENT_TO_STRING,
     NEW_RET_NO_SELF,
     SINGLE_CHAR_PATTERN,
@@ -1677,7 +1695,6 @@ impl_lint_pass!(Methods => [
     SEARCH_IS_SOME,
     FILTER_NEXT,
     SKIP_WHILE_NEXT,
-    FILTER_MAP,
     FILTER_MAP_IDENTITY,
     MANUAL_FILTER_MAP,
     MANUAL_FIND_MAP,
@@ -1741,7 +1758,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
 
         match expr.kind {
             hir::ExprKind::Call(func, args) => {
-                from_iter_instead_of_collect::check(cx, expr, args, &func.kind);
+                from_iter_instead_of_collect::check(cx, expr, args, func);
             },
             hir::ExprKind::MethodCall(method_call, ref method_span, args, _) => {
                 or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
@@ -1942,6 +1959,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
             ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
             ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
             ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
+            ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span),
             ("collect", []) => match method_call!(recv) {
                 Some(("cloned", [recv2], _)) => iter_cloned_collect::check(cx, expr, recv2),
                 Some(("map", [m_recv, m_arg], _)) => {
@@ -1965,10 +1983,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                 unnecessary_filter_map::check(cx, expr, arg);
                 filter_map_identity::check(cx, expr, arg, span);
             },
-            ("flat_map", [flm_arg]) => match method_call!(recv) {
-                Some(("filter", [_, _], _)) => filter_flat_map::check(cx, expr),
-                Some(("filter_map", [_, _], _)) => filter_map_flat_map::check(cx, expr),
-                _ => flat_map_identity::check(cx, expr, flm_arg, span),
+            ("flat_map", [arg]) => {
+                flat_map_identity::check(cx, expr, arg, span);
+                flat_map_option::check(cx, expr, arg, span);
             },
             ("flatten", []) => {
                 if let Some(("map", [recv, map_arg], _)) = method_call!(recv) {
@@ -1993,7 +2010,6 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                         ("filter", [f_arg]) => {
                             filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false)
                         },
-                        ("filter_map", [_]) => filter_map_map::check(cx, expr),
                         ("find", [f_arg]) => filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true),
                         _ => {},
                     }
@@ -2025,10 +2041,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                 }
             },
             ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
-            ("to_os_string", []) => implicit_clone::check(cx, expr, sym::OsStr),
-            ("to_owned", []) => implicit_clone::check(cx, expr, sym::ToOwned),
-            ("to_path_buf", []) => implicit_clone::check(cx, expr, sym::Path),
-            ("to_vec", []) => implicit_clone::check(cx, expr, sym::slice),
+            ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
+                implicit_clone::check(cx, name, expr, recv, span);
+            },
             ("unwrap", []) => match method_call!(recv) {
                 Some(("get", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, false),
                 Some(("get_mut", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, true),
diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs
index 013a6f90ac97b..36a1c13d5be1e 100644
--- a/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs
@@ -1,9 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_lang_ctor;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{match_qpath, paths};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
+use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_lint::LateContext;
 use rustc_span::symbol::sym;
 
@@ -32,7 +33,7 @@ pub(super) fn check<'tcx>(
 
     let (lint_name, msg, instead, hint) = {
         let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind {
-            match_qpath(qpath, &paths::OPTION_NONE)
+            is_lang_ctor(cx, qpath, OptionNone)
         } else {
             return;
         };
@@ -43,7 +44,7 @@ pub(super) fn check<'tcx>(
         }
 
         let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind {
-            match_qpath(qpath, &paths::OPTION_SOME)
+            is_lang_ctor(cx, qpath, OptionSome)
         } else {
             false
         };
diff --git a/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs b/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs
index 0ae65c0c01dba..1a5894e48d14c 100644
--- a/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{match_def_path, match_qpath, paths};
+use clippy_utils::{is_expr_path_def_path, match_def_path, paths};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -12,8 +12,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
     if_chain! {
         if let hir::ExprKind::Call(callee, args) = recv.kind;
         if args.is_empty();
-        if let hir::ExprKind::Path(ref path) = callee.kind;
-        if match_qpath(path, &paths::MEM_MAYBEUNINIT_UNINIT);
+        if is_expr_path_def_path(cx, callee, &paths::MEM_MAYBEUNINIT_UNINIT);
         if !is_maybe_uninit_ty_valid(cx, cx.typeck_results().expr_ty_adjusted(expr));
         then {
             span_lint(
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 0f28bfdf09e89..b61c4ffe9b3ae 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
@@ -1,8 +1,9 @@
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::usage::mutated_variables;
-use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths};
+use clippy_utils::{is_lang_ctor, is_trait_method, path_to_local_id};
 use rustc_hir as hir;
 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
+use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
 use rustc_span::sym;
@@ -54,14 +55,12 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc
     match &expr.kind {
         hir::ExprKind::Call(func, args) => {
             if let hir::ExprKind::Path(ref path) = func.kind {
-                if match_qpath(path, &paths::OPTION_SOME) {
+                if is_lang_ctor(cx, path, OptionSome) {
                     if path_to_local_id(&args[0], arg_id) {
                         return (false, false);
                     }
                     return (true, false);
                 }
-                // We don't know. It might do anything.
-                return (true, true);
             }
             (true, true)
         },
@@ -85,7 +84,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc
             let else_check = check_expression(cx, arg_id, else_arm);
             (if_check.0 | else_check.0, if_check.1 | else_check.1)
         },
-        hir::ExprKind::Path(path) if match_qpath(path, &paths::OPTION_NONE) => (false, true),
+        hir::ExprKind::Path(path) if is_lang_ctor(cx, path, OptionNone) => (false, true),
         _ => (true, true),
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs b/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs
index 1e0de249a91f1..6e2bcb113c2c1 100644
--- a/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs
@@ -102,6 +102,14 @@ pub(super) fn check<'tcx>(
             .iter()
             .all(|conv| conv.check(cx, self_ty, item_name, implements_trait, is_trait_item))
     }) {
+        // don't lint if it implements a trait but not willing to check `Copy` types conventions (see #7032)
+        if implements_trait
+            && !conventions
+                .iter()
+                .any(|conv| matches!(conv, Convention::IsSelfTypeCopy(_)))
+        {
+            return;
+        }
         if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) {
             let suggestion = {
                 if conventions.len() > 1 {
diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs
index afced5a5ce58a..0b0cd9be46cf4 100644
--- a/src/tools/clippy/clippy_lints/src/misc.rs
+++ b/src/tools/clippy/clippy_lints/src/misc.rs
@@ -20,8 +20,8 @@ use rustc_span::symbol::sym;
 use crate::consts::{constant, Constant};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{
-    get_item_name, get_parent_expr, higher, in_constant, is_diagnostic_assoc_item, is_integer_const, iter_input_pats,
-    last_path_segment, match_qpath, unsext, SpanlessEq,
+    expr_path_res, get_item_name, get_parent_expr, higher, in_constant, is_diag_trait_item, is_integer_const,
+    iter_input_pats, last_path_segment, match_any_def_paths, paths, unsext, SpanlessEq,
 };
 
 declare_clippy_lint! {
@@ -555,8 +555,8 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left:
         ExprKind::MethodCall(.., args, _) if args.len() == 1 => {
             if_chain!(
                 if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
-                if is_diagnostic_assoc_item(cx, expr_def_id, sym::ToString)
-                    || is_diagnostic_assoc_item(cx, expr_def_id, sym::ToOwned);
+                if is_diag_trait_item(cx, expr_def_id, sym::ToString)
+                    || is_diag_trait_item(cx, expr_def_id, sym::ToOwned);
                 then {
                     (cx.typeck_results().expr_ty(&args[0]), snippet(cx, args[0].span, ".."))
                 } else {
@@ -564,13 +564,13 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left:
                 }
             )
         },
-        ExprKind::Call(path, 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.typeck_results().expr_ty(&v[0]), snippet(cx, v[0].span, ".."))
-                } else {
-                    return;
-                }
+        ExprKind::Call(path, [arg]) => {
+            if expr_path_res(cx, path)
+                .opt_def_id()
+                .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM]))
+                .is_some()
+            {
+                (cx.typeck_results().expr_ty(arg), snippet(cx, arg.span, ".."))
             } else {
                 return;
             }
diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
index 0dc02431ad538..93b7a897405ae 100644
--- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
@@ -138,7 +138,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
 
         let mir = cx.tcx.optimized_mir(def_id);
 
-        if let Err((span, err)) = is_min_const_fn(cx.tcx, mir) {
+        if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv.as_ref()) {
             if rustc_mir::const_eval::is_min_const_fn(cx.tcx, def_id.to_def_id()) {
                 cx.tcx.sess.span_err(span, &err);
             }
diff --git a/src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs b/src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs
index 6a52de4f71364..64e9dc85466eb 100644
--- a/src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs
+++ b/src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs
@@ -4,7 +4,7 @@ use clippy_utils::sext;
 use if_chain::if_chain;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{self};
+use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use std::fmt::Display;
 
diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs
index 96a58d1410f22..dd4581986377f 100644
--- a/src/tools/clippy/clippy_lints/src/needless_bool.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs
@@ -5,7 +5,7 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{is_expn_of, parent_node_is_if_expr};
+use clippy_utils::{is_else_clause, is_expn_of};
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
@@ -81,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
                     snip = snip.make_return();
                 }
 
-                if parent_node_is_if_expr(e, cx) {
+                if is_else_clause(cx.tcx, e) {
                     snip = snip.blockify()
                 }
 
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
index 780e224129347..e33a33e238633 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
@@ -279,7 +279,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
                             spans.extend(
                                 deref_span
                                     .iter()
-                                    .cloned()
+                                    .copied()
                                     .map(|span| (span, format!("*{}", snippet(cx, span, "<expr>")))),
                             );
                             spans.sort_by_key(|&(span, _)| span);
diff --git a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs
index 9852633b7342e..cfe7ae6630e04 100644
--- a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs
@@ -1,9 +1,11 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_lang_ctor;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{differing_macro_contexts, is_ok_ctor, is_some_ctor, meets_msrv};
+use clippy_utils::{differing_macro_contexts, meets_msrv};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
+use rustc_hir::LangItem::{OptionSome, ResultOk};
 use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_semver::RustcVersion;
@@ -159,8 +161,8 @@ fn is_some_or_ok_call<'a>(
     if_chain! {
         // Check outer expression matches CALL_IDENT(ARGUMENT) format
         if let ExprKind::Call(path, args) = &expr.kind;
-        if let ExprKind::Path(QPath::Resolved(None, path)) = &path.kind;
-        if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res);
+        if let ExprKind::Path(ref qpath) = &path.kind;
+        if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk);
 
         // Extract inner expression from ARGUMENT
         if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind;
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
index 1b9120ae45f54..e527adbb8929d 100644
--- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
+++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
@@ -1,11 +1,11 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::paths;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::usage::contains_return_break_continue_macro;
-use clippy_utils::{eager_or_lazy, get_enclosing_block, in_macro, match_qpath};
+use clippy_utils::{eager_or_lazy, get_enclosing_block, in_macro, is_lang_ctor};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
+use rustc_hir::LangItem::OptionSome;
 use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -164,7 +164,7 @@ fn detect_option_if_let_else<'tcx>(
         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 match_qpath(struct_qpath, &paths::OPTION_SOME);
+        if is_lang_ctor(cx, struct_qpath, OptionSome);
         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);
diff --git a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
index d32b937b209c5..cef74d87e7c01 100644
--- a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{find_macro_calls, return_ty};
+use clippy_utils::{find_macro_calls, is_expn_of, return_ty};
 use rustc_hir as hir;
 use rustc_hir::intravisit::FnKind;
 use rustc_lint::{LateContext, LateLintPass};
@@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn {
 }
 
 fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) {
-    let panics = find_macro_calls(
+    let mut panics = find_macro_calls(
         &[
             "unimplemented",
             "unreachable",
@@ -61,12 +61,10 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir
             "assert",
             "assert_eq",
             "assert_ne",
-            "debug_assert",
-            "debug_assert_eq",
-            "debug_assert_ne",
         ],
         body,
     );
+    panics.retain(|span| is_expn_of(*span, "debug_assert").is_none());
     if !panics.is_empty() {
         span_lint_and_then(
             cx,
diff --git a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
index 1e94685894723..1a680e7607e0b 100644
--- a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
+++ b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
@@ -74,7 +74,7 @@ declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANI
 
 impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if match_panic_call(cx, expr).is_some() {
+        if match_panic_call(cx, expr).is_some() && is_expn_of(expr.span, "debug_assert").is_none() {
             let span = get_outer_span(expr);
             if is_expn_of(expr.span, "unimplemented").is_some() {
                 span_lint(
diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
index 6f80e447c7ddd..6b64846c24d10 100644
--- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
+++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
@@ -42,6 +42,14 @@ declare_clippy_lint! {
     /// false positives in cases involving multiple lifetimes that are bounded by
     /// each other.
     ///
+    /// Also, it does not take account of other similar cases where getting memory addresses
+    /// matters; namely, returning the pointer to the argument in question,
+    /// and passing the argument, as both references and pointers,
+    /// to a function that needs the memory address. For further details, refer to
+    /// [this issue](https://github.com/rust-lang/rust-clippy/issues/5953)
+    /// that explains a real case in which this false positive
+    /// led to an **undefined behaviour** introduced with unsafe code.
+    ///
     /// **Example:**
     ///
     /// ```rust
diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs
index 09fcdb5faf842..b0674f9067836 100644
--- a/src/tools/clippy/clippy_lints/src/ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr.rs
@@ -4,7 +4,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the
 use clippy_utils::ptr::get_spans;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{is_type_diagnostic_item, match_type, walk_ptrs_hir_ty};
-use clippy_utils::{is_allowed, match_qpath, paths};
+use clippy_utils::{expr_path_res, is_allowed, match_any_def_paths, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{
@@ -15,6 +15,7 @@ use rustc_lint::{LateContext, LateLintPass};
 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::{sym, MultiSpan};
 use std::borrow::Cow;
 
@@ -94,7 +95,7 @@ declare_clippy_lint! {
     /// ```
     pub CMP_NULL,
     style,
-    "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead."
+    "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead"
 }
 
 declare_clippy_lint! {
@@ -119,7 +120,28 @@ declare_clippy_lint! {
     "fns that create mutable refs from immutable ref args"
 }
 
-declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF]);
+declare_clippy_lint! {
+    /// **What it does:** This lint checks for invalid usages of `ptr::null`.
+    ///
+    /// **Why is this bad?** This causes undefined behavior.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// ```ignore
+    /// // Bad. Undefined behavior
+    /// unsafe { std::slice::from_raw_parts(ptr::null(), 0); }
+    /// ```
+    ///
+    /// // Good
+    /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); }
+    /// ```
+    pub INVALID_NULL_PTR_USAGE,
+    correctness,
+    "invalid usage of a null pointer, suggesting `NonNull::dangling()` instead"
+}
+
+declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE]);
 
 impl<'tcx> LateLintPass<'tcx> for Ptr {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
@@ -153,7 +175,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
 
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if let ExprKind::Binary(ref op, l, r) = expr.kind {
-            if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(l) || is_null_path(r)) {
+            if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(cx, l) || is_null_path(cx, r)) {
                 span_lint(
                     cx,
                     CMP_NULL,
@@ -161,6 +183,55 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
                     "comparing with null is better expressed by the `.is_null()` method",
                 );
             }
+        } else {
+            check_invalid_ptr_usage(cx, expr);
+        }
+    }
+}
+
+fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+    // (fn_path, arg_indices) - `arg_indices` are the `arg` positions where null would cause U.B.
+    const INVALID_NULL_PTR_USAGE_TABLE: [(&[&str], &[usize]); 16] = [
+        (&paths::SLICE_FROM_RAW_PARTS, &[0]),
+        (&paths::SLICE_FROM_RAW_PARTS_MUT, &[0]),
+        (&paths::PTR_COPY, &[0, 1]),
+        (&paths::PTR_COPY_NONOVERLAPPING, &[0, 1]),
+        (&paths::PTR_READ, &[0]),
+        (&paths::PTR_READ_UNALIGNED, &[0]),
+        (&paths::PTR_READ_VOLATILE, &[0]),
+        (&paths::PTR_REPLACE, &[0]),
+        (&paths::PTR_SLICE_FROM_RAW_PARTS, &[0]),
+        (&paths::PTR_SLICE_FROM_RAW_PARTS_MUT, &[0]),
+        (&paths::PTR_SWAP, &[0, 1]),
+        (&paths::PTR_SWAP_NONOVERLAPPING, &[0, 1]),
+        (&paths::PTR_WRITE, &[0]),
+        (&paths::PTR_WRITE_UNALIGNED, &[0]),
+        (&paths::PTR_WRITE_VOLATILE, &[0]),
+        (&paths::PTR_WRITE_BYTES, &[0]),
+    ];
+
+    if_chain! {
+        if let ExprKind::Call(ref fun, ref args) = expr.kind;
+        if let ExprKind::Path(ref qpath) = fun.kind;
+        if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
+        let fun_def_path = cx.get_def_path(fun_def_id).into_iter().map(Symbol::to_ident_string).collect::<Vec<_>>();
+        if let Some(&(_, arg_indices)) = INVALID_NULL_PTR_USAGE_TABLE
+            .iter()
+            .find(|&&(fn_path, _)| fn_path == fun_def_path);
+        then {
+            for &arg_idx in arg_indices {
+                if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) {
+                    span_lint_and_sugg(
+                        cx,
+                        INVALID_NULL_PTR_USAGE,
+                        arg.span,
+                        "pointer must be non-null",
+                        "change this to",
+                        "core::ptr::NonNull::dangling().as_ptr()".to_string(),
+                        Applicability::MachineApplicable,
+                    );
+                }
+            }
         }
     }
 }
@@ -345,13 +416,12 @@ fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability,
     }
 }
 
-fn is_null_path(expr: &Expr<'_>) -> bool {
-    if let ExprKind::Call(pathexp, args) = expr.kind {
-        if args.is_empty() {
-            if let ExprKind::Path(ref path) = pathexp.kind {
-                return match_qpath(path, &paths::PTR_NULL) || match_qpath(path, &paths::PTR_NULL_MUT);
-            }
-        }
+fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    if let ExprKind::Call(pathexp, []) = expr.kind {
+        expr_path_res(cx, pathexp).opt_def_id().map_or(false, |id| {
+            match_any_def_paths(cx, id, &[&paths::PTR_NULL, &paths::PTR_NULL_MUT]).is_some()
+        })
+    } else {
+        false
     }
-    false
 }
diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs
index 6d720f43851a0..30bee21390068 100644
--- a/src/tools/clippy/clippy_lints/src/question_mark.rs
+++ b/src/tools/clippy/clippy_lints/src/question_mark.rs
@@ -1,12 +1,13 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_lang_ctor;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{eq_expr_value, match_def_path, match_qpath, paths};
+use clippy_utils::{eq_expr_value, path_to_local_id};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{def, BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind};
+use rustc_hir::LangItem::{OptionNone, OptionSome};
+use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::sym;
@@ -100,15 +101,14 @@ impl QuestionMark {
             if Self::is_option(cx, subject);
 
             if let PatKind::TupleStruct(path1, fields, None) = &arms[0].pat.kind;
-            if match_qpath(path1, &["Some"]);
-            if let PatKind::Binding(annot, _, bind, _) = &fields[0].kind;
+            if is_lang_ctor(cx, path1, OptionSome);
+            if let PatKind::Binding(annot, bind_id, _, _) = fields[0].kind;
             let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut);
 
             if let ExprKind::Block(block, None) = &arms[0].body.kind;
             if block.stmts.is_empty();
             if let Some(trailing_expr) = &block.expr;
-            if let ExprKind::Path(path) = &trailing_expr.kind;
-            if match_qpath(path, &[&bind.as_str()]);
+            if path_to_local_id(trailing_expr, bind_id);
 
             if let PatKind::Wild = arms[1].pat.kind;
             if Self::expression_returns_none(cx, arms[1].body);
@@ -156,15 +156,7 @@ impl QuestionMark {
                 false
             },
             ExprKind::Ret(Some(expr)) => Self::expression_returns_none(cx, expr),
-            ExprKind::Path(ref qp) => {
-                if let Res::Def(DefKind::Ctor(def::CtorOf::Variant, def::CtorKind::Const), def_id) =
-                    cx.qpath_res(qp, expression.hir_id)
-                {
-                    return match_def_path(cx, def_id, &paths::OPTION_NONE);
-                }
-
-                false
-            },
+            ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
             _ => false,
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs
index af772cf4a145d..b565c77aaecff 100644
--- a/src/tools/clippy/clippy_lints/src/returns.rs
+++ b/src/tools/clippy/clippy_lints/src/returns.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::snippet_opt;
-use clippy_utils::{fn_def_id, in_macro, match_qpath};
+use clippy_utils::{fn_def_id, in_macro, path_to_local_id};
 use if_chain::if_chain;
 use rustc_ast::ast::Attribute;
 use rustc_errors::Applicability;
@@ -84,9 +84,8 @@ impl<'tcx> LateLintPass<'tcx> for Return {
             if local.ty.is_none();
             if cx.tcx.hir().attrs(local.hir_id).is_empty();
             if let Some(initexpr) = &local.init;
-            if let PatKind::Binding(.., ident, _) = local.pat.kind;
-            if let ExprKind::Path(qpath) = &retexpr.kind;
-            if match_qpath(qpath, &[&*ident.name.as_str()]);
+            if let PatKind::Binding(_, local_id, _, _) = local.pat.kind;
+            if path_to_local_id(retexpr, local_id);
             if !last_statement_borrows(cx, initexpr);
             if !in_external_macro(cx.sess(), initexpr.span);
             if !in_external_macro(cx.sess(), retexpr.span);
@@ -223,6 +222,7 @@ fn check_final_expr<'tcx>(
             },
             _ => (),
         },
+        ExprKind::DropTemps(expr) => check_final_expr(cx, expr, None, RetReplacement::Empty),
         _ => (),
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
index c9d72aabb6a3c..6104103580e98 100644
--- a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
@@ -1,11 +1,10 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
 use clippy_utils::in_macro;
-use if_chain::if_chain;
-use rustc_ast::{Item, ItemKind, UseTreeKind};
+use rustc_ast::{ptr::P, Crate, Item, ItemKind, ModKind, UseTreeKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::edition::Edition;
+use rustc_span::{edition::Edition, symbol::kw, Span, Symbol};
 
 declare_clippy_lint! {
     /// **What it does:** Checking for imports with single component use path.
@@ -38,26 +37,120 @@ declare_clippy_lint! {
 declare_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]);
 
 impl EarlyLintPass for SingleComponentPathImports {
-    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
-        if_chain! {
-            if !in_macro(item.span);
-            if cx.sess.opts.edition >= Edition::Edition2018;
-            if !item.vis.kind.is_pub();
-            if let ItemKind::Use(use_tree) = &item.kind;
-            if let segments = &use_tree.prefix.segments;
-            if segments.len() == 1;
-            if let UseTreeKind::Simple(None, _, _) = use_tree.kind;
-            then {
+    fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
+        if cx.sess.opts.edition < Edition::Edition2018 {
+            return;
+        }
+        check_mod(cx, &krate.items);
+    }
+}
+
+fn check_mod(cx: &EarlyContext<'_>, items: &[P<Item>]) {
+    // keep track of imports reused with `self` keyword,
+    // such as `self::crypto_hash` in the example below
+    // ```rust,ignore
+    // use self::crypto_hash::{Algorithm, Hasher};
+    // ```
+    let mut imports_reused_with_self = Vec::new();
+
+    // keep track of single use statements
+    // such as `crypto_hash` in the example below
+    // ```rust,ignore
+    // use crypto_hash;
+    // ```
+    let mut single_use_usages = Vec::new();
+
+    for item in items {
+        track_uses(cx, &item, &mut imports_reused_with_self, &mut single_use_usages);
+    }
+
+    for single_use in &single_use_usages {
+        if !imports_reused_with_self.contains(&single_use.0) {
+            let can_suggest = single_use.2;
+            if can_suggest {
                 span_lint_and_sugg(
                     cx,
                     SINGLE_COMPONENT_PATH_IMPORTS,
-                    item.span,
+                    single_use.1,
                     "this import is redundant",
                     "remove it entirely",
                     String::new(),
-                    Applicability::MachineApplicable
+                    Applicability::MachineApplicable,
+                );
+            } else {
+                span_lint_and_help(
+                    cx,
+                    SINGLE_COMPONENT_PATH_IMPORTS,
+                    single_use.1,
+                    "this import is redundant",
+                    None,
+                    "remove this import",
                 );
             }
         }
     }
 }
+
+fn track_uses(
+    cx: &EarlyContext<'_>,
+    item: &Item,
+    imports_reused_with_self: &mut Vec<Symbol>,
+    single_use_usages: &mut Vec<(Symbol, Span, bool)>,
+) {
+    if in_macro(item.span) || item.vis.kind.is_pub() {
+        return;
+    }
+
+    match &item.kind {
+        ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => {
+            check_mod(cx, &items);
+        },
+        ItemKind::Use(use_tree) => {
+            let segments = &use_tree.prefix.segments;
+
+            // keep track of `use some_module;` usages
+            if segments.len() == 1 {
+                if let UseTreeKind::Simple(None, _, _) = use_tree.kind {
+                    let ident = &segments[0].ident;
+                    single_use_usages.push((ident.name, item.span, true));
+                }
+                return;
+            }
+
+            if segments.is_empty() {
+                // keep track of `use {some_module, some_other_module};` usages
+                if let UseTreeKind::Nested(trees) = &use_tree.kind {
+                    for tree in trees {
+                        let segments = &tree.0.prefix.segments;
+                        if segments.len() == 1 {
+                            if let UseTreeKind::Simple(None, _, _) = tree.0.kind {
+                                let ident = &segments[0].ident;
+                                single_use_usages.push((ident.name, tree.0.span, false));
+                            }
+                        }
+                    }
+                }
+            } else {
+                // keep track of `use self::some_module` usages
+                if segments[0].ident.name == kw::SelfLower {
+                    // simple case such as `use self::module::SomeStruct`
+                    if segments.len() > 1 {
+                        imports_reused_with_self.push(segments[1].ident.name);
+                        return;
+                    }
+
+                    // nested case such as `use self::{module1::Struct1, module2::Struct2}`
+                    if let UseTreeKind::Nested(trees) = &use_tree.kind {
+                        for tree in trees {
+                            let segments = &tree.0.prefix.segments;
+                            if !segments.is_empty() {
+                                imports_reused_with_self.push(segments[0].ident.name);
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        _ => {},
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs
index 09e0086681556..cd2bdec1707b3 100644
--- a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs
+++ b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs
@@ -65,8 +65,8 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool)
 
 fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> {
     const FUNCTIONS: [&[&str]; 8] = [
-        &paths::COPY_NONOVERLAPPING,
-        &paths::COPY,
+        &paths::PTR_COPY_NONOVERLAPPING,
+        &paths::PTR_COPY,
         &paths::WRITE_BYTES,
         &paths::PTR_SWAP_NONOVERLAPPING,
         &paths::PTR_SLICE_FROM_RAW_PARTS,
diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
index 8cf89ae456ee8..191781be000cf 100644
--- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
+++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
@@ -1,6 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{get_enclosing_block, match_qpath, SpanlessEq};
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{get_enclosing_block, is_expr_path_def_path, path_to_local, path_to_local_id, paths, SpanlessEq};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -9,7 +10,7 @@ use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, QPath,
 use rustc_lint::{LateContext, LateLintPass, Lint};
 use rustc_middle::hir::map::Map;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::Symbol;
+use rustc_span::symbol::sym;
 
 declare_clippy_lint! {
     /// **What it does:** Checks slow zero-filled vector initialization
@@ -46,8 +47,8 @@ declare_lint_pass!(SlowVectorInit => [SLOW_VECTOR_INITIALIZATION]);
 /// assigned to a variable. For example, `let mut vec = Vec::with_capacity(0)` or
 /// `vec = Vec::with_capacity(0)`
 struct VecAllocation<'tcx> {
-    /// Symbol of the local variable name
-    variable_name: Symbol,
+    /// HirId of the variable
+    local_id: HirId,
 
     /// Reference to the expression which allocates the vector
     allocation_expr: &'tcx Expr<'tcx>,
@@ -72,16 +73,15 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit {
         if_chain! {
             if let ExprKind::Assign(left, right, _) = expr.kind;
 
-            // Extract variable name
-            if let ExprKind::Path(QPath::Resolved(_, path)) = left.kind;
-            if let Some(variable_name) = path.segments.get(0);
+            // Extract variable
+            if let Some(local_id) = path_to_local(left);
 
             // Extract len argument
-            if let Some(len_arg) = Self::is_vec_with_capacity(right);
+            if let Some(len_arg) = Self::is_vec_with_capacity(cx, right);
 
             then {
                 let vi = VecAllocation {
-                    variable_name: variable_name.ident.name,
+                    local_id,
                     allocation_expr: right,
                     len_expr: len_arg,
                 };
@@ -95,13 +95,13 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit {
         // Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)`
         if_chain! {
             if let StmtKind::Local(local) = stmt.kind;
-            if let PatKind::Binding(BindingAnnotation::Mutable, .., variable_name, None) = local.pat.kind;
+            if let PatKind::Binding(BindingAnnotation::Mutable, local_id, _, None) = local.pat.kind;
             if let Some(init) = local.init;
-            if let Some(len_arg) = Self::is_vec_with_capacity(init);
+            if let Some(len_arg) = Self::is_vec_with_capacity(cx, init);
 
             then {
                 let vi = VecAllocation {
-                    variable_name: variable_name.name,
+                    local_id,
                     allocation_expr: init,
                     len_expr: len_arg,
                 };
@@ -115,19 +115,18 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit {
 impl SlowVectorInit {
     /// Checks if the given expression is `Vec::with_capacity(..)`. It will return the expression
     /// of the first argument of `with_capacity` call if it matches or `None` if it does not.
-    fn is_vec_with_capacity<'tcx>(expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
+    fn is_vec_with_capacity<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
         if_chain! {
-            if let ExprKind::Call(func, args) = expr.kind;
-            if let ExprKind::Path(ref path) = func.kind;
-            if match_qpath(path, &["Vec", "with_capacity"]);
-            if args.len() == 1;
-
+            if let ExprKind::Call(func, [arg]) = expr.kind;
+            if let ExprKind::Path(QPath::TypeRelative(ty, name)) = func.kind;
+            if name.ident.as_str() == "with_capacity";
+            if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::vec_type);
             then {
-                return Some(&args[0]);
+                Some(arg)
+            } else {
+                None
             }
         }
-
-        None
     }
 
     /// Search initialization for the given vector
@@ -208,11 +207,9 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
     fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) {
         if_chain! {
             if self.initialization_found;
-            if let ExprKind::MethodCall(path, _, args, _) = expr.kind;
-            if let ExprKind::Path(ref qpath_subj) = args[0].kind;
-            if match_qpath(qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]);
+            if let ExprKind::MethodCall(path, _, [self_arg, extend_arg], _) = expr.kind;
+            if path_to_local_id(self_arg, self.vec_alloc.local_id);
             if path.ident.name == sym!(extend);
-            if let Some(extend_arg) = args.get(1);
             if self.is_repeat_take(extend_arg);
 
             then {
@@ -225,11 +222,9 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
     fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'_>) {
         if_chain! {
             if self.initialization_found;
-            if let ExprKind::MethodCall(path, _, args, _) = expr.kind;
-            if let ExprKind::Path(ref qpath_subj) = args[0].kind;
-            if match_qpath(qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]);
+            if let ExprKind::MethodCall(path, _, [self_arg, len_arg, fill_arg], _) = expr.kind;
+            if path_to_local_id(self_arg, self.vec_alloc.local_id);
             if path.ident.name == sym!(resize);
-            if let (Some(len_arg), Some(fill_arg)) = (args.get(1), args.get(2));
 
             // Check that is filled with 0
             if let ExprKind::Lit(ref lit) = fill_arg.kind;
@@ -252,7 +247,7 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
 
             // Check that take is applied to `repeat(0)`
             if let Some(repeat_expr) = take_args.get(0);
-            if Self::is_repeat_zero(repeat_expr);
+            if self.is_repeat_zero(repeat_expr);
 
             // Check that len expression is equals to `with_capacity` expression
             if let Some(len_arg) = take_args.get(1);
@@ -267,21 +262,19 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
     }
 
     /// Returns `true` if given expression is `repeat(0)`
-    fn is_repeat_zero(expr: &Expr<'_>) -> bool {
+    fn is_repeat_zero(&self, expr: &Expr<'_>) -> bool {
         if_chain! {
-            if let ExprKind::Call(fn_expr, repeat_args) = expr.kind;
-            if let ExprKind::Path(ref qpath_repeat) = fn_expr.kind;
-            if match_qpath(qpath_repeat, &["repeat"]);
-            if let Some(repeat_arg) = repeat_args.get(0);
+            if let ExprKind::Call(fn_expr, [repeat_arg]) = expr.kind;
+            if is_expr_path_def_path(self.cx, fn_expr, &paths::ITER_REPEAT);
             if let ExprKind::Lit(ref lit) = repeat_arg.kind;
             if let LitKind::Int(0, _) = lit.node;
 
             then {
-                return true
+                true
+            } else {
+                false
             }
         }
-
-        false
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
index cb2237e531262..4272935bc310e 100644
--- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
+++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
@@ -195,7 +195,7 @@ fn attempt_to_emit_no_difference_lint(
     i: usize,
     expected_loc: IdentLocation,
 ) {
-    if let Some(binop) = binops.get(i).cloned() {
+    if let Some(binop) = binops.get(i).copied() {
         // We need to try and figure out which identifier we should
         // suggest using instead. Since there could be multiple
         // replacement candidates in a given expression, and we're
diff --git a/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs b/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs
index a0492a88f912a..e2c144709f5b7 100644
--- a/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs
+++ b/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs
@@ -104,30 +104,32 @@ fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> {
     // tracker to decide if the last group of tabs is not closed by a non-tab character
     let mut is_active = false;
 
-    let chars_array: Vec<_> = the_str.chars().collect();
+    // Note that we specifically need the char _byte_ indices here, not the positional indexes
+    // within the char array to deal with multi-byte characters properly. `char_indices` does
+    // exactly that. It provides an iterator over tuples of the form `(byte position, char)`.
+    let char_indices: Vec<_> = the_str.char_indices().collect();
 
-    if chars_array == vec!['\t'] {
+    if let [(_, '\t')] = char_indices.as_slice() {
         return vec![(0, 1)];
     }
 
-    for (index, arr) in chars_array.windows(2).enumerate() {
-        let index = u32::try_from(index).expect(line_length_way_to_long);
-        match arr {
-            ['\t', '\t'] => {
+    for entry in char_indices.windows(2) {
+        match entry {
+            [(_, '\t'), (_, '\t')] => {
                 // either string starts with double tab, then we have to set it active,
                 // otherwise is_active is true anyway
                 is_active = true;
             },
-            [_, '\t'] => {
+            [(_, _), (index_b, '\t')] => {
                 // as ['\t', '\t'] is excluded, this has to be a start of a tab group,
                 // set indices accordingly
                 is_active = true;
-                current_start = index + 1;
+                current_start = u32::try_from(*index_b).unwrap();
             },
-            ['\t', _] => {
+            [(_, '\t'), (index_b, _)] => {
                 // this now has to be an end of the group, hence we have to push a new tuple
                 is_active = false;
-                spans.push((current_start, index + 1));
+                spans.push((current_start, u32::try_from(*index_b).unwrap()));
             },
             _ => {},
         }
@@ -137,7 +139,7 @@ fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> {
     if is_active {
         spans.push((
             current_start,
-            u32::try_from(the_str.chars().count()).expect(line_length_way_to_long),
+            u32::try_from(char_indices.last().unwrap().0 + 1).expect(line_length_way_to_long),
         ));
     }
 
@@ -148,6 +150,13 @@ fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> {
 mod tests_for_get_chunks_of_tabs {
     use super::get_chunks_of_tabs;
 
+    #[test]
+    fn test_unicode_han_string() {
+        let res = get_chunks_of_tabs(" \u{4f4d}\t");
+
+        assert_eq!(res, vec![(4, 5)]);
+    }
+
     #[test]
     fn test_empty_string() {
         let res = get_chunks_of_tabs("");
diff --git a/src/tools/clippy/clippy_lints/src/to_string_in_display.rs b/src/tools/clippy/clippy_lints/src/to_string_in_display.rs
index ae05a8da37bc9..4fb297ac6c699 100644
--- a/src/tools/clippy/clippy_lints/src/to_string_in_display.rs
+++ b/src/tools/clippy/clippy_lints/src/to_string_in_display.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{is_diagnostic_assoc_item, match_def_path, path_to_local_id, paths};
+use clippy_utils::{is_diag_trait_item, match_def_path, path_to_local_id, paths};
 use if_chain::if_chain;
 use rustc_hir::{Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -95,7 +95,7 @@ impl LateLintPass<'_> for ToStringInDisplay {
             if let ExprKind::MethodCall(path, _, args, _) = expr.kind;
             if path.ident.name == sym!(to_string);
             if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
-            if is_diagnostic_assoc_item(cx, expr_def_id, sym::ToString);
+            if is_diag_trait_item(cx, expr_def_id, sym::ToString);
             if path_to_local_id(&args[0], self_hir_id);
             then {
                 span_lint(
diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
index 86ac916df6cba..569113910c982 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
@@ -274,7 +274,7 @@ declare_clippy_lint! {
     /// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };
     /// ```
     pub TRANSMUTE_PTR_TO_PTR,
-    complexity,
+    pedantic,
     "transmutes from a pointer to a pointer / a reference to a reference"
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/transmuting_null.rs b/src/tools/clippy/clippy_lints/src/transmuting_null.rs
index 0be05d3e0cf3f..888ecab10461a 100644
--- a/src/tools/clippy/clippy_lints/src/transmuting_null.rs
+++ b/src/tools/clippy/clippy_lints/src/transmuting_null.rs
@@ -1,6 +1,6 @@
 use crate::consts::{constant_context, Constant};
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{match_qpath, paths};
+use clippy_utils::{is_expr_path_def_path, paths};
 use if_chain::if_chain;
 use rustc_ast::LitKind;
 use rustc_hir::{Expr, ExprKind};
@@ -37,18 +37,15 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull {
         }
 
         if_chain! {
-            if let ExprKind::Call(func, args) = expr.kind;
-            if let ExprKind::Path(ref path) = func.kind;
-            if match_qpath(path, &paths::STD_MEM_TRANSMUTE);
-            if args.len() == 1;
+            if let ExprKind::Call(func, [arg]) = expr.kind;
+            if is_expr_path_def_path(cx, func, &paths::TRANSMUTE);
 
             then {
-
                 // Catching transmute over constants that resolve to `null`.
                 let mut const_eval_context = constant_context(cx, cx.typeck_results());
                 if_chain! {
-                    if let ExprKind::Path(ref _qpath) = args[0].kind;
-                    let x = const_eval_context.expr(&args[0]);
+                    if let ExprKind::Path(ref _qpath) = arg.kind;
+                    let x = const_eval_context.expr(arg);
                     if let Some(Constant::RawPtr(0)) = x;
                     then {
                         span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
@@ -58,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull {
                 // Catching:
                 // `std::mem::transmute(0 as *const i32)`
                 if_chain! {
-                    if let ExprKind::Cast(inner_expr, _cast_ty) = args[0].kind;
+                    if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind;
                     if let ExprKind::Lit(ref lit) = inner_expr.kind;
                     if let LitKind::Int(0, _) = lit.node;
                     then {
@@ -69,10 +66,8 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull {
                 // Catching:
                 // `std::mem::transmute(std::ptr::null::<i32>())`
                 if_chain! {
-                    if let ExprKind::Call(func1, args1) = args[0].kind;
-                    if let ExprKind::Path(ref path1) = func1.kind;
-                    if match_qpath(path1, &paths::STD_PTR_NULL);
-                    if args1.is_empty();
+                    if let ExprKind::Call(func1, []) = arg.kind;
+                    if is_expr_path_def_path(cx, func1, &paths::PTR_NULL);
                     then {
                         span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
                     }
diff --git a/src/tools/clippy/clippy_lints/src/try_err.rs b/src/tools/clippy/clippy_lints/src/try_err.rs
index 23a1953ffaceb..ebb39ea4877de 100644
--- a/src/tools/clippy/clippy_lints/src/try_err.rs
+++ b/src/tools/clippy/clippy_lints/src/try_err.rs
@@ -1,9 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::{snippet, snippet_with_macro_callsite};
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{differing_macro_contexts, in_macro, match_def_path, match_qpath, paths};
+use clippy_utils::{differing_macro_contexts, get_parent_expr, in_macro, is_lang_ctor, match_def_path, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
+use rustc_hir::LangItem::ResultErr;
 use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
@@ -68,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for TryErr {
             if let ExprKind::Call(err_fun, err_args) = try_arg.kind;
             if let Some(err_arg) = err_args.get(0);
             if let ExprKind::Path(ref err_fun_path) = err_fun.kind;
-            if match_qpath(err_fun_path, &paths::RESULT_ERR);
+            if is_lang_ctor(cx, err_fun_path, ResultErr);
             if let Some(return_ty) = find_return_type(cx, &expr.kind);
             then {
                 let prefix;
@@ -101,10 +102,15 @@ impl<'tcx> LateLintPass<'tcx> for TryErr {
                 } else {
                     snippet(cx, err_arg.span, "_")
                 };
+                let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) {
+                    "" // already returns
+                } else {
+                    "return "
+                };
                 let suggestion = if err_ty == expr_err_ty {
-                    format!("return {}{}{}", prefix, origin_snippet, suffix)
+                    format!("{}{}{}{}", ret_prefix, prefix, origin_snippet, suffix)
                 } else {
-                    format!("return {}{}.into(){}", prefix, origin_snippet, suffix)
+                    format!("{}{}{}.into(){}", ret_prefix, prefix, origin_snippet, suffix)
                 };
 
                 span_lint_and_sugg(
diff --git a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
index 1425d8f3f37ed..bdeff035e5ec9 100644
--- a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
+++ b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
-use clippy_utils::{match_path, paths};
+use clippy_utils::{match_def_path, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{
@@ -28,7 +28,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m
                     _ => None,
                 });
                 then {
-                    if is_any_trait(inner) {
+                    if is_any_trait(cx, inner) {
                         // Ignore `Box<Any>` types; see issue #1884 for details.
                         return false;
                     }
@@ -84,13 +84,14 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m
 }
 
 // Returns true if given type is `Any` trait.
-fn is_any_trait(t: &hir::Ty<'_>) -> bool {
+fn is_any_trait(cx: &LateContext<'_>, t: &hir::Ty<'_>) -> bool {
     if_chain! {
         if let TyKind::TraitObject(traits, ..) = t.kind;
         if !traits.is_empty();
+        if let Some(trait_did) = traits[0].trait_ref.trait_def_id();
         // Only Send/Sync can be used as additional traits, so it is enough to
         // check only the first trait.
-        if match_path(traits[0].trait_ref.path, &paths::ANY_TRAIT);
+        if match_def_path(cx, trait_did, &paths::ANY_TRAIT);
         then {
             return true;
         }
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs b/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs
new file mode 100644
index 0000000000000..48c54d79cf113
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs
@@ -0,0 +1,67 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use if_chain::if_chain;
+use rustc_ast::{Item, ItemKind, UseTreeKind};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::kw;
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for imports ending in `::{self}`.
+    ///
+    /// **Why is this bad?** In most cases, this can be written much more cleanly by omitting `::{self}`.
+    ///
+    /// **Known problems:** Removing `::{self}` will cause any non-module items at the same path to also be imported.
+    /// This might cause a naming conflict (https://github.com/rust-lang/rustfmt/issues/3568). This lint makes no attempt
+    /// to detect this scenario and that is why it is a restriction lint.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// use std::io::{self};
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// use std::io;
+    /// ```
+    pub UNNECESSARY_SELF_IMPORTS,
+    restriction,
+    "imports ending in `::{self}`, which can be omitted"
+}
+
+declare_lint_pass!(UnnecessarySelfImports => [UNNECESSARY_SELF_IMPORTS]);
+
+impl EarlyLintPass for UnnecessarySelfImports {
+    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+        if_chain! {
+            if let ItemKind::Use(use_tree) = &item.kind;
+            if let UseTreeKind::Nested(nodes) = &use_tree.kind;
+            if let [(self_tree, _)] = &**nodes;
+            if let [self_seg] = &*self_tree.prefix.segments;
+            if self_seg.ident.name == kw::SelfLower;
+            if let Some(last_segment) = use_tree.prefix.segments.last();
+
+            then {
+                span_lint_and_then(
+                    cx,
+                    UNNECESSARY_SELF_IMPORTS,
+                    item.span,
+                    "import ending with `::{self}`",
+                    |diag| {
+                        diag.span_suggestion(
+                            last_segment.span().with_hi(item.span.hi()),
+                            "consider omitting `::{self}`",
+                            format!(
+                                "{}{};",
+                                last_segment.ident,
+                                if let UseTreeKind::Simple(Some(alias), ..) = self_tree.kind { format!(" as {}", alias) } else { String::new() },
+                            ),
+                            Applicability::MaybeIncorrect,
+                        );
+                        diag.note("this will slightly change semantics; any non-module items at the same path will also be imported");
+                    },
+                );
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
index 5bb417cb1be4b..f2f1410aed742 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
@@ -1,9 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
-use clippy_utils::{contains_return, in_macro, match_qpath, paths, return_ty, visitors::find_all_ret_expressions};
+use clippy_utils::{contains_return, in_macro, is_lang_ctor, return_ty, visitors::find_all_ret_expressions};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
+use rustc_hir::LangItem::{OptionSome, ResultOk};
 use rustc_hir::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
@@ -85,11 +86,11 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
         }
 
         // Get the wrapper and inner types, if can't, abort.
-        let (return_type_label, path, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id).kind() {
+        let (return_type_label, lang_item, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id).kind() {
             if cx.tcx.is_diagnostic_item(sym::option_type, adt_def.did) {
-                ("Option", &paths::OPTION_SOME, subst.type_at(0))
+                ("Option", OptionSome, subst.type_at(0))
             } else if cx.tcx.is_diagnostic_item(sym::result_type, adt_def.did) {
-                ("Result", &paths::RESULT_OK, subst.type_at(0))
+                ("Result", ResultOk, subst.type_at(0))
             } else {
                 return;
             }
@@ -103,14 +104,12 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
             if_chain! {
                 if !in_macro(ret_expr.span);
                 // Check if a function call.
-                if let ExprKind::Call(func, args) = ret_expr.kind;
-                // Get the Path of the function call.
-                if let ExprKind::Path(ref qpath) = func.kind;
+                if let ExprKind::Call(func, [arg]) = ret_expr.kind;
                 // Check if OPTION_SOME or RESULT_OK, depending on return type.
-                if match_qpath(qpath, path);
-                if args.len() == 1;
+                if let ExprKind::Path(qpath) = &func.kind;
+                if is_lang_ctor(cx, qpath, lang_item);
                 // Make sure the function argument does not contain a return expression.
-                if !contains_return(&args[0]);
+                if !contains_return(arg);
                 then {
                     suggs.push(
                         (
@@ -118,7 +117,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
                             if inner_type.is_unit() {
                                 "".to_string()
                             } else {
-                                snippet(cx, args[0].span.source_callsite(), "..").to_string()
+                                snippet(cx, arg.span.source_callsite(), "..").to_string()
                             }
                         )
                     );
diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
index 024ab03fd418e..5e8e530f480fb 100644
--- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
@@ -41,7 +41,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
         };
 
         match expr.kind {
-            hir::ExprKind::Match(res, _, _) if is_try(expr).is_some() => {
+            hir::ExprKind::Match(res, _, _) if is_try(cx, expr).is_some() => {
                 if let hir::ExprKind::Call(func, args) = res.kind {
                     if matches!(
                         func.kind,
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
index cf8039d6059b6..3d3d0e19d2622 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
@@ -3,7 +3,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sug
 use clippy_utils::source::snippet;
 use clippy_utils::ty::match_type;
 use clippy_utils::{
-    is_else_clause, is_expn_of, match_def_path, match_qpath, method_calls, path_to_res, paths, run_lints, SpanlessEq,
+    is_else_clause, is_expn_of, is_expr_path_def_path, match_def_path, method_calls, path_to_res, paths, run_lints,
+    SpanlessEq,
 };
 use if_chain::if_chain;
 use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId};
@@ -578,8 +579,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
 
         if_chain! {
             if let ExprKind::Call(func, and_then_args) = expr.kind;
-            if let ExprKind::Path(ref path) = func.kind;
-            if match_qpath(path, &["span_lint_and_then"]);
+            if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]);
             if and_then_args.len() == 5;
             if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind;
             let body = cx.tcx.hir().body(*body_id);
@@ -761,8 +761,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
         if_chain! {
             // Check if this is a call to utils::match_type()
             if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind;
-            if let ExprKind::Path(fn_qpath) = &fn_path.kind;
-            if match_qpath(fn_qpath, &["utils", "match_type"]);
+            if is_expr_path_def_path(cx, fn_path, &["clippy_utils", "ty", "match_type"]);
             // Extract the path to the matched type
             if let Some(segments) = path_to_matched_type(cx, ty_path);
             let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect();
@@ -771,6 +770,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
             let diag_items = cx.tcx.diagnostic_items(ty_did.krate);
             if let Some(item_name) = diag_items.iter().find_map(|(k, v)| if *v == ty_did { Some(k) } else { None });
             then {
+                // TODO: check paths constants from external crates.
                 let cx_snippet = snippet(cx, context.span, "_");
                 let ty_snippet = snippet(cx, ty.span, "_");
 
@@ -778,9 +778,9 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
                     cx,
                     MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
                     expr.span,
-                    "usage of `utils::match_type()` on a type diagnostic item",
+                    "usage of `clippy_utils::ty::match_type()` on a type diagnostic item",
                     "try",
-                    format!("utils::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name),
+                    format!("clippy_utils::ty::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name),
                     Applicability::MaybeIncorrect,
                 );
             }
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index 12a47a6b7036d..7e962472c07f5 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -573,7 +573,7 @@ impl Write {
                         diag.multipart_suggestion(
                             "try this",
                             iter::once((comma_span.to(token_expr.span), String::new()))
-                                .chain(fmt_spans.iter().cloned().zip(iter::repeat(replacement)))
+                                .chain(fmt_spans.iter().copied().zip(iter::repeat(replacement)))
                                 .collect(),
                             Applicability::MachineApplicable,
                         );
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index eaea3e636f9c3..93e10c836cc7f 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -5,6 +5,7 @@
 #![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)]
 
 use crate::{both, over};
+use if_chain::if_chain;
 use rustc_ast::ptr::P;
 use rustc_ast::{self as ast, *};
 use rustc_span::symbol::Ident;
@@ -571,3 +572,34 @@ pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool {
         _ => false,
     }
 }
+
+/// Extract args from an assert-like macro.
+///
+/// Currently working with:
+/// - `assert_eq!` and `assert_ne!`
+/// - `debug_assert_eq!` and `debug_assert_ne!`
+///
+/// For example:
+///
+/// `debug_assert_eq!(a, b)` will return Some([a, b])
+pub fn extract_assert_macro_args(mut expr: &Expr) -> Option<[&Expr; 2]> {
+    if_chain! {
+        if let ExprKind::If(_, ref block, _) = expr.kind;
+        if let StmtKind::Semi(ref e) = block.stmts.get(0)?.kind;
+        then {
+            expr = e;
+        }
+    }
+    if_chain! {
+        if let ExprKind::Block(ref block, _) = expr.kind;
+        if let StmtKind::Expr(ref expr) = block.stmts.get(0)?.kind;
+        if let ExprKind::Match(ref match_expr, _) = expr.kind;
+        if let ExprKind::Tup(ref tup) = match_expr.kind;
+        if let [a, b, ..] = tup.as_slice();
+        if let (&ExprKind::AddrOf(_, _, ref a), &ExprKind::AddrOf(_, _, ref b)) = (&a.kind, &b.kind);
+        then {
+            return Some([&*a, &*b]);
+        }
+    }
+    None
+}
diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs
index 7ec8452bf4c67..c0584e1e22694 100644
--- a/src/tools/clippy/clippy_utils/src/attrs.rs
+++ b/src/tools/clippy/clippy_utils/src/attrs.rs
@@ -151,10 +151,9 @@ pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool {
 
 /// Return true if the attributes contain `#[doc(hidden)]`
 pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool {
-    #[allow(clippy::filter_map)]
     attrs
         .iter()
         .filter(|attr| attr.has_name(sym::doc))
-        .flat_map(ast::Attribute::meta_item_list)
+        .filter_map(ast::Attribute::meta_item_list)
         .any(|l| attr::list_contains_name(&l, sym::hidden))
 }
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index f695f1a61e716..07ae6e924e28b 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -96,6 +96,16 @@ impl HirEqInterExpr<'_, '_, '_> {
     pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
         match (&left.kind, &right.kind) {
             (&StmtKind::Local(ref l), &StmtKind::Local(ref r)) => {
+                // This additional check ensures that the type of the locals are equivalent even if the init
+                // expression or type have some inferred parts.
+                if let Some(typeck) = self.inner.maybe_typeck_results {
+                    let l_ty = typeck.pat_ty(&l.pat);
+                    let r_ty = typeck.pat_ty(&r.pat);
+                    if !rustc_middle::ty::TyS::same_type(l_ty, r_ty) {
+                        return false;
+                    }
+                }
+
                 // eq_pat adds the HirIds to the locals map. We therefor call it last to make sure that
                 // these only get added if the init and type is equal.
                 both(&l.init, &r.init, |l, r| self.eq_expr(l, r))
@@ -424,7 +434,7 @@ fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'
                                 TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
                             )
                         })
-                        .ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().cloned()) =>
+                        .ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied()) =>
                 {
                     kind
                 },
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index c847712ec2e9e..9564432ee48e1 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -10,6 +10,7 @@
 // (Currently there is no way to opt into sysroot crates without `extern crate`.)
 extern crate rustc_ast;
 extern crate rustc_ast_pretty;
+extern crate rustc_attr;
 extern crate rustc_data_structures;
 extern crate rustc_errors;
 extern crate rustc_hir;
@@ -57,13 +58,14 @@ use if_chain::if_chain;
 use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir as hir;
-use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
+use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
-use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor};
+use rustc_hir::LangItem::{ResultErr, ResultOk};
 use rustc_hir::{
-    def, Arm, BindingAnnotation, Block, Body, Constness, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, ImplItem,
-    ImplItemKind, Item, ItemKind, LangItem, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, QPath,
-    TraitItem, TraitItemKind, TraitRef, TyKind,
+    def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl,
+    ImplItem, ImplItemKind, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment,
+    QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind,
 };
 use rustc_lint::{LateContext, Level, Lint, LintContext};
 use rustc_middle::hir::exports::Export;
@@ -80,7 +82,7 @@ use rustc_span::{Span, DUMMY_SP};
 use rustc_target::abi::Integer;
 
 use crate::consts::{constant, Constant};
-use crate::ty::is_recursively_primitive_type;
+use crate::ty::{can_partially_move_ty, is_recursively_primitive_type};
 
 pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
     if let Ok(version) = RustcVersion::parse(msrv) {
@@ -222,6 +224,19 @@ pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool {
     }
 }
 
+/// Checks if a `QPath` resolves to a constructor of a `LangItem`.
+/// For example, use this to check whether a function call or a pattern is `Some(..)`.
+pub fn is_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem) -> bool {
+    if let QPath::Resolved(_, path) = qpath {
+        if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res {
+            if let Ok(item_id) = cx.tcx.lang_items().require(lang_item) {
+                return cx.tcx.parent(ctor_id) == Some(item_id);
+            }
+        }
+    }
+    false
+}
+
 /// Returns `true` if this `span` was expanded by any macro.
 #[must_use]
 pub fn in_macro(span: Span) -> bool {
@@ -279,27 +294,29 @@ pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str])
     trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
 }
 
-/// Checks if the method call given in `def_id` belongs to a trait or other container with a given
-/// diagnostic item
-pub fn is_diagnostic_assoc_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
-    cx.tcx
-        .opt_associated_item(def_id)
-        .and_then(|associated_item| match associated_item.container {
-            rustc_ty::TraitContainer(assoc_def_id) => Some(assoc_def_id),
-            rustc_ty::ImplContainer(assoc_def_id) => match cx.tcx.type_of(assoc_def_id).kind() {
-                rustc_ty::Adt(adt, _) => Some(adt.did),
-                rustc_ty::Slice(_) => cx.tcx.get_diagnostic_item(sym::slice), // this isn't perfect but it works
-                _ => None,
-            },
-        })
-        .map_or(false, |assoc_def_id| cx.tcx.is_diagnostic_item(diag_item, assoc_def_id))
+/// Checks if a method is defined in an impl of a diagnostic item
+pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
+    if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
+        if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
+            return cx.tcx.is_diagnostic_item(diag_item, adt.did);
+        }
+    }
+    false
+}
+
+/// Checks if a method is in a diagnostic item trait
+pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
+    if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
+        return cx.tcx.is_diagnostic_item(diag_item, trait_did);
+    }
+    false
 }
 
 /// Checks if the method call given in `expr` belongs to the given trait.
 pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
     cx.typeck_results()
         .type_dependent_def_id(expr.hir_id)
-        .map_or(false, |did| is_diagnostic_assoc_item(cx, did, diag_item))
+        .map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
 }
 
 /// Checks if an expression references a variable of the given name.
@@ -380,6 +397,29 @@ pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
     }
 }
 
+/// If the expression is a path, resolve it. Otherwise, return `Res::Err`.
+pub fn expr_path_res(cx: &LateContext<'_>, expr: &Expr<'_>) -> Res {
+    if let ExprKind::Path(p) = &expr.kind {
+        cx.qpath_res(p, expr.hir_id)
+    } else {
+        Res::Err
+    }
+}
+
+/// Resolves the path to a `DefId` and checks if it matches the given path.
+pub fn is_qpath_def_path(cx: &LateContext<'_>, path: &QPath<'_>, hir_id: HirId, segments: &[&str]) -> bool {
+    cx.qpath_res(path, hir_id)
+        .opt_def_id()
+        .map_or(false, |id| match_def_path(cx, id, segments))
+}
+
+/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
+pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
+    expr_path_res(cx, expr)
+        .opt_def_id()
+        .map_or(false, |id| match_def_path(cx, id, segments))
+}
+
 /// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
 /// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
 /// `QPath::Resolved.1.res.opt_def_id()`.
@@ -408,20 +448,6 @@ pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
         .all(|(a, b)| a.ident.name.as_str() == *b)
 }
 
-/// Matches a `Path` against a slice of segment string literals, e.g.
-///
-/// # Examples
-/// ```rust,ignore
-/// match_path_ast(path, &["std", "rt", "begin_unwind"])
-/// ```
-pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool {
-    path.segments
-        .iter()
-        .rev()
-        .zip(segments.iter().rev())
-        .all(|(a, b)| a.ident.name.as_str() == *b)
-}
-
 /// If the expression is a path to a local, returns the canonical `HirId` of the local.
 pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
     if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind {
@@ -522,6 +548,73 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
     None
 }
 
+/// Checks if the top level expression can be moved into a closure as is.
+pub fn can_move_expr_to_closure_no_visit(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, jump_targets: &[HirId]) -> bool {
+    match expr.kind {
+        ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
+        | ExprKind::Continue(Destination { target_id: Ok(id), .. })
+            if jump_targets.contains(&id) =>
+        {
+            true
+        },
+        ExprKind::Break(..)
+        | ExprKind::Continue(_)
+        | ExprKind::Ret(_)
+        | ExprKind::Yield(..)
+        | ExprKind::InlineAsm(_)
+        | ExprKind::LlvmInlineAsm(_) => false,
+        // Accessing a field of a local value can only be done if the type isn't
+        // partially moved.
+        ExprKind::Field(base_expr, _)
+            if matches!(
+                base_expr.kind,
+                ExprKind::Path(QPath::Resolved(_, Path { res: Res::Local(_), .. }))
+            ) && can_partially_move_ty(cx, cx.typeck_results().expr_ty(base_expr)) =>
+        {
+            // TODO: check if the local has been partially moved. Assume it has for now.
+            false
+        }
+        _ => true,
+    }
+}
+
+/// Checks if the expression can be moved into a closure as is.
+pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+    struct V<'cx, 'tcx> {
+        cx: &'cx LateContext<'tcx>,
+        loops: Vec<HirId>,
+        allow_closure: bool,
+    }
+    impl Visitor<'tcx> for V<'_, 'tcx> {
+        type Map = ErasedMap<'tcx>;
+        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+            NestedVisitorMap::None
+        }
+
+        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
+            if !self.allow_closure {
+                return;
+            }
+            if let ExprKind::Loop(b, ..) = e.kind {
+                self.loops.push(e.hir_id);
+                self.visit_block(b);
+                self.loops.pop();
+            } else {
+                self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops);
+                walk_expr(self, e);
+            }
+        }
+    }
+
+    let mut v = V {
+        cx,
+        allow_closure: true,
+        loops: Vec::new(),
+    };
+    v.visit_expr(expr);
+    v.allow_closure
+}
+
 /// Returns the method names and argument list of nested method call expressions that make up
 /// `expr`. method/span lists are sorted with the most recent call first.
 pub fn method_calls<'tcx>(
@@ -960,7 +1053,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
 /// the function once on the given pattern.
 pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
     if let PatKind::Or(pats) = pat.kind {
-        pats.iter().cloned().for_each(f)
+        pats.iter().copied().for_each(f)
     } else {
         f(pat)
     }
@@ -1011,11 +1104,11 @@ pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl It
 
 /// Checks if a given expression is a match expression expanded from the `?`
 /// operator or the `try` macro.
-pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
-    fn is_ok(arm: &Arm<'_>) -> bool {
+pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
+    fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
         if_chain! {
             if let PatKind::TupleStruct(ref path, ref pat, None) = arm.pat.kind;
-            if match_qpath(path, &paths::RESULT_OK[1..]);
+            if is_lang_ctor(cx, path, ResultOk);
             if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind;
             if path_to_local_id(arm.body, hir_id);
             then {
@@ -1025,9 +1118,9 @@ pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
         false
     }
 
-    fn is_err(arm: &Arm<'_>) -> bool {
+    fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
         if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
-            match_qpath(path, &paths::RESULT_ERR[1..])
+            is_lang_ctor(cx, path, ResultErr)
         } else {
             false
         }
@@ -1043,8 +1136,8 @@ pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
             if arms.len() == 2;
             if arms[0].guard.is_none();
             if arms[1].guard.is_none();
-            if (is_ok(&arms[0]) && is_err(&arms[1])) ||
-                (is_ok(&arms[1]) && is_err(&arms[0]));
+            if (is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) ||
+                (is_ok(cx, &arms[1]) && is_err(cx, &arms[0]));
             then {
                 return Some(expr);
             }
@@ -1131,29 +1224,47 @@ pub fn match_function_call<'tcx>(
     None
 }
 
+/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if
+/// any.
+pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
+    let search_path = cx.get_def_path(did);
+    paths
+        .iter()
+        .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
+}
+
+/// Checks if the given `DefId` matches the path.
 pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {
-    // We have to convert `syms` to `&[Symbol]` here because rustc's `match_def_path`
-    // accepts only that. We should probably move to Symbols in Clippy as well.
-    let syms = syms.iter().map(|p| Symbol::intern(p)).collect::<Vec<Symbol>>();
-    cx.match_def_path(did, &syms)
+    // We should probably move to Symbols in Clippy as well rather than interning every time.
+    let path = cx.get_def_path(did);
+    syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
 }
 
-pub fn match_panic_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx [Expr<'tcx>]> {
-    match_function_call(cx, expr, &paths::BEGIN_PANIC)
-        .or_else(|| match_function_call(cx, expr, &paths::BEGIN_PANIC_FMT))
-        .or_else(|| match_function_call(cx, expr, &paths::PANIC_ANY))
-        .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC))
-        .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC_FMT))
-        .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC_STR))
+pub fn match_panic_call(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+    if let ExprKind::Call(func, [arg]) = expr.kind {
+        expr_path_res(cx, func)
+            .opt_def_id()
+            .map_or(false, |id| match_panic_def_id(cx, id))
+            .then(|| arg)
+    } else {
+        None
+    }
 }
 
 pub fn match_panic_def_id(cx: &LateContext<'_>, did: DefId) -> bool {
-    match_def_path(cx, did, &paths::BEGIN_PANIC)
-        || match_def_path(cx, did, &paths::BEGIN_PANIC_FMT)
-        || match_def_path(cx, did, &paths::PANIC_ANY)
-        || match_def_path(cx, did, &paths::PANICKING_PANIC)
-        || match_def_path(cx, did, &paths::PANICKING_PANIC_FMT)
-        || match_def_path(cx, did, &paths::PANICKING_PANIC_STR)
+    match_any_def_paths(
+        cx,
+        did,
+        &[
+            &paths::BEGIN_PANIC,
+            &paths::BEGIN_PANIC_FMT,
+            &paths::PANIC_ANY,
+            &paths::PANICKING_PANIC,
+            &paths::PANICKING_PANIC_FMT,
+            &paths::PANICKING_PANIC_STR,
+        ],
+    )
+    .is_some()
 }
 
 /// Returns the list of condition expressions and the list of blocks in a
@@ -1189,21 +1300,6 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>,
     (conds, blocks)
 }
 
-/// This function returns true if the given expression is the `else` or `if else` part of an if
-/// statement
-pub fn parent_node_is_if_expr(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool {
-    let map = cx.tcx.hir();
-    let parent_id = map.get_parent_node(expr.hir_id);
-    let parent_node = map.get(parent_id);
-    matches!(
-        parent_node,
-        Node::Expr(Expr {
-            kind: ExprKind::If(_, _, _),
-            ..
-        })
-    )
-}
-
 // Finds the `#[must_use]` attribute, if any
 pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> {
     attrs.iter().find(|a| a.has_name(sym::must_use))
@@ -1228,6 +1324,51 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     did.map_or(false, |did| must_use_attr(&cx.tcx.get_attrs(did)).is_some())
 }
 
+/// Gets the node where an expression is either used, or it's type is unified with another branch.
+pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
+    let map = tcx.hir();
+    let mut child_id = expr.hir_id;
+    let mut iter = map.parent_iter(child_id);
+    loop {
+        match iter.next() {
+            None => break None,
+            Some((id, Node::Block(_))) => child_id = id,
+            Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
+            Some((_, Node::Expr(expr))) => match expr.kind {
+                ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
+                ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
+                ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
+                _ => break Some(Node::Expr(expr)),
+            },
+            Some((_, node)) => break Some(node),
+        }
+    }
+}
+
+/// Checks if the result of an expression is used, or it's type is unified with another branch.
+pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
+    !matches!(
+        get_expr_use_or_unification_node(tcx, expr),
+        None | Some(Node::Stmt(Stmt {
+            kind: StmtKind::Expr(_)
+                | StmtKind::Semi(_)
+                | StmtKind::Local(Local {
+                    pat: Pat {
+                        kind: PatKind::Wild,
+                        ..
+                    },
+                    ..
+                }),
+            ..
+        }))
+    )
+}
+
+/// Checks if the expression is the final expression returned from a block.
+pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
+    matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..)))
+}
+
 pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
     cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
         if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
@@ -1397,28 +1538,43 @@ pub fn peel_hir_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
     peel(pat, 0)
 }
 
+/// Peels of expressions while the given closure returns `Some`.
+pub fn peel_hir_expr_while<'tcx>(
+    mut expr: &'tcx Expr<'tcx>,
+    mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
+) -> &'tcx Expr<'tcx> {
+    while let Some(e) = f(expr) {
+        expr = e;
+    }
+    expr
+}
+
 /// Peels off up to the given number of references on the expression. Returns the underlying
 /// expression and the number of references removed.
 pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
-    fn f(expr: &'a Expr<'a>, count: usize, target: usize) -> (&'a Expr<'a>, usize) {
-        match expr.kind {
-            ExprKind::AddrOf(_, _, expr) if count != target => f(expr, count + 1, target),
-            _ => (expr, count),
-        }
-    }
-    f(expr, 0, count)
+    let mut remaining = count;
+    let e = peel_hir_expr_while(expr, |e| match e.kind {
+        ExprKind::AddrOf(BorrowKind::Ref, _, e) if remaining != 0 => {
+            remaining -= 1;
+            Some(e)
+        },
+        _ => None,
+    });
+    (e, count - remaining)
 }
 
 /// Peels off all references on the expression. Returns the underlying expression and the number of
 /// references removed.
 pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
-    fn f(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
-        match expr.kind {
-            ExprKind::AddrOf(BorrowKind::Ref, _, expr) => f(expr, count + 1),
-            _ => (expr, count),
-        }
-    }
-    f(expr, 0)
+    let mut count = 0;
+    let e = peel_hir_expr_while(expr, |e| match e.kind {
+        ExprKind::AddrOf(BorrowKind::Ref, _, e) => {
+            count += 1;
+            Some(e)
+        },
+        _ => None,
+    });
+    (e, count)
 }
 
 #[macro_export]
@@ -1450,27 +1606,3 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
         }
     }
 }
-
-/// Check if the resolution of a given path is an `Ok` variant of `Result`.
-pub fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool {
-    if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() {
-        if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
-            if let Some(variant_id) = cx.tcx.parent(id) {
-                return variant_id == ok_id;
-            }
-        }
-    }
-    false
-}
-
-/// Check if the resolution of a given path is a `Some` variant of `Option`.
-pub fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool {
-    if let Some(some_id) = cx.tcx.lang_items().option_some_variant() {
-        if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
-            if let Some(variant_id) = cx.tcx.parent(id) {
-                return variant_id == some_id;
-            }
-        }
-    }
-    false
-}
diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs
index 3b4c4070c0ed3..5e6733a300f2c 100644
--- a/src/tools/clippy/clippy_utils/src/paths.rs
+++ b/src/tools/clippy/clippy_utils/src/paths.rs
@@ -4,7 +4,7 @@
 //! Whenever possible, please consider diagnostic items over hardcoded paths.
 //! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
 
-pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"];
+pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"];
 pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
 pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
 pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
@@ -13,13 +13,13 @@ pub(super) const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_
 pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "BinaryHeap"];
 pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
 pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"];
+pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"];
 pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"];
+pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"];
 pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"];
 pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
 pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"];
 pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"];
-pub const COPY: [&str; 4] = ["core", "intrinsics", "", "copy_nonoverlapping"];
-pub const COPY_NONOVERLAPPING: [&str; 4] = ["core", "intrinsics", "", "copy"];
 pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
 pub const CSTRING_AS_C_STR: [&str; 5] = ["std", "ffi", "c_str", "CString", "as_c_str"];
 pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"];
@@ -44,10 +44,14 @@ pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments
 pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];
 pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
 pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"];
+pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"];
+pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"];
 pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
 pub const HASH: [&str; 3] = ["core", "hash", "Hash"];
 pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"];
+pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"];
 pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
+pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"];
 pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"];
 #[cfg(feature = "internal-lints")]
 pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
@@ -60,8 +64,9 @@ pub const INTO: [&str; 3] = ["core", "convert", "Into"];
 pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"];
 pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
 pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
-pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"];
-pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"];
+pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"];
+pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
+pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
 #[cfg(feature = "internal-lints")]
 pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
 #[cfg(feature = "internal-lints")]
@@ -100,12 +105,23 @@ pub const PERMISSIONS_FROM_MODE: [&str; 7] = ["std", "sys", "unix", "ext", "fs",
 pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
 pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"];
 pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"];
+pub const PTR_COPY: [&str; 4] = ["core", "intrinsics", "", "copy"];
+pub const PTR_COPY_NONOVERLAPPING: [&str; 4] = ["core", "intrinsics", "", "copy_nonoverlapping"];
 pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
 pub const PTR_NULL: [&str; 3] = ["core", "ptr", "null"];
 pub const PTR_NULL_MUT: [&str; 3] = ["core", "ptr", "null_mut"];
 pub const PTR_SLICE_FROM_RAW_PARTS: [&str; 3] = ["core", "ptr", "slice_from_raw_parts"];
 pub const PTR_SLICE_FROM_RAW_PARTS_MUT: [&str; 3] = ["core", "ptr", "slice_from_raw_parts_mut"];
 pub const PTR_SWAP_NONOVERLAPPING: [&str; 3] = ["core", "ptr", "swap_nonoverlapping"];
+pub const PTR_READ: [&str; 3] = ["core", "ptr", "read"];
+pub const PTR_READ_UNALIGNED: [&str; 3] = ["core", "ptr", "read_unaligned"];
+pub const PTR_READ_VOLATILE: [&str; 3] = ["core", "ptr", "read_volatile"];
+pub const PTR_REPLACE: [&str; 3] = ["core", "ptr", "replace"];
+pub const PTR_SWAP: [&str; 3] = ["core", "ptr", "swap"];
+pub const PTR_WRITE: [&str; 3] = ["core", "ptr", "write"];
+pub const PTR_WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"];
+pub const PTR_WRITE_UNALIGNED: [&str; 3] = ["core", "ptr", "write_unaligned"];
+pub const PTR_WRITE_VOLATILE: [&str; 3] = ["core", "ptr", "write_volatile"];
 pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"];
 pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
 pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
@@ -117,7 +133,6 @@ pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
 pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
 pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
 pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
-pub const REPEAT: [&str; 3] = ["core", "iter", "repeat"];
 pub const RESULT: [&str; 3] = ["core", "result", "Result"];
 pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"];
 pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
@@ -131,10 +146,8 @@ pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec
 pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"];
 pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];
 pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"];
-pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"];
+pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"];
 pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
-pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"];
-pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"];
 pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
 pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
 pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
index b52cbf31e35aa..b2ce58b597b3d 100644
--- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
+++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
@@ -1,3 +1,8 @@
+// This code used to be a part of `rustc` but moved to Clippy as a result of
+// https://github.com/rust-lang/rust/issues/76618. Because of that, it contains unused code and some
+// of terminologies might not be relevant in the context of Clippy. Note that its behavior might
+// differ from the time of `rustc` even if the name stays the same.
+
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_middle::mir::{
@@ -6,6 +11,7 @@ use rustc_middle::mir::{
 };
 use rustc_middle::ty::subst::GenericArgKind;
 use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
+use rustc_semver::RustcVersion;
 use rustc_span::symbol::sym;
 use rustc_span::Span;
 use rustc_target::spec::abi::Abi::RustIntrinsic;
@@ -13,7 +19,7 @@ use std::borrow::Cow;
 
 type McfResult = Result<(), (Span, Cow<'static, str>)>;
 
-pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult {
+pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&RustcVersion>) -> McfResult {
     let def_id = body.source.def_id();
     let mut current = def_id;
     loop {
@@ -70,7 +76,7 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult {
     )?;
 
     for bb in body.basic_blocks() {
-        check_terminator(tcx, body, bb.terminator())?;
+        check_terminator(tcx, body, bb.terminator(), msrv)?;
         for stmt in &bb.statements {
             check_statement(tcx, body, def_id, stmt)?;
         }
@@ -268,7 +274,12 @@ fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'t
     Ok(())
 }
 
-fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>) -> McfResult {
+fn check_terminator(
+    tcx: TyCtxt<'tcx>,
+    body: &'a Body<'tcx>,
+    terminator: &Terminator<'tcx>,
+    msrv: Option<&RustcVersion>,
+) -> McfResult {
     let span = terminator.source_info.span;
     match &terminator.kind {
         TerminatorKind::FalseEdge { .. }
@@ -305,7 +316,7 @@ fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Termin
         } => {
             let fn_ty = func.ty(body, tcx);
             if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
-                if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) {
+                if !is_const_fn(tcx, fn_def_id, msrv) {
                     return Err((
                         span,
                         format!(
@@ -350,3 +361,24 @@ fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Termin
         TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
     }
 }
+
+fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> bool {
+    rustc_mir::const_eval::is_const_fn(tcx, def_id)
+        && if let Some(const_stab) = tcx.lookup_const_stability(def_id) {
+            if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level {
+                // Checking MSRV is manually necessary because `rustc` has no such concept. This entire
+                // function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`.
+                // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
+                crate::meets_msrv(
+                    msrv,
+                    &RustcVersion::parse(&since.as_str())
+                        .expect("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted"),
+                )
+            } else {
+                // `rustc_mir::const_eval::is_const_fn` should return false for unstably const functions.
+                unreachable!();
+            }
+        } else {
+            true
+        }
+}
diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs
index 2d794d48dc5ff..53180d1f9f54f 100644
--- a/src/tools/clippy/clippy_utils/src/source.rs
+++ b/src/tools/clippy/clippy_utils/src/source.rs
@@ -66,6 +66,15 @@ pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
     snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
 }
 
+/// Gets a snippet of the indentation of the line of a span
+pub fn snippet_indent<T: LintContext>(cx: &T, span: Span) -> Option<String> {
+    snippet_opt(cx, line_span(cx, span)).map(|mut s| {
+        let len = s.len() - s.trim_start().len();
+        s.truncate(len);
+        s
+    })
+}
+
 // If the snippet is empty, it's an attribute that was inserted during macro
 // expansion and we want to ignore those, because they could come from external
 // sources that the user has no control over.
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index 807cfbc4c7f1f..64a80f2554fa4 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -13,7 +13,7 @@ use rustc_lint::LateContext;
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
 use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TypeFoldable, UintTy};
 use rustc_span::sym;
-use rustc_span::symbol::Symbol;
+use rustc_span::symbol::{Ident, Symbol};
 use rustc_span::DUMMY_SP;
 use rustc_trait_selection::traits::query::normalize::AtExt;
 
@@ -52,6 +52,25 @@ pub fn contains_adt_constructor(ty: Ty<'_>, adt: &AdtDef) -> bool {
     })
 }
 
+/// Resolves `<T as Iterator>::Item` for `T`
+/// Do not invoke without first verifying that the type implements `Iterator`
+pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
+    cx.tcx
+        .get_diagnostic_item(sym::Iterator)
+        .and_then(|iter_did| {
+            cx.tcx.associated_items(iter_did).find_by_name_and_kind(
+                cx.tcx,
+                Ident::from_str("Item"),
+                ty::AssocKind::Type,
+                iter_did,
+            )
+        })
+        .map(|assoc| {
+            let proj = cx.tcx.mk_projection(assoc.def_id, cx.tcx.mk_substs_trait(ty, &[]));
+            cx.tcx.normalize_erasing_regions(cx.param_env, proj)
+        })
+}
+
 /// Returns true if ty has `iter` or `iter_mut` methods
 pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> {
     // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
diff --git a/src/tools/clippy/doc/adding_lints.md b/src/tools/clippy/doc/adding_lints.md
index 99b86953d51a6..50f0d724016ff 100644
--- a/src/tools/clippy/doc/adding_lints.md
+++ b/src/tools/clippy/doc/adding_lints.md
@@ -625,7 +625,7 @@ in the following steps:
 Here are some pointers to things you are likely going to need for every lint:
 
 * [Clippy utils][utils] - Various helper functions. Maybe the function you need
-  is already in here (`implements_trait`, `match_path`, `snippet`, etc)
+  is already in here (`implements_trait`, `match_def_path`, `snippet`, etc)
 * [Clippy diagnostics][diagnostics]
 * [The `if_chain` macro][if_chain]
 * [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro]
diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs
index 2041aed2b97c7..bfb0c3b3f74ec 100644
--- a/src/tools/clippy/lintcheck/src/main.rs
+++ b/src/tools/clippy/lintcheck/src/main.rs
@@ -5,7 +5,7 @@
 // When a new lint is introduced, we can search the results for new warnings and check for false
 // positives.
 
-#![allow(clippy::filter_map, clippy::collapsible_else_if)]
+#![allow(clippy::collapsible_else_if)]
 
 use std::ffi::OsStr;
 use std::process::Command;
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index 7e4d12b8632be..cd398451783d6 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2021-04-08"
+channel = "nightly-2021-04-22"
 components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
diff --git a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed
index e588c23345e2f..7764cc8da7861 100644
--- a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed
+++ b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed
@@ -2,58 +2,18 @@
 #![deny(clippy::internal)]
 #![feature(rustc_private)]
 
+extern crate clippy_utils;
 extern crate rustc_ast;
 extern crate rustc_errors;
 extern crate rustc_lint;
 extern crate rustc_session;
 extern crate rustc_span;
 
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
 use rustc_ast::ast::Expr;
-use rustc_errors::{Applicability, DiagnosticBuilder};
-use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
-
-#[allow(unused_variables)]
-pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F)
-where
-    F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>),
-{
-}
-
-#[allow(unused_variables)]
-fn span_lint_and_help<'a, T: LintContext>(
-    cx: &'a T,
-    lint: &'static Lint,
-    span: Span,
-    msg: &str,
-    option_span: Option<Span>,
-    help: &str,
-) {
-}
-
-#[allow(unused_variables)]
-fn span_lint_and_note<'a, T: LintContext>(
-    cx: &'a T,
-    lint: &'static Lint,
-    span: Span,
-    msg: &str,
-    note_span: Option<Span>,
-    note: &str,
-) {
-}
-
-#[allow(unused_variables)]
-fn span_lint_and_sugg<'a, T: LintContext>(
-    cx: &'a T,
-    lint: &'static Lint,
-    sp: Span,
-    msg: &str,
-    help: &str,
-    sugg: String,
-    applicability: Applicability,
-) {
-}
 
 declare_tool_lint! {
     pub clippy::TEST_LINT,
diff --git a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs
index d5dd3bb562b42..bdd296db8320b 100644
--- a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs
+++ b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs
@@ -2,58 +2,18 @@
 #![deny(clippy::internal)]
 #![feature(rustc_private)]
 
+extern crate clippy_utils;
 extern crate rustc_ast;
 extern crate rustc_errors;
 extern crate rustc_lint;
 extern crate rustc_session;
 extern crate rustc_span;
 
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
 use rustc_ast::ast::Expr;
-use rustc_errors::{Applicability, DiagnosticBuilder};
-use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
-
-#[allow(unused_variables)]
-pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F)
-where
-    F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>),
-{
-}
-
-#[allow(unused_variables)]
-fn span_lint_and_help<'a, T: LintContext>(
-    cx: &'a T,
-    lint: &'static Lint,
-    span: Span,
-    msg: &str,
-    option_span: Option<Span>,
-    help: &str,
-) {
-}
-
-#[allow(unused_variables)]
-fn span_lint_and_note<'a, T: LintContext>(
-    cx: &'a T,
-    lint: &'static Lint,
-    span: Span,
-    msg: &str,
-    note_span: Option<Span>,
-    note: &str,
-) {
-}
-
-#[allow(unused_variables)]
-fn span_lint_and_sugg<'a, T: LintContext>(
-    cx: &'a T,
-    lint: &'static Lint,
-    sp: Span,
-    msg: &str,
-    help: &str,
-    sugg: String,
-    applicability: Applicability,
-) {
-}
 
 declare_tool_lint! {
     pub clippy::TEST_LINT,
diff --git a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr
index 874d4a9f255c2..0632b03857737 100644
--- a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr
+++ b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr
@@ -1,5 +1,5 @@
 error: this call is collapsible
-  --> $DIR/collapsible_span_lint_calls.rs:75:9
+  --> $DIR/collapsible_span_lint_calls.rs:35:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable);
@@ -14,7 +14,7 @@ LL | #![deny(clippy::internal)]
    = note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]`
 
 error: this call is collapsible
-  --> $DIR/collapsible_span_lint_calls.rs:78:9
+  --> $DIR/collapsible_span_lint_calls.rs:38:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.span_help(expr.span, help_msg);
@@ -22,7 +22,7 @@ LL | |         });
    | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)`
 
 error: this call is collapsible
-  --> $DIR/collapsible_span_lint_calls.rs:81:9
+  --> $DIR/collapsible_span_lint_calls.rs:41:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.help(help_msg);
@@ -30,7 +30,7 @@ LL | |         });
    | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)`
 
 error: this call is collspible
-  --> $DIR/collapsible_span_lint_calls.rs:84:9
+  --> $DIR/collapsible_span_lint_calls.rs:44:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.span_note(expr.span, note_msg);
@@ -38,7 +38,7 @@ LL | |         });
    | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)`
 
 error: this call is collspible
-  --> $DIR/collapsible_span_lint_calls.rs:87:9
+  --> $DIR/collapsible_span_lint_calls.rs:47:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.note(note_msg);
diff --git a/src/tools/clippy/tests/ui-internal/match_type_on_diag_item.rs b/src/tools/clippy/tests/ui-internal/match_type_on_diag_item.rs
index fe950b0aa7c70..063f0c6460c5e 100644
--- a/src/tools/clippy/tests/ui-internal/match_type_on_diag_item.rs
+++ b/src/tools/clippy/tests/ui-internal/match_type_on_diag_item.rs
@@ -1,29 +1,18 @@
 #![deny(clippy::internal)]
 #![feature(rustc_private)]
 
+extern crate clippy_utils;
 extern crate rustc_hir;
 extern crate rustc_lint;
 extern crate rustc_middle;
+
 #[macro_use]
 extern crate rustc_session;
+use clippy_utils::{paths, ty::match_type};
 use rustc_hir::Expr;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::Ty;
 
-mod paths {
-    pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"];
-}
-
-mod utils {
-    use super::*;
-
-    pub fn match_type(_cx: &LateContext<'_>, _ty: Ty<'_>, _path: &[&str]) -> bool {
-        false
-    }
-}
-
-use utils::match_type;
-
 declare_lint! {
     pub TEST_LINT,
     Warn,
@@ -38,12 +27,12 @@ impl<'tcx> LateLintPass<'tcx> for Pass {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr) {
         let ty = cx.typeck_results().expr_ty(expr);
 
-        let _ = match_type(cx, ty, &paths::VEC);
+        let _ = match_type(cx, ty, &paths::VEC); // FIXME: Doesn't lint external paths
         let _ = match_type(cx, ty, &OPTION);
         let _ = match_type(cx, ty, &["core", "result", "Result"]);
 
         let rc_path = &["alloc", "rc", "Rc"];
-        let _ = utils::match_type(cx, ty, rc_path);
+        let _ = clippy_utils::ty::match_type(cx, ty, rc_path);
     }
 }
 
diff --git a/src/tools/clippy/tests/ui-internal/match_type_on_diag_item.stderr b/src/tools/clippy/tests/ui-internal/match_type_on_diag_item.stderr
index 82465dbaf6ecc..714729605658c 100644
--- a/src/tools/clippy/tests/ui-internal/match_type_on_diag_item.stderr
+++ b/src/tools/clippy/tests/ui-internal/match_type_on_diag_item.stderr
@@ -1,8 +1,8 @@
-error: usage of `utils::match_type()` on a type diagnostic item
-  --> $DIR/match_type_on_diag_item.rs:41:17
+error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
+  --> $DIR/match_type_on_diag_item.rs:31:17
    |
-LL |         let _ = match_type(cx, ty, &paths::VEC);
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::vec_type)`
+LL |         let _ = match_type(cx, ty, &OPTION);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::option_type)`
    |
 note: the lint level is defined here
   --> $DIR/match_type_on_diag_item.rs:1:9
@@ -11,23 +11,17 @@ LL | #![deny(clippy::internal)]
    |         ^^^^^^^^^^^^^^^^
    = note: `#[deny(clippy::match_type_on_diagnostic_item)]` implied by `#[deny(clippy::internal)]`
 
-error: usage of `utils::match_type()` on a type diagnostic item
-  --> $DIR/match_type_on_diag_item.rs:42:17
-   |
-LL |         let _ = match_type(cx, ty, &OPTION);
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::option_type)`
-
-error: usage of `utils::match_type()` on a type diagnostic item
-  --> $DIR/match_type_on_diag_item.rs:43:17
+error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
+  --> $DIR/match_type_on_diag_item.rs:32:17
    |
 LL |         let _ = match_type(cx, ty, &["core", "result", "Result"]);
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::result_type)`
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::result_type)`
 
-error: usage of `utils::match_type()` on a type diagnostic item
-  --> $DIR/match_type_on_diag_item.rs:46:17
+error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
+  --> $DIR/match_type_on_diag_item.rs:35:17
    |
-LL |         let _ = utils::match_type(cx, ty, rc_path);
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::Rc)`
+LL |         let _ = clippy_utils::ty::match_type(cx, ty, rc_path);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Rc)`
 
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui/asm_syntax.rs b/src/tools/clippy/tests/ui/asm_syntax.rs
index 658cae397e149..4a62f6f2909f3 100644
--- a/src/tools/clippy/tests/ui/asm_syntax.rs
+++ b/src/tools/clippy/tests/ui/asm_syntax.rs
@@ -1,5 +1,7 @@
-#![feature(asm)]
 // only-x86_64
+// ignore-aarch64
+
+#![feature(asm)]
 
 #[warn(clippy::inline_asm_x86_intel_syntax)]
 mod warn_intel {
@@ -23,6 +25,7 @@ mod warn_att {
     }
 }
 
+#[cfg(target_arch = "x86_64")]
 fn main() {
     unsafe {
         warn_att::use_asm();
diff --git a/src/tools/clippy/tests/ui/asm_syntax.stderr b/src/tools/clippy/tests/ui/asm_syntax.stderr
index 27b51166eacb8..e3abbe086586e 100644
--- a/src/tools/clippy/tests/ui/asm_syntax.stderr
+++ b/src/tools/clippy/tests/ui/asm_syntax.stderr
@@ -1,5 +1,5 @@
 error: Intel x86 assembly syntax used
-  --> $DIR/asm_syntax.rs:7:9
+  --> $DIR/asm_syntax.rs:9:9
    |
 LL |         asm!("");
    |         ^^^^^^^^^
@@ -8,7 +8,7 @@ LL |         asm!("");
    = help: use AT&T x86 assembly syntax
 
 error: Intel x86 assembly syntax used
-  --> $DIR/asm_syntax.rs:8:9
+  --> $DIR/asm_syntax.rs:10:9
    |
 LL |         asm!("", options());
    |         ^^^^^^^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL |         asm!("", options());
    = help: use AT&T x86 assembly syntax
 
 error: Intel x86 assembly syntax used
-  --> $DIR/asm_syntax.rs:9:9
+  --> $DIR/asm_syntax.rs:11:9
    |
 LL |         asm!("", options(nostack));
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -24,7 +24,7 @@ LL |         asm!("", options(nostack));
    = help: use AT&T x86 assembly syntax
 
 error: AT&T x86 assembly syntax used
-  --> $DIR/asm_syntax.rs:21:9
+  --> $DIR/asm_syntax.rs:23:9
    |
 LL |         asm!("", options(att_syntax));
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -33,7 +33,7 @@ LL |         asm!("", options(att_syntax));
    = help: use Intel x86 assembly syntax
 
 error: AT&T x86 assembly syntax used
-  --> $DIR/asm_syntax.rs:22:9
+  --> $DIR/asm_syntax.rs:24:9
    |
 LL |         asm!("", options(nostack, att_syntax));
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.rs b/src/tools/clippy/tests/ui/bool_assert_comparison.rs
new file mode 100644
index 0000000000000..2de402fae8c7d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/bool_assert_comparison.rs
@@ -0,0 +1,59 @@
+#![warn(clippy::bool_assert_comparison)]
+
+macro_rules! a {
+    () => {
+        true
+    };
+}
+macro_rules! b {
+    () => {
+        true
+    };
+}
+
+fn main() {
+    assert_eq!("a".len(), 1);
+    assert_eq!("a".is_empty(), false);
+    assert_eq!("".is_empty(), true);
+    assert_eq!(true, "".is_empty());
+    assert_eq!(a!(), b!());
+    assert_eq!(a!(), "".is_empty());
+    assert_eq!("".is_empty(), b!());
+
+    assert_ne!("a".len(), 1);
+    assert_ne!("a".is_empty(), false);
+    assert_ne!("".is_empty(), true);
+    assert_ne!(true, "".is_empty());
+    assert_ne!(a!(), b!());
+    assert_ne!(a!(), "".is_empty());
+    assert_ne!("".is_empty(), b!());
+
+    debug_assert_eq!("a".len(), 1);
+    debug_assert_eq!("a".is_empty(), false);
+    debug_assert_eq!("".is_empty(), true);
+    debug_assert_eq!(true, "".is_empty());
+    debug_assert_eq!(a!(), b!());
+    debug_assert_eq!(a!(), "".is_empty());
+    debug_assert_eq!("".is_empty(), b!());
+
+    debug_assert_ne!("a".len(), 1);
+    debug_assert_ne!("a".is_empty(), false);
+    debug_assert_ne!("".is_empty(), true);
+    debug_assert_ne!(true, "".is_empty());
+    debug_assert_ne!(a!(), b!());
+    debug_assert_ne!(a!(), "".is_empty());
+    debug_assert_ne!("".is_empty(), b!());
+
+    // assert with error messages
+    assert_eq!("a".len(), 1, "tadam {}", 1);
+    assert_eq!("a".len(), 1, "tadam {}", true);
+    assert_eq!("a".is_empty(), false, "tadam {}", 1);
+    assert_eq!("a".is_empty(), false, "tadam {}", true);
+    assert_eq!(false, "a".is_empty(), "tadam {}", true);
+
+    debug_assert_eq!("a".len(), 1, "tadam {}", 1);
+    debug_assert_eq!("a".len(), 1, "tadam {}", true);
+    debug_assert_eq!("a".is_empty(), false, "tadam {}", 1);
+    debug_assert_eq!("a".is_empty(), false, "tadam {}", true);
+    debug_assert_eq!(false, "a".is_empty(), "tadam {}", true);
+}
diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.stderr b/src/tools/clippy/tests/ui/bool_assert_comparison.stderr
new file mode 100644
index 0000000000000..f57acf520d5f1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/bool_assert_comparison.stderr
@@ -0,0 +1,112 @@
+error: used `assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:16:5
+   |
+LL |     assert_eq!("a".is_empty(), false);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+   |
+   = note: `-D clippy::bool-assert-comparison` implied by `-D warnings`
+
+error: used `assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:17:5
+   |
+LL |     assert_eq!("".is_empty(), true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:18:5
+   |
+LL |     assert_eq!(true, "".is_empty());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_ne!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:24:5
+   |
+LL |     assert_ne!("a".is_empty(), false);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_ne!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:25:5
+   |
+LL |     assert_ne!("".is_empty(), true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_ne!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:26:5
+   |
+LL |     assert_ne!(true, "".is_empty());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:32:5
+   |
+LL |     debug_assert_eq!("a".is_empty(), false);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:33:5
+   |
+LL |     debug_assert_eq!("".is_empty(), true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:34:5
+   |
+LL |     debug_assert_eq!(true, "".is_empty());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_ne!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:40:5
+   |
+LL |     debug_assert_ne!("a".is_empty(), false);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_ne!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:41:5
+   |
+LL |     debug_assert_ne!("".is_empty(), true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_ne!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:42:5
+   |
+LL |     debug_assert_ne!(true, "".is_empty());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:50:5
+   |
+LL |     assert_eq!("a".is_empty(), false, "tadam {}", 1);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:51:5
+   |
+LL |     assert_eq!("a".is_empty(), false, "tadam {}", true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:52:5
+   |
+LL |     assert_eq!(false, "a".is_empty(), "tadam {}", true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:56:5
+   |
+LL |     debug_assert_eq!("a".is_empty(), false, "tadam {}", 1);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:57:5
+   |
+LL |     debug_assert_eq!("a".is_empty(), false, "tadam {}", true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: used `debug_assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:58:5
+   |
+LL |     debug_assert_eq!(false, "a".is_empty(), "tadam {}", true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
+error: aborting due to 18 previous errors
+
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.rs b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.rs
index c389c243d447e..ce2040bdeb82d 100644
--- a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.rs
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.rs
@@ -206,4 +206,18 @@ fn fp_test() {
     }
 }
 
+fn fp_if_let_issue7054() {
+    // This shouldn't trigger the lint
+    let string;
+    let _x = if let true = true {
+        ""
+    } else if true {
+        string = "x".to_owned();
+        &string
+    } else {
+        string = "y".to_owned();
+        &string
+    };
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs
index e65bcfd78737f..51a46481399b4 100644
--- a/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs
+++ b/src/tools/clippy/tests/ui/branches_sharing_code/shared_at_top.rs
@@ -100,4 +100,15 @@ fn check_if_same_than_else_mask() {
     }
 }
 
+#[allow(clippy::vec_init_then_push)]
+fn pf_local_with_inferred_type_issue7053() {
+    if true {
+        let mut v = Vec::new();
+        v.push(0);
+    } else {
+        let mut v = Vec::new();
+        v.push("");
+    };
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/cloned_instead_of_copied.fixed b/src/tools/clippy/tests/ui/cloned_instead_of_copied.fixed
new file mode 100644
index 0000000000000..4eb999e18e64e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cloned_instead_of_copied.fixed
@@ -0,0 +1,15 @@
+// run-rustfix
+#![warn(clippy::cloned_instead_of_copied)]
+
+fn main() {
+    // yay
+    let _ = [1].iter().copied();
+    let _ = vec!["hi"].iter().copied();
+    let _ = Some(&1).copied();
+    let _ = Box::new([1].iter()).copied();
+    let _ = Box::new(Some(&1)).copied();
+
+    // nay
+    let _ = [String::new()].iter().cloned();
+    let _ = Some(&String::new()).cloned();
+}
diff --git a/src/tools/clippy/tests/ui/cloned_instead_of_copied.rs b/src/tools/clippy/tests/ui/cloned_instead_of_copied.rs
new file mode 100644
index 0000000000000..894496c0ebbb5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cloned_instead_of_copied.rs
@@ -0,0 +1,15 @@
+// run-rustfix
+#![warn(clippy::cloned_instead_of_copied)]
+
+fn main() {
+    // yay
+    let _ = [1].iter().cloned();
+    let _ = vec!["hi"].iter().cloned();
+    let _ = Some(&1).cloned();
+    let _ = Box::new([1].iter()).cloned();
+    let _ = Box::new(Some(&1)).cloned();
+
+    // nay
+    let _ = [String::new()].iter().cloned();
+    let _ = Some(&String::new()).cloned();
+}
diff --git a/src/tools/clippy/tests/ui/cloned_instead_of_copied.stderr b/src/tools/clippy/tests/ui/cloned_instead_of_copied.stderr
new file mode 100644
index 0000000000000..e0707d3214689
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cloned_instead_of_copied.stderr
@@ -0,0 +1,34 @@
+error: used `cloned` where `copied` could be used instead
+  --> $DIR/cloned_instead_of_copied.rs:6:24
+   |
+LL |     let _ = [1].iter().cloned();
+   |                        ^^^^^^ help: try: `copied`
+   |
+   = note: `-D clippy::cloned-instead-of-copied` implied by `-D warnings`
+
+error: used `cloned` where `copied` could be used instead
+  --> $DIR/cloned_instead_of_copied.rs:7:31
+   |
+LL |     let _ = vec!["hi"].iter().cloned();
+   |                               ^^^^^^ help: try: `copied`
+
+error: used `cloned` where `copied` could be used instead
+  --> $DIR/cloned_instead_of_copied.rs:8:22
+   |
+LL |     let _ = Some(&1).cloned();
+   |                      ^^^^^^ help: try: `copied`
+
+error: used `cloned` where `copied` could be used instead
+  --> $DIR/cloned_instead_of_copied.rs:9:34
+   |
+LL |     let _ = Box::new([1].iter()).cloned();
+   |                                  ^^^^^^ help: try: `copied`
+
+error: used `cloned` where `copied` could be used instead
+  --> $DIR/cloned_instead_of_copied.rs:10:32
+   |
+LL |     let _ = Box::new(Some(&1)).cloned();
+   |                                ^^^^^^ help: try: `copied`
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-5835.rs b/src/tools/clippy/tests/ui/crashes/ice-5835.rs
new file mode 100644
index 0000000000000..5e99cb432b6e2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-5835.rs
@@ -0,0 +1,9 @@
+#[rustfmt::skip]
+pub struct Foo {
+    /// 位	
+    ///   ^ Do not remove this tab character.
+    ///   It was required to trigger the ICE.
+    pub bar: u8,
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-5835.stderr b/src/tools/clippy/tests/ui/crashes/ice-5835.stderr
new file mode 100644
index 0000000000000..c972bcb60a0cd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-5835.stderr
@@ -0,0 +1,10 @@
+error: using tabs in doc comments is not recommended
+  --> $DIR/ice-5835.rs:3:10
+   |
+LL |     /// 位    
+   |           ^^^^ help: consider using four spaces per tab
+   |
+   = note: `-D clippy::tabs-in-doc-comments` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/deprecated.rs b/src/tools/clippy/tests/ui/deprecated.rs
index fc444c0bea720..dbf0b03af769c 100644
--- a/src/tools/clippy/tests/ui/deprecated.rs
+++ b/src/tools/clippy/tests/ui/deprecated.rs
@@ -11,5 +11,6 @@
 #[warn(clippy::panic_params)]
 #[warn(clippy::unknown_clippy_lints)]
 #[warn(clippy::find_map)]
+#[warn(clippy::filter_map)]
 
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/deprecated.stderr b/src/tools/clippy/tests/ui/deprecated.stderr
index 64efcd18f8891..3e125c1dab568 100644
--- a/src/tools/clippy/tests/ui/deprecated.stderr
+++ b/src/tools/clippy/tests/ui/deprecated.stderr
@@ -24,23 +24,23 @@ error: lint `clippy::unused_collect` has been removed: `collect` has been marked
 LL | #[warn(clippy::unused_collect)]
    |        ^^^^^^^^^^^^^^^^^^^^^^
 
-error: lint `clippy::invalid_ref` has been removed: superseded by rustc lint `invalid_value`
+error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
   --> $DIR/deprecated.rs:5:8
    |
 LL | #[warn(clippy::invalid_ref)]
-   |        ^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
 
-error: lint `clippy::into_iter_on_array` has been removed: this lint has been uplifted to rustc and is now called `array_into_iter`
+error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
   --> $DIR/deprecated.rs:6:8
    |
 LL | #[warn(clippy::into_iter_on_array)]
-   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
 
-error: lint `clippy::unused_label` has been removed: this lint has been uplifted to rustc and is now called `unused_labels`
+error: lint `clippy::unused_label` has been renamed to `unused_labels`
   --> $DIR/deprecated.rs:7:8
    |
 LL | #[warn(clippy::unused_label)]
-   |        ^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
 
 error: lint `clippy::regex_macro` has been removed: the regex! macro has been removed from the regex crate in 2018
   --> $DIR/deprecated.rs:8:8
@@ -48,29 +48,29 @@ error: lint `clippy::regex_macro` has been removed: the regex! macro has been re
 LL | #[warn(clippy::regex_macro)]
    |        ^^^^^^^^^^^^^^^^^^^
 
-error: lint `clippy::drop_bounds` has been removed: this lint has been uplifted to rustc and is now called `drop_bounds`
+error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
   --> $DIR/deprecated.rs:9:8
    |
 LL | #[warn(clippy::drop_bounds)]
-   |        ^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
 
-error: lint `clippy::temporary_cstring_as_ptr` has been removed: this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`
+error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
   --> $DIR/deprecated.rs:10:8
    |
 LL | #[warn(clippy::temporary_cstring_as_ptr)]
-   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
 
-error: lint `clippy::panic_params` has been removed: this lint has been uplifted to rustc and is now called `panic_fmt`
+error: lint `clippy::panic_params` has been renamed to `non_fmt_panic`
   --> $DIR/deprecated.rs:11:8
    |
 LL | #[warn(clippy::panic_params)]
-   |        ^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panic`
 
-error: lint `clippy::unknown_clippy_lints` has been removed: this lint has been integrated into the `unknown_lints` rustc lint
+error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
   --> $DIR/deprecated.rs:12:8
    |
 LL | #[warn(clippy::unknown_clippy_lints)]
-   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
 
 error: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint
   --> $DIR/deprecated.rs:13:8
@@ -78,11 +78,17 @@ error: lint `clippy::find_map` has been removed: this lint has been replaced by
 LL | #[warn(clippy::find_map)]
    |        ^^^^^^^^^^^^^^^^
 
+error: lint `clippy::filter_map` has been removed: this lint has been replaced by `manual_filter_map`, a more specific lint
+  --> $DIR/deprecated.rs:14:8
+   |
+LL | #[warn(clippy::filter_map)]
+   |        ^^^^^^^^^^^^^^^^^^
+
 error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7
   --> $DIR/deprecated.rs:1:8
    |
 LL | #[warn(clippy::unstable_as_slice)]
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 14 previous errors
+error: aborting due to 15 previous errors
 
diff --git a/src/tools/clippy/tests/ui/entry.fixed b/src/tools/clippy/tests/ui/entry.fixed
new file mode 100644
index 0000000000000..cfad3090ba38d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/entry.fixed
@@ -0,0 +1,155 @@
+// run-rustfix
+
+#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)]
+#![warn(clippy::map_entry)]
+#![feature(asm)]
+
+use std::collections::{BTreeMap, HashMap};
+use std::hash::Hash;
+
+macro_rules! m {
+    ($e:expr) => {{ $e }};
+}
+
+macro_rules! insert {
+    ($map:expr, $key:expr, $val:expr) => {
+        $map.insert($key, $val)
+    };
+}
+
+fn foo() {}
+
+fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, m2: &mut HashMap<K, V>, k: K, k2: K, v: V, v2: V) {
+    // or_insert(v)
+    m.entry(k).or_insert(v);
+
+    // semicolon on insert, use or_insert_with(..)
+    m.entry(k).or_insert_with(|| {
+        if true {
+            v
+        } else {
+            v2
+        }
+    });
+
+    // semicolon on if, use or_insert_with(..)
+    m.entry(k).or_insert_with(|| {
+        if true {
+            v
+        } else {
+            v2
+        }
+    });
+
+    // early return, use if let
+    if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+        if true {
+            e.insert(v);
+        } else {
+            e.insert(v2);
+            return;
+        }
+    }
+
+    // use or_insert_with(..)
+    m.entry(k).or_insert_with(|| {
+        foo();
+        v
+    });
+
+    // semicolon on insert and match, use or_insert_with(..)
+    m.entry(k).or_insert_with(|| {
+        match 0 {
+            1 if true => {
+                v
+            },
+            _ => {
+                v2
+            },
+        }
+    });
+
+    // one branch doesn't insert, use if let
+    if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+        match 0 {
+            0 => foo(),
+            _ => {
+                e.insert(v2);
+            },
+        };
+    }
+
+    // use or_insert_with
+    m.entry(k).or_insert_with(|| {
+        foo();
+        match 0 {
+            0 if false => {
+                v
+            },
+            1 => {
+                foo();
+                v
+            },
+            2 | 3 => {
+                for _ in 0..2 {
+                    foo();
+                }
+                if true {
+                    v
+                } else {
+                    v2
+                }
+            },
+            _ => {
+                v2
+            },
+        }
+    });
+
+    // ok, insert in loop
+    if !m.contains_key(&k) {
+        for _ in 0..2 {
+            m.insert(k, v);
+        }
+    }
+
+    // macro_expansion test, use or_insert(..)
+    m.entry(m!(k)).or_insert_with(|| m!(v));
+
+    // ok, map used before insertion
+    if !m.contains_key(&k) {
+        let _ = m.len();
+        m.insert(k, v);
+    }
+
+    // ok, inline asm
+    if !m.contains_key(&k) {
+        unsafe { asm!("nop") }
+        m.insert(k, v);
+    }
+
+    // ok, different keys.
+    if !m.contains_key(&k) {
+        m.insert(k2, v);
+    }
+
+    // ok, different maps
+    if !m.contains_key(&k) {
+        m2.insert(k, v);
+    }
+
+    // ok, insert in macro
+    if !m.contains_key(&k) {
+        insert!(m, k, v);
+    }
+}
+
+fn btree_map<K: Eq + Ord + Copy, V: Copy>(m: &mut BTreeMap<K, V>, k: K, v: V, v2: V) {
+    // insert then do something, use if let
+    if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) {
+        e.insert(v);
+        foo();
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/entry.rs b/src/tools/clippy/tests/ui/entry.rs
new file mode 100644
index 0000000000000..fa9280b58de11
--- /dev/null
+++ b/src/tools/clippy/tests/ui/entry.rs
@@ -0,0 +1,159 @@
+// run-rustfix
+
+#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)]
+#![warn(clippy::map_entry)]
+#![feature(asm)]
+
+use std::collections::{BTreeMap, HashMap};
+use std::hash::Hash;
+
+macro_rules! m {
+    ($e:expr) => {{ $e }};
+}
+
+macro_rules! insert {
+    ($map:expr, $key:expr, $val:expr) => {
+        $map.insert($key, $val)
+    };
+}
+
+fn foo() {}
+
+fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, m2: &mut HashMap<K, V>, k: K, k2: K, v: V, v2: V) {
+    // or_insert(v)
+    if !m.contains_key(&k) {
+        m.insert(k, v);
+    }
+
+    // semicolon on insert, use or_insert_with(..)
+    if !m.contains_key(&k) {
+        if true {
+            m.insert(k, v);
+        } else {
+            m.insert(k, v2);
+        }
+    }
+
+    // semicolon on if, use or_insert_with(..)
+    if !m.contains_key(&k) {
+        if true {
+            m.insert(k, v)
+        } else {
+            m.insert(k, v2)
+        };
+    }
+
+    // early return, use if let
+    if !m.contains_key(&k) {
+        if true {
+            m.insert(k, v);
+        } else {
+            m.insert(k, v2);
+            return;
+        }
+    }
+
+    // use or_insert_with(..)
+    if !m.contains_key(&k) {
+        foo();
+        m.insert(k, v);
+    }
+
+    // semicolon on insert and match, use or_insert_with(..)
+    if !m.contains_key(&k) {
+        match 0 {
+            1 if true => {
+                m.insert(k, v);
+            },
+            _ => {
+                m.insert(k, v2);
+            },
+        };
+    }
+
+    // one branch doesn't insert, use if let
+    if !m.contains_key(&k) {
+        match 0 {
+            0 => foo(),
+            _ => {
+                m.insert(k, v2);
+            },
+        };
+    }
+
+    // use or_insert_with
+    if !m.contains_key(&k) {
+        foo();
+        match 0 {
+            0 if false => {
+                m.insert(k, v);
+            },
+            1 => {
+                foo();
+                m.insert(k, v);
+            },
+            2 | 3 => {
+                for _ in 0..2 {
+                    foo();
+                }
+                if true {
+                    m.insert(k, v);
+                } else {
+                    m.insert(k, v2);
+                };
+            },
+            _ => {
+                m.insert(k, v2);
+            },
+        }
+    }
+
+    // ok, insert in loop
+    if !m.contains_key(&k) {
+        for _ in 0..2 {
+            m.insert(k, v);
+        }
+    }
+
+    // macro_expansion test, use or_insert(..)
+    if !m.contains_key(&m!(k)) {
+        m.insert(m!(k), m!(v));
+    }
+
+    // ok, map used before insertion
+    if !m.contains_key(&k) {
+        let _ = m.len();
+        m.insert(k, v);
+    }
+
+    // ok, inline asm
+    if !m.contains_key(&k) {
+        unsafe { asm!("nop") }
+        m.insert(k, v);
+    }
+
+    // ok, different keys.
+    if !m.contains_key(&k) {
+        m.insert(k2, v);
+    }
+
+    // ok, different maps
+    if !m.contains_key(&k) {
+        m2.insert(k, v);
+    }
+
+    // ok, insert in macro
+    if !m.contains_key(&k) {
+        insert!(m, k, v);
+    }
+}
+
+fn btree_map<K: Eq + Ord + Copy, V: Copy>(m: &mut BTreeMap<K, V>, k: K, v: V, v2: V) {
+    // insert then do something, use if let
+    if !m.contains_key(&k) {
+        m.insert(k, v);
+        foo();
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/entry.stderr b/src/tools/clippy/tests/ui/entry.stderr
new file mode 100644
index 0000000000000..2f075a97010a6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/entry.stderr
@@ -0,0 +1,186 @@
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry.rs:24:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         m.insert(k, v);
+LL | |     }
+   | |_____^ help: try this: `m.entry(k).or_insert(v);`
+   |
+   = note: `-D clippy::map-entry` implied by `-D warnings`
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry.rs:29:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         if true {
+LL | |             m.insert(k, v);
+LL | |         } else {
+LL | |             m.insert(k, v2);
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     m.entry(k).or_insert_with(|| {
+LL |         if true {
+LL |             v
+LL |         } else {
+LL |             v2
+LL |         }
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry.rs:38:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         if true {
+LL | |             m.insert(k, v)
+LL | |         } else {
+LL | |             m.insert(k, v2)
+LL | |         };
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     m.entry(k).or_insert_with(|| {
+LL |         if true {
+LL |             v
+LL |         } else {
+LL |             v2
+LL |         }
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry.rs:47:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         if true {
+LL | |             m.insert(k, v);
+LL | |         } else {
+...  |
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+LL |         if true {
+LL |             e.insert(v);
+LL |         } else {
+LL |             e.insert(v2);
+LL |             return;
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry.rs:57:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         foo();
+LL | |         m.insert(k, v);
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     m.entry(k).or_insert_with(|| {
+LL |         foo();
+LL |         v
+LL |     });
+   |
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry.rs:63:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         match 0 {
+LL | |             1 if true => {
+LL | |                 m.insert(k, v);
+...  |
+LL | |         };
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     m.entry(k).or_insert_with(|| {
+LL |         match 0 {
+LL |             1 if true => {
+LL |                 v
+LL |             },
+LL |             _ => {
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry.rs:75:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         match 0 {
+LL | |             0 => foo(),
+LL | |             _ => {
+...  |
+LL | |         };
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+LL |         match 0 {
+LL |             0 => foo(),
+LL |             _ => {
+LL |                 e.insert(v2);
+LL |             },
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry.rs:85:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         foo();
+LL | |         match 0 {
+LL | |             0 if false => {
+...  |
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     m.entry(k).or_insert_with(|| {
+LL |         foo();
+LL |         match 0 {
+LL |             0 if false => {
+LL |                 v
+LL |             },
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry.rs:119:5
+   |
+LL | /     if !m.contains_key(&m!(k)) {
+LL | |         m.insert(m!(k), m!(v));
+LL | |     }
+   | |_____^ help: try this: `m.entry(m!(k)).or_insert_with(|| m!(v));`
+
+error: usage of `contains_key` followed by `insert` on a `BTreeMap`
+  --> $DIR/entry.rs:153:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         m.insert(k, v);
+LL | |         foo();
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) {
+LL |         e.insert(v);
+LL |         foo();
+LL |     }
+   |
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/entry_fixable.fixed b/src/tools/clippy/tests/ui/entry_fixable.fixed
deleted file mode 100644
index dcdaae7e72430..0000000000000
--- a/src/tools/clippy/tests/ui/entry_fixable.fixed
+++ /dev/null
@@ -1,15 +0,0 @@
-// run-rustfix
-
-#![allow(unused, clippy::needless_pass_by_value)]
-#![warn(clippy::map_entry)]
-
-use std::collections::{BTreeMap, HashMap};
-use std::hash::Hash;
-
-fn foo() {}
-
-fn insert_if_absent0<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
-    m.entry(k).or_insert(v);
-}
-
-fn main() {}
diff --git a/src/tools/clippy/tests/ui/entry_fixable.rs b/src/tools/clippy/tests/ui/entry_fixable.rs
deleted file mode 100644
index 55d5b21568d0e..0000000000000
--- a/src/tools/clippy/tests/ui/entry_fixable.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-// run-rustfix
-
-#![allow(unused, clippy::needless_pass_by_value)]
-#![warn(clippy::map_entry)]
-
-use std::collections::{BTreeMap, HashMap};
-use std::hash::Hash;
-
-fn foo() {}
-
-fn insert_if_absent0<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
-    if !m.contains_key(&k) {
-        m.insert(k, v);
-    }
-}
-
-fn main() {}
diff --git a/src/tools/clippy/tests/ui/entry_fixable.stderr b/src/tools/clippy/tests/ui/entry_fixable.stderr
deleted file mode 100644
index 87403200ced50..0000000000000
--- a/src/tools/clippy/tests/ui/entry_fixable.stderr
+++ /dev/null
@@ -1,12 +0,0 @@
-error: usage of `contains_key` followed by `insert` on a `HashMap`
-  --> $DIR/entry_fixable.rs:12:5
-   |
-LL | /     if !m.contains_key(&k) {
-LL | |         m.insert(k, v);
-LL | |     }
-   | |_____^ help: consider using: `m.entry(k).or_insert(v);`
-   |
-   = note: `-D clippy::map-entry` implied by `-D warnings`
-
-error: aborting due to previous error
-
diff --git a/src/tools/clippy/tests/ui/entry_unfixable.rs b/src/tools/clippy/tests/ui/entry_unfixable.rs
deleted file mode 100644
index f530fc023cfbf..0000000000000
--- a/src/tools/clippy/tests/ui/entry_unfixable.rs
+++ /dev/null
@@ -1,73 +0,0 @@
-#![allow(unused, clippy::needless_pass_by_value)]
-#![warn(clippy::map_entry)]
-
-use std::collections::{BTreeMap, HashMap};
-use std::hash::Hash;
-
-fn foo() {}
-
-fn insert_if_absent2<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
-    if !m.contains_key(&k) {
-        m.insert(k, v)
-    } else {
-        None
-    };
-}
-
-fn insert_if_present2<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
-    if m.contains_key(&k) {
-        None
-    } else {
-        m.insert(k, v)
-    };
-}
-
-fn insert_if_absent3<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
-    if !m.contains_key(&k) {
-        foo();
-        m.insert(k, v)
-    } else {
-        None
-    };
-}
-
-fn insert_if_present3<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) {
-    if m.contains_key(&k) {
-        None
-    } else {
-        foo();
-        m.insert(k, v)
-    };
-}
-
-fn insert_in_btreemap<K: Ord, V>(m: &mut BTreeMap<K, V>, k: K, v: V) {
-    if !m.contains_key(&k) {
-        foo();
-        m.insert(k, v)
-    } else {
-        None
-    };
-}
-
-// should not trigger
-fn insert_other_if_absent<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, o: K, v: V) {
-    if !m.contains_key(&k) {
-        m.insert(o, v);
-    }
-}
-
-// should not trigger, because the one uses different HashMap from another one
-fn insert_from_different_map<K: Eq + Hash, V>(m: HashMap<K, V>, n: &mut HashMap<K, V>, k: K, v: V) {
-    if !m.contains_key(&k) {
-        n.insert(k, v);
-    }
-}
-
-// should not trigger, because the one uses different HashMap from another one
-fn insert_from_different_map2<K: Eq + Hash, V>(m: &mut HashMap<K, V>, n: &mut HashMap<K, V>, k: K, v: V) {
-    if !m.contains_key(&k) {
-        n.insert(k, v);
-    }
-}
-
-fn main() {}
diff --git a/src/tools/clippy/tests/ui/entry_unfixable.stderr b/src/tools/clippy/tests/ui/entry_unfixable.stderr
deleted file mode 100644
index e58c8d22dc45e..0000000000000
--- a/src/tools/clippy/tests/ui/entry_unfixable.stderr
+++ /dev/null
@@ -1,57 +0,0 @@
-error: usage of `contains_key` followed by `insert` on a `HashMap`
-  --> $DIR/entry_unfixable.rs:10:5
-   |
-LL | /     if !m.contains_key(&k) {
-LL | |         m.insert(k, v)
-LL | |     } else {
-LL | |         None
-LL | |     };
-   | |_____^ consider using `m.entry(k)`
-   |
-   = note: `-D clippy::map-entry` implied by `-D warnings`
-
-error: usage of `contains_key` followed by `insert` on a `HashMap`
-  --> $DIR/entry_unfixable.rs:18:5
-   |
-LL | /     if m.contains_key(&k) {
-LL | |         None
-LL | |     } else {
-LL | |         m.insert(k, v)
-LL | |     };
-   | |_____^ consider using `m.entry(k)`
-
-error: usage of `contains_key` followed by `insert` on a `HashMap`
-  --> $DIR/entry_unfixable.rs:26:5
-   |
-LL | /     if !m.contains_key(&k) {
-LL | |         foo();
-LL | |         m.insert(k, v)
-LL | |     } else {
-LL | |         None
-LL | |     };
-   | |_____^ consider using `m.entry(k)`
-
-error: usage of `contains_key` followed by `insert` on a `HashMap`
-  --> $DIR/entry_unfixable.rs:35:5
-   |
-LL | /     if m.contains_key(&k) {
-LL | |         None
-LL | |     } else {
-LL | |         foo();
-LL | |         m.insert(k, v)
-LL | |     };
-   | |_____^ consider using `m.entry(k)`
-
-error: usage of `contains_key` followed by `insert` on a `BTreeMap`
-  --> $DIR/entry_unfixable.rs:44:5
-   |
-LL | /     if !m.contains_key(&k) {
-LL | |         foo();
-LL | |         m.insert(k, v)
-LL | |     } else {
-LL | |         None
-LL | |     };
-   | |_____^ consider using `m.entry(k)`
-
-error: aborting due to 5 previous errors
-
diff --git a/src/tools/clippy/tests/ui/entry_with_else.fixed b/src/tools/clippy/tests/ui/entry_with_else.fixed
new file mode 100644
index 0000000000000..2332fa6313ff3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/entry_with_else.fixed
@@ -0,0 +1,73 @@
+// run-rustfix
+
+#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)]
+#![warn(clippy::map_entry)]
+
+use std::collections::{BTreeMap, HashMap};
+use std::hash::Hash;
+
+macro_rules! m {
+    ($e:expr) => {{ $e }};
+}
+
+fn foo() {}
+
+fn insert_if_absent0<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2: V) {
+    match m.entry(k) {
+        std::collections::hash_map::Entry::Vacant(e) => {
+            e.insert(v);
+        }
+        std::collections::hash_map::Entry::Occupied(mut e) => {
+            e.insert(v2);
+        }
+    }
+
+    match m.entry(k) {
+        std::collections::hash_map::Entry::Occupied(mut e) => {
+            e.insert(v);
+        }
+        std::collections::hash_map::Entry::Vacant(e) => {
+            e.insert(v2);
+        }
+    }
+
+    if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+        e.insert(v);
+    } else {
+        foo();
+    }
+
+    if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) {
+        e.insert(v);
+    } else {
+        foo();
+    }
+
+    match m.entry(k) {
+        std::collections::hash_map::Entry::Vacant(e) => {
+            e.insert(v);
+        }
+        std::collections::hash_map::Entry::Occupied(mut e) => {
+            e.insert(v2);
+        }
+    }
+
+    match m.entry(k) {
+        std::collections::hash_map::Entry::Occupied(mut e) => {
+            if true { Some(e.insert(v)) } else { Some(e.insert(v2)) }
+        }
+        std::collections::hash_map::Entry::Vacant(e) => {
+            e.insert(v);
+            None
+        }
+    };
+
+    if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) {
+        foo();
+        Some(e.insert(v))
+    } else {
+        None
+    };
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/entry_with_else.rs b/src/tools/clippy/tests/ui/entry_with_else.rs
new file mode 100644
index 0000000000000..2ff0c038efe27
--- /dev/null
+++ b/src/tools/clippy/tests/ui/entry_with_else.rs
@@ -0,0 +1,60 @@
+// run-rustfix
+
+#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)]
+#![warn(clippy::map_entry)]
+
+use std::collections::{BTreeMap, HashMap};
+use std::hash::Hash;
+
+macro_rules! m {
+    ($e:expr) => {{ $e }};
+}
+
+fn foo() {}
+
+fn insert_if_absent0<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, k: K, v: V, v2: V) {
+    if !m.contains_key(&k) {
+        m.insert(k, v);
+    } else {
+        m.insert(k, v2);
+    }
+
+    if m.contains_key(&k) {
+        m.insert(k, v);
+    } else {
+        m.insert(k, v2);
+    }
+
+    if !m.contains_key(&k) {
+        m.insert(k, v);
+    } else {
+        foo();
+    }
+
+    if !m.contains_key(&k) {
+        foo();
+    } else {
+        m.insert(k, v);
+    }
+
+    if !m.contains_key(&k) {
+        m.insert(k, v);
+    } else {
+        m.insert(k, v2);
+    }
+
+    if m.contains_key(&k) {
+        if true { m.insert(k, v) } else { m.insert(k, v2) }
+    } else {
+        m.insert(k, v)
+    };
+
+    if m.contains_key(&k) {
+        foo();
+        m.insert(k, v)
+    } else {
+        None
+    };
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/entry_with_else.stderr b/src/tools/clippy/tests/ui/entry_with_else.stderr
new file mode 100644
index 0000000000000..6f62ff8d37457
--- /dev/null
+++ b/src/tools/clippy/tests/ui/entry_with_else.stderr
@@ -0,0 +1,142 @@
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry_with_else.rs:16:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         m.insert(k, v);
+LL | |     } else {
+LL | |         m.insert(k, v2);
+LL | |     }
+   | |_____^
+   |
+   = note: `-D clippy::map-entry` implied by `-D warnings`
+help: try this
+   |
+LL |     match m.entry(k) {
+LL |         std::collections::hash_map::Entry::Vacant(e) => {
+LL |             e.insert(v);
+LL |         }
+LL |         std::collections::hash_map::Entry::Occupied(mut e) => {
+LL |             e.insert(v2);
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry_with_else.rs:22:5
+   |
+LL | /     if m.contains_key(&k) {
+LL | |         m.insert(k, v);
+LL | |     } else {
+LL | |         m.insert(k, v2);
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     match m.entry(k) {
+LL |         std::collections::hash_map::Entry::Occupied(mut e) => {
+LL |             e.insert(v);
+LL |         }
+LL |         std::collections::hash_map::Entry::Vacant(e) => {
+LL |             e.insert(v2);
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry_with_else.rs:28:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         m.insert(k, v);
+LL | |     } else {
+LL | |         foo();
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) {
+LL |         e.insert(v);
+LL |     } else {
+LL |         foo();
+LL |     }
+   |
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry_with_else.rs:34:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         foo();
+LL | |     } else {
+LL | |         m.insert(k, v);
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) {
+LL |         e.insert(v);
+LL |     } else {
+LL |         foo();
+LL |     }
+   |
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry_with_else.rs:40:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         m.insert(k, v);
+LL | |     } else {
+LL | |         m.insert(k, v2);
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL |     match m.entry(k) {
+LL |         std::collections::hash_map::Entry::Vacant(e) => {
+LL |             e.insert(v);
+LL |         }
+LL |         std::collections::hash_map::Entry::Occupied(mut e) => {
+LL |             e.insert(v2);
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry_with_else.rs:46:5
+   |
+LL | /     if m.contains_key(&k) {
+LL | |         if true { m.insert(k, v) } else { m.insert(k, v2) }
+LL | |     } else {
+LL | |         m.insert(k, v)
+LL | |     };
+   | |_____^
+   |
+help: try this
+   |
+LL |     match m.entry(k) {
+LL |         std::collections::hash_map::Entry::Occupied(mut e) => {
+LL |             if true { Some(e.insert(v)) } else { Some(e.insert(v2)) }
+LL |         }
+LL |         std::collections::hash_map::Entry::Vacant(e) => {
+LL |             e.insert(v);
+ ...
+
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry_with_else.rs:52:5
+   |
+LL | /     if m.contains_key(&k) {
+LL | |         foo();
+LL | |         m.insert(k, v)
+LL | |     } else {
+LL | |         None
+LL | |     };
+   | |_____^
+   |
+help: try this
+   |
+LL |     if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) {
+LL |         foo();
+LL |         Some(e.insert(v))
+LL |     } else {
+LL |         None
+LL |     };
+   |
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/filter_methods.rs b/src/tools/clippy/tests/ui/filter_methods.rs
deleted file mode 100644
index 96121b114ce6c..0000000000000
--- a/src/tools/clippy/tests/ui/filter_methods.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-#![warn(clippy::all, clippy::pedantic)]
-#![allow(clippy::let_underscore_drop)]
-#![allow(clippy::missing_docs_in_private_items)]
-
-fn main() {
-    let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect();
-
-    let _: Vec<_> = vec![5_i8; 6]
-        .into_iter()
-        .filter(|&x| x == 0)
-        .flat_map(|x| x.checked_mul(2))
-        .collect();
-
-    let _: Vec<_> = vec![5_i8; 6]
-        .into_iter()
-        .filter_map(|x| x.checked_mul(2))
-        .flat_map(|x| x.checked_mul(2))
-        .collect();
-
-    let _: Vec<_> = vec![5_i8; 6]
-        .into_iter()
-        .filter_map(|x| x.checked_mul(2))
-        .map(|x| x.checked_mul(2))
-        .collect();
-}
diff --git a/src/tools/clippy/tests/ui/filter_methods.stderr b/src/tools/clippy/tests/ui/filter_methods.stderr
deleted file mode 100644
index c7b4f28be3a44..0000000000000
--- a/src/tools/clippy/tests/ui/filter_methods.stderr
+++ /dev/null
@@ -1,39 +0,0 @@
-error: called `filter(..).flat_map(..)` on an `Iterator`
-  --> $DIR/filter_methods.rs:8:21
-   |
-LL |       let _: Vec<_> = vec![5_i8; 6]
-   |  _____________________^
-LL | |         .into_iter()
-LL | |         .filter(|&x| x == 0)
-LL | |         .flat_map(|x| x.checked_mul(2))
-   | |_______________________________________^
-   |
-   = note: `-D clippy::filter-map` implied by `-D warnings`
-   = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()`
-
-error: called `filter_map(..).flat_map(..)` on an `Iterator`
-  --> $DIR/filter_methods.rs:14:21
-   |
-LL |       let _: Vec<_> = vec![5_i8; 6]
-   |  _____________________^
-LL | |         .into_iter()
-LL | |         .filter_map(|x| x.checked_mul(2))
-LL | |         .flat_map(|x| x.checked_mul(2))
-   | |_______________________________________^
-   |
-   = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()`
-
-error: called `filter_map(..).map(..)` on an `Iterator`
-  --> $DIR/filter_methods.rs:20:21
-   |
-LL |       let _: Vec<_> = vec![5_i8; 6]
-   |  _____________________^
-LL | |         .into_iter()
-LL | |         .filter_map(|x| x.checked_mul(2))
-LL | |         .map(|x| x.checked_mul(2))
-   | |__________________________________^
-   |
-   = help: this is more succinctly expressed by only calling `.filter_map(..)` instead
-
-error: aborting due to 3 previous errors
-
diff --git a/src/tools/clippy/tests/ui/flat_map_option.fixed b/src/tools/clippy/tests/ui/flat_map_option.fixed
new file mode 100644
index 0000000000000..6a34f008995cd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/flat_map_option.fixed
@@ -0,0 +1,13 @@
+// run-rustfix
+#![warn(clippy::flat_map_option)]
+#![allow(clippy::redundant_closure, clippy::unnecessary_filter_map)]
+
+fn main() {
+    // yay
+    let c = |x| Some(x);
+    let _ = [1].iter().filter_map(c);
+    let _ = [1].iter().filter_map(Some);
+
+    // nay
+    let _ = [1].iter().flat_map(|_| &Some(1));
+}
diff --git a/src/tools/clippy/tests/ui/flat_map_option.rs b/src/tools/clippy/tests/ui/flat_map_option.rs
new file mode 100644
index 0000000000000..2479abddbf04e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/flat_map_option.rs
@@ -0,0 +1,13 @@
+// run-rustfix
+#![warn(clippy::flat_map_option)]
+#![allow(clippy::redundant_closure, clippy::unnecessary_filter_map)]
+
+fn main() {
+    // yay
+    let c = |x| Some(x);
+    let _ = [1].iter().flat_map(c);
+    let _ = [1].iter().flat_map(Some);
+
+    // nay
+    let _ = [1].iter().flat_map(|_| &Some(1));
+}
diff --git a/src/tools/clippy/tests/ui/flat_map_option.stderr b/src/tools/clippy/tests/ui/flat_map_option.stderr
new file mode 100644
index 0000000000000..a9d8056dee978
--- /dev/null
+++ b/src/tools/clippy/tests/ui/flat_map_option.stderr
@@ -0,0 +1,16 @@
+error: used `flat_map` where `filter_map` could be used instead
+  --> $DIR/flat_map_option.rs:8:24
+   |
+LL |     let _ = [1].iter().flat_map(c);
+   |                        ^^^^^^^^ help: try: `filter_map`
+   |
+   = note: `-D clippy::flat-map-option` implied by `-D warnings`
+
+error: used `flat_map` where `filter_map` could be used instead
+  --> $DIR/flat_map_option.rs:9:24
+   |
+LL |     let _ = [1].iter().flat_map(Some);
+   |                        ^^^^^^^^ help: try: `filter_map`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/for_loop_fixable.fixed b/src/tools/clippy/tests/ui/for_loop_fixable.fixed
index 249a88a0b3982..f44928d408386 100644
--- a/src/tools/clippy/tests/ui/for_loop_fixable.fixed
+++ b/src/tools/clippy/tests/ui/for_loop_fixable.fixed
@@ -281,3 +281,29 @@ mod issue_4958 {
         for _ in rr.into_iter() {}
     }
 }
+
+// explicit_into_iter_loop
+#[warn(clippy::explicit_into_iter_loop)]
+mod issue_6900 {
+    struct S;
+    impl S {
+        #[allow(clippy::should_implement_trait)]
+        pub fn into_iter<T>(self) -> I<T> {
+            unimplemented!()
+        }
+    }
+
+    struct I<T>(T);
+    impl<T> Iterator for I<T> {
+        type Item = T;
+        fn next(&mut self) -> Option<Self::Item> {
+            unimplemented!()
+        }
+    }
+
+    fn f() {
+        for _ in S.into_iter::<u32>() {
+            unimplemented!()
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/for_loop_fixable.rs b/src/tools/clippy/tests/ui/for_loop_fixable.rs
index 306d85a6351e1..5b1eb3ee4dcd2 100644
--- a/src/tools/clippy/tests/ui/for_loop_fixable.rs
+++ b/src/tools/clippy/tests/ui/for_loop_fixable.rs
@@ -281,3 +281,29 @@ mod issue_4958 {
         for _ in rr.into_iter() {}
     }
 }
+
+// explicit_into_iter_loop
+#[warn(clippy::explicit_into_iter_loop)]
+mod issue_6900 {
+    struct S;
+    impl S {
+        #[allow(clippy::should_implement_trait)]
+        pub fn into_iter<T>(self) -> I<T> {
+            unimplemented!()
+        }
+    }
+
+    struct I<T>(T);
+    impl<T> Iterator for I<T> {
+        type Item = T;
+        fn next(&mut self) -> Option<Self::Item> {
+            unimplemented!()
+        }
+    }
+
+    fn f() {
+        for _ in S.into_iter::<u32>() {
+            unimplemented!()
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/format.fixed b/src/tools/clippy/tests/ui/format.fixed
index 740a22a07d747..e4cfb005fd1d0 100644
--- a/src/tools/clippy/tests/ui/format.fixed
+++ b/src/tools/clippy/tests/ui/format.fixed
@@ -65,4 +65,8 @@ fn main() {
     // False positive
     let a = "foo".to_string();
     let _ = Some(a + "bar");
+
+    // Wrap it with braces
+    let v: Vec<String> = vec!["foo".to_string(), "bar".to_string()];
+    let _s: String = (&*v.join("\n")).to_string();
 }
diff --git a/src/tools/clippy/tests/ui/format.rs b/src/tools/clippy/tests/ui/format.rs
index b604d79cca373..683957f0ff0f7 100644
--- a/src/tools/clippy/tests/ui/format.rs
+++ b/src/tools/clippy/tests/ui/format.rs
@@ -67,4 +67,8 @@ fn main() {
     // False positive
     let a = "foo".to_string();
     let _ = Some(format!("{}", a + "bar"));
+
+    // Wrap it with braces
+    let v: Vec<String> = vec!["foo".to_string(), "bar".to_string()];
+    let _s: String = format!("{}", &*v.join("\n"));
 }
diff --git a/src/tools/clippy/tests/ui/format.stderr b/src/tools/clippy/tests/ui/format.stderr
index 96df7f37f7792..2017eb2b3838f 100644
--- a/src/tools/clippy/tests/ui/format.stderr
+++ b/src/tools/clippy/tests/ui/format.stderr
@@ -87,5 +87,11 @@ error: useless use of `format!`
 LL |     let _ = Some(format!("{}", a + "bar"));
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"`
 
-error: aborting due to 13 previous errors
+error: useless use of `format!`
+  --> $DIR/format.rs:73:22
+   |
+LL |     let _s: String = format!("{}", &*v.join("/n"));
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()`
+
+error: aborting due to 14 previous errors
 
diff --git a/src/tools/clippy/tests/ui/from_over_into.stderr b/src/tools/clippy/tests/ui/from_over_into.stderr
index b101d2704fbda..2951e6bdac430 100644
--- a/src/tools/clippy/tests/ui/from_over_into.stderr
+++ b/src/tools/clippy/tests/ui/from_over_into.stderr
@@ -5,7 +5,7 @@ LL | impl Into<StringWrapper> for String {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::from-over-into` implied by `-D warnings`
-   = help: consider to implement `From` instead
+   = help: consider to implement `From<std::string::String>` instead
 
 error: aborting due to previous error
 
diff --git a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed
index 8d9c311003508..d1025743790a9 100644
--- a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed
+++ b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed
@@ -13,6 +13,15 @@ struct Foo {
     z: i32,
 }
 
+macro_rules! new_foo {
+    () => {
+        let x = 1;
+        let y = 1;
+        let z = 1;
+        Foo { y, x, z }
+    };
+}
+
 mod without_base {
     use super::Foo;
 
@@ -24,6 +33,10 @@ mod without_base {
         // Should lint.
         Foo { x, y, z };
 
+        // Should NOT lint.
+        // issue #7069.
+        new_foo!();
+
         // Shoule NOT lint because the order is the same as in the definition.
         Foo { x, y, z };
 
diff --git a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs
index 63fac9105015d..b095aa64a2174 100644
--- a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs
+++ b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs
@@ -13,6 +13,15 @@ struct Foo {
     z: i32,
 }
 
+macro_rules! new_foo {
+    () => {
+        let x = 1;
+        let y = 1;
+        let z = 1;
+        Foo { y, x, z }
+    };
+}
+
 mod without_base {
     use super::Foo;
 
@@ -24,6 +33,10 @@ mod without_base {
         // Should lint.
         Foo { y, x, z };
 
+        // Should NOT lint.
+        // issue #7069.
+        new_foo!();
+
         // Shoule NOT lint because the order is the same as in the definition.
         Foo { x, y, z };
 
diff --git a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr
index d021bb19579f4..ef308dedb1661 100644
--- a/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr
+++ b/src/tools/clippy/tests/ui/inconsistent_struct_constructor.stderr
@@ -1,5 +1,5 @@
 error: struct constructor field order is inconsistent with struct definition field order
-  --> $DIR/inconsistent_struct_constructor.rs:25:9
+  --> $DIR/inconsistent_struct_constructor.rs:34:9
    |
 LL |         Foo { y, x, z };
    |         ^^^^^^^^^^^^^^^ help: try: `Foo { x, y, z }`
@@ -7,7 +7,7 @@ LL |         Foo { y, x, z };
    = note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings`
 
 error: struct constructor field order is inconsistent with struct definition field order
-  --> $DIR/inconsistent_struct_constructor.rs:43:9
+  --> $DIR/inconsistent_struct_constructor.rs:56:9
    |
 LL | /         Foo {
 LL | |             z,
diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage.fixed b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.fixed
new file mode 100644
index 0000000000000..4f5322ebf202f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.fixed
@@ -0,0 +1,49 @@
+// run-rustfix
+
+fn main() {
+    unsafe {
+        let _slice: &[usize] = std::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0);
+        let _slice: &[usize] = std::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0);
+
+        let _slice: &[usize] = std::slice::from_raw_parts_mut(core::ptr::NonNull::dangling().as_ptr(), 0);
+
+        std::ptr::copy::<usize>(core::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0);
+        std::ptr::copy::<usize>(std::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0);
+
+        std::ptr::copy_nonoverlapping::<usize>(core::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0);
+        std::ptr::copy_nonoverlapping::<usize>(std::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0);
+
+        struct A; // zero sized struct
+        assert_eq!(std::mem::size_of::<A>(), 0);
+
+        let _a: A = std::ptr::read(core::ptr::NonNull::dangling().as_ptr());
+        let _a: A = std::ptr::read(core::ptr::NonNull::dangling().as_ptr());
+
+        let _a: A = std::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr());
+        let _a: A = std::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr());
+
+        let _a: A = std::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr());
+        let _a: A = std::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr());
+
+        let _a: A = std::ptr::replace(core::ptr::NonNull::dangling().as_ptr(), A);
+
+        let _slice: *const [usize] = std::ptr::slice_from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0);
+        let _slice: *const [usize] = std::ptr::slice_from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0);
+
+        let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(core::ptr::NonNull::dangling().as_ptr(), 0);
+
+        std::ptr::swap::<A>(core::ptr::NonNull::dangling().as_ptr(), &mut A);
+        std::ptr::swap::<A>(&mut A, core::ptr::NonNull::dangling().as_ptr());
+
+        std::ptr::swap_nonoverlapping::<A>(core::ptr::NonNull::dangling().as_ptr(), &mut A, 0);
+        std::ptr::swap_nonoverlapping::<A>(&mut A, core::ptr::NonNull::dangling().as_ptr(), 0);
+
+        std::ptr::write(core::ptr::NonNull::dangling().as_ptr(), A);
+
+        std::ptr::write_unaligned(core::ptr::NonNull::dangling().as_ptr(), A);
+
+        std::ptr::write_volatile(core::ptr::NonNull::dangling().as_ptr(), A);
+
+        std::ptr::write_bytes::<usize>(core::ptr::NonNull::dangling().as_ptr(), 42, 0);
+    }
+}
diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage.rs b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.rs
new file mode 100644
index 0000000000000..ae51c52d8af0c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.rs
@@ -0,0 +1,49 @@
+// run-rustfix
+
+fn main() {
+    unsafe {
+        let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null(), 0);
+        let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null_mut(), 0);
+
+        let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0);
+
+        std::ptr::copy::<usize>(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0);
+        std::ptr::copy::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0);
+
+        std::ptr::copy_nonoverlapping::<usize>(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0);
+        std::ptr::copy_nonoverlapping::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0);
+
+        struct A; // zero sized struct
+        assert_eq!(std::mem::size_of::<A>(), 0);
+
+        let _a: A = std::ptr::read(std::ptr::null());
+        let _a: A = std::ptr::read(std::ptr::null_mut());
+
+        let _a: A = std::ptr::read_unaligned(std::ptr::null());
+        let _a: A = std::ptr::read_unaligned(std::ptr::null_mut());
+
+        let _a: A = std::ptr::read_volatile(std::ptr::null());
+        let _a: A = std::ptr::read_volatile(std::ptr::null_mut());
+
+        let _a: A = std::ptr::replace(std::ptr::null_mut(), A);
+
+        let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null(), 0);
+        let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0);
+
+        let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0);
+
+        std::ptr::swap::<A>(std::ptr::null_mut(), &mut A);
+        std::ptr::swap::<A>(&mut A, std::ptr::null_mut());
+
+        std::ptr::swap_nonoverlapping::<A>(std::ptr::null_mut(), &mut A, 0);
+        std::ptr::swap_nonoverlapping::<A>(&mut A, std::ptr::null_mut(), 0);
+
+        std::ptr::write(std::ptr::null_mut(), A);
+
+        std::ptr::write_unaligned(std::ptr::null_mut(), A);
+
+        std::ptr::write_volatile(std::ptr::null_mut(), A);
+
+        std::ptr::write_bytes::<usize>(std::ptr::null_mut(), 42, 0);
+    }
+}
diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage.stderr b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.stderr
new file mode 100644
index 0000000000000..532c36abe5196
--- /dev/null
+++ b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.stderr
@@ -0,0 +1,154 @@
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:5:59
+   |
+LL |         let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null(), 0);
+   |                                                           ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+   |
+   = note: `#[deny(clippy::invalid_null_ptr_usage)]` on by default
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:6:59
+   |
+LL |         let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null_mut(), 0);
+   |                                                           ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:8:63
+   |
+LL |         let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0);
+   |                                                               ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:10:33
+   |
+LL |         std::ptr::copy::<usize>(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0);
+   |                                 ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:11:73
+   |
+LL |         std::ptr::copy::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0);
+   |                                                                         ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:13:48
+   |
+LL |         std::ptr::copy_nonoverlapping::<usize>(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0);
+   |                                                ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:14:88
+   |
+LL |         std::ptr::copy_nonoverlapping::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0);
+   |                                                                                        ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:19:36
+   |
+LL |         let _a: A = std::ptr::read(std::ptr::null());
+   |                                    ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:20:36
+   |
+LL |         let _a: A = std::ptr::read(std::ptr::null_mut());
+   |                                    ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:22:46
+   |
+LL |         let _a: A = std::ptr::read_unaligned(std::ptr::null());
+   |                                              ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:23:46
+   |
+LL |         let _a: A = std::ptr::read_unaligned(std::ptr::null_mut());
+   |                                              ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:25:45
+   |
+LL |         let _a: A = std::ptr::read_volatile(std::ptr::null());
+   |                                             ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:26:45
+   |
+LL |         let _a: A = std::ptr::read_volatile(std::ptr::null_mut());
+   |                                             ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:28:39
+   |
+LL |         let _a: A = std::ptr::replace(std::ptr::null_mut(), A);
+   |                                       ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:30:69
+   |
+LL |         let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null(), 0);
+   |                                                                     ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:31:69
+   |
+LL |         let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0);
+   |                                                                     ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:33:73
+   |
+LL |         let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0);
+   |                                                                         ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:35:29
+   |
+LL |         std::ptr::swap::<A>(std::ptr::null_mut(), &mut A);
+   |                             ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:36:37
+   |
+LL |         std::ptr::swap::<A>(&mut A, std::ptr::null_mut());
+   |                                     ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:38:44
+   |
+LL |         std::ptr::swap_nonoverlapping::<A>(std::ptr::null_mut(), &mut A, 0);
+   |                                            ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:39:52
+   |
+LL |         std::ptr::swap_nonoverlapping::<A>(&mut A, std::ptr::null_mut(), 0);
+   |                                                    ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:41:25
+   |
+LL |         std::ptr::write(std::ptr::null_mut(), A);
+   |                         ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:43:35
+   |
+LL |         std::ptr::write_unaligned(std::ptr::null_mut(), A);
+   |                                   ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:45:34
+   |
+LL |         std::ptr::write_volatile(std::ptr::null_mut(), A);
+   |                                  ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: pointer must be non-null
+  --> $DIR/invalid_null_ptr_usage.rs:47:40
+   |
+LL |         std::ptr::write_bytes::<usize>(std::ptr::null_mut(), 42, 0);
+   |                                        ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()`
+
+error: aborting due to 25 previous errors
+
diff --git a/src/tools/clippy/tests/ui/macro_use_imports.fixed b/src/tools/clippy/tests/ui/macro_use_imports.fixed
index 91e34c62160a1..51c66a46368db 100644
--- a/src/tools/clippy/tests/ui/macro_use_imports.fixed
+++ b/src/tools/clippy/tests/ui/macro_use_imports.fixed
@@ -4,7 +4,7 @@
 // run-rustfix
 // ignore-32bit
 
-#![allow(unused_imports, unreachable_code, unused_variables, dead_code)]
+#![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)]
 #![allow(clippy::single_component_path_imports)]
 #![warn(clippy::macro_use_imports)]
 
@@ -40,4 +40,8 @@ mod a {
     }
 }
 
+// issue #7015, ICE due to calling `item_children` with local `DefId`
+#[macro_use]
+use a as b;
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/macro_use_imports.rs b/src/tools/clippy/tests/ui/macro_use_imports.rs
index 9c3c50c5d49f2..2011129bc944d 100644
--- a/src/tools/clippy/tests/ui/macro_use_imports.rs
+++ b/src/tools/clippy/tests/ui/macro_use_imports.rs
@@ -4,7 +4,7 @@
 // run-rustfix
 // ignore-32bit
 
-#![allow(unused_imports, unreachable_code, unused_variables, dead_code)]
+#![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)]
 #![allow(clippy::single_component_path_imports)]
 #![warn(clippy::macro_use_imports)]
 
@@ -40,4 +40,8 @@ mod a {
     }
 }
 
+// issue #7015, ICE due to calling `item_children` with local `DefId`
+#[macro_use]
+use a as b;
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_map_option.fixed b/src/tools/clippy/tests/ui/manual_map_option.fixed
index ee01584577786..40d01df6379a6 100644
--- a/src/tools/clippy/tests/ui/manual_map_option.fixed
+++ b/src/tools/clippy/tests/ui/manual_map_option.fixed
@@ -146,4 +146,11 @@ fn main() {
             None => None,
         };
     }
+
+    // #7077
+    let s = &String::new();
+    let _: Option<&str> = match Some(s) {
+        Some(s) => Some(s),
+        None => None,
+    };
 }
diff --git a/src/tools/clippy/tests/ui/manual_map_option.rs b/src/tools/clippy/tests/ui/manual_map_option.rs
index 29509bddfd94d..cfef0c5cc4ec6 100644
--- a/src/tools/clippy/tests/ui/manual_map_option.rs
+++ b/src/tools/clippy/tests/ui/manual_map_option.rs
@@ -212,4 +212,11 @@ fn main() {
             None => None,
         };
     }
+
+    // #7077
+    let s = &String::new();
+    let _: Option<&str> = match Some(s) {
+        Some(s) => Some(s),
+        None => None,
+    };
 }
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/auxiliary/helper.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/auxiliary/helper.rs
new file mode 100644
index 0000000000000..7b9dc76b8f1d4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/auxiliary/helper.rs
@@ -0,0 +1,8 @@
+// This file provides a const function that is unstably const forever.
+
+#![feature(staged_api)]
+#![stable(feature = "1", since = "1.0.0")]
+
+#[stable(feature = "1", since = "1.0.0")]
+#[rustc_const_unstable(feature = "foo", issue = "none")]
+pub const fn unstably_const_fn() {}
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs
index ba352ef9ee932..7cda1aaa3c228 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs
@@ -2,9 +2,14 @@
 //! compilation error.
 //! The .stderr output of this test should be empty. Otherwise it's a bug somewhere.
 
+// aux-build:helper.rs
+
 #![warn(clippy::missing_const_for_fn)]
 #![allow(incomplete_features)]
 #![feature(start, const_generics)]
+#![feature(custom_inner_attributes)]
+
+extern crate helper;
 
 struct Game;
 
@@ -101,3 +106,17 @@ fn const_generic_return<T, const N: usize>(t: &[T]) -> &[T; N] {
 
     unsafe { &*p }
 }
+
+// Do not lint this because it calls a function whose constness is unstable.
+fn unstably_const_fn() {
+    helper::unstably_const_fn()
+}
+
+mod const_fn_stabilized_after_msrv {
+    #![clippy::msrv = "1.46.0"]
+
+    // Do not lint this because `u8::is_ascii_digit` is stabilized as a const function in 1.47.0.
+    fn const_fn_stabilized_after_msrv(byte: u8) {
+        byte.is_ascii_digit();
+    }
+}
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
index c6f44b7daa342..0accb516f5f6b 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
@@ -1,6 +1,7 @@
 #![warn(clippy::missing_const_for_fn)]
 #![allow(incomplete_features, clippy::let_and_return)]
 #![feature(const_generics)]
+#![feature(custom_inner_attributes)]
 
 use std::mem::transmute;
 
@@ -70,5 +71,14 @@ mod with_drop {
     }
 }
 
+mod const_fn_stabilized_before_msrv {
+    #![clippy::msrv = "1.47.0"]
+
+    // This could be const because `u8::is_ascii_digit` is a stable const function in 1.47.
+    fn const_fn_stabilized_before_msrv(byte: u8) {
+        byte.is_ascii_digit();
+    }
+}
+
 // Should not be const
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
index 74d32b8a1aa93..63c211f39fa1e 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
@@ -1,5 +1,5 @@
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:13:5
+  --> $DIR/could_be_const.rs:14:5
    |
 LL | /     pub fn new() -> Self {
 LL | |         Self { guess: 42 }
@@ -9,7 +9,7 @@ LL | |     }
    = note: `-D clippy::missing-const-for-fn` implied by `-D warnings`
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:17:5
+  --> $DIR/could_be_const.rs:18:5
    |
 LL | /     fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] {
 LL | |         b
@@ -17,7 +17,7 @@ LL | |     }
    | |_____^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:23:1
+  --> $DIR/could_be_const.rs:24:1
    |
 LL | / fn one() -> i32 {
 LL | |     1
@@ -25,7 +25,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:28:1
+  --> $DIR/could_be_const.rs:29:1
    |
 LL | / fn two() -> i32 {
 LL | |     let abc = 2;
@@ -34,7 +34,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:34:1
+  --> $DIR/could_be_const.rs:35:1
    |
 LL | / fn string() -> String {
 LL | |     String::new()
@@ -42,7 +42,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:39:1
+  --> $DIR/could_be_const.rs:40:1
    |
 LL | / unsafe fn four() -> i32 {
 LL | |     4
@@ -50,7 +50,7 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:44:1
+  --> $DIR/could_be_const.rs:45:1
    |
 LL | / fn generic<T>(t: T) -> T {
 LL | |     t
@@ -58,12 +58,20 @@ LL | | }
    | |_^
 
 error: this could be a `const fn`
-  --> $DIR/could_be_const.rs:67:9
+  --> $DIR/could_be_const.rs:68:9
    |
 LL | /         pub fn b(self, a: &A) -> B {
 LL | |             B
 LL | |         }
    | |_________^
 
-error: aborting due to 8 previous errors
+error: this could be a `const fn`
+  --> $DIR/could_be_const.rs:78:5
+   |
+LL | /     fn const_fn_stabilized_before_msrv(byte: u8) {
+LL | |         byte.is_ascii_digit();
+LL | |     }
+   | |_____^
+
+error: aborting due to 9 previous errors
 
diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed
index 82d95cc041fb3..5c4fd466c0418 100644
--- a/src/tools/clippy/tests/ui/needless_return.fixed
+++ b/src/tools/clippy/tests/ui/needless_return.fixed
@@ -1,4 +1,5 @@
 // run-rustfix
+// edition:2018
 
 #![allow(unused)]
 #![allow(
@@ -125,10 +126,85 @@ mod issue6501 {
     }
 }
 
-fn main() {
-    let _ = test_end_of_fn();
-    let _ = test_no_semicolon();
-    let _ = test_if_block();
-    let _ = test_match(true);
-    test_closure();
+async fn async_test_end_of_fn() -> bool {
+    if true {
+        // no error!
+        return true;
+    }
+    true
+}
+
+async fn async_test_no_semicolon() -> bool {
+    true
+}
+
+async fn async_test_if_block() -> bool {
+    if true {
+        true
+    } else {
+        false
+    }
+}
+
+async fn async_test_match(x: bool) -> bool {
+    match x {
+        true => false,
+        false => {
+            true
+        },
+    }
+}
+
+async fn async_test_closure() {
+    let _ = || {
+        true
+    };
+    let _ = || true;
+}
+
+async fn async_test_macro_call() -> i32 {
+    return the_answer!();
+}
+
+async fn async_test_void_fun() {
+    
+}
+
+async fn async_test_void_if_fun(b: bool) {
+    if b {
+        
+    } else {
+        
+    }
+}
+
+async fn async_test_void_match(x: u32) {
+    match x {
+        0 => (),
+        _ => {},
+    }
+}
+
+async fn async_read_line() -> String {
+    use std::io::BufRead;
+    let stdin = ::std::io::stdin();
+    return stdin.lock().lines().next().unwrap().unwrap();
 }
+
+async fn async_borrows_but_not_last(value: bool) -> String {
+    if value {
+        use std::io::BufRead;
+        let stdin = ::std::io::stdin();
+        let _a = stdin.lock().lines().next().unwrap().unwrap();
+        String::from("test")
+    } else {
+        String::new()
+    }
+}
+
+async fn async_test_return_in_macro() {
+    needed_return!(10);
+    needed_return!(0);
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs
index 8a471f802e111..34811db7413a3 100644
--- a/src/tools/clippy/tests/ui/needless_return.rs
+++ b/src/tools/clippy/tests/ui/needless_return.rs
@@ -1,4 +1,5 @@
 // run-rustfix
+// edition:2018
 
 #![allow(unused)]
 #![allow(
@@ -125,10 +126,85 @@ mod issue6501 {
     }
 }
 
-fn main() {
-    let _ = test_end_of_fn();
-    let _ = test_no_semicolon();
-    let _ = test_if_block();
-    let _ = test_match(true);
-    test_closure();
+async fn async_test_end_of_fn() -> bool {
+    if true {
+        // no error!
+        return true;
+    }
+    return true;
+}
+
+async fn async_test_no_semicolon() -> bool {
+    return true;
+}
+
+async fn async_test_if_block() -> bool {
+    if true {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+async fn async_test_match(x: bool) -> bool {
+    match x {
+        true => return false,
+        false => {
+            return true;
+        },
+    }
+}
+
+async fn async_test_closure() {
+    let _ = || {
+        return true;
+    };
+    let _ = || return true;
+}
+
+async fn async_test_macro_call() -> i32 {
+    return the_answer!();
+}
+
+async fn async_test_void_fun() {
+    return;
+}
+
+async fn async_test_void_if_fun(b: bool) {
+    if b {
+        return;
+    } else {
+        return;
+    }
+}
+
+async fn async_test_void_match(x: u32) {
+    match x {
+        0 => (),
+        _ => return,
+    }
+}
+
+async fn async_read_line() -> String {
+    use std::io::BufRead;
+    let stdin = ::std::io::stdin();
+    return stdin.lock().lines().next().unwrap().unwrap();
 }
+
+async fn async_borrows_but_not_last(value: bool) -> String {
+    if value {
+        use std::io::BufRead;
+        let stdin = ::std::io::stdin();
+        let _a = stdin.lock().lines().next().unwrap().unwrap();
+        return String::from("test");
+    } else {
+        return String::new();
+    }
+}
+
+async fn async_test_return_in_macro() {
+    needed_return!(10);
+    needed_return!(0);
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_return.stderr b/src/tools/clippy/tests/ui/needless_return.stderr
index 075db22456f73..74dda971fdabb 100644
--- a/src/tools/clippy/tests/ui/needless_return.stderr
+++ b/src/tools/clippy/tests/ui/needless_return.stderr
@@ -1,5 +1,5 @@
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:23:5
+  --> $DIR/needless_return.rs:24:5
    |
 LL |     return true;
    |     ^^^^^^^^^^^^ help: remove `return`: `true`
@@ -7,106 +7,190 @@ LL |     return true;
    = note: `-D clippy::needless-return` implied by `-D warnings`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:27:5
+  --> $DIR/needless_return.rs:28:5
    |
 LL |     return true;
    |     ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:32:9
+  --> $DIR/needless_return.rs:33:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:34:9
+  --> $DIR/needless_return.rs:35:9
    |
 LL |         return false;
    |         ^^^^^^^^^^^^^ help: remove `return`: `false`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:40:17
+  --> $DIR/needless_return.rs:41:17
    |
 LL |         true => return false,
    |                 ^^^^^^^^^^^^ help: remove `return`: `false`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:42:13
+  --> $DIR/needless_return.rs:43:13
    |
 LL |             return true;
    |             ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:49:9
+  --> $DIR/needless_return.rs:50:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:51:16
+  --> $DIR/needless_return.rs:52:16
    |
 LL |     let _ = || return true;
    |                ^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:59:5
+  --> $DIR/needless_return.rs:60:5
    |
 LL |     return;
    |     ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:64:9
+  --> $DIR/needless_return.rs:65:9
    |
 LL |         return;
    |         ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:66:9
+  --> $DIR/needless_return.rs:67:9
    |
 LL |         return;
    |         ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:73:14
+  --> $DIR/needless_return.rs:74:14
    |
 LL |         _ => return,
    |              ^^^^^^ help: replace `return` with an empty block: `{}`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:88:9
+  --> $DIR/needless_return.rs:89:9
    |
 LL |         return String::from("test");
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:90:9
+  --> $DIR/needless_return.rs:91:9
    |
 LL |         return String::new();
    |         ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:111:32
+  --> $DIR/needless_return.rs:112:32
    |
 LL |         bar.unwrap_or_else(|_| return)
    |                                ^^^^^^ help: replace `return` with an empty block: `{}`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:116:13
+  --> $DIR/needless_return.rs:117:13
    |
 LL |             return;
    |             ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:118:20
+  --> $DIR/needless_return.rs:119:20
    |
 LL |         let _ = || return;
    |                    ^^^^^^ help: replace `return` with an empty block: `{}`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:124:32
+  --> $DIR/needless_return.rs:125:32
    |
 LL |         res.unwrap_or_else(|_| return Foo)
    |                                ^^^^^^^^^^ help: remove `return`: `Foo`
 
-error: aborting due to 18 previous errors
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:134:5
+   |
+LL |     return true;
+   |     ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:138:5
+   |
+LL |     return true;
+   |     ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:143:9
+   |
+LL |         return true;
+   |         ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:145:9
+   |
+LL |         return false;
+   |         ^^^^^^^^^^^^^ help: remove `return`: `false`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:151:17
+   |
+LL |         true => return false,
+   |                 ^^^^^^^^^^^^ help: remove `return`: `false`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:153:13
+   |
+LL |             return true;
+   |             ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:160:9
+   |
+LL |         return true;
+   |         ^^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:162:16
+   |
+LL |     let _ = || return true;
+   |                ^^^^^^^^^^^ help: remove `return`: `true`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:170:5
+   |
+LL |     return;
+   |     ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:175:9
+   |
+LL |         return;
+   |         ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:177:9
+   |
+LL |         return;
+   |         ^^^^^^^ help: remove `return`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:184:14
+   |
+LL |         _ => return,
+   |              ^^^^^^ help: replace `return` with an empty block: `{}`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:199:9
+   |
+LL |         return String::from("test");
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
+
+error: unneeded `return` statement
+  --> $DIR/needless_return.rs:201:9
+   |
+LL |         return String::new();
+   |         ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
+
+error: aborting due to 32 previous errors
 
diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.rs b/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.rs
index b60c79f97c865..c4fcd7e70944c 100644
--- a/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.rs
+++ b/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.rs
@@ -1,44 +1,39 @@
 #![warn(clippy::panic_in_result_fn)]
 #![allow(clippy::unnecessary_wraps)]
 
+// debug_assert should never trigger the `panic_in_result_fn` lint
+
 struct A;
 
 impl A {
-    fn result_with_debug_assert_with_message(x: i32) -> Result<bool, String> // should emit lint
-    {
+    fn result_with_debug_assert_with_message(x: i32) -> Result<bool, String> {
         debug_assert!(x == 5, "wrong argument");
         Ok(true)
     }
 
-    fn result_with_debug_assert_eq(x: i32) -> Result<bool, String> // should emit lint
-    {
+    fn result_with_debug_assert_eq(x: i32) -> Result<bool, String> {
         debug_assert_eq!(x, 5);
         Ok(true)
     }
 
-    fn result_with_debug_assert_ne(x: i32) -> Result<bool, String> // should emit lint
-    {
+    fn result_with_debug_assert_ne(x: i32) -> Result<bool, String> {
         debug_assert_ne!(x, 1);
         Ok(true)
     }
 
-    fn other_with_debug_assert_with_message(x: i32) // should not emit lint
-    {
+    fn other_with_debug_assert_with_message(x: i32) {
         debug_assert!(x == 5, "wrong argument");
     }
 
-    fn other_with_debug_assert_eq(x: i32) // should not emit lint
-    {
+    fn other_with_debug_assert_eq(x: i32) {
         debug_assert_eq!(x, 5);
     }
 
-    fn other_with_debug_assert_ne(x: i32) // should not emit lint
-    {
+    fn other_with_debug_assert_ne(x: i32) {
         debug_assert_ne!(x, 1);
     }
 
-    fn result_without_banned_functions() -> Result<bool, String> // should not emit lint
-    {
+    fn result_without_banned_functions() -> Result<bool, String> {
         let debug_assert = "debug_assert!";
         println!("No {}", debug_assert);
         Ok(true)
diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.stderr b/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.stderr
deleted file mode 100644
index ec18e89698c56..0000000000000
--- a/src/tools/clippy/tests/ui/panic_in_result_fn_debug_assertions.stderr
+++ /dev/null
@@ -1,57 +0,0 @@
-error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
-  --> $DIR/panic_in_result_fn_debug_assertions.rs:7:5
-   |
-LL | /     fn result_with_debug_assert_with_message(x: i32) -> Result<bool, String> // should emit lint
-LL | |     {
-LL | |         debug_assert!(x == 5, "wrong argument");
-LL | |         Ok(true)
-LL | |     }
-   | |_____^
-   |
-   = note: `-D clippy::panic-in-result-fn` implied by `-D warnings`
-   = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
-note: return Err() instead of panicking
-  --> $DIR/panic_in_result_fn_debug_assertions.rs:9:9
-   |
-LL |         debug_assert!(x == 5, "wrong argument");
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
-  --> $DIR/panic_in_result_fn_debug_assertions.rs:13:5
-   |
-LL | /     fn result_with_debug_assert_eq(x: i32) -> Result<bool, String> // should emit lint
-LL | |     {
-LL | |         debug_assert_eq!(x, 5);
-LL | |         Ok(true)
-LL | |     }
-   | |_____^
-   |
-   = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
-note: return Err() instead of panicking
-  --> $DIR/panic_in_result_fn_debug_assertions.rs:15:9
-   |
-LL |         debug_assert_eq!(x, 5);
-   |         ^^^^^^^^^^^^^^^^^^^^^^^
-   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`
-  --> $DIR/panic_in_result_fn_debug_assertions.rs:19:5
-   |
-LL | /     fn result_with_debug_assert_ne(x: i32) -> Result<bool, String> // should emit lint
-LL | |     {
-LL | |         debug_assert_ne!(x, 1);
-LL | |         Ok(true)
-LL | |     }
-   | |_____^
-   |
-   = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
-note: return Err() instead of panicking
-  --> $DIR/panic_in_result_fn_debug_assertions.rs:21:9
-   |
-LL |         debug_assert_ne!(x, 1);
-   |         ^^^^^^^^^^^^^^^^^^^^^^^
-   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: aborting due to 3 previous errors
-
diff --git a/src/tools/clippy/tests/ui/panicking_macros.rs b/src/tools/clippy/tests/ui/panicking_macros.rs
index 77fcb8dfd02fd..93b236f7473d5 100644
--- a/src/tools/clippy/tests/ui/panicking_macros.rs
+++ b/src/tools/clippy/tests/ui/panicking_macros.rs
@@ -1,5 +1,5 @@
 #![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)]
-#![allow(clippy::assertions_on_constants)]
+#![allow(clippy::assertions_on_constants, clippy::eq_op)]
 
 extern crate core;
 
@@ -43,6 +43,18 @@ fn core_versions() {
     unreachable!();
 }
 
+fn debug_assert() {
+    debug_assert!(true);
+    debug_assert_eq!(true, true);
+    debug_assert_ne!(true, false);
+}
+
+fn debug_assert_msg() {
+    debug_assert!(true, "test");
+    debug_assert_eq!(true, true, "test");
+    debug_assert_ne!(true, false, "test");
+}
+
 fn main() {
     panic();
     todo();
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.fixed
new file mode 100644
index 0000000000000..794ed542435d1
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.fixed
@@ -0,0 +1,58 @@
+// run-rustfix
+
+// Issue #5746
+#![warn(clippy::redundant_pattern_matching)]
+#![allow(clippy::if_same_then_else)]
+use std::task::Poll::{Pending, Ready};
+
+fn main() {
+    let m = std::sync::Mutex::new((0, 0));
+
+    // Result
+    if m.lock().is_ok() {}
+    if Err::<(), _>(m.lock().unwrap().0).is_err() {}
+
+    {
+        if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() {}
+    }
+    if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() {
+    } else {
+    }
+    if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() {}
+    if Err::<std::sync::MutexGuard<()>, _>(()).is_err() {}
+
+    if Ok::<_, ()>(String::new()).is_ok() {}
+    if Err::<(), _>((String::new(), ())).is_err() {}
+
+    // Option
+    if Some(m.lock()).is_some() {}
+    if Some(m.lock().unwrap().0).is_some() {}
+
+    {
+        if None::<std::sync::MutexGuard<()>>.is_none() {}
+    }
+    if None::<std::sync::MutexGuard<()>>.is_none() {
+    } else {
+    }
+
+    if None::<std::sync::MutexGuard<()>>.is_none() {}
+
+    if Some(String::new()).is_some() {}
+    if Some((String::new(), ())).is_some() {}
+
+    // Poll
+    if Ready(m.lock()).is_ready() {}
+    if Ready(m.lock().unwrap().0).is_ready() {}
+
+    {
+        if Pending::<std::sync::MutexGuard<()>>.is_pending() {}
+    }
+    if Pending::<std::sync::MutexGuard<()>>.is_pending() {
+    } else {
+    }
+
+    if Pending::<std::sync::MutexGuard<()>>.is_pending() {}
+
+    if Ready(String::new()).is_ready() {}
+    if Ready((String::new(), ())).is_ready() {}
+}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.rs
new file mode 100644
index 0000000000000..b9c82d86f618b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.rs
@@ -0,0 +1,58 @@
+// run-rustfix
+
+// Issue #5746
+#![warn(clippy::redundant_pattern_matching)]
+#![allow(clippy::if_same_then_else)]
+use std::task::Poll::{Pending, Ready};
+
+fn main() {
+    let m = std::sync::Mutex::new((0, 0));
+
+    // Result
+    if let Ok(_) = m.lock() {}
+    if let Err(_) = Err::<(), _>(m.lock().unwrap().0) {}
+
+    {
+        if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {}
+    }
+    if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {
+    } else {
+    }
+    if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {}
+    if let Err(_) = Err::<std::sync::MutexGuard<()>, _>(()) {}
+
+    if let Ok(_) = Ok::<_, ()>(String::new()) {}
+    if let Err(_) = Err::<(), _>((String::new(), ())) {}
+
+    // Option
+    if let Some(_) = Some(m.lock()) {}
+    if let Some(_) = Some(m.lock().unwrap().0) {}
+
+    {
+        if let None = None::<std::sync::MutexGuard<()>> {}
+    }
+    if let None = None::<std::sync::MutexGuard<()>> {
+    } else {
+    }
+
+    if let None = None::<std::sync::MutexGuard<()>> {}
+
+    if let Some(_) = Some(String::new()) {}
+    if let Some(_) = Some((String::new(), ())) {}
+
+    // Poll
+    if let Ready(_) = Ready(m.lock()) {}
+    if let Ready(_) = Ready(m.lock().unwrap().0) {}
+
+    {
+        if let Pending = Pending::<std::sync::MutexGuard<()>> {}
+    }
+    if let Pending = Pending::<std::sync::MutexGuard<()>> {
+    } else {
+    }
+
+    if let Pending = Pending::<std::sync::MutexGuard<()>> {}
+
+    if let Ready(_) = Ready(String::new()) {}
+    if let Ready(_) = Ready((String::new(), ())) {}
+}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.stderr
new file mode 100644
index 0000000000000..eb7aa70ee2738
--- /dev/null
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.stderr
@@ -0,0 +1,171 @@
+error: redundant pattern matching, consider using `is_ok()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:12:12
+   |
+LL |     if let Ok(_) = m.lock() {}
+   |     -------^^^^^----------- help: try this: `if m.lock().is_ok()`
+   |
+   = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
+   = note: this will change drop order of the result, as well as all temporaries
+   = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_err()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:13:12
+   |
+LL |     if let Err(_) = Err::<(), _>(m.lock().unwrap().0) {}
+   |     -------^^^^^^------------------------------------ help: try this: `if Err::<(), _>(m.lock().unwrap().0).is_err()`
+   |
+   = note: this will change drop order of the result, as well as all temporaries
+   = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_ok()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:16:16
+   |
+LL |         if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {}
+   |         -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()`
+   |
+   = note: this will change drop order of the result, as well as all temporaries
+   = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_ok()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:18:12
+   |
+LL |     if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {
+   |     -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()`
+   |
+   = note: this will change drop order of the result, as well as all temporaries
+   = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_ok()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:21:12
+   |
+LL |     if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {}
+   |     -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()`
+
+error: redundant pattern matching, consider using `is_err()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:22:12
+   |
+LL |     if let Err(_) = Err::<std::sync::MutexGuard<()>, _>(()) {}
+   |     -------^^^^^^------------------------------------------ help: try this: `if Err::<std::sync::MutexGuard<()>, _>(()).is_err()`
+
+error: redundant pattern matching, consider using `is_ok()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:24:12
+   |
+LL |     if let Ok(_) = Ok::<_, ()>(String::new()) {}
+   |     -------^^^^^----------------------------- help: try this: `if Ok::<_, ()>(String::new()).is_ok()`
+
+error: redundant pattern matching, consider using `is_err()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:25:12
+   |
+LL |     if let Err(_) = Err::<(), _>((String::new(), ())) {}
+   |     -------^^^^^^------------------------------------ help: try this: `if Err::<(), _>((String::new(), ())).is_err()`
+
+error: redundant pattern matching, consider using `is_some()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:28:12
+   |
+LL |     if let Some(_) = Some(m.lock()) {}
+   |     -------^^^^^^^----------------- help: try this: `if Some(m.lock()).is_some()`
+   |
+   = note: this will change drop order of the result, as well as all temporaries
+   = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_some()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:29:12
+   |
+LL |     if let Some(_) = Some(m.lock().unwrap().0) {}
+   |     -------^^^^^^^---------------------------- help: try this: `if Some(m.lock().unwrap().0).is_some()`
+   |
+   = note: this will change drop order of the result, as well as all temporaries
+   = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_none()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:32:16
+   |
+LL |         if let None = None::<std::sync::MutexGuard<()>> {}
+   |         -------^^^^------------------------------------ help: try this: `if None::<std::sync::MutexGuard<()>>.is_none()`
+   |
+   = note: this will change drop order of the result, as well as all temporaries
+   = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_none()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:34:12
+   |
+LL |     if let None = None::<std::sync::MutexGuard<()>> {
+   |     -------^^^^------------------------------------ help: try this: `if None::<std::sync::MutexGuard<()>>.is_none()`
+   |
+   = note: this will change drop order of the result, as well as all temporaries
+   = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_none()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:38:12
+   |
+LL |     if let None = None::<std::sync::MutexGuard<()>> {}
+   |     -------^^^^------------------------------------ help: try this: `if None::<std::sync::MutexGuard<()>>.is_none()`
+
+error: redundant pattern matching, consider using `is_some()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:40:12
+   |
+LL |     if let Some(_) = Some(String::new()) {}
+   |     -------^^^^^^^---------------------- help: try this: `if Some(String::new()).is_some()`
+
+error: redundant pattern matching, consider using `is_some()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:41:12
+   |
+LL |     if let Some(_) = Some((String::new(), ())) {}
+   |     -------^^^^^^^---------------------------- help: try this: `if Some((String::new(), ())).is_some()`
+
+error: redundant pattern matching, consider using `is_ready()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:44:12
+   |
+LL |     if let Ready(_) = Ready(m.lock()) {}
+   |     -------^^^^^^^^------------------ help: try this: `if Ready(m.lock()).is_ready()`
+   |
+   = note: this will change drop order of the result, as well as all temporaries
+   = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_ready()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:45:12
+   |
+LL |     if let Ready(_) = Ready(m.lock().unwrap().0) {}
+   |     -------^^^^^^^^----------------------------- help: try this: `if Ready(m.lock().unwrap().0).is_ready()`
+   |
+   = note: this will change drop order of the result, as well as all temporaries
+   = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_pending()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:48:16
+   |
+LL |         if let Pending = Pending::<std::sync::MutexGuard<()>> {}
+   |         -------^^^^^^^--------------------------------------- help: try this: `if Pending::<std::sync::MutexGuard<()>>.is_pending()`
+   |
+   = note: this will change drop order of the result, as well as all temporaries
+   = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_pending()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:50:12
+   |
+LL |     if let Pending = Pending::<std::sync::MutexGuard<()>> {
+   |     -------^^^^^^^--------------------------------------- help: try this: `if Pending::<std::sync::MutexGuard<()>>.is_pending()`
+   |
+   = note: this will change drop order of the result, as well as all temporaries
+   = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important
+
+error: redundant pattern matching, consider using `is_pending()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:54:12
+   |
+LL |     if let Pending = Pending::<std::sync::MutexGuard<()>> {}
+   |     -------^^^^^^^--------------------------------------- help: try this: `if Pending::<std::sync::MutexGuard<()>>.is_pending()`
+
+error: redundant pattern matching, consider using `is_ready()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:56:12
+   |
+LL |     if let Ready(_) = Ready(String::new()) {}
+   |     -------^^^^^^^^----------------------- help: try this: `if Ready(String::new()).is_ready()`
+
+error: redundant pattern matching, consider using `is_ready()`
+  --> $DIR/redundant_pattern_matching_drop_order.rs:57:12
+   |
+LL |     if let Ready(_) = Ready((String::new(), ())) {}
+   |     -------^^^^^^^^----------------------------- help: try this: `if Ready((String::new(), ())).is_ready()`
+
+error: aborting due to 22 previous errors
+
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed
index 66f580a0a6834..997144772669b 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed
@@ -2,7 +2,12 @@
 
 #![warn(clippy::all)]
 #![warn(clippy::redundant_pattern_matching)]
-#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)]
+#![allow(
+    unused_must_use,
+    clippy::needless_bool,
+    clippy::match_like_matches_macro,
+    clippy::if_same_then_else
+)]
 
 fn main() {
     if None::<()>.is_none() {}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs
index f18b27b8b95c3..8309847e18162 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs
@@ -2,7 +2,12 @@
 
 #![warn(clippy::all)]
 #![warn(clippy::redundant_pattern_matching)]
-#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)]
+#![allow(
+    unused_must_use,
+    clippy::needless_bool,
+    clippy::match_like_matches_macro,
+    clippy::if_same_then_else
+)]
 
 fn main() {
     if let None = None::<()> {}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr
index 58482a0ab70dc..613a30d4a4845 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr
@@ -1,5 +1,5 @@
 error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching_option.rs:8:12
+  --> $DIR/redundant_pattern_matching_option.rs:13:12
    |
 LL |     if let None = None::<()> {}
    |     -------^^^^------------- help: try this: `if None::<()>.is_none()`
@@ -7,43 +7,43 @@ LL |     if let None = None::<()> {}
    = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching_option.rs:10:12
+  --> $DIR/redundant_pattern_matching_option.rs:15: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_option.rs:12:12
+  --> $DIR/redundant_pattern_matching_option.rs:17: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_option.rs:18:15
+  --> $DIR/redundant_pattern_matching_option.rs:23: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_option.rs:20:15
+  --> $DIR/redundant_pattern_matching_option.rs:25: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_option.rs:22:15
+  --> $DIR/redundant_pattern_matching_option.rs:27:15
    |
 LL |     while let None = None::<()> {}
    |     ----------^^^^------------- help: try this: `while None::<()>.is_none()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching_option.rs:25:15
+  --> $DIR/redundant_pattern_matching_option.rs:30:15
    |
 LL |     while let Some(_) = v.pop() {
    |     ----------^^^^^^^---------- help: try this: `while v.pop().is_some()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching_option.rs:33:5
+  --> $DIR/redundant_pattern_matching_option.rs:38:5
    |
 LL | /     match Some(42) {
 LL | |         Some(_) => true,
@@ -52,7 +52,7 @@ LL | |     };
    | |_____^ help: try this: `Some(42).is_some()`
 
 error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching_option.rs:38:5
+  --> $DIR/redundant_pattern_matching_option.rs:43:5
    |
 LL | /     match None::<()> {
 LL | |         Some(_) => false,
@@ -61,7 +61,7 @@ LL | |     };
    | |_____^ help: try this: `None::<()>.is_none()`
 
 error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching_option.rs:43:13
+  --> $DIR/redundant_pattern_matching_option.rs:48:13
    |
 LL |       let _ = match None::<()> {
    |  _____________^
@@ -71,49 +71,49 @@ LL | |     };
    | |_____^ help: try this: `None::<()>.is_none()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching_option.rs:49:20
+  --> $DIR/redundant_pattern_matching_option.rs:54:20
    |
 LL |     let _ = 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_option.rs:53:20
+  --> $DIR/redundant_pattern_matching_option.rs:58: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_option.rs:55:19
+  --> $DIR/redundant_pattern_matching_option.rs:60:19
    |
 LL |     } else if let None = gen_opt() {
    |            -------^^^^------------ help: try this: `if gen_opt().is_none()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching_option.rs:74:12
+  --> $DIR/redundant_pattern_matching_option.rs:79:12
    |
 LL |     if let Some(_) = Some(42) {}
    |     -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
 
 error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching_option.rs:76:12
+  --> $DIR/redundant_pattern_matching_option.rs:81: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_option.rs:78:15
+  --> $DIR/redundant_pattern_matching_option.rs:83: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_option.rs:80:15
+  --> $DIR/redundant_pattern_matching_option.rs:85:15
    |
 LL |     while let None = None::<()> {}
    |     ----------^^^^------------- help: try this: `while None::<()>.is_none()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching_option.rs:82:5
+  --> $DIR/redundant_pattern_matching_option.rs:87:5
    |
 LL | /     match Some(42) {
 LL | |         Some(_) => true,
@@ -122,7 +122,7 @@ LL | |     };
    | |_____^ help: try this: `Some(42).is_some()`
 
 error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching_option.rs:87:5
+  --> $DIR/redundant_pattern_matching_option.rs:92:5
    |
 LL | /     match None::<()> {
 LL | |         Some(_) => false,
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed
index 465aa80dac27c..c297745380404 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.fixed
@@ -2,7 +2,12 @@
 
 #![warn(clippy::all)]
 #![warn(clippy::redundant_pattern_matching)]
-#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)]
+#![allow(
+    unused_must_use,
+    clippy::needless_bool,
+    clippy::match_like_matches_macro,
+    clippy::if_same_then_else
+)]
 
 use std::task::Poll::{self, Pending, Ready};
 
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs
index 7891ff353b13e..665c8c417504d 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.rs
@@ -2,7 +2,12 @@
 
 #![warn(clippy::all)]
 #![warn(clippy::redundant_pattern_matching)]
-#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)]
+#![allow(
+    unused_must_use,
+    clippy::needless_bool,
+    clippy::match_like_matches_macro,
+    clippy::if_same_then_else
+)]
 
 use std::task::Poll::{self, Pending, Ready};
 
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.stderr
index 5ffc6c47c90a2..5ecf024a733a3 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.stderr
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_poll.stderr
@@ -1,5 +1,5 @@
 error: redundant pattern matching, consider using `is_pending()`
-  --> $DIR/redundant_pattern_matching_poll.rs:10:12
+  --> $DIR/redundant_pattern_matching_poll.rs:15:12
    |
 LL |     if let Pending = Pending::<()> {}
    |     -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()`
@@ -7,37 +7,37 @@ LL |     if let Pending = Pending::<()> {}
    = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
 
 error: redundant pattern matching, consider using `is_ready()`
-  --> $DIR/redundant_pattern_matching_poll.rs:12:12
+  --> $DIR/redundant_pattern_matching_poll.rs:17:12
    |
 LL |     if let Ready(_) = Ready(42) {}
    |     -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()`
 
 error: redundant pattern matching, consider using `is_ready()`
-  --> $DIR/redundant_pattern_matching_poll.rs:14:12
+  --> $DIR/redundant_pattern_matching_poll.rs:19:12
    |
 LL |     if let Ready(_) = Ready(42) {
    |     -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()`
 
 error: redundant pattern matching, consider using `is_ready()`
-  --> $DIR/redundant_pattern_matching_poll.rs:20:15
+  --> $DIR/redundant_pattern_matching_poll.rs:25:15
    |
 LL |     while let Ready(_) = Ready(42) {}
    |     ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()`
 
 error: redundant pattern matching, consider using `is_pending()`
-  --> $DIR/redundant_pattern_matching_poll.rs:22:15
+  --> $DIR/redundant_pattern_matching_poll.rs:27:15
    |
 LL |     while let Pending = Ready(42) {}
    |     ----------^^^^^^^------------ help: try this: `while Ready(42).is_pending()`
 
 error: redundant pattern matching, consider using `is_pending()`
-  --> $DIR/redundant_pattern_matching_poll.rs:24:15
+  --> $DIR/redundant_pattern_matching_poll.rs:29:15
    |
 LL |     while let Pending = Pending::<()> {}
    |     ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()`
 
 error: redundant pattern matching, consider using `is_ready()`
-  --> $DIR/redundant_pattern_matching_poll.rs:30:5
+  --> $DIR/redundant_pattern_matching_poll.rs:35:5
    |
 LL | /     match Ready(42) {
 LL | |         Ready(_) => true,
@@ -46,7 +46,7 @@ LL | |     };
    | |_____^ help: try this: `Ready(42).is_ready()`
 
 error: redundant pattern matching, consider using `is_pending()`
-  --> $DIR/redundant_pattern_matching_poll.rs:35:5
+  --> $DIR/redundant_pattern_matching_poll.rs:40:5
    |
 LL | /     match Pending::<()> {
 LL | |         Ready(_) => false,
@@ -55,7 +55,7 @@ LL | |     };
    | |_____^ help: try this: `Pending::<()>.is_pending()`
 
 error: redundant pattern matching, consider using `is_pending()`
-  --> $DIR/redundant_pattern_matching_poll.rs:40:13
+  --> $DIR/redundant_pattern_matching_poll.rs:45:13
    |
 LL |       let _ = match Pending::<()> {
    |  _____________^
@@ -65,49 +65,49 @@ LL | |     };
    | |_____^ help: try this: `Pending::<()>.is_pending()`
 
 error: redundant pattern matching, consider using `is_ready()`
-  --> $DIR/redundant_pattern_matching_poll.rs:46:20
+  --> $DIR/redundant_pattern_matching_poll.rs:51:20
    |
 LL |     let _ = if let Ready(_) = poll { true } else { false };
    |             -------^^^^^^^^------- help: try this: `if poll.is_ready()`
 
 error: redundant pattern matching, consider using `is_ready()`
-  --> $DIR/redundant_pattern_matching_poll.rs:50:20
+  --> $DIR/redundant_pattern_matching_poll.rs:55:20
    |
 LL |     let _ = if let Ready(_) = gen_poll() {
    |             -------^^^^^^^^------------- help: try this: `if gen_poll().is_ready()`
 
 error: redundant pattern matching, consider using `is_pending()`
-  --> $DIR/redundant_pattern_matching_poll.rs:52:19
+  --> $DIR/redundant_pattern_matching_poll.rs:57:19
    |
 LL |     } else if let Pending = gen_poll() {
    |            -------^^^^^^^------------- help: try this: `if gen_poll().is_pending()`
 
 error: redundant pattern matching, consider using `is_ready()`
-  --> $DIR/redundant_pattern_matching_poll.rs:68:12
+  --> $DIR/redundant_pattern_matching_poll.rs:73:12
    |
 LL |     if let Ready(_) = Ready(42) {}
    |     -------^^^^^^^^------------ help: try this: `if Ready(42).is_ready()`
 
 error: redundant pattern matching, consider using `is_pending()`
-  --> $DIR/redundant_pattern_matching_poll.rs:70:12
+  --> $DIR/redundant_pattern_matching_poll.rs:75:12
    |
 LL |     if let Pending = Pending::<()> {}
    |     -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_pending()`
 
 error: redundant pattern matching, consider using `is_ready()`
-  --> $DIR/redundant_pattern_matching_poll.rs:72:15
+  --> $DIR/redundant_pattern_matching_poll.rs:77:15
    |
 LL |     while let Ready(_) = Ready(42) {}
    |     ----------^^^^^^^^------------ help: try this: `while Ready(42).is_ready()`
 
 error: redundant pattern matching, consider using `is_pending()`
-  --> $DIR/redundant_pattern_matching_poll.rs:74:15
+  --> $DIR/redundant_pattern_matching_poll.rs:79:15
    |
 LL |     while let Pending = Pending::<()> {}
    |     ----------^^^^^^^---------------- help: try this: `while Pending::<()>.is_pending()`
 
 error: redundant pattern matching, consider using `is_ready()`
-  --> $DIR/redundant_pattern_matching_poll.rs:76:5
+  --> $DIR/redundant_pattern_matching_poll.rs:81:5
    |
 LL | /     match Ready(42) {
 LL | |         Ready(_) => true,
@@ -116,7 +116,7 @@ LL | |     };
    | |_____^ help: try this: `Ready(42).is_ready()`
 
 error: redundant pattern matching, consider using `is_pending()`
-  --> $DIR/redundant_pattern_matching_poll.rs:81:5
+  --> $DIR/redundant_pattern_matching_poll.rs:86:5
    |
 LL | /     match Pending::<()> {
 LL | |         Ready(_) => false,
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed
index e94c5704b4891..d7af5d762ae4a 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed
@@ -7,7 +7,8 @@
     clippy::needless_bool,
     clippy::match_like_matches_macro,
     clippy::unnecessary_wraps,
-    deprecated
+    deprecated,
+    clippy::if_same_then_else
 )]
 
 fn main() {
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs
index 5d1752942322b..e06d4485ae4f2 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs
@@ -7,7 +7,8 @@
     clippy::needless_bool,
     clippy::match_like_matches_macro,
     clippy::unnecessary_wraps,
-    deprecated
+    deprecated,
+    clippy::if_same_then_else
 )]
 
 fn main() {
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr
index d6a46babb7795..e06f095da20c6 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr
@@ -1,5 +1,5 @@
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching_result.rs:15:12
+  --> $DIR/redundant_pattern_matching_result.rs:16:12
    |
 LL |     if let Ok(_) = &result {}
    |     -------^^^^^---------- help: try this: `if result.is_ok()`
@@ -7,31 +7,31 @@ LL |     if let Ok(_) = &result {}
    = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
 
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching_result.rs:17:12
+  --> $DIR/redundant_pattern_matching_result.rs:18:12
    |
 LL |     if let Ok(_) = Ok::<i32, i32>(42) {}
    |     -------^^^^^--------------------- help: try this: `if Ok::<i32, i32>(42).is_ok()`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching_result.rs:19:12
+  --> $DIR/redundant_pattern_matching_result.rs:20: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_ok()`
-  --> $DIR/redundant_pattern_matching_result.rs:21:15
+  --> $DIR/redundant_pattern_matching_result.rs:22: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_result.rs:23:15
+  --> $DIR/redundant_pattern_matching_result.rs:24: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_ok()`
-  --> $DIR/redundant_pattern_matching_result.rs:33:5
+  --> $DIR/redundant_pattern_matching_result.rs:34:5
    |
 LL | /     match Ok::<i32, i32>(42) {
 LL | |         Ok(_) => true,
@@ -40,7 +40,7 @@ LL | |     };
    | |_____^ help: try this: `Ok::<i32, i32>(42).is_ok()`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching_result.rs:38:5
+  --> $DIR/redundant_pattern_matching_result.rs:39:5
    |
 LL | /     match Ok::<i32, i32>(42) {
 LL | |         Ok(_) => false,
@@ -49,7 +49,7 @@ LL | |     };
    | |_____^ help: try this: `Ok::<i32, i32>(42).is_err()`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching_result.rs:43:5
+  --> $DIR/redundant_pattern_matching_result.rs:44:5
    |
 LL | /     match Err::<i32, i32>(42) {
 LL | |         Ok(_) => false,
@@ -58,7 +58,7 @@ LL | |     };
    | |_____^ help: try this: `Err::<i32, i32>(42).is_err()`
 
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching_result.rs:48:5
+  --> $DIR/redundant_pattern_matching_result.rs:49:5
    |
 LL | /     match Err::<i32, i32>(42) {
 LL | |         Ok(_) => true,
@@ -67,73 +67,73 @@ LL | |     };
    | |_____^ help: try this: `Err::<i32, i32>(42).is_ok()`
 
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching_result.rs:53:20
+  --> $DIR/redundant_pattern_matching_result.rs:54: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_ok()`
-  --> $DIR/redundant_pattern_matching_result.rs:59:20
+  --> $DIR/redundant_pattern_matching_result.rs:60:20
    |
 LL |     let _ = 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_result.rs:61:19
+  --> $DIR/redundant_pattern_matching_result.rs:62: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_result.rs:84:19
+  --> $DIR/redundant_pattern_matching_result.rs:85: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_result.rs:85:16
+  --> $DIR/redundant_pattern_matching_result.rs:86: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_result.rs:91:12
+  --> $DIR/redundant_pattern_matching_result.rs:92: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_result.rs:92:15
+  --> $DIR/redundant_pattern_matching_result.rs:93:15
    |
 LL |     while let Some(_) = m!() {}
    |     ----------^^^^^^^------- help: try this: `while m!().is_some()`
 
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching_result.rs:110:12
+  --> $DIR/redundant_pattern_matching_result.rs:111:12
    |
 LL |     if let Ok(_) = Ok::<i32, i32>(42) {}
    |     -------^^^^^--------------------- help: try this: `if Ok::<i32, i32>(42).is_ok()`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching_result.rs:112:12
+  --> $DIR/redundant_pattern_matching_result.rs:113: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_ok()`
-  --> $DIR/redundant_pattern_matching_result.rs:114:15
+  --> $DIR/redundant_pattern_matching_result.rs:115: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_result.rs:116:15
+  --> $DIR/redundant_pattern_matching_result.rs:117: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_ok()`
-  --> $DIR/redundant_pattern_matching_result.rs:118:5
+  --> $DIR/redundant_pattern_matching_result.rs:119:5
    |
 LL | /     match Ok::<i32, i32>(42) {
 LL | |         Ok(_) => true,
@@ -142,7 +142,7 @@ LL | |     };
    | |_____^ help: try this: `Ok::<i32, i32>(42).is_ok()`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching_result.rs:123:5
+  --> $DIR/redundant_pattern_matching_result.rs:124:5
    |
 LL | /     match Err::<i32, i32>(42) {
 LL | |         Ok(_) => false,
diff --git a/src/tools/clippy/tests/ui/repl_uninit.rs b/src/tools/clippy/tests/ui/repl_uninit.rs
index ad5b8e4857d17..6c7e2b854dc14 100644
--- a/src/tools/clippy/tests/ui/repl_uninit.rs
+++ b/src/tools/clippy/tests/ui/repl_uninit.rs
@@ -1,5 +1,5 @@
-#![allow(deprecated, invalid_value)]
-#![warn(clippy::all)]
+#![allow(deprecated, invalid_value, clippy::uninit_assumed_init)]
+#![warn(clippy::mem_replace_with_uninit)]
 
 use std::mem;
 
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports.fixed b/src/tools/clippy/tests/ui/single_component_path_imports.fixed
index a7a8499b58f00..f66b445b7b6a3 100644
--- a/src/tools/clippy/tests/ui/single_component_path_imports.fixed
+++ b/src/tools/clippy/tests/ui/single_component_path_imports.fixed
@@ -19,3 +19,16 @@ fn main() {
     // False positive #5154, shouldn't trigger lint.
     m!();
 }
+
+mod hello_mod {
+    
+    #[allow(dead_code)]
+    fn hello_mod() {}
+}
+
+mod hi_mod {
+    use self::regex::{Regex, RegexSet};
+    use regex;
+    #[allow(dead_code)]
+    fn hi_mod() {}
+}
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports.rs b/src/tools/clippy/tests/ui/single_component_path_imports.rs
index 9a427e90ad3df..09d4865859584 100644
--- a/src/tools/clippy/tests/ui/single_component_path_imports.rs
+++ b/src/tools/clippy/tests/ui/single_component_path_imports.rs
@@ -19,3 +19,16 @@ fn main() {
     // False positive #5154, shouldn't trigger lint.
     m!();
 }
+
+mod hello_mod {
+    use regex;
+    #[allow(dead_code)]
+    fn hello_mod() {}
+}
+
+mod hi_mod {
+    use self::regex::{Regex, RegexSet};
+    use regex;
+    #[allow(dead_code)]
+    fn hi_mod() {}
+}
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports.stderr b/src/tools/clippy/tests/ui/single_component_path_imports.stderr
index 519ada0169a6d..7005fa8f125d3 100644
--- a/src/tools/clippy/tests/ui/single_component_path_imports.stderr
+++ b/src/tools/clippy/tests/ui/single_component_path_imports.stderr
@@ -1,10 +1,16 @@
+error: this import is redundant
+  --> $DIR/single_component_path_imports.rs:24:5
+   |
+LL |     use regex;
+   |     ^^^^^^^^^^ help: remove it entirely
+   |
+   = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
+
 error: this import is redundant
   --> $DIR/single_component_path_imports.rs:6:1
    |
 LL | use regex;
    | ^^^^^^^^^^ help: remove it entirely
-   |
-   = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
 
-error: aborting due to previous error
+error: aborting due to 2 previous errors
 
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports_nested_first.rs b/src/tools/clippy/tests/ui/single_component_path_imports_nested_first.rs
new file mode 100644
index 0000000000000..94117061b270d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_component_path_imports_nested_first.rs
@@ -0,0 +1,17 @@
+// edition:2018
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+use regex;
+use serde as edres;
+pub use serde;
+
+fn main() {
+    regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
+}
+
+mod root_nested_use_mod {
+    use {regex, serde};
+    #[allow(dead_code)]
+    fn root_nested_use_mod() {}
+}
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports_nested_first.stderr b/src/tools/clippy/tests/ui/single_component_path_imports_nested_first.stderr
new file mode 100644
index 0000000000000..0c3256c1ce43a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_component_path_imports_nested_first.stderr
@@ -0,0 +1,25 @@
+error: this import is redundant
+  --> $DIR/single_component_path_imports_nested_first.rs:14:10
+   |
+LL |     use {regex, serde};
+   |          ^^^^^
+   |
+   = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
+   = help: remove this import
+
+error: this import is redundant
+  --> $DIR/single_component_path_imports_nested_first.rs:14:17
+   |
+LL |     use {regex, serde};
+   |                 ^^^^^
+   |
+   = help: remove this import
+
+error: this import is redundant
+  --> $DIR/single_component_path_imports_nested_first.rs:5:1
+   |
+LL | use regex;
+   | ^^^^^^^^^^ help: remove it entirely
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports_self_after.rs b/src/tools/clippy/tests/ui/single_component_path_imports_self_after.rs
new file mode 100644
index 0000000000000..94319ade0ac4b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_component_path_imports_self_after.rs
@@ -0,0 +1,16 @@
+// edition:2018
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+use self::regex::{Regex as xeger, RegexSet as tesxeger};
+pub use self::{
+    regex::{Regex, RegexSet},
+    some_mod::SomeType,
+};
+use regex;
+
+mod some_mod {
+    pub struct SomeType;
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/single_component_path_imports_self_before.rs b/src/tools/clippy/tests/ui/single_component_path_imports_self_before.rs
new file mode 100644
index 0000000000000..c7437b234566a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_component_path_imports_self_before.rs
@@ -0,0 +1,17 @@
+// edition:2018
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+use regex;
+
+use self::regex::{Regex as xeger, RegexSet as tesxeger};
+pub use self::{
+    regex::{Regex, RegexSet},
+    some_mod::SomeType,
+};
+
+mod some_mod {
+    pub struct SomeType;
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/single_match.rs b/src/tools/clippy/tests/ui/single_match.rs
index ca884b41c4579..b1819e08d53bf 100644
--- a/src/tools/clippy/tests/ui/single_match.rs
+++ b/src/tools/clippy/tests/ui/single_match.rs
@@ -135,6 +135,14 @@ fn if_suggestion() {
         Bar::A => println!(),
         _ => (),
     }
+
+    // issue #7038
+    struct X;
+    let x = Some(X);
+    match x {
+        None => println!(),
+        _ => (),
+    };
 }
 
 macro_rules! single_match {
diff --git a/src/tools/clippy/tests/ui/single_match.stderr b/src/tools/clippy/tests/ui/single_match.stderr
index 7ea6955b7401e..9ef2a8668a6fa 100644
--- a/src/tools/clippy/tests/ui/single_match.stderr
+++ b/src/tools/clippy/tests/ui/single_match.stderr
@@ -119,5 +119,14 @@ LL | |         _ => (),
 LL | |     }
    | |_____^ help: try this: `if let Bar::A = x { println!() }`
 
-error: aborting due to 12 previous errors
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+  --> $DIR/single_match.rs:142:5
+   |
+LL | /     match x {
+LL | |         None => println!(),
+LL | |         _ => (),
+LL | |     };
+   | |_____^ help: try this: `if let None = x { println!() }`
+
+error: aborting due to 13 previous errors
 
diff --git a/src/tools/clippy/tests/ui/string_lit_as_bytes.fixed b/src/tools/clippy/tests/ui/string_lit_as_bytes.fixed
index dd22bfa5c53ef..df2256e4f97de 100644
--- a/src/tools/clippy/tests/ui/string_lit_as_bytes.fixed
+++ b/src/tools/clippy/tests/ui/string_lit_as_bytes.fixed
@@ -22,7 +22,7 @@ fn str_lit_as_bytes() {
 
     let current_version = env!("CARGO_PKG_VERSION").as_bytes();
 
-    let includestr = include_bytes!("entry_unfixable.rs");
+    let includestr = include_bytes!("string_lit_as_bytes.rs");
 
     let _ = b"string with newline\t\n";
 }
diff --git a/src/tools/clippy/tests/ui/string_lit_as_bytes.rs b/src/tools/clippy/tests/ui/string_lit_as_bytes.rs
index d2a710ed6b8ca..c6bf8f732ed9f 100644
--- a/src/tools/clippy/tests/ui/string_lit_as_bytes.rs
+++ b/src/tools/clippy/tests/ui/string_lit_as_bytes.rs
@@ -22,7 +22,7 @@ fn str_lit_as_bytes() {
 
     let current_version = env!("CARGO_PKG_VERSION").as_bytes();
 
-    let includestr = include_str!("entry_unfixable.rs").as_bytes();
+    let includestr = include_str!("string_lit_as_bytes.rs").as_bytes();
 
     let _ = "string with newline\t\n".as_bytes();
 }
diff --git a/src/tools/clippy/tests/ui/string_lit_as_bytes.stderr b/src/tools/clippy/tests/ui/string_lit_as_bytes.stderr
index e0ddb070b5044..f47d6161c6cf2 100644
--- a/src/tools/clippy/tests/ui/string_lit_as_bytes.stderr
+++ b/src/tools/clippy/tests/ui/string_lit_as_bytes.stderr
@@ -27,8 +27,8 @@ LL |     let bs = "lit to owned".to_owned().into_bytes();
 error: calling `as_bytes()` on `include_str!(..)`
   --> $DIR/string_lit_as_bytes.rs:25:22
    |
-LL |     let includestr = include_str!("entry_unfixable.rs").as_bytes();
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `include_bytes!(..)` instead: `include_bytes!("entry_unfixable.rs")`
+LL |     let includestr = include_str!("string_lit_as_bytes.rs").as_bytes();
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `include_bytes!(..)` instead: `include_bytes!("string_lit_as_bytes.rs")`
 
 error: calling `as_bytes()` on a string literal
   --> $DIR/string_lit_as_bytes.rs:27:13
diff --git a/src/tools/clippy/tests/ui/suspicious_else_formatting.rs b/src/tools/clippy/tests/ui/suspicious_else_formatting.rs
index 226010ec6df3a..547615b10d9fb 100644
--- a/src/tools/clippy/tests/ui/suspicious_else_formatting.rs
+++ b/src/tools/clippy/tests/ui/suspicious_else_formatting.rs
@@ -40,6 +40,7 @@ fn main() {
     {
     }
 
+    // This is fine, though weird. Allman style braces on the else.
     if foo() {
     }
     else
@@ -76,4 +77,29 @@ fn main() {
     }
     if foo() {
     }
+
+    // Almost Allman style braces. Lint these.
+    if foo() {
+    }
+
+    else
+    {
+
+    }
+
+    if foo() {
+    }
+    else
+
+    {
+
+    }
+
+    // #3864 - Allman style braces
+    if foo()
+    {
+    }
+    else
+    {
+    }
 }
diff --git a/src/tools/clippy/tests/ui/suspicious_else_formatting.stderr b/src/tools/clippy/tests/ui/suspicious_else_formatting.stderr
index bbc036d376fef..d8d67b4138ab3 100644
--- a/src/tools/clippy/tests/ui/suspicious_else_formatting.stderr
+++ b/src/tools/clippy/tests/ui/suspicious_else_formatting.stderr
@@ -41,37 +41,50 @@ LL | |     {
    |
    = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
 
-error: this is an `else {..}` but the formatting might hide it
-  --> $DIR/suspicious_else_formatting.rs:44:6
+error: this is an `else if` but the formatting might hide it
+  --> $DIR/suspicious_else_formatting.rs:51:6
    |
-LL |       }
+LL |       } else
    |  ______^
-LL | |     else
-LL | |     {
+LL | |     if foo() { // the span of the above error should continue here
    | |____^
    |
-   = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
+   = note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
 
 error: this is an `else if` but the formatting might hide it
-  --> $DIR/suspicious_else_formatting.rs:50:6
+  --> $DIR/suspicious_else_formatting.rs:56:6
    |
-LL |       } else
+LL |       }
    |  ______^
+LL | |     else
 LL | |     if foo() { // the span of the above error should continue here
    | |____^
    |
    = note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
 
-error: this is an `else if` but the formatting might hide it
-  --> $DIR/suspicious_else_formatting.rs:55:6
+error: this is an `else {..}` but the formatting might hide it
+  --> $DIR/suspicious_else_formatting.rs:83:6
    |
 LL |       }
    |  ______^
+LL | |
 LL | |     else
-LL | |     if foo() { // the span of the above error should continue here
+LL | |     {
    | |____^
    |
-   = note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
+   = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
+
+error: this is an `else {..}` but the formatting might hide it
+  --> $DIR/suspicious_else_formatting.rs:91:6
+   |
+LL |       }
+   |  ______^
+LL | |     else
+LL | |
+LL | |     {
+   | |____^
+   |
+   = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
 
-error: aborting due to 8 previous errors
+error: aborting due to 9 previous errors
 
diff --git a/src/tools/clippy/tests/ui/try_err.fixed b/src/tools/clippy/tests/ui/try_err.fixed
index 5b96bb59c5f18..264194419c739 100644
--- a/src/tools/clippy/tests/ui/try_err.fixed
+++ b/src/tools/clippy/tests/ui/try_err.fixed
@@ -160,3 +160,11 @@ pub fn poll_next(ready: bool) -> Poll<Option<io::Result<()>>> {
 
     Poll::Ready(None)
 }
+
+// Tests that `return` is not duplicated
+pub fn try_return(x: bool) -> Result<i32, i32> {
+    if x {
+        return Err(42);
+    }
+    Ok(0)
+}
diff --git a/src/tools/clippy/tests/ui/try_err.rs b/src/tools/clippy/tests/ui/try_err.rs
index f220d697d2cd7..bc6979bf45719 100644
--- a/src/tools/clippy/tests/ui/try_err.rs
+++ b/src/tools/clippy/tests/ui/try_err.rs
@@ -160,3 +160,11 @@ pub fn poll_next(ready: bool) -> Poll<Option<io::Result<()>>> {
 
     Poll::Ready(None)
 }
+
+// Tests that `return` is not duplicated
+pub fn try_return(x: bool) -> Result<i32, i32> {
+    if x {
+        return Err(42)?;
+    }
+    Ok(0)
+}
diff --git a/src/tools/clippy/tests/ui/try_err.stderr b/src/tools/clippy/tests/ui/try_err.stderr
index 2c01d37192e8e..8f332a9b6492c 100644
--- a/src/tools/clippy/tests/ui/try_err.stderr
+++ b/src/tools/clippy/tests/ui/try_err.stderr
@@ -74,5 +74,11 @@ error: returning an `Err(_)` with the `?` operator
 LL |         Err(io::ErrorKind::NotFound)?
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))`
 
-error: aborting due to 10 previous errors
+error: returning an `Err(_)` with the `?` operator
+  --> $DIR/try_err.rs:167:16
+   |
+LL |         return Err(42)?;
+   |                ^^^^^^^^ help: try this: `Err(42)`
+
+error: aborting due to 11 previous errors
 
diff --git a/src/tools/clippy/tests/ui/uninit.rs b/src/tools/clippy/tests/ui/uninit.rs
index f42b884e0f0e5..1ed3883c1f060 100644
--- a/src/tools/clippy/tests/ui/uninit.rs
+++ b/src/tools/clippy/tests/ui/uninit.rs
@@ -1,6 +1,6 @@
 #![feature(stmt_expr_attributes)]
 
-use std::mem::MaybeUninit;
+use std::mem::{self, MaybeUninit};
 
 fn main() {
     let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
@@ -19,4 +19,7 @@ fn main() {
 
     // This is OK, because all constitutent types are uninit-compatible.
     let _: (MaybeUninit<usize>, [MaybeUninit<bool>; 2]) = unsafe { MaybeUninit::uninit().assume_init() };
+
+    // Was a false negative.
+    let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() };
 }
diff --git a/src/tools/clippy/tests/ui/uninit.stderr b/src/tools/clippy/tests/ui/uninit.stderr
index a37233ecddaee..85b64a8419ab0 100644
--- a/src/tools/clippy/tests/ui/uninit.stderr
+++ b/src/tools/clippy/tests/ui/uninit.stderr
@@ -12,5 +12,11 @@ error: this call for this type may be undefined behavior
 LL |     let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() };
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 2 previous errors
+error: this call for this type may be undefined behavior
+  --> $DIR/uninit.rs:24:29
+   |
+LL |     let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() };
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_self_imports.fixed b/src/tools/clippy/tests/ui/unnecessary_self_imports.fixed
new file mode 100644
index 0000000000000..1185eaa1d552f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_self_imports.fixed
@@ -0,0 +1,10 @@
+// run-rustfix
+#![warn(clippy::unnecessary_self_imports)]
+#![allow(unused_imports, dead_code)]
+
+use std::collections::hash_map::{self, *};
+use std::fs as alias;
+use std::io::{self, Read};
+use std::rc;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/unnecessary_self_imports.rs b/src/tools/clippy/tests/ui/unnecessary_self_imports.rs
new file mode 100644
index 0000000000000..56bfbc09402ad
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_self_imports.rs
@@ -0,0 +1,10 @@
+// run-rustfix
+#![warn(clippy::unnecessary_self_imports)]
+#![allow(unused_imports, dead_code)]
+
+use std::collections::hash_map::{self, *};
+use std::fs::{self as alias};
+use std::io::{self, Read};
+use std::rc::{self};
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/unnecessary_self_imports.stderr b/src/tools/clippy/tests/ui/unnecessary_self_imports.stderr
new file mode 100644
index 0000000000000..83a5618c983d3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_self_imports.stderr
@@ -0,0 +1,23 @@
+error: import ending with `::{self}`
+  --> $DIR/unnecessary_self_imports.rs:6:1
+   |
+LL | use std::fs::{self as alias};
+   | ^^^^^^^^^--------------------
+   |          |
+   |          help: consider omitting `::{self}`: `fs as alias;`
+   |
+   = note: `-D clippy::unnecessary-self-imports` implied by `-D warnings`
+   = note: this will slightly change semantics; any non-module items at the same path will also be imported
+
+error: import ending with `::{self}`
+  --> $DIR/unnecessary_self_imports.rs:8:1
+   |
+LL | use std::rc::{self};
+   | ^^^^^^^^^-----------
+   |          |
+   |          help: consider omitting `::{self}`: `rc;`
+   |
+   = note: this will slightly change semantics; any non-module items at the same path will also be imported
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/wrong_self_convention2.rs b/src/tools/clippy/tests/ui/wrong_self_convention2.rs
index 8b42aa59e1323..ae3a740d405d6 100644
--- a/src/tools/clippy/tests/ui/wrong_self_convention2.rs
+++ b/src/tools/clippy/tests/ui/wrong_self_convention2.rs
@@ -30,3 +30,15 @@ mod issue6983 {
         }
     }
 }
+
+mod issue7032 {
+    trait Foo {
+        fn from_usize(x: usize) -> Self;
+    }
+    // don't trigger
+    impl Foo for usize {
+        fn from_usize(x: usize) -> Self {
+            x
+        }
+    }
+}
diff --git a/src/tools/clippy/util/gh-pages/index.html b/src/tools/clippy/util/gh-pages/index.html
index 082cb35c2e034..27ecb532dd00e 100644
--- a/src/tools/clippy/util/gh-pages/index.html
+++ b/src/tools/clippy/util/gh-pages/index.html
@@ -133,7 +133,7 @@
             opacity: 30%;
         }
 
-        p > code {
+        :not(pre) > code {
             color: var(--inline-code-color);
             background-color: var(--inline-code-bg);
         }