about summary refs log tree commit diff
path: root/clippy_lints/src
diff options
context:
space:
mode:
Diffstat (limited to 'clippy_lints/src')
-rw-r--r--clippy_lints/src/atomic_ordering.rs106
-rw-r--r--clippy_lints/src/await_holding_lock.rs2
-rw-r--r--clippy_lints/src/bit_mask.rs2
-rw-r--r--clippy_lints/src/bytecount.rs7
-rw-r--r--clippy_lints/src/consts.rs4
-rw-r--r--clippy_lints/src/doc.rs2
-rw-r--r--clippy_lints/src/duration_subsec.rs4
-rw-r--r--clippy_lints/src/entry.rs4
-rw-r--r--clippy_lints/src/fallible_impl_from.rs6
-rw-r--r--clippy_lints/src/format.rs4
-rw-r--r--clippy_lints/src/indexing_slicing.rs2
-rw-r--r--clippy_lints/src/inherent_to_string.rs4
-rw-r--r--clippy_lints/src/len_zero.rs4
-rw-r--r--clippy_lints/src/lib.rs23
-rw-r--r--clippy_lints/src/loops.rs10
-rw-r--r--clippy_lints/src/manual_strip.rs245
-rw-r--r--clippy_lints/src/map_err_ignore.rs147
-rw-r--r--clippy_lints/src/match_on_vec_items.rs5
-rw-r--r--clippy_lints/src/matches.rs6
-rw-r--r--clippy_lints/src/methods/bind_instead_of_map.rs21
-rw-r--r--clippy_lints/src/methods/mod.rs88
-rw-r--r--clippy_lints/src/methods/unnecessary_lazy_eval.rs76
-rw-r--r--clippy_lints/src/misc.rs26
-rw-r--r--clippy_lints/src/misc_early.rs6
-rw-r--r--clippy_lints/src/mut_key.rs10
-rw-r--r--clippy_lints/src/non_copy_const.rs95
-rw-r--r--clippy_lints/src/open_options.rs6
-rw-r--r--clippy_lints/src/option_if_let_else.rs37
-rw-r--r--clippy_lints/src/panic_in_result_fn.rs90
-rw-r--r--clippy_lints/src/path_buf_push_overwrite.rs4
-rw-r--r--clippy_lints/src/redundant_clone.rs5
-rw-r--r--clippy_lints/src/repeat_once.rs4
-rw-r--r--clippy_lints/src/shadow.rs5
-rw-r--r--clippy_lints/src/strings.rs4
-rw-r--r--clippy_lints/src/swap.rs3
-rw-r--r--clippy_lints/src/types.rs150
-rw-r--r--clippy_lints/src/unwrap_in_result.rs6
-rw-r--r--clippy_lints/src/useless_conversion.rs10
-rw-r--r--clippy_lints/src/utils/diagnostics.rs9
-rw-r--r--clippy_lints/src/utils/eager_or_lazy.rs128
-rw-r--r--clippy_lints/src/utils/internal_lints.rs119
-rw-r--r--clippy_lints/src/utils/mod.rs14
-rw-r--r--clippy_lints/src/utils/paths.rs4
-rw-r--r--clippy_lints/src/utils/sugg.rs2
-rw-r--r--clippy_lints/src/utils/usage.rs66
-rw-r--r--clippy_lints/src/write.rs6
46 files changed, 1285 insertions, 296 deletions
diff --git a/clippy_lints/src/atomic_ordering.rs b/clippy_lints/src/atomic_ordering.rs
index 2d964ac2b9f..703d8a6f62b 100644
--- a/clippy_lints/src/atomic_ordering.rs
+++ b/clippy_lints/src/atomic_ordering.rs
@@ -8,7 +8,8 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for usage of invalid atomic
-    /// ordering in atomic loads/stores and memory fences.
+    /// ordering in atomic loads/stores/exchanges/updates and
+    /// memory fences.
     ///
     /// **Why is this bad?** Using an invalid atomic ordering
     /// will cause a panic at run-time.
@@ -17,22 +18,35 @@ declare_clippy_lint! {
     ///
     /// **Example:**
     /// ```rust,no_run
-    /// # use std::sync::atomic::{self, AtomicBool, Ordering};
+    /// # use std::sync::atomic::{self, AtomicU8, Ordering};
     ///
-    /// let x = AtomicBool::new(true);
+    /// let x = AtomicU8::new(0);
     ///
+    /// // Bad: `Release` and `AcqRel` cannot be used for `load`.
     /// let _ = x.load(Ordering::Release);
     /// let _ = x.load(Ordering::AcqRel);
     ///
-    /// x.store(false, Ordering::Acquire);
-    /// x.store(false, Ordering::AcqRel);
+    /// // Bad: `Acquire` and `AcqRel` cannot be used for `store`.
+    /// x.store(1, Ordering::Acquire);
+    /// x.store(2, Ordering::AcqRel);
     ///
+    /// // Bad: `Relaxed` cannot be used as a fence's ordering.
     /// atomic::fence(Ordering::Relaxed);
     /// atomic::compiler_fence(Ordering::Relaxed);
+    ///
+    /// // Bad: `Release` and `AcqRel` are both always invalid
+    /// // for the failure ordering (the last arg).
+    /// let _ = x.compare_exchange(1, 2, Ordering::SeqCst, Ordering::Release);
+    /// let _ = x.compare_exchange_weak(2, 3, Ordering::AcqRel, Ordering::AcqRel);
+    ///
+    /// // Bad: The failure ordering is not allowed to be
+    /// // stronger than the success order, and `SeqCst` is
+    /// // stronger than `Relaxed`.
+    /// let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |val| Some(val + val));
     /// ```
     pub INVALID_ATOMIC_ORDERING,
     correctness,
-    "usage of invalid atomic ordering in atomic loads/stores and memory fences"
+    "usage of invalid atomic ordering in atomic operations and memory fences"
 }
 
 declare_lint_pass!(AtomicOrdering => [INVALID_ATOMIC_ORDERING]);
@@ -127,9 +141,89 @@ fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) {
     }
 }
 
+fn opt_ordering_defid(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<DefId> {
+    if let ExprKind::Path(ref ord_qpath) = ord_arg.kind {
+        cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id()
+    } else {
+        None
+    }
+}
+
+fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) {
+    if_chain! {
+        if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind;
+        let method = method_path.ident.name.as_str();
+        if type_is_atomic(cx, &args[0]);
+        if method == "compare_exchange" || method == "compare_exchange_weak" || method == "fetch_update";
+        let (success_order_arg, failure_order_arg) = if method == "fetch_update" {
+            (&args[1], &args[2])
+        } else {
+            (&args[3], &args[4])
+        };
+        if let Some(fail_ordering_def_id) = opt_ordering_defid(cx, failure_order_arg);
+        then {
+            // Helper type holding on to some checking and error reporting data. Has
+            // - (success ordering name,
+            // - list of failure orderings forbidden by the success order,
+            // - suggestion message)
+            type OrdLintInfo = (&'static str, &'static [&'static str], &'static str);
+            let relaxed: OrdLintInfo = ("Relaxed", &["SeqCst", "Acquire"], "ordering mode `Relaxed`");
+            let acquire: OrdLintInfo = ("Acquire", &["SeqCst"], "ordering modes `Acquire` or `Relaxed`");
+            let seq_cst: OrdLintInfo = ("SeqCst", &[], "ordering modes `Acquire`, `SeqCst` or `Relaxed`");
+            let release = ("Release", relaxed.1, relaxed.2);
+            let acqrel = ("AcqRel", acquire.1, acquire.2);
+            let search = [relaxed, acquire, seq_cst, release, acqrel];
+
+            let success_lint_info = opt_ordering_defid(cx, success_order_arg)
+                .and_then(|success_ord_def_id| -> Option<OrdLintInfo> {
+                    search
+                        .iter()
+                        .find(|(ordering, ..)| {
+                            match_def_path(cx, success_ord_def_id,
+                                &["core", "sync", "atomic", "Ordering", ordering])
+                        })
+                        .copied()
+                });
+
+            if match_ordering_def_path(cx, fail_ordering_def_id, &["Release", "AcqRel"]) {
+                // If we don't know the success order is, use what we'd suggest
+                // if it were maximally permissive.
+                let suggested = success_lint_info.unwrap_or(seq_cst).2;
+                span_lint_and_help(
+                    cx,
+                    INVALID_ATOMIC_ORDERING,
+                    failure_order_arg.span,
+                    &format!(
+                        "{}'s failure ordering may not be `Release` or `AcqRel`",
+                        method,
+                    ),
+                    None,
+                    &format!("consider using {} instead", suggested),
+                );
+            } else if let Some((success_ord_name, bad_ords_given_success, suggested)) = success_lint_info {
+                if match_ordering_def_path(cx, fail_ordering_def_id, bad_ords_given_success) {
+                    span_lint_and_help(
+                        cx,
+                        INVALID_ATOMIC_ORDERING,
+                        failure_order_arg.span,
+                        &format!(
+                            "{}'s failure ordering may not be stronger than the success ordering of `{}`",
+                            method,
+                            success_ord_name,
+                        ),
+                        None,
+                        &format!("consider using {} instead", suggested),
+                    );
+                }
+            }
+        }
+    }
+}
+
 impl<'tcx> LateLintPass<'tcx> for AtomicOrdering {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         check_atomic_load_store(cx, expr);
         check_memory_fence(cx, expr);
+        check_atomic_compare_exchange(cx, expr);
     }
 }
diff --git a/clippy_lints/src/await_holding_lock.rs b/clippy_lints/src/await_holding_lock.rs
index f18e7e5d997..367534499fd 100644
--- a/clippy_lints/src/await_holding_lock.rs
+++ b/clippy_lints/src/await_holding_lock.rs
@@ -10,7 +10,7 @@ declare_clippy_lint! {
     /// **What it does:** Checks for calls to await while holding a
     /// non-async-aware MutexGuard.
     ///
-    /// **Why is this bad?** The Mutex types found in syd::sync and parking_lot
+    /// **Why is this bad?** The Mutex types found in std::sync and parking_lot
     /// are not designed to operate in an async context across await points.
     ///
     /// There are two potential solutions. One is to use an asynx-aware Mutex
diff --git a/clippy_lints/src/bit_mask.rs b/clippy_lints/src/bit_mask.rs
index 81a34021e8a..a4ee54076ee 100644
--- a/clippy_lints/src/bit_mask.rs
+++ b/clippy_lints/src/bit_mask.rs
@@ -90,7 +90,7 @@ declare_clippy_lint! {
     /// if x & 0b1111 == 0 { }
     /// ```
     pub VERBOSE_BIT_MASK,
-    style,
+    pedantic,
     "expressions where a bit mask is less readable than the corresponding method call"
 }
 
diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs
index 189c07427ae..d7d02ebf985 100644
--- a/clippy_lints/src/bytecount.rs
+++ b/clippy_lints/src/bytecount.rs
@@ -1,6 +1,5 @@
 use crate::utils::{
-    contains_name, get_pat_name, match_type, paths, single_segment_path, snippet_with_applicability,
-    span_lint_and_sugg, walk_ptrs_ty,
+    contains_name, get_pat_name, match_type, paths, single_segment_path, snippet_with_applicability, span_lint_and_sugg,
 };
 use if_chain::if_chain;
 use rustc_ast::ast::UintTy;
@@ -53,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount {
                     if let ExprKind::Binary(ref op, ref l, ref r) = body.value.kind;
                     if op.node == BinOpKind::Eq;
                     if match_type(cx,
-                               walk_ptrs_ty(cx.typeck_results().expr_ty(&filter_args[0])),
+                               cx.typeck_results().expr_ty(&filter_args[0]).peel_refs(),
                                &paths::SLICE_ITER);
                     then {
                         let needle = match get_path_name(l) {
@@ -63,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount {
                                 _ => { return; }
                             }
                         };
-                        if ty::Uint(UintTy::U8) != *walk_ptrs_ty(cx.typeck_results().expr_ty(needle)).kind() {
+                        if ty::Uint(UintTy::U8) != *cx.typeck_results().expr_ty(needle).peel_refs().kind() {
                             return;
                         }
                         let haystack = if let ExprKind::MethodCall(ref path, _, ref args, _) =
diff --git a/clippy_lints/src/consts.rs b/clippy_lints/src/consts.rs
index 3ee022e4e68..0000d39263e 100644
--- a/clippy_lints/src/consts.rs
+++ b/clippy_lints/src/consts.rs
@@ -21,7 +21,7 @@ pub enum Constant {
     /// A `String` (e.g., "abc").
     Str(String),
     /// A binary string (e.g., `b"abc"`).
-    Binary(Lrc<Vec<u8>>),
+    Binary(Lrc<[u8]>),
     /// A single `char` (e.g., `'a'`).
     Char(char),
     /// An integer's bit representation.
@@ -155,7 +155,7 @@ pub fn lit_to_constant(lit: &LitKind, ty: Option<Ty<'_>>) -> Constant {
     match *lit {
         LitKind::Str(ref is, _) => Constant::Str(is.to_string()),
         LitKind::Byte(b) => Constant::Int(u128::from(b)),
-        LitKind::ByteStr(ref s) => Constant::Binary(Lrc::clone(s)),
+        LitKind::ByteStr(ref s) => Constant::Binary(Lrc::from(s.as_slice())),
         LitKind::Char(c) => Constant::Char(c),
         LitKind::Int(n, _) => Constant::Int(n),
         LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty {
diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs
index 50121a054c7..62bb70af06e 100644
--- a/clippy_lints/src/doc.rs
+++ b/clippy_lints/src/doc.rs
@@ -534,7 +534,7 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) {
             return false;
         }
 
-        let s = if s.ends_with('s') { &s[..s.len() - 1] } else { s };
+        let s = s.strip_suffix('s').unwrap_or(s);
 
         s.chars().all(char::is_alphanumeric)
             && s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1
diff --git a/clippy_lints/src/duration_subsec.rs b/clippy_lints/src/duration_subsec.rs
index 8ece44878fe..c0529a34cc4 100644
--- a/clippy_lints/src/duration_subsec.rs
+++ b/clippy_lints/src/duration_subsec.rs
@@ -7,7 +7,7 @@ use rustc_span::source_map::Spanned;
 
 use crate::consts::{constant, Constant};
 use crate::utils::paths;
-use crate::utils::{match_type, snippet_with_applicability, span_lint_and_sugg, walk_ptrs_ty};
+use crate::utils::{match_type, snippet_with_applicability, span_lint_and_sugg};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for calculation of subsecond microseconds or milliseconds
@@ -43,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for DurationSubsec {
         if_chain! {
             if let ExprKind::Binary(Spanned { node: BinOpKind::Div, .. }, ref left, ref right) = expr.kind;
             if let ExprKind::MethodCall(ref method_path, _ , ref args, _) = left.kind;
-            if match_type(cx, walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0])), &paths::DURATION);
+            if match_type(cx, cx.typeck_results().expr_ty(&args[0]).peel_refs(), &paths::DURATION);
             if let Some((Constant::Int(divisor), _)) = constant(cx, cx.typeck_results(), right);
             then {
                 let suggested_fn = match (method_path.ident.as_str().as_ref(), divisor) {
diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs
index d616502a82a..35a5d00f4aa 100644
--- a/clippy_lints/src/entry.rs
+++ b/clippy_lints/src/entry.rs
@@ -1,6 +1,6 @@
 use crate::utils::SpanlessEq;
 use crate::utils::{get_item_name, higher, is_type_diagnostic_item, match_type, paths, snippet, snippet_opt};
-use crate::utils::{snippet_with_applicability, span_lint_and_then, walk_ptrs_ty};
+use crate::utils::{snippet_with_applicability, span_lint_and_then};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
@@ -106,7 +106,7 @@ fn check_cond<'a>(cx: &LateContext<'_>, check: &'a Expr<'a>) -> Option<(&'static
         if let ExprKind::AddrOf(BorrowKind::Ref, _, ref key) = params[1].kind;
         then {
             let map = &params[0];
-            let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(map));
+            let obj_ty = cx.typeck_results().expr_ty(map).peel_refs();
 
             return if match_type(cx, obj_ty, &paths::BTREEMAP) {
                 Some(("BTreeMap", map, key))
diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs
index 000762334f6..a9e05fddbe7 100644
--- a/clippy_lints/src/fallible_impl_from.rs
+++ b/clippy_lints/src/fallible_impl_from.rs
@@ -1,7 +1,5 @@
 use crate::utils::paths::{BEGIN_PANIC, BEGIN_PANIC_FMT, FROM_TRAIT};
-use crate::utils::{
-    is_expn_of, is_type_diagnostic_item, match_def_path, method_chain_args, span_lint_and_then, walk_ptrs_ty,
-};
+use crate::utils::{is_expn_of, is_type_diagnostic_item, match_def_path, method_chain_args, span_lint_and_then};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
@@ -96,7 +94,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h
 
             // check for `unwrap`
             if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
-                let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0]));
+                let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
                 if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type))
                     || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type))
                 {
diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs
index 8bd85af8768..d6541010bca 100644
--- a/clippy_lints/src/format.rs
+++ b/clippy_lints/src/format.rs
@@ -1,7 +1,7 @@
 use crate::utils::paths;
 use crate::utils::{
     is_expn_of, is_type_diagnostic_item, last_path_segment, match_def_path, match_function_call, snippet,
-    span_lint_and_then, walk_ptrs_ty,
+    span_lint_and_then,
 };
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
@@ -90,7 +90,7 @@ fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: &
         if let PatKind::Tuple(ref pats, None) = arms[0].pat.kind;
         if pats.len() == 1;
         then {
-            let ty = walk_ptrs_ty(cx.typeck_results().pat_ty(&pats[0]));
+            let ty = cx.typeck_results().pat_ty(&pats[0]).peel_refs();
             if *ty.kind() != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym!(string_type)) {
                 return None;
             }
diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs
index a28eda8be15..741195f3b10 100644
--- a/clippy_lints/src/indexing_slicing.rs
+++ b/clippy_lints/src/indexing_slicing.rs
@@ -88,7 +88,7 @@ declare_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING]
 impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if let ExprKind::Index(ref array, ref index) = &expr.kind {
-            let ty = cx.typeck_results().expr_ty(array);
+            let ty = cx.typeck_results().expr_ty(array).peel_refs();
             if let Some(range) = higher::range(index) {
                 // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..]
                 if let ty::Array(_, s) = ty.kind() {
diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs
index f330fa8fab8..0877b44d901 100644
--- a/clippy_lints/src/inherent_to_string.rs
+++ b/clippy_lints/src/inherent_to_string.rs
@@ -5,7 +5,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
 
 use crate::utils::{
     get_trait_def_id, implements_trait, is_type_diagnostic_item, paths, return_ty, span_lint_and_help,
-    trait_ref_of_method, walk_ptrs_ty,
+    trait_ref_of_method,
 };
 
 declare_clippy_lint! {
@@ -125,7 +125,7 @@ fn show_lint(cx: &LateContext<'_>, item: &ImplItem<'_>) {
     // Get the real type of 'self'
     let fn_def_id = cx.tcx.hir().local_def_id(item.hir_id);
     let self_type = cx.tcx.fn_sig(fn_def_id).input(0);
-    let self_type = walk_ptrs_ty(self_type.skip_binder());
+    let self_type = self_type.skip_binder().peel_refs();
 
     // Emit either a warning or an error
     if implements_trait(cx, self_type, display_trait_id, &[]) {
diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs
index 42a98dc963d..c9c4891bb08 100644
--- a/clippy_lints/src/len_zero.rs
+++ b/clippy_lints/src/len_zero.rs
@@ -1,4 +1,4 @@
-use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty};
+use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg};
 use rustc_ast::ast::LitKind;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -285,7 +285,7 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
         })
     }
 
-    let ty = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr));
+    let ty = &cx.typeck_results().expr_ty(expr).peel_refs();
     match ty.kind() {
         ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| {
             cx.tcx
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 2020ef78509..58112ac8da5 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -230,7 +230,9 @@ mod macro_use;
 mod main_recursion;
 mod manual_async_fn;
 mod manual_non_exhaustive;
+mod manual_strip;
 mod map_clone;
+mod map_err_ignore;
 mod map_identity;
 mod map_unit_fn;
 mod match_on_vec_items;
@@ -269,6 +271,7 @@ mod open_options;
 mod option_env_unwrap;
 mod option_if_let_else;
 mod overflow_check_conditional;
+mod panic_in_result_fn;
 mod panic_unimplemented;
 mod partialeq_ne_impl;
 mod path_buf_push_overwrite;
@@ -625,7 +628,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &main_recursion::MAIN_RECURSION,
         &manual_async_fn::MANUAL_ASYNC_FN,
         &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
+        &manual_strip::MANUAL_STRIP,
         &map_clone::MAP_CLONE,
+        &map_err_ignore::MAP_ERR_IGNORE,
         &map_identity::MAP_IDENTITY,
         &map_unit_fn::OPTION_MAP_UNIT_FN,
         &map_unit_fn::RESULT_MAP_UNIT_FN,
@@ -751,6 +756,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &option_env_unwrap::OPTION_ENV_UNWRAP,
         &option_if_let_else::OPTION_IF_LET_ELSE,
         &overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
+        &panic_in_result_fn::PANIC_IN_RESULT_FN,
         &panic_unimplemented::PANIC,
         &panic_unimplemented::PANIC_PARAMS,
         &panic_unimplemented::TODO,
@@ -835,6 +841,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &types::LET_UNIT_VALUE,
         &types::LINKEDLIST,
         &types::OPTION_OPTION,
+        &types::RC_BUFFER,
         &types::REDUNDANT_ALLOCATION,
         &types::TYPE_COMPLEXITY,
         &types::UNIT_ARG,
@@ -863,6 +870,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &utils::internal_lints::COMPILER_LINT_FUNCTIONS,
         &utils::internal_lints::DEFAULT_LINT,
         &utils::internal_lints::LINT_WITHOUT_LINT_PASS,
+        &utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
         &utils::internal_lints::OUTER_EXPN_EXPN_DATA,
         &utils::internal_lints::PRODUCE_ICE,
         &vec::USELESS_VEC,
@@ -918,6 +926,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub);
     store.register_late_pass(|| box methods::Methods);
     store.register_late_pass(|| box map_clone::MapClone);
+    store.register_late_pass(|| box map_err_ignore::MapErrIgnore);
     store.register_late_pass(|| box shadow::Shadow);
     store.register_late_pass(|| box types::LetUnitValue);
     store.register_late_pass(|| box types::UnitCmp);
@@ -1091,6 +1100,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box manual_async_fn::ManualAsyncFn);
     store.register_early_pass(|| box redundant_field_names::RedundantFieldNames);
     store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero);
+    store.register_late_pass(|| box panic_in_result_fn::PanicInResultFn);
+
     let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
     store.register_early_pass(move || box non_expressive_names::NonExpressiveNames {
         single_char_binding_names_threshold,
@@ -1105,6 +1116,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box self_assignment::SelfAssignment);
     store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs);
     store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync);
+    store.register_late_pass(|| box manual_strip::ManualStrip);
+    store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem);
 
     store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
         LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@@ -1135,6 +1148,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS),
         LintId::of(&missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
         LintId::of(&modulo_arithmetic::MODULO_ARITHMETIC),
+        LintId::of(&panic_in_result_fn::PANIC_IN_RESULT_FN),
         LintId::of(&panic_unimplemented::PANIC),
         LintId::of(&panic_unimplemented::TODO),
         LintId::of(&panic_unimplemented::UNIMPLEMENTED),
@@ -1152,6 +1166,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
         LintId::of(&attrs::INLINE_ALWAYS),
         LintId::of(&await_holding_lock::AWAIT_HOLDING_LOCK),
+        LintId::of(&bit_mask::VERBOSE_BIT_MASK),
         LintId::of(&checked_conversions::CHECKED_CONVERSIONS),
         LintId::of(&copies::MATCH_SAME_ARMS),
         LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION),
@@ -1180,6 +1195,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP),
         LintId::of(&loops::EXPLICIT_ITER_LOOP),
         LintId::of(&macro_use::MACRO_USE_IMPORTS),
+        LintId::of(&map_err_ignore::MAP_ERR_IGNORE),
         LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS),
         LintId::of(&matches::MATCH_BOOL),
         LintId::of(&matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
@@ -1230,6 +1246,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS),
         LintId::of(&utils::internal_lints::DEFAULT_LINT),
         LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS),
+        LintId::of(&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM),
         LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA),
         LintId::of(&utils::internal_lints::PRODUCE_ICE),
     ]);
@@ -1249,7 +1266,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&attrs::USELESS_ATTRIBUTE),
         LintId::of(&bit_mask::BAD_BIT_MASK),
         LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK),
-        LintId::of(&bit_mask::VERBOSE_BIT_MASK),
         LintId::of(&blacklisted_name::BLACKLISTED_NAME),
         LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
         LintId::of(&booleans::LOGIC_BUG),
@@ -1330,6 +1346,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&main_recursion::MAIN_RECURSION),
         LintId::of(&manual_async_fn::MANUAL_ASYNC_FN),
         LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
+        LintId::of(&manual_strip::MANUAL_STRIP),
         LintId::of(&map_clone::MAP_CLONE),
         LintId::of(&map_identity::MAP_IDENTITY),
         LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN),
@@ -1474,6 +1491,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&types::CHAR_LIT_AS_U8),
         LintId::of(&types::FN_TO_NUMERIC_CAST),
         LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
+        LintId::of(&types::RC_BUFFER),
         LintId::of(&types::REDUNDANT_ALLOCATION),
         LintId::of(&types::TYPE_COMPLEXITY),
         LintId::of(&types::UNIT_ARG),
@@ -1507,7 +1525,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&assign_ops::ASSIGN_OP_PATTERN),
         LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
         LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS),
-        LintId::of(&bit_mask::VERBOSE_BIT_MASK),
         LintId::of(&blacklisted_name::BLACKLISTED_NAME),
         LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
         LintId::of(&collapsible_if::COLLAPSIBLE_IF),
@@ -1622,6 +1639,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&loops::EXPLICIT_COUNTER_LOOP),
         LintId::of(&loops::MUT_RANGE_BOUND),
         LintId::of(&loops::WHILE_LET_LOOP),
+        LintId::of(&manual_strip::MANUAL_STRIP),
         LintId::of(&map_identity::MAP_IDENTITY),
         LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN),
         LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN),
@@ -1775,6 +1793,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
         LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE),
         LintId::of(&types::BOX_VEC),
+        LintId::of(&types::RC_BUFFER),
         LintId::of(&types::REDUNDANT_ALLOCATION),
         LintId::of(&vec::USELESS_VEC),
     ]);
diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs
index 6c54c07869a..3410341a1e3 100644
--- a/clippy_lints/src/loops.rs
+++ b/clippy_lints/src/loops.rs
@@ -1114,7 +1114,7 @@ fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(&
             if let Some(self_expr) = args.get(0);
             if let Some(pushed_item) = args.get(1);
             // Check that the method being called is push() on a Vec
-            if match_type(cx, cx.typeck_results().expr_ty(self_expr), &paths::VEC);
+            if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym!(vec_type));
             if path.ident.name.as_str() == "push";
             then {
                 return Some((self_expr, pushed_item))
@@ -2601,11 +2601,9 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont
                         span,
                         NEEDLESS_COLLECT_MSG,
                         |diag| {
-                            let (arg, pred) = if contains_arg.starts_with('&') {
-                                ("x", &contains_arg[1..])
-                            } else {
-                                ("&x", &*contains_arg)
-                            };
+                            let (arg, pred) = contains_arg
+                                    .strip_prefix('&')
+                                    .map_or(("&x", &*contains_arg), |s| ("x", s));
                             diag.span_suggestion(
                                 span,
                                 "replace with",
diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs
new file mode 100644
index 00000000000..4afb0ab3bad
--- /dev/null
+++ b/clippy_lints/src/manual_strip.rs
@@ -0,0 +1,245 @@
+use crate::consts::{constant, Constant};
+use crate::utils::usage::mutated_variables;
+use crate::utils::{
+    eq_expr_value, higher, match_def_path, multispan_sugg, paths, qpath_res, snippet, span_lint_and_then,
+};
+
+use if_chain::if_chain;
+use rustc_ast::ast::LitKind;
+use rustc_hir::def::Res;
+use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
+use rustc_hir::BinOpKind;
+use rustc_hir::{BorrowKind, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::hir::map::Map;
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Spanned;
+use rustc_span::Span;
+
+declare_clippy_lint! {
+    /// **What it does:**
+    /// Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using
+    /// the pattern's length.
+    ///
+    /// **Why is this bad?**
+    /// Using `str:strip_{prefix,suffix}` is safer and may have better performance as there is no
+    /// slicing which may panic and the compiler does not need to insert this panic code. It is
+    /// also sometimes more readable as it removes the need for duplicating or storing the pattern
+    /// used by `str::{starts,ends}_with` and in the slicing.
+    ///
+    /// **Known problems:**
+    /// None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// let s = "hello, world!";
+    /// if s.starts_with("hello, ") {
+    ///     assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let s = "hello, world!";
+    /// if let Some(end) = s.strip_prefix("hello, ") {
+    ///     assert_eq!(end.to_uppercase(), "WORLD!");
+    /// }
+    /// ```
+    pub MANUAL_STRIP,
+    complexity,
+    "suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing"
+}
+
+declare_lint_pass!(ManualStrip => [MANUAL_STRIP]);
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+enum StripKind {
+    Prefix,
+    Suffix,
+}
+
+impl<'tcx> LateLintPass<'tcx> for ManualStrip {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if_chain! {
+            if let Some((cond, then, _)) = higher::if_block(&expr);
+            if let ExprKind::MethodCall(_, _, [target_arg, pattern], _) = cond.kind;
+            if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id);
+            if let ExprKind::Path(target_path) = &target_arg.kind;
+            then {
+                let strip_kind = if match_def_path(cx, method_def_id, &paths::STR_STARTS_WITH) {
+                    StripKind::Prefix
+                } else if match_def_path(cx, method_def_id, &paths::STR_ENDS_WITH) {
+                    StripKind::Suffix
+                } else {
+                    return;
+                };
+                let target_res = qpath_res(cx, &target_path, target_arg.hir_id);
+                if target_res == Res::Err {
+                    return;
+                };
+
+                if_chain! {
+                    if let Res::Local(hir_id) = target_res;
+                    if let Some(used_mutably) = mutated_variables(then, cx);
+                    if used_mutably.contains(&hir_id);
+                    then {
+                        return;
+                    }
+                }
+
+                let strippings = find_stripping(cx, strip_kind, target_res, pattern, then);
+                if !strippings.is_empty() {
+
+                    let kind_word = match strip_kind {
+                        StripKind::Prefix => "prefix",
+                        StripKind::Suffix => "suffix",
+                    };
+
+                    let test_span = expr.span.until(then.span);
+                    span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {} manually", kind_word), |diag| {
+                        diag.span_note(test_span, &format!("the {} was tested here", kind_word));
+                        multispan_sugg(
+                            diag,
+                            &format!("try using the `strip_{}` method", kind_word),
+                            vec![(test_span,
+                                  format!("if let Some(<stripped>) = {}.strip_{}({}) ",
+                                          snippet(cx, target_arg.span, ".."),
+                                          kind_word,
+                                          snippet(cx, pattern.span, "..")))]
+                            .into_iter().chain(strippings.into_iter().map(|span| (span, "<stripped>".into()))),
+                        )
+                    });
+                }
+            }
+        }
+    }
+}
+
+// Returns `Some(arg)` if `expr` matches `arg.len()` and `None` otherwise.
+fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+    if_chain! {
+        if let ExprKind::MethodCall(_, _, [arg], _) = expr.kind;
+        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+        if match_def_path(cx, method_def_id, &paths::STR_LEN);
+        then {
+            Some(arg)
+        } else {
+            None
+        }
+    }
+}
+
+// Returns the length of the `expr` if it's a constant string or char.
+fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
+    let (value, _) = constant(cx, cx.typeck_results(), expr)?;
+    match value {
+        Constant::Str(value) => Some(value.len() as u128),
+        Constant::Char(value) => Some(value.len_utf8() as u128),
+        _ => None,
+    }
+}
+
+// Tests if `expr` equals the length of the pattern.
+fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'tcx Expr<'_>) -> bool {
+    if let ExprKind::Lit(Spanned {
+        node: LitKind::Int(n, _),
+        ..
+    }) = expr.kind
+    {
+        constant_length(cx, pattern).map_or(false, |length| length == n)
+    } else {
+        len_arg(cx, expr).map_or(false, |arg| eq_expr_value(cx, pattern, arg))
+    }
+}
+
+// Tests if `expr` is a `&str`.
+fn is_ref_str(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    match cx.typeck_results().expr_ty_adjusted(&expr).kind() {
+        ty::Ref(_, ty, _) => ty.is_str(),
+        _ => false,
+    }
+}
+
+// Removes the outer `AddrOf` expression if needed.
+fn peel_ref<'a>(expr: &'a Expr<'_>) -> &'a Expr<'a> {
+    if let ExprKind::AddrOf(BorrowKind::Ref, _, unref) = &expr.kind {
+        unref
+    } else {
+        expr
+    }
+}
+
+// Find expressions where `target` is stripped using the length of `pattern`.
+// We'll suggest replacing these expressions with the result of the `strip_{prefix,suffix}`
+// method.
+fn find_stripping<'tcx>(
+    cx: &LateContext<'tcx>,
+    strip_kind: StripKind,
+    target: Res,
+    pattern: &'tcx Expr<'_>,
+    expr: &'tcx Expr<'_>,
+) -> Vec<Span> {
+    struct StrippingFinder<'a, 'tcx> {
+        cx: &'a LateContext<'tcx>,
+        strip_kind: StripKind,
+        target: Res,
+        pattern: &'tcx Expr<'tcx>,
+        results: Vec<Span>,
+    }
+
+    impl<'a, 'tcx> Visitor<'tcx> for StrippingFinder<'a, 'tcx> {
+        type Map = Map<'tcx>;
+        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+            NestedVisitorMap::None
+        }
+
+        fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
+            if_chain! {
+                if is_ref_str(self.cx, ex);
+                let unref = peel_ref(ex);
+                if let ExprKind::Index(indexed, index) = &unref.kind;
+                if let Some(range) = higher::range(index);
+                if let higher::Range { start, end, .. } = range;
+                if let ExprKind::Path(path) = &indexed.kind;
+                if qpath_res(self.cx, path, ex.hir_id) == self.target;
+                then {
+                    match (self.strip_kind, start, end) {
+                        (StripKind::Prefix, Some(start), None) => {
+                            if eq_pattern_length(self.cx, self.pattern, start) {
+                                self.results.push(ex.span);
+                                return;
+                            }
+                        },
+                        (StripKind::Suffix, None, Some(end)) => {
+                            if_chain! {
+                                if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, left, right) = end.kind;
+                                if let Some(left_arg) = len_arg(self.cx, left);
+                                if let ExprKind::Path(left_path) = &left_arg.kind;
+                                if qpath_res(self.cx, left_path, left_arg.hir_id) == self.target;
+                                if eq_pattern_length(self.cx, self.pattern, right);
+                                then {
+                                    self.results.push(ex.span);
+                                    return;
+                                }
+                            }
+                        },
+                        _ => {}
+                    }
+                }
+            }
+
+            walk_expr(self, ex);
+        }
+    }
+
+    let mut finder = StrippingFinder {
+        cx,
+        strip_kind,
+        target,
+        pattern,
+        results: vec![],
+    };
+    walk_expr(&mut finder, expr);
+    finder.results
+}
diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs
new file mode 100644
index 00000000000..5298e16a04d
--- /dev/null
+++ b/clippy_lints/src/map_err_ignore.rs
@@ -0,0 +1,147 @@
+use crate::utils::span_lint_and_help;
+
+use rustc_hir::{CaptureBy, Expr, ExprKind, PatKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for instances of `map_err(|_| Some::Enum)`
+    ///
+    /// **Why is this bad?** This map_err throws away the original error rather than allowing the enum to contain and report the cause of the error
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// Before:
+    /// ```rust
+    /// use std::fmt;
+    ///
+    /// #[derive(Debug)]
+    /// enum Error {
+    ///     Indivisible,
+    ///     Remainder(u8),
+    /// }
+    ///
+    /// impl fmt::Display for Error {
+    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+    ///         match self {
+    ///             Error::Indivisible => write!(f, "could not divide input by three"),
+    ///             Error::Remainder(remainder) => write!(
+    ///                 f,
+    ///                 "input is not divisible by three, remainder = {}",
+    ///                 remainder
+    ///             ),
+    ///         }
+    ///     }
+    /// }
+    ///
+    /// impl std::error::Error for Error {}
+    ///
+    /// fn divisible_by_3(input: &str) -> Result<(), Error> {
+    ///     input
+    ///         .parse::<i32>()
+    ///         .map_err(|_| Error::Indivisible)
+    ///         .map(|v| v % 3)
+    ///         .and_then(|remainder| {
+    ///             if remainder == 0 {
+    ///                 Ok(())
+    ///             } else {
+    ///                 Err(Error::Remainder(remainder as u8))
+    ///             }
+    ///         })
+    /// }
+    ///  ```
+    ///
+    ///  After:
+    ///  ```rust
+    /// use std::{fmt, num::ParseIntError};
+    ///
+    /// #[derive(Debug)]
+    /// enum Error {
+    ///     Indivisible(ParseIntError),
+    ///     Remainder(u8),
+    /// }
+    ///
+    /// impl fmt::Display for Error {
+    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+    ///         match self {
+    ///             Error::Indivisible(_) => write!(f, "could not divide input by three"),
+    ///             Error::Remainder(remainder) => write!(
+    ///                 f,
+    ///                 "input is not divisible by three, remainder = {}",
+    ///                 remainder
+    ///             ),
+    ///         }
+    ///     }
+    /// }
+    ///
+    /// impl std::error::Error for Error {
+    ///     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+    ///         match self {
+    ///             Error::Indivisible(source) => Some(source),
+    ///             _ => None,
+    ///         }
+    ///     }
+    /// }
+    ///
+    /// fn divisible_by_3(input: &str) -> Result<(), Error> {
+    ///     input
+    ///         .parse::<i32>()
+    ///         .map_err(Error::Indivisible)
+    ///         .map(|v| v % 3)
+    ///         .and_then(|remainder| {
+    ///             if remainder == 0 {
+    ///                 Ok(())
+    ///             } else {
+    ///                 Err(Error::Remainder(remainder as u8))
+    ///             }
+    ///         })
+    /// }
+    /// ```
+    pub MAP_ERR_IGNORE,
+    pedantic,
+    "`map_err` should not ignore the original error"
+}
+
+declare_lint_pass!(MapErrIgnore => [MAP_ERR_IGNORE]);
+
+impl<'tcx> LateLintPass<'tcx> for MapErrIgnore {
+    // do not try to lint if this is from a macro or desugaring
+    fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) {
+        if e.span.from_expansion() {
+            return;
+        }
+
+        // check if this is a method call (e.g. x.foo())
+        if let ExprKind::MethodCall(ref method, _t_span, ref args, _) = e.kind {
+            // only work if the method name is `map_err` and there are only 2 arguments (e.g. x.map_err(|_|[1]
+            // Enum::Variant[2]))
+            if method.ident.as_str() == "map_err" && args.len() == 2 {
+                // make sure the first argument is a closure, and grab the CaptureRef, body_id, and body_span fields
+                if let ExprKind::Closure(capture, _, body_id, body_span, _) = args[1].kind {
+                    // check if this is by Reference (meaning there's no move statement)
+                    if capture == CaptureBy::Ref {
+                        // Get the closure body to check the parameters and values
+                        let closure_body = cx.tcx.hir().body(body_id);
+                        // make sure there's only one parameter (`|_|`)
+                        if closure_body.params.len() == 1 {
+                            // make sure that parameter is the wild token (`_`)
+                            if let PatKind::Wild = closure_body.params[0].pat.kind {
+                                // span the area of the closure capture and warn that the
+                                // original error will be thrown away
+                                span_lint_and_help(
+                                    cx,
+                                    MAP_ERR_IGNORE,
+                                    body_span,
+                                    "`map_err(|_|...` ignores the original error",
+                                    None,
+                                    "Consider wrapping the error in an enum variant",
+                                );
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/clippy_lints/src/match_on_vec_items.rs b/clippy_lints/src/match_on_vec_items.rs
index 57966452253..331b6c6c34a 100644
--- a/clippy_lints/src/match_on_vec_items.rs
+++ b/clippy_lints/src/match_on_vec_items.rs
@@ -1,4 +1,3 @@
-use crate::utils::walk_ptrs_ty;
 use crate::utils::{is_type_diagnostic_item, is_type_lang_item, snippet, span_lint_and_sugg};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -90,12 +89,12 @@ fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opti
 
 fn is_vector(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     let ty = cx.typeck_results().expr_ty(expr);
-    let ty = walk_ptrs_ty(ty);
+    let ty = ty.peel_refs();
     is_type_diagnostic_item(cx, ty, sym!(vec_type))
 }
 
 fn is_full_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     let ty = cx.typeck_results().expr_ty(expr);
-    let ty = walk_ptrs_ty(ty);
+    let ty = ty.peel_refs();
     is_type_lang_item(cx, ty, LangItem::RangeFull)
 }
diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs
index 819846ebc79..b1a4e06d4c3 100644
--- a/clippy_lints/src/matches.rs
+++ b/clippy_lints/src/matches.rs
@@ -6,7 +6,7 @@ use crate::utils::{
     expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable,
     is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, multispan_sugg, remove_blocks, snippet,
     snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
-    span_lint_and_then, walk_ptrs_ty,
+    span_lint_and_then,
 };
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
@@ -502,7 +502,7 @@ impl_lint_pass!(Matches => [
 
 impl<'tcx> LateLintPass<'tcx> for Matches {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if in_external_macro(cx.sess(), expr.span) {
+        if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) {
             return;
         }
 
@@ -794,7 +794,7 @@ fn check_overlapping_arms<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms
 }
 
 fn check_wild_err_arm(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
-    let ex_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(ex));
+    let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs();
     if is_type_diagnostic_item(cx, ex_ty, sym!(result_type)) {
         for arm in arms {
             if let PatKind::TupleStruct(ref path, ref inner, _) = arm.pat.kind {
diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs
index 498f12518f8..ae37942e55a 100644
--- a/clippy_lints/src/methods/bind_instead_of_map.rs
+++ b/clippy_lints/src/methods/bind_instead_of_map.rs
@@ -12,6 +12,7 @@ use rustc_middle::hir::map::Map;
 use rustc_span::Span;
 
 pub(crate) struct OptionAndThenSome;
+
 impl BindInsteadOfMap for OptionAndThenSome {
     const TYPE_NAME: &'static str = "Option";
     const TYPE_QPATH: &'static [&'static str] = &paths::OPTION;
@@ -24,6 +25,7 @@ impl BindInsteadOfMap for OptionAndThenSome {
 }
 
 pub(crate) struct ResultAndThenOk;
+
 impl BindInsteadOfMap for ResultAndThenOk {
     const TYPE_NAME: &'static str = "Result";
     const TYPE_QPATH: &'static [&'static str] = &paths::RESULT;
@@ -36,6 +38,7 @@ impl BindInsteadOfMap for ResultAndThenOk {
 }
 
 pub(crate) struct ResultOrElseErrInfo;
+
 impl BindInsteadOfMap for ResultOrElseErrInfo {
     const TYPE_NAME: &'static str = "Result";
     const TYPE_QPATH: &'static [&'static str] = &paths::RESULT;
@@ -120,9 +123,9 @@ pub(crate) trait BindInsteadOfMap {
         }
     }
 
-    fn lint_closure(cx: &LateContext<'_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) {
+    fn lint_closure(cx: &LateContext<'_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) -> bool {
         let mut suggs = Vec::new();
-        let can_sugg = find_all_ret_expressions(cx, closure_expr, |ret_expr| {
+        let can_sugg: bool = find_all_ret_expressions(cx, closure_expr, |ret_expr| {
             if_chain! {
                 if !in_macro(ret_expr.span);
                 if let hir::ExprKind::Call(ref func_path, ref args) = ret_expr.kind;
@@ -153,12 +156,13 @@ pub(crate) trait BindInsteadOfMap {
                 )
             });
         }
+        can_sugg
     }
 
     /// Lint use of `_.and_then(|x| Some(y))` for `Option`s
-    fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
+    fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) -> bool {
         if !match_type(cx, cx.typeck_results().expr_ty(&args[0]), Self::TYPE_QPATH) {
-            return;
+            return false;
         }
 
         match args[1].kind {
@@ -166,8 +170,10 @@ pub(crate) trait BindInsteadOfMap {
                 let closure_body = cx.tcx.hir().body(body_id);
                 let closure_expr = remove_blocks(&closure_body.value);
 
-                if !Self::lint_closure_autofixable(cx, expr, args, closure_expr, closure_args_span) {
-                    Self::lint_closure(cx, expr, closure_expr);
+                if Self::lint_closure_autofixable(cx, expr, args, closure_expr, closure_args_span) {
+                    true
+                } else {
+                    Self::lint_closure(cx, expr, closure_expr)
                 }
             },
             // `_.and_then(Some)` case, which is no-op.
@@ -181,8 +187,9 @@ pub(crate) trait BindInsteadOfMap {
                     snippet(cx, args[0].span, "..").into(),
                     Applicability::MachineApplicable,
                 );
+                true
             },
-            _ => {},
+            _ => false,
         }
     }
 }
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index ba69c8266b1..dadd0f8ebb7 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -25,14 +25,15 @@ use rustc_span::source_map::Span;
 use rustc_span::symbol::{sym, SymbolStr};
 
 use crate::consts::{constant, Constant};
+use crate::utils::eager_or_lazy::is_lazyness_candidate;
 use crate::utils::usage::mutated_variables;
 use crate::utils::{
     contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro,
-    is_copy, is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats,
-    last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls,
-    method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability,
-    snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
-    span_lint_and_then, sugg, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq,
+    is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath,
+    match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty,
+    single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint,
+    span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth,
+    SpanlessEq,
 };
 
 declare_clippy_lint! {
@@ -1454,18 +1455,21 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
             ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]),
             ["unwrap_or_else", "map"] => {
                 if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]) {
-                    unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or");
+                    unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or");
                 }
             },
             ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]),
             ["and_then", ..] => {
-                unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "and");
-                bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]);
-                bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]);
+                let biom_option_linted = bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]);
+                let biom_result_linted = bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]);
+                if !biom_option_linted && !biom_result_linted {
+                    unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "and");
+                }
             },
             ["or_else", ..] => {
-                unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "or");
-                bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]);
+                if !bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]) {
+                    unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "or");
+                }
             },
             ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]),
             ["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]),
@@ -1508,9 +1512,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
             ["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]),
             ["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false),
             ["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true),
-            ["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or"),
-            ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "get_or_insert"),
-            ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "ok_or"),
+            ["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"),
+            ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "get_or_insert"),
+            ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"),
             _ => {},
         }
 
@@ -1714,37 +1718,6 @@ fn lint_or_fun_call<'tcx>(
     name: &str,
     args: &'tcx [hir::Expr<'_>],
 ) {
-    // Searches an expression for method calls or function calls that aren't ctors
-    struct FunCallFinder<'a, 'tcx> {
-        cx: &'a LateContext<'tcx>,
-        found: bool,
-    }
-
-    impl<'a, 'tcx> intravisit::Visitor<'tcx> for FunCallFinder<'a, 'tcx> {
-        type Map = Map<'tcx>;
-
-        fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
-            let call_found = match &expr.kind {
-                // ignore enum and struct constructors
-                hir::ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr),
-                hir::ExprKind::MethodCall(..) => true,
-                _ => false,
-            };
-
-            if call_found {
-                self.found |= true;
-            }
-
-            if !self.found {
-                intravisit::walk_expr(self, expr);
-            }
-        }
-
-        fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
-            intravisit::NestedVisitorMap::None
-        }
-    }
-
     /// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`.
     fn check_unwrap_or_default(
         cx: &LateContext<'_>,
@@ -1801,14 +1774,14 @@ fn lint_or_fun_call<'tcx>(
     ) {
         if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = &arg.kind {
             if path.ident.as_str() == "len" {
-                let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0]));
+                let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
 
                 match ty.kind() {
                     ty::Slice(_) | ty::Array(_, _) => return,
                     _ => (),
                 }
 
-                if match_type(cx, ty, &paths::VEC) {
+                if is_type_diagnostic_item(cx, ty, sym!(vec_type)) {
                     return;
                 }
             }
@@ -1825,8 +1798,7 @@ fn lint_or_fun_call<'tcx>(
         if_chain! {
             if know_types.iter().any(|k| k.2.contains(&name));
 
-            let mut finder = FunCallFinder { cx: &cx, found: false };
-            if { finder.visit_expr(&arg); finder.found };
+            if is_lazyness_candidate(cx, arg);
             if !contains_return(&arg);
 
             let self_ty = cx.typeck_results().expr_ty(self_expr);
@@ -1909,7 +1881,7 @@ fn lint_expect_fun_call(
                         && (method_name.ident.name == sym!(as_str) || method_name.ident.name == sym!(as_ref))
                         && {
                             let arg_type = cx.typeck_results().expr_ty(&call_args[0]);
-                            let base_type = walk_ptrs_ty(arg_type);
+                            let base_type = arg_type.peel_refs();
                             *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym!(string_type))
                         }
                     {
@@ -2170,7 +2142,7 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp
 }
 
 fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
-    let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(arg));
+    let obj_ty = cx.typeck_results().expr_ty(arg).peel_refs();
 
     if let ty::Adt(_, subst) = obj_ty.kind() {
         let caller_type = if is_type_diagnostic_item(cx, obj_ty, sym::Rc) {
@@ -2201,7 +2173,7 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E
     let arg = &args[1];
     if let Some(arglists) = method_chain_args(arg, &["chars"]) {
         let target = &arglists[0][0];
-        let self_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(target));
+        let self_ty = cx.typeck_results().expr_ty(target).peel_refs();
         let ref_str = if *self_ty.kind() == ty::Str {
             ""
         } else if is_type_diagnostic_item(cx, self_ty, sym!(string_type)) {
@@ -2229,7 +2201,7 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E
 }
 
 fn lint_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
-    let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0]));
+    let obj_ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
     if is_type_diagnostic_item(cx, obj_ty, sym!(string_type)) {
         lint_string_extend(cx, expr, args);
     }
@@ -2412,7 +2384,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_
         }
     } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(caller_expr), sym!(vec_type))
         || matches!(
-            &walk_ptrs_ty(cx.typeck_results().expr_ty(caller_expr)).kind(),
+            &cx.typeck_results().expr_ty(caller_expr).peel_refs().kind(),
             ty::Array(_, _)
         )
     {
@@ -2615,7 +2587,7 @@ fn derefs_to_slice<'tcx>(
 
 /// lint use of `unwrap()` for `Option`s and `Result`s
 fn lint_unwrap(cx: &LateContext<'_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::Expr<'_>]) {
-    let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&unwrap_args[0]));
+    let obj_ty = cx.typeck_results().expr_ty(&unwrap_args[0]).peel_refs();
 
     let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) {
         Some((UNWRAP_USED, "an Option", "None"))
@@ -2643,7 +2615,7 @@ fn lint_unwrap(cx: &LateContext<'_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::E
 
 /// lint use of `expect()` for `Option`s and `Result`s
 fn lint_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expect_args: &[hir::Expr<'_>]) {
-    let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&expect_args[0]));
+    let obj_ty = cx.typeck_results().expr_ty(&expect_args[0]).peel_refs();
 
     let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) {
         Some((EXPECT_USED, "an Option", "None"))
@@ -3162,7 +3134,7 @@ fn lint_chars_cmp(
         if segment.ident.name == sym!(Some);
         then {
             let mut applicability = Applicability::MachineApplicable;
-            let self_ty = walk_ptrs_ty(cx.typeck_results().expr_ty_adjusted(&args[0][0]));
+            let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs();
 
             if *self_ty.kind() != ty::Str {
                 return false;
@@ -3374,7 +3346,7 @@ fn lint_into_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, self_ref_ty: Ty<'_
             INTO_ITER_ON_REF,
             method_span,
             &format!(
-                "this `.into_iter()` call is equivalent to `.{}()` and will not move the `{}`",
+                "this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`",
                 method_name, kind,
             ),
             "call directly",
diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs
index 31517659c34..08b3eab9b7c 100644
--- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs
+++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs
@@ -1,78 +1,17 @@
-use crate::utils::{is_type_diagnostic_item, match_qpath, snippet, span_lint_and_sugg};
-use if_chain::if_chain;
+use crate::utils::{eager_or_lazy, usage};
+use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_sugg};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 
 use super::UNNECESSARY_LAZY_EVALUATIONS;
 
-// Return true if the expression is an accessor of any of the arguments
-fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool {
-    params.iter().any(|arg| {
-        if_chain! {
-            if let hir::PatKind::Binding(_, _, ident, _) = arg.pat.kind;
-            if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind;
-            if let [p, ..] = path.segments;
-            then {
-                ident.name == p.ident.name
-            } else {
-                false
-            }
-        }
-    })
-}
-
-fn match_any_qpath(path: &hir::QPath<'_>, paths: &[&[&str]]) -> bool {
-    paths.iter().any(|candidate| match_qpath(path, candidate))
-}
-
-fn can_simplify(expr: &hir::Expr<'_>, params: &[hir::Param<'_>], variant_calls: bool) -> bool {
-    match expr.kind {
-        // Closures returning literals can be unconditionally simplified
-        hir::ExprKind::Lit(_) => true,
-
-        hir::ExprKind::Index(ref object, ref index) => {
-            // arguments are not being indexed into
-            if expr_uses_argument(object, params) {
-                false
-            } else {
-                // arguments are not used as index
-                !expr_uses_argument(index, params)
-            }
-        },
-
-        // Reading fields can be simplified if the object is not an argument of the closure
-        hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params),
-
-        // Paths can be simplified if the root is not the argument, this also covers None
-        hir::ExprKind::Path(_) => !expr_uses_argument(expr, params),
-
-        // Calls to Some, Ok, Err can be considered literals if they don't derive an argument
-        hir::ExprKind::Call(ref func, ref args) => if_chain! {
-            if variant_calls; // Disable lint when rules conflict with bind_instead_of_map
-            if let hir::ExprKind::Path(ref path) = func.kind;
-            if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]);
-            then {
-                // Recursively check all arguments
-                args.iter().all(|arg| can_simplify(arg, params, variant_calls))
-            } else {
-                false
-            }
-        },
-
-        // For anything more complex than the above, a closure is probably the right solution,
-        // or the case is handled by an other lint
-        _ => false,
-    }
-}
-
 /// lint use of `<fn>_else(simple closure)` for `Option`s and `Result`s that can be
 /// replaced with `<fn>(return value of simple closure)`
 pub(super) fn lint<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &'tcx hir::Expr<'_>,
     args: &'tcx [hir::Expr<'_>],
-    allow_variant_calls: bool,
     simplify_using: &str,
 ) {
     let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type));
@@ -81,10 +20,13 @@ pub(super) fn lint<'tcx>(
     if is_option || is_result {
         if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind {
             let body = cx.tcx.hir().body(eid);
-            let ex = &body.value;
-            let params = &body.params;
+            let body_expr = &body.value;
+
+            if usage::BindingUsageFinder::are_params_used(cx, body) {
+                return;
+            }
 
-            if can_simplify(ex, params, allow_variant_calls) {
+            if eager_or_lazy::is_eagerness_candidate(cx, body_expr) {
                 let msg = if is_option {
                     "unnecessary closure used to substitute value for `Option::None`"
                 } else {
@@ -101,7 +43,7 @@ pub(super) fn lint<'tcx>(
                         "{0}.{1}({2})",
                         snippet(cx, args[0].span, ".."),
                         simplify_using,
-                        snippet(cx, ex.span, ".."),
+                        snippet(cx, body_expr.span, ".."),
                     ),
                     Applicability::MachineApplicable,
                 );
diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs
index d4a50dd9013..909e79f661a 100644
--- a/clippy_lints/src/misc.rs
+++ b/clippy_lints/src/misc.rs
@@ -17,7 +17,7 @@ use crate::utils::sugg::Sugg;
 use crate::utils::{
     get_item_name, get_parent_expr, higher, implements_trait, in_constant, is_integer_const, iter_input_pats,
     last_path_segment, match_qpath, match_trait_method, paths, snippet, snippet_opt, span_lint, span_lint_and_sugg,
-    span_lint_and_then, span_lint_hir_and_then, walk_ptrs_ty, SpanlessEq,
+    span_lint_and_then, span_lint_hir_and_then, SpanlessEq,
 };
 
 declare_clippy_lint! {
@@ -99,11 +99,11 @@ declare_clippy_lint! {
     /// if y != x {} // where both are floats
     ///
     /// // Good
-    /// let error = f64::EPSILON; // Use an epsilon for comparison
+    /// let error_margin = f64::EPSILON; // Use an epsilon for comparison
     /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
-    /// // let error = std::f64::EPSILON;
-    /// if (y - 1.23f64).abs() < error { }
-    /// if (y - x).abs() > error { }
+    /// // let error_margin = std::f64::EPSILON;
+    /// if (y - 1.23f64).abs() < error_margin { }
+    /// if (y - x).abs() > error_margin { }
     /// ```
     pub FLOAT_CMP,
     correctness,
@@ -242,10 +242,10 @@ declare_clippy_lint! {
     /// if x == ONE { } // where both are floats
     ///
     /// // Good
-    /// let error = f64::EPSILON; // Use an epsilon for comparison
+    /// let error_margin = f64::EPSILON; // Use an epsilon for comparison
     /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
-    /// // let error = std::f64::EPSILON;
-    /// if (x - ONE).abs() < error { }
+    /// // let error_margin = std::f64::EPSILON;
+    /// if (x - ONE).abs() < error_margin { }
     /// ```
     pub FLOAT_CMP_CONST,
     restriction,
@@ -411,16 +411,16 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
                         if !is_comparing_arrays {
                             diag.span_suggestion(
                                 expr.span,
-                                "consider comparing them within some error",
+                                "consider comparing them within some margin of error",
                                 format!(
-                                    "({}).abs() {} error",
+                                    "({}).abs() {} error_margin",
                                     lhs - rhs,
                                     if op == BinOpKind::Eq { '<' } else { '>' }
                                 ),
                                 Applicability::HasPlaceholders, // snippet
                             );
                         }
-                        diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error`");
+                        diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`");
                     });
                 } else if op == BinOpKind::Rem && is_integer_const(cx, right, 1) {
                     span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
@@ -561,7 +561,7 @@ fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 }
 
 fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    let value = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr)).kind();
+    let value = &cx.typeck_results().expr_ty(expr).peel_refs().kind();
 
     if let ty::Array(arr_ty, _) = value {
         return matches!(arr_ty.kind(), ty::Float(_));
@@ -571,7 +571,7 @@ fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 }
 
 fn is_array(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    matches!(&walk_ptrs_ty(cx.typeck_results().expr_ty(expr)).kind(), ty::Array(_, _))
+    matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _))
 }
 
 fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) {
diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs
index 02789735c17..9cb1cfb915d 100644
--- a/clippy_lints/src/misc_early.rs
+++ b/clippy_lints/src/misc_early.rs
@@ -377,8 +377,8 @@ impl EarlyLintPass for MiscEarlyLints {
             if let PatKind::Ident(_, ident, None) = arg.pat.kind {
                 let arg_name = ident.to_string();
 
-                if arg_name.starts_with('_') {
-                    if let Some(correspondence) = registered_names.get(&arg_name[1..]) {
+                if let Some(arg_name) = arg_name.strip_prefix('_') {
+                    if let Some(correspondence) = registered_names.get(arg_name) {
                         span_lint(
                             cx,
                             DUPLICATE_UNDERSCORE_ARGUMENT,
@@ -386,7 +386,7 @@ impl EarlyLintPass for MiscEarlyLints {
                             &format!(
                                 "`{}` already exists, having another argument having almost the same \
                                  name makes code comprehension and documentation more difficult",
-                                arg_name[1..].to_owned()
+                                arg_name
                             ),
                         );
                     }
diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs
index 7423107e8f9..8a2dbdc50ea 100644
--- a/clippy_lints/src/mut_key.rs
+++ b/clippy_lints/src/mut_key.rs
@@ -1,4 +1,4 @@
-use crate::utils::{match_def_path, paths, span_lint, trait_ref_of_method, walk_ptrs_ty};
+use crate::utils::{match_def_path, paths, span_lint, trait_ref_of_method};
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{Adt, Array, RawPtr, Ref, Slice, Tuple, Ty, TypeAndMut};
@@ -12,8 +12,10 @@ declare_clippy_lint! {
     /// `BtreeSet` rely on either the hash or the order of keys be unchanging,
     /// so having types with interior mutability is a bad idea.
     ///
-    /// **Known problems:** We don't currently account for `Rc` or `Arc`, so
-    /// this may yield false positives.
+    /// **Known problems:** It's correct to use a struct, that contains interior mutability
+    /// as a key, when its `Hash` implementation doesn't access any of the interior mutable types.
+    /// However, this lint is unable to recognize this, so it causes a false positive in theses cases.
+    /// The `bytes` crate is a great example of this.
     ///
     /// **Example:**
     /// ```rust
@@ -96,7 +98,7 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, item_hir_id: hir::HirId, decl: &hir::
 // We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased
 // generics (because the compiler cannot ensure immutability for unknown types).
 fn check_ty<'tcx>(cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
-    let ty = walk_ptrs_ty(ty);
+    let ty = ty.peel_refs();
     if let Adt(def, substs) = ty.kind() {
         if [&paths::HASHMAP, &paths::BTREEMAP, &paths::HASHSET, &paths::BTREESET]
             .iter()
diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs
index 73eabd4207e..bb44eeb6adc 100644
--- a/clippy_lints/src/non_copy_const.rs
+++ b/clippy_lints/src/non_copy_const.rs
@@ -6,14 +6,16 @@ use std::ptr;
 
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp};
+use rustc_infer::traits::specialization_graph;
 use rustc_lint::{LateContext, LateLintPass, Lint};
 use rustc_middle::ty::adjustment::Adjust;
-use rustc_middle::ty::{Ty, TypeFlags};
+use rustc_middle::ty::{AssocKind, Ty};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::{InnerSpan, Span, DUMMY_SP};
 use rustc_typeck::hir_ty_to_ty;
 
-use crate::utils::{in_constant, is_copy, qpath_res, span_lint_and_then};
+use crate::utils::{in_constant, qpath_res, span_lint_and_then};
+use if_chain::if_chain;
 
 declare_clippy_lint! {
     /// **What it does:** Checks for declaration of `const` items which is interior
@@ -83,11 +85,10 @@ declare_clippy_lint! {
     "referencing `const` with interior mutability"
 }
 
-#[allow(dead_code)]
 #[derive(Copy, Clone)]
 enum Source {
     Item { item: Span },
-    Assoc { item: Span, ty: Span },
+    Assoc { item: Span },
     Expr { expr: Span },
 }
 
@@ -110,10 +111,15 @@ impl Source {
 }
 
 fn verify_ty_bound<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, source: Source) {
-    if ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env) || is_copy(cx, ty) {
-        // An `UnsafeCell` is `!Copy`, and an `UnsafeCell` is also the only type which
-        // is `!Freeze`, thus if our type is `Copy` we can be sure it must be `Freeze`
-        // as well.
+    // Ignore types whose layout is unknown since `is_freeze` reports every generic types as `!Freeze`,
+    // making it indistinguishable from `UnsafeCell`. i.e. it isn't a tool to prove a type is
+    // 'unfrozen'. However, this code causes a false negative in which
+    // a type contains a layout-unknown type, but also a unsafe cell like `const CELL: Cell<T>`.
+    // Yet, it's better than `ty.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_PROJECTION)`
+    // since it works when a pointer indirection involves (`Cell<*const T>`).
+    // Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option;
+    // but I'm not sure whether it's a decent way, if possible.
+    if cx.tcx.layout_of(cx.param_env.and(ty)).is_err() || ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env) {
         return;
     }
 
@@ -127,11 +133,7 @@ fn verify_ty_bound<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, source: Source) {
                 let const_kw_span = span.from_inner(InnerSpan::new(0, 5));
                 diag.span_label(const_kw_span, "make this a static item (maybe with lazy_static)");
             },
-            Source::Assoc { ty: ty_span, .. } => {
-                if ty.flags().intersects(TypeFlags::HAS_FREE_LOCAL_NAMES) {
-                    diag.span_label(ty_span, &format!("consider requiring `{}` to be `Copy`", ty));
-                }
-            },
+            Source::Assoc { .. } => (),
             Source::Expr { .. } => {
                 diag.help("assign this const to a local or static variable, and use the variable here");
             },
@@ -152,14 +154,10 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
     fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'_>) {
         if let TraitItemKind::Const(hir_ty, ..) = &trait_item.kind {
             let ty = hir_ty_to_ty(cx.tcx, hir_ty);
-            verify_ty_bound(
-                cx,
-                ty,
-                Source::Assoc {
-                    ty: hir_ty.span,
-                    item: trait_item.span,
-                },
-            );
+            // Normalize assoc types because ones originated from generic params
+            // bounded other traits could have their bound.
+            let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
+            verify_ty_bound(cx, normalized, Source::Assoc { item: trait_item.span });
         }
     }
 
@@ -167,17 +165,50 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
         if let ImplItemKind::Const(hir_ty, ..) = &impl_item.kind {
             let item_hir_id = cx.tcx.hir().get_parent_node(impl_item.hir_id);
             let item = cx.tcx.hir().expect_item(item_hir_id);
-            // Ensure the impl is an inherent impl.
-            if let ItemKind::Impl { of_trait: None, .. } = item.kind {
-                let ty = hir_ty_to_ty(cx.tcx, hir_ty);
-                verify_ty_bound(
-                    cx,
-                    ty,
-                    Source::Assoc {
-                        ty: hir_ty.span,
-                        item: impl_item.span,
-                    },
-                );
+
+            match &item.kind {
+                ItemKind::Impl {
+                    of_trait: Some(of_trait_ref),
+                    ..
+                } => {
+                    if_chain! {
+                        // Lint a trait impl item only when the definition is a generic type,
+                        // assuming a assoc const is not meant to be a interior mutable type.
+                        if let Some(of_trait_def_id) = of_trait_ref.trait_def_id();
+                        if let Some(of_assoc_item) = specialization_graph::Node::Trait(of_trait_def_id)
+                            .item(cx.tcx, impl_item.ident, AssocKind::Const, of_trait_def_id);
+                        if cx
+                            .tcx
+                            .layout_of(cx.tcx.param_env(of_trait_def_id).and(
+                                // Normalize assoc types because ones originated from generic params
+                                // bounded other traits could have their bound at the trait defs;
+                                // and, in that case, the definition is *not* generic.
+                                cx.tcx.normalize_erasing_regions(
+                                    cx.tcx.param_env(of_trait_def_id),
+                                    cx.tcx.type_of(of_assoc_item.def_id),
+                                ),
+                            ))
+                            .is_err();
+                        then {
+                            let ty = hir_ty_to_ty(cx.tcx, hir_ty);
+                            let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
+                            verify_ty_bound(
+                                cx,
+                                normalized,
+                                Source::Assoc {
+                                    item: impl_item.span,
+                                },
+                            );
+                        }
+                    }
+                },
+                ItemKind::Impl { of_trait: None, .. } => {
+                    let ty = hir_ty_to_ty(cx.tcx, hir_ty);
+                    // Normalize assoc types originated from generic params.
+                    let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
+                    verify_ty_bound(cx, normalized, Source::Assoc { item: impl_item.span });
+                },
+                _ => (),
             }
         }
     }
diff --git a/clippy_lints/src/open_options.rs b/clippy_lints/src/open_options.rs
index e99d0317ba2..73a99a3a2f8 100644
--- a/clippy_lints/src/open_options.rs
+++ b/clippy_lints/src/open_options.rs
@@ -1,4 +1,4 @@
-use crate::utils::{match_type, paths, span_lint, walk_ptrs_ty};
+use crate::utils::{match_type, paths, span_lint};
 use rustc_ast::ast::LitKind;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -30,7 +30,7 @@ declare_lint_pass!(OpenOptions => [NONSENSICAL_OPEN_OPTIONS]);
 impl<'tcx> LateLintPass<'tcx> for OpenOptions {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
         if let ExprKind::MethodCall(ref path, _, ref arguments, _) = e.kind {
-            let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&arguments[0]));
+            let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs();
             if path.ident.name == sym!(open) && match_type(cx, obj_ty, &paths::OPEN_OPTIONS) {
                 let mut options = Vec::new();
                 get_open_options(cx, &arguments[0], &mut options);
@@ -58,7 +58,7 @@ enum OpenOption {
 
 fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec<(OpenOption, Argument)>) {
     if let ExprKind::MethodCall(ref path, _, ref arguments, _) = argument.kind {
-        let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&arguments[0]));
+        let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs();
 
         // Only proceed if this is a call on some object of type std::fs::OpenOptions
         if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 2 {
diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs
index 9494efe736c..4a3eb9c983a 100644
--- a/clippy_lints/src/option_if_let_else.rs
+++ b/clippy_lints/src/option_if_let_else.rs
@@ -1,6 +1,7 @@
 use crate::utils;
+use crate::utils::eager_or_lazy;
 use crate::utils::sugg::Sugg;
-use crate::utils::{match_type, paths, span_lint_and_sugg};
+use crate::utils::{is_type_diagnostic_item, paths, span_lint_and_sugg};
 use if_chain::if_chain;
 
 use rustc_errors::Applicability;
@@ -13,22 +14,16 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
 declare_clippy_lint! {
     /// **What it does:**
     /// Lints usage of  `if let Some(v) = ... { y } else { x }` which is more
-    /// idiomatically done with `Option::map_or` (if the else bit is a simple
-    /// expression) or `Option::map_or_else` (if the else bit is a longer
-    /// block).
+    /// idiomatically done with `Option::map_or` (if the else bit is a pure
+    /// expression) or `Option::map_or_else` (if the else bit is an impure
+    /// expresion).
     ///
     /// **Why is this bad?**
     /// Using the dedicated functions of the Option type is clearer and
     /// more concise than an if let expression.
     ///
     /// **Known problems:**
-    /// This lint uses whether the block is just an expression or if it has
-    /// more statements to decide whether to use `Option::map_or` or
-    /// `Option::map_or_else`. If you have a single expression which calls
-    /// an expensive function, then it would be more efficient to use
-    /// `Option::map_or_else`, but this lint would suggest `Option::map_or`.
-    ///
-    /// Also, this lint uses a deliberately conservative metric for checking
+    /// This lint uses a deliberately conservative metric for checking
     /// if the inside of either body contains breaks or continues which will
     /// cause it to not suggest a fix if either block contains a loop with
     /// continues or breaks contained within the loop.
@@ -73,7 +68,7 @@ declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]);
 fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
     if let ExprKind::MethodCall(ref path, _, &[ref receiver], _) = &expr.kind {
         path.ident.name.to_ident_string() == "ok"
-            && match_type(cx, &cx.typeck_results().expr_ty(&receiver), &paths::RESULT)
+            && is_type_diagnostic_item(cx, &cx.typeck_results().expr_ty(&receiver), sym!(result_type))
     } else {
         false
     }
@@ -92,6 +87,7 @@ struct OptionIfLetElseOccurence {
 struct ReturnBreakContinueMacroVisitor {
     seen_return_break_continue: bool,
 }
+
 impl ReturnBreakContinueMacroVisitor {
     fn new() -> ReturnBreakContinueMacroVisitor {
         ReturnBreakContinueMacroVisitor {
@@ -99,6 +95,7 @@ impl ReturnBreakContinueMacroVisitor {
         }
     }
 }
+
 impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor {
     type Map = Map<'tcx>;
     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
@@ -157,7 +154,7 @@ fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> {
 }
 
 /// If this is the else body of an if/else expression, then we need to wrap
-/// it in curcly braces. Otherwise, we don't.
+/// it in curly braces. Otherwise, we don't.
 fn should_wrap_in_braces(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| {
         if let Some(Expr {
@@ -199,7 +196,10 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo
 /// If this expression is the option if let/else construct we're detecting, then
 /// this function returns an `OptionIfLetElseOccurence` struct with details if
 /// this construct is found, or None if this construct is not found.
-fn detect_option_if_let_else(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<OptionIfLetElseOccurence> {
+fn detect_option_if_let_else<'tcx>(
+    cx: &'_ LateContext<'tcx>,
+    expr: &'_ Expr<'tcx>,
+) -> Option<OptionIfLetElseOccurence> {
     if_chain! {
         if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly
         if let ExprKind::Match(cond_expr, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind;
@@ -214,10 +214,7 @@ fn detect_option_if_let_else(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Op
             let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" };
             let some_body = extract_body_from_arm(&arms[0])?;
             let none_body = extract_body_from_arm(&arms[1])?;
-            let method_sugg = match &none_body.kind {
-                ExprKind::Block(..) => "map_or_else",
-                _ => "map_or",
-            };
+            let method_sugg = if eager_or_lazy::is_eagerness_candidate(cx, none_body) { "map_or" } else { "map_or_else" };
             let capture_name = id.name.to_ident_string();
             let wrap_braces = should_wrap_in_braces(cx, expr);
             let (as_ref, as_mut) = match &cond_expr.kind {
@@ -243,8 +240,8 @@ fn detect_option_if_let_else(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Op
     }
 }
 
-impl<'a> LateLintPass<'a> for OptionIfLetElse {
-    fn check_expr(&mut self, cx: &LateContext<'a>, expr: &Expr<'_>) {
+impl<'tcx> LateLintPass<'tcx> for OptionIfLetElse {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
         if let Some(detection) = detect_option_if_let_else(cx, expr) {
             span_lint_and_sugg(
                 cx,
diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs
new file mode 100644
index 00000000000..4077aba6ef1
--- /dev/null
+++ b/clippy_lints/src/panic_in_result_fn.rs
@@ -0,0 +1,90 @@
+use crate::utils::{is_expn_of, is_type_diagnostic_item, return_ty, span_lint_and_then};
+use rustc_hir as hir;
+use rustc_hir::intravisit::{self, FnKind, NestedVisitorMap, Visitor};
+use rustc_hir::Expr;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::hir::map::Map;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!` or `unreachable!` in a function of type result.
+    ///
+    /// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence unimplemented, panic and unreachable should be avoided.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// fn result_with_panic() -> Result<bool, String>
+    /// {
+    ///     panic!("error");
+    /// }
+    /// ```
+    pub PANIC_IN_RESULT_FN,
+    restriction,
+    "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` "
+}
+
+declare_lint_pass!(PanicInResultFn  => [PANIC_IN_RESULT_FN]);
+
+impl<'tcx> LateLintPass<'tcx> for PanicInResultFn {
+    fn check_fn(
+        &mut self,
+        cx: &LateContext<'tcx>,
+        fn_kind: FnKind<'tcx>,
+        _: &'tcx hir::FnDecl<'tcx>,
+        body: &'tcx hir::Body<'tcx>,
+        span: Span,
+        hir_id: hir::HirId,
+    ) {
+        if !matches!(fn_kind, FnKind::Closure(_))
+            && is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type))
+        {
+            lint_impl_body(cx, span, body);
+        }
+    }
+}
+
+struct FindPanicUnimplementedUnreachable {
+    result: Vec<Span>,
+}
+
+impl<'tcx> Visitor<'tcx> for FindPanicUnimplementedUnreachable {
+    type Map = Map<'tcx>;
+
+    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+        if ["unimplemented", "unreachable", "panic", "todo"]
+            .iter()
+            .any(|fun| is_expn_of(expr.span, fun).is_some())
+        {
+            self.result.push(expr.span);
+        }
+        // and check sub-expressions
+        intravisit::walk_expr(self, expr);
+    }
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
+}
+
+fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) {
+    let mut panics = FindPanicUnimplementedUnreachable { result: Vec::new() };
+    panics.visit_expr(&body.value);
+    if !panics.result.is_empty() {
+        span_lint_and_then(
+            cx,
+            PANIC_IN_RESULT_FN,
+            impl_span,
+            "used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`",
+            move |diag| {
+                diag.help(
+                    "`unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing",
+                );
+                diag.span_note(panics.result, "return Err() instead of panicking");
+            },
+        );
+    }
+}
diff --git a/clippy_lints/src/path_buf_push_overwrite.rs b/clippy_lints/src/path_buf_push_overwrite.rs
index b8583402928..6eeb031d383 100644
--- a/clippy_lints/src/path_buf_push_overwrite.rs
+++ b/clippy_lints/src/path_buf_push_overwrite.rs
@@ -1,4 +1,4 @@
-use crate::utils::{match_type, paths, span_lint_and_sugg, walk_ptrs_ty};
+use crate::utils::{match_type, paths, span_lint_and_sugg};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -46,7 +46,7 @@ impl<'tcx> LateLintPass<'tcx> for PathBufPushOverwrite {
             if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind;
             if path.ident.name == sym!(push);
             if args.len() == 2;
-            if match_type(cx, walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0])), &paths::PATH_BUF);
+            if match_type(cx, cx.typeck_results().expr_ty(&args[0]).peel_refs(), &paths::PATH_BUF);
             if let Some(get_index_arg) = args.get(1);
             if let ExprKind::Lit(ref lit) = get_index_arg.kind;
             if let LitKind::Str(ref path_lit, _) = lit.node;
diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs
index 1615f192370..4d20a819804 100644
--- a/clippy_lints/src/redundant_clone.rs
+++ b/clippy_lints/src/redundant_clone.rs
@@ -240,10 +240,9 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
                         );
                         let mut app = Applicability::MaybeIncorrect;
 
-                        let mut call_snip = &snip[dot + 1..];
+                        let call_snip = &snip[dot + 1..];
                         // Machine applicable when `call_snip` looks like `foobar()`
-                        if call_snip.ends_with("()") {
-                            call_snip = call_snip[..call_snip.len()-2].trim();
+                        if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) {
                             if call_snip.as_bytes().iter().all(|b| b.is_ascii_alphabetic() || *b == b'_') {
                                 app = Applicability::MachineApplicable;
                             }
diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs
index c0890018d46..ae601353009 100644
--- a/clippy_lints/src/repeat_once.rs
+++ b/clippy_lints/src/repeat_once.rs
@@ -1,5 +1,5 @@
 use crate::consts::{constant_context, Constant};
-use crate::utils::{in_macro, is_type_diagnostic_item, snippet, span_lint_and_sugg, walk_ptrs_ty};
+use crate::utils::{in_macro, is_type_diagnostic_item, snippet, span_lint_and_sugg};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
@@ -44,7 +44,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce {
             if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(&count);
             if !in_macro(receiver.span);
             then {
-                let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&receiver));
+                let ty = cx.typeck_results().expr_ty(&receiver).peel_refs();
                 if ty.is_str() {
                     span_lint_and_sugg(
                         cx,
diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs
index 087d50c90e6..225fe58906f 100644
--- a/clippy_lints/src/shadow.rs
+++ b/clippy_lints/src/shadow.rs
@@ -25,7 +25,6 @@ declare_clippy_lint! {
     /// **Example:**
     /// ```rust
     /// # let x = 1;
-    ///
     /// // Bad
     /// let x = &x;
     ///
@@ -75,7 +74,9 @@ declare_clippy_lint! {
     /// names to bindings or introducing more scopes to contain the bindings.
     ///
     /// **Known problems:** This lint, as the other shadowing related lints,
-    /// currently only catches very simple patterns.
+    /// currently only catches very simple patterns. Note that
+    /// `allow`/`warn`/`deny`/`forbid` attributes only work on the function level
+    /// for this lint.
     ///
     /// **Example:**
     /// ```rust
diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs
index 7a659bf779c..15b66684eab 100644
--- a/clippy_lints/src/strings.rs
+++ b/clippy_lints/src/strings.rs
@@ -8,7 +8,7 @@ use rustc_span::source_map::Spanned;
 use if_chain::if_chain;
 
 use crate::utils::SpanlessEq;
-use crate::utils::{get_parent_expr, is_allowed, is_type_diagnostic_item, span_lint, span_lint_and_sugg, walk_ptrs_ty};
+use crate::utils::{get_parent_expr, is_allowed, is_type_diagnostic_item, span_lint, span_lint_and_sugg};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for string appends of the form `x = x + y` (without
@@ -134,7 +134,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd {
 }
 
 fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
-    is_type_diagnostic_item(cx, walk_ptrs_ty(cx.typeck_results().expr_ty(e)), sym!(string_type))
+    is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym!(string_type))
 }
 
 fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool {
diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs
index 47a73ca9a24..54b38d9f4ce 100644
--- a/clippy_lints/src/swap.rs
+++ b/clippy_lints/src/swap.rs
@@ -1,7 +1,6 @@
 use crate::utils::sugg::Sugg;
 use crate::utils::{
     differing_macro_contexts, eq_expr_value, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_then,
-    walk_ptrs_ty,
 };
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -194,7 +193,7 @@ fn check_for_slice<'a>(cx: &LateContext<'_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr<
     if let ExprKind::Index(ref lhs1, ref idx1) = lhs1.kind {
         if let ExprKind::Index(ref lhs2, ref idx2) = lhs2.kind {
             if eq_expr_value(cx, lhs1, lhs2) {
-                let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(lhs1));
+                let ty = cx.typeck_results().expr_ty(lhs1).peel_refs();
 
                 if matches!(ty.kind(), ty::Slice(_))
                     || matches!(ty.kind(), ty::Array(_, _))
diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs
index 6c6188d61ad..a29a199b8c3 100644
--- a/clippy_lints/src/types.rs
+++ b/clippy_lints/src/types.rs
@@ -215,11 +215,41 @@ declare_clippy_lint! {
     "redundant allocation"
 }
 
+declare_clippy_lint! {
+    /// **What it does:** Checks for Rc<T> and Arc<T> when T is a mutable buffer type such as String or Vec
+    ///
+    /// **Why is this bad?** Expressions such as Rc<String> have no advantage over Rc<str>, since
+    /// it is larger and involves an extra level of indirection, and doesn't implement Borrow<str>.
+    ///
+    /// While mutating a buffer type would still be possible with Rc::get_mut(), it only
+    /// works if there are no additional references yet, which defeats the purpose of
+    /// enclosing it in a shared ownership type. Instead, additionally wrapping the inner
+    /// type with an interior mutable container (such as RefCell or Mutex) would normally
+    /// be used.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// ```rust,ignore
+    /// # use std::rc::Rc;
+    /// fn foo(interned: Rc<String>) { ... }
+    /// ```
+    ///
+    /// Better:
+    ///
+    /// ```rust,ignore
+    /// fn foo(interned: Rc<str>) { ... }
+    /// ```
+    pub RC_BUFFER,
+    perf,
+    "shared ownership of a buffer type"
+}
+
 pub struct Types {
     vec_box_size_threshold: u64,
 }
 
-impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION]);
+impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER]);
 
 impl<'tcx> LateLintPass<'tcx> for Types {
     fn check_fn(&mut self, cx: &LateContext<'_>, _: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, id: HirId) {
@@ -272,6 +302,19 @@ fn match_type_parameter(cx: &LateContext<'_>, qpath: &QPath<'_>, path: &[&str])
     None
 }
 
+fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> {
+    if match_type_parameter(cx, qpath, &paths::STRING).is_some() {
+        return Some("str");
+    }
+    if match_type_parameter(cx, qpath, &paths::OS_STRING).is_some() {
+        return Some("std::ffi::OsStr");
+    }
+    if match_type_parameter(cx, qpath, &paths::PATH_BUF).is_some() {
+        return Some("std::path::Path");
+    }
+    None
+}
+
 fn match_borrows_parameter(_cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<Span> {
     let last = last_path_segment(qpath);
     if_chain! {
@@ -321,14 +364,15 @@ impl Types {
                 if let Some(def_id) = res.opt_def_id() {
                     if Some(def_id) == cx.tcx.lang_items().owned_box() {
                         if let Some(span) = match_borrows_parameter(cx, qpath) {
+                            let mut applicability = Applicability::MachineApplicable;
                             span_lint_and_sugg(
                                 cx,
                                 REDUNDANT_ALLOCATION,
                                 hir_ty.span,
                                 "usage of `Box<&T>`",
                                 "try",
-                                snippet(cx, span, "..").to_string(),
-                                Applicability::MachineApplicable,
+                                snippet_with_applicability(cx, span, "..", &mut applicability).to_string(),
+                                applicability,
                             );
                             return; // don't recurse into the type
                         }
@@ -345,14 +389,15 @@ impl Types {
                         }
                     } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
                         if let Some(span) = match_type_parameter(cx, qpath, &paths::RC) {
+                            let mut applicability = Applicability::MachineApplicable;
                             span_lint_and_sugg(
                                 cx,
                                 REDUNDANT_ALLOCATION,
                                 hir_ty.span,
                                 "usage of `Rc<Rc<T>>`",
                                 "try",
-                                snippet(cx, span, "..").to_string(),
-                                Applicability::MachineApplicable,
+                                snippet_with_applicability(cx, span, "..", &mut applicability).to_string(),
+                                applicability,
                             );
                             return; // don't recurse into the type
                         }
@@ -368,25 +413,109 @@ impl Types {
                                 GenericArg::Type(ty) => ty.span,
                                 _ => return,
                             };
+                            let mut applicability = Applicability::MachineApplicable;
                             span_lint_and_sugg(
                                 cx,
                                 REDUNDANT_ALLOCATION,
                                 hir_ty.span,
                                 "usage of `Rc<Box<T>>`",
                                 "try",
-                                format!("Rc<{}>", snippet(cx, inner_span, "..")),
+                                format!(
+                                    "Rc<{}>",
+                                    snippet_with_applicability(cx, inner_span, "..", &mut applicability)
+                                ),
+                                applicability,
+                            );
+                            return; // don't recurse into the type
+                        }
+                        if let Some(alternate) = match_buffer_type(cx, qpath) {
+                            span_lint_and_sugg(
+                                cx,
+                                RC_BUFFER,
+                                hir_ty.span,
+                                "usage of `Rc<T>` when T is a buffer type",
+                                "try",
+                                format!("Rc<{}>", alternate),
+                                Applicability::MachineApplicable,
+                            );
+                            return; // don't recurse into the type
+                        }
+                        if match_type_parameter(cx, qpath, &paths::VEC).is_some() {
+                            let vec_ty = match &last_path_segment(qpath).args.unwrap().args[0] {
+                                GenericArg::Type(ty) => match &ty.kind {
+                                    TyKind::Path(qpath) => qpath,
+                                    _ => return,
+                                },
+                                _ => return,
+                            };
+                            let inner_span = match &last_path_segment(&vec_ty).args.unwrap().args[0] {
+                                GenericArg::Type(ty) => ty.span,
+                                _ => return,
+                            };
+                            let mut applicability = Applicability::MachineApplicable;
+                            span_lint_and_sugg(
+                                cx,
+                                RC_BUFFER,
+                                hir_ty.span,
+                                "usage of `Rc<T>` when T is a buffer type",
+                                "try",
+                                format!(
+                                    "Rc<[{}]>",
+                                    snippet_with_applicability(cx, inner_span, "..", &mut applicability)
+                                ),
                                 Applicability::MachineApplicable,
                             );
                             return; // don't recurse into the type
                         }
                         if let Some(span) = match_borrows_parameter(cx, qpath) {
+                            let mut applicability = Applicability::MachineApplicable;
                             span_lint_and_sugg(
                                 cx,
                                 REDUNDANT_ALLOCATION,
                                 hir_ty.span,
                                 "usage of `Rc<&T>`",
                                 "try",
-                                snippet(cx, span, "..").to_string(),
+                                snippet_with_applicability(cx, span, "..", &mut applicability).to_string(),
+                                applicability,
+                            );
+                            return; // don't recurse into the type
+                        }
+                    } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) {
+                        if let Some(alternate) = match_buffer_type(cx, qpath) {
+                            span_lint_and_sugg(
+                                cx,
+                                RC_BUFFER,
+                                hir_ty.span,
+                                "usage of `Arc<T>` when T is a buffer type",
+                                "try",
+                                format!("Arc<{}>", alternate),
+                                Applicability::MachineApplicable,
+                            );
+                            return; // don't recurse into the type
+                        }
+                        if match_type_parameter(cx, qpath, &paths::VEC).is_some() {
+                            let vec_ty = match &last_path_segment(qpath).args.unwrap().args[0] {
+                                GenericArg::Type(ty) => match &ty.kind {
+                                    TyKind::Path(qpath) => qpath,
+                                    _ => return,
+                                },
+                                _ => return,
+                            };
+                            let inner_span = match &last_path_segment(&vec_ty).args.unwrap().args[0] {
+                                GenericArg::Type(ty) => ty.span,
+                                _ => return,
+                            };
+                            let mut applicability = Applicability::MachineApplicable;
+                            span_lint_and_sugg(
+                                cx,
+                                RC_BUFFER,
+                                hir_ty.span,
+                                "usage of `Arc<T>` when T is a buffer type",
+                                "try",
+                                format!(
+                                    "Arc<[{}]>",
+                                    snippet_with_applicability(cx, inner_span, "..", &mut applicability)
+                                ),
                                 Applicability::MachineApplicable,
                             );
                             return; // don't recurse into the type
@@ -546,7 +675,6 @@ impl Types {
                             // details.
                             return;
                         }
-                        let mut applicability = Applicability::MachineApplicable;
                         span_lint_and_sugg(
                             cx,
                             BORROWED_BOX,
@@ -556,8 +684,12 @@ impl Types {
                             format!(
                                 "&{}{}",
                                 ltopt,
-                                &snippet_with_applicability(cx, inner.span, "..", &mut applicability)
+                                &snippet(cx, inner.span, "..")
                             ),
+                            // To make this `MachineApplicable`, at least one needs to check if it isn't a trait item
+                            // because the trait impls of it will break otherwise;
+                            // and there may be other cases that result in invalid code.
+                            // For example, type coercion doesn't work nicely.
                             Applicability::Unspecified,
                         );
                         return; // don't recurse into the type
diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs
index 1c7e62ecd3d..0f8797243ec 100644
--- a/clippy_lints/src/unwrap_in_result.rs
+++ b/clippy_lints/src/unwrap_in_result.rs
@@ -1,4 +1,4 @@
-use crate::utils::{is_type_diagnostic_item, method_chain_args, return_ty, span_lint_and_then, walk_ptrs_ty};
+use crate::utils::{is_type_diagnostic_item, method_chain_args, return_ty, span_lint_and_then};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
@@ -81,7 +81,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> {
     fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
         // check for `expect`
         if let Some(arglists) = method_chain_args(expr, &["expect"]) {
-            let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0]));
+            let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
             if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type))
                 || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type))
             {
@@ -91,7 +91,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> {
 
         // check for `unwrap`
         if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
-            let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0]));
+            let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
             if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type))
                 || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type))
             {
diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs
index 615440e15f3..4e4a206a583 100644
--- a/clippy_lints/src/useless_conversion.rs
+++ b/clippy_lints/src/useless_conversion.rs
@@ -72,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
                             cx,
                             USELESS_CONVERSION,
                             e.span,
-                            "useless conversion to the same type",
+                            &format!("useless conversion to the same type: `{}`", b),
                             "consider removing `.into()`",
                             sugg,
                             Applicability::MachineApplicable, // snippet
@@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
                             cx,
                             USELESS_CONVERSION,
                             e.span,
-                            "useless conversion to the same type",
+                            &format!("useless conversion to the same type: `{}`", b),
                             "consider removing `.into_iter()`",
                             sugg,
                             Applicability::MachineApplicable, // snippet
@@ -116,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
                                 cx,
                                 USELESS_CONVERSION,
                                 e.span,
-                                "useless conversion to the same type",
+                                &format!("useless conversion to the same type: `{}`", b),
                                 None,
                                 "consider removing `.try_into()`",
                             );
@@ -147,7 +147,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
                                     cx,
                                     USELESS_CONVERSION,
                                     e.span,
-                                    "useless conversion to the same type",
+                                    &format!("useless conversion to the same type: `{}`", b),
                                     None,
                                     &hint,
                                 );
@@ -166,7 +166,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
                                     cx,
                                     USELESS_CONVERSION,
                                     e.span,
-                                    "useless conversion to the same type",
+                                    &format!("useless conversion to the same type: `{}`", b),
                                     &sugg_msg,
                                     sugg.to_string(),
                                     Applicability::MachineApplicable, // snippet
diff --git a/clippy_lints/src/utils/diagnostics.rs b/clippy_lints/src/utils/diagnostics.rs
index e4e65b5f4d4..0a58231558e 100644
--- a/clippy_lints/src/utils/diagnostics.rs
+++ b/clippy_lints/src/utils/diagnostics.rs
@@ -51,6 +51,8 @@ pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<Mult
 ///
 /// The `help` message can be optionally attached to a `Span`.
 ///
+/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
+///
 /// # Example
 ///
 /// ```ignore
@@ -87,6 +89,8 @@ pub fn span_lint_and_help<'a, T: LintContext>(
 /// The `note` message is presented separately from the main lint message
 /// and is attached to a specific span:
 ///
+/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
+///
 /// # Example
 ///
 /// ```ignore
@@ -126,6 +130,7 @@ pub fn span_lint_and_note<'a, T: LintContext>(
 /// Like `span_lint` but allows to add notes, help and suggestions using a closure.
 ///
 /// If you need to customize your lint output a lot, use this function.
+/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
 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>),
@@ -168,6 +173,10 @@ pub fn span_lint_hir_and_then(
 /// In the example below, `help` is `"try"` and `sugg` is the suggested replacement `".any(|x| x >
 /// 2)"`.
 ///
+/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
+///
+/// # Example
+///
 /// ```ignore
 /// error: This `.fold` can be more succinctly expressed as `.any`
 /// --> $DIR/methods.rs:390:13
diff --git a/clippy_lints/src/utils/eager_or_lazy.rs b/clippy_lints/src/utils/eager_or_lazy.rs
new file mode 100644
index 00000000000..6938d9971d9
--- /dev/null
+++ b/clippy_lints/src/utils/eager_or_lazy.rs
@@ -0,0 +1,128 @@
+//! Utilities for evaluating whether eagerly evaluated expressions can be made lazy and vice versa.
+//!
+//! Things to consider:
+//!  - has the expression side-effects?
+//!  - is the expression computationally expensive?
+//!
+//! See lints:
+//!  - unnecessary-lazy-evaluations
+//!  - or-fun-call
+//!  - option-if-let-else
+
+use crate::utils::is_ctor_or_promotable_const_function;
+use rustc_hir::def::{DefKind, Res};
+
+use rustc_hir::intravisit;
+use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
+
+use rustc_hir::{Block, Expr, ExprKind, Path, QPath};
+use rustc_lint::LateContext;
+use rustc_middle::hir::map::Map;
+
+/// Is the expr pure (is it free from side-effects)?
+/// This function is named so to stress that it isn't exhaustive and returns FNs.
+fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool {
+    match expr.kind {
+        ExprKind::Lit(..) | ExprKind::Path(..) | ExprKind::Field(..) => true,
+        ExprKind::AddrOf(_, _, addr_of_expr) => identify_some_pure_patterns(addr_of_expr),
+        ExprKind::Tup(tup_exprs) => tup_exprs.iter().all(|expr| identify_some_pure_patterns(expr)),
+        ExprKind::Struct(_, fields, expr) => {
+            fields.iter().all(|f| identify_some_pure_patterns(f.expr))
+                && expr.map_or(true, |e| identify_some_pure_patterns(e))
+        },
+        ExprKind::Call(
+            &Expr {
+                kind:
+                    ExprKind::Path(QPath::Resolved(
+                        _,
+                        Path {
+                            res: Res::Def(DefKind::Ctor(..) | DefKind::Variant, ..),
+                            ..
+                        },
+                    )),
+                ..
+            },
+            args,
+        ) => args.iter().all(|expr| identify_some_pure_patterns(expr)),
+        ExprKind::Block(
+            &Block {
+                stmts,
+                expr: Some(expr),
+                ..
+            },
+            _,
+        ) => stmts.is_empty() && identify_some_pure_patterns(expr),
+        ExprKind::Box(..)
+        | ExprKind::Array(..)
+        | ExprKind::Call(..)
+        | ExprKind::MethodCall(..)
+        | ExprKind::Binary(..)
+        | ExprKind::Unary(..)
+        | ExprKind::Cast(..)
+        | ExprKind::Type(..)
+        | ExprKind::DropTemps(..)
+        | ExprKind::Loop(..)
+        | ExprKind::Match(..)
+        | ExprKind::Closure(..)
+        | ExprKind::Block(..)
+        | ExprKind::Assign(..)
+        | ExprKind::AssignOp(..)
+        | ExprKind::Index(..)
+        | ExprKind::Break(..)
+        | ExprKind::Continue(..)
+        | ExprKind::Ret(..)
+        | ExprKind::InlineAsm(..)
+        | ExprKind::LlvmInlineAsm(..)
+        | ExprKind::Repeat(..)
+        | ExprKind::Yield(..)
+        | ExprKind::Err => false,
+    }
+}
+
+/// Identify some potentially computationally expensive patterns.
+/// This function is named so to stress that its implementation is non-exhaustive.
+/// It returns FNs and FPs.
+fn identify_some_potentially_expensive_patterns<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+    // Searches an expression for method calls or function calls that aren't ctors
+    struct FunCallFinder<'a, 'tcx> {
+        cx: &'a LateContext<'tcx>,
+        found: bool,
+    }
+
+    impl<'a, 'tcx> intravisit::Visitor<'tcx> for FunCallFinder<'a, 'tcx> {
+        type Map = Map<'tcx>;
+
+        fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+            let call_found = match &expr.kind {
+                // ignore enum and struct constructors
+                ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr),
+                ExprKind::MethodCall(..) => true,
+                _ => false,
+            };
+
+            if call_found {
+                self.found |= true;
+            }
+
+            if !self.found {
+                intravisit::walk_expr(self, expr);
+            }
+        }
+
+        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+            NestedVisitorMap::None
+        }
+    }
+
+    let mut finder = FunCallFinder { cx, found: false };
+    finder.visit_expr(expr);
+    finder.found
+}
+
+pub fn is_eagerness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+    !identify_some_potentially_expensive_patterns(cx, expr) && identify_some_pure_patterns(expr)
+}
+
+pub fn is_lazyness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+    identify_some_potentially_expensive_patterns(cx, expr)
+}
diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs
index 8fa5d22210a..bfe426a25eb 100644
--- a/clippy_lints/src/utils/internal_lints.rs
+++ b/clippy_lints/src/utils/internal_lints.rs
@@ -1,6 +1,6 @@
 use crate::utils::{
-    is_expn_of, match_def_path, match_qpath, match_type, method_calls, paths, run_lints, snippet, span_lint,
-    span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, SpanlessEq,
+    is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, qpath_res, run_lints,
+    snippet, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq,
 };
 use if_chain::if_chain;
 use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, NodeId};
@@ -11,7 +11,7 @@ use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::hir_id::CRATE_HIR_ID;
 use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
-use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Path, StmtKind, Ty, TyKind};
+use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind};
 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
 use rustc_middle::hir::map::Map;
 use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
@@ -206,6 +206,29 @@ declare_clippy_lint! {
     "found collapsible `span_lint_and_then` calls"
 }
 
+declare_clippy_lint! {
+    /// **What it does:** Checks for calls to `utils::match_type()` on a type diagnostic item
+    /// and suggests to use `utils::is_type_diagnostic_item()` instead.
+    ///
+    /// **Why is this bad?** `utils::is_type_diagnostic_item()` does not require hardcoded paths.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// Bad:
+    /// ```rust,ignore
+    /// utils::match_type(cx, ty, &paths::VEC)
+    /// ```
+    ///
+    /// Good:
+    /// ```rust,ignore
+    /// utils::is_type_diagnostic_item(cx, ty, sym!(vec_type))
+    /// ```
+    pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
+    internal,
+    "using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`"
+}
+
 declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
 
 impl EarlyLintPass for ClippyLintsInternal {
@@ -404,7 +427,7 @@ impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions {
             if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind;
             let fn_name = path.ident;
             if let Some(sugg) = self.map.get(&*fn_name.as_str());
-            let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0]));
+            let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
             if match_type(cx, ty, &paths::EARLY_CONTEXT)
                 || match_type(cx, ty, &paths::LATE_CONTEXT);
             then {
@@ -437,7 +460,7 @@ impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass {
             let args = arg_lists[1];
             if args.len() == 1;
             let self_arg = &args[0];
-            let self_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(self_arg));
+            let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
             if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT);
             then {
                 span_lint_and_sugg(
@@ -652,3 +675,89 @@ fn suggest_note(
         Applicability::MachineApplicable,
     );
 }
+
+declare_lint_pass!(MatchTypeOnDiagItem => [MATCH_TYPE_ON_DIAGNOSTIC_ITEM]);
+
+impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
+        if !run_lints(cx, &[MATCH_TYPE_ON_DIAGNOSTIC_ITEM], expr.hir_id) {
+            return;
+        }
+
+        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"]);
+            // 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();
+            if let Some(ty_did) = path_to_res(cx, &segments[..]).and_then(|res| res.opt_def_id());
+            // Check if the matched type is a diagnostic item
+            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 {
+                let cx_snippet = snippet(cx, context.span, "_");
+                let ty_snippet = snippet(cx, ty.span, "_");
+
+                span_lint_and_sugg(
+                    cx,
+                    MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
+                    expr.span,
+                    "usage of `utils::match_type()` on a type diagnostic item",
+                    "try",
+                    format!("utils::is_type_diagnostic_item({}, {}, sym!({}))", cx_snippet, ty_snippet, item_name),
+                    Applicability::MaybeIncorrect,
+                );
+            }
+        }
+    }
+}
+
+fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Vec<SymbolStr>> {
+    use rustc_hir::ItemKind;
+
+    match &expr.kind {
+        ExprKind::AddrOf(.., expr) => return path_to_matched_type(cx, expr),
+        ExprKind::Path(qpath) => match qpath_res(cx, qpath, expr.hir_id) {
+            Res::Local(hir_id) => {
+                let parent_id = cx.tcx.hir().get_parent_node(hir_id);
+                if let Some(Node::Local(local)) = cx.tcx.hir().find(parent_id) {
+                    if let Some(init) = local.init {
+                        return path_to_matched_type(cx, init);
+                    }
+                }
+            },
+            Res::Def(DefKind::Const | DefKind::Static, def_id) => {
+                if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) {
+                    if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind {
+                        let body = cx.tcx.hir().body(body_id);
+                        return path_to_matched_type(cx, &body.value);
+                    }
+                }
+            },
+            _ => {},
+        },
+        ExprKind::Array(exprs) => {
+            let segments: Vec<SymbolStr> = exprs
+                .iter()
+                .filter_map(|expr| {
+                    if let ExprKind::Lit(lit) = &expr.kind {
+                        if let LitKind::Str(sym, _) = lit.node {
+                            return Some(sym.as_str());
+                        }
+                    }
+
+                    None
+                })
+                .collect();
+
+            if segments.len() == exprs.len() {
+                return Some(segments);
+            }
+        },
+        _ => {},
+    }
+
+    None
+}
diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs
index 3ebbfed6456..ea52741b7cc 100644
--- a/clippy_lints/src/utils/mod.rs
+++ b/clippy_lints/src/utils/mod.rs
@@ -10,6 +10,7 @@ pub mod comparisons;
 pub mod conf;
 pub mod constants;
 mod diagnostics;
+pub mod eager_or_lazy;
 pub mod higher;
 mod hir_utils;
 pub mod inspector;
@@ -130,6 +131,9 @@ pub fn is_wild<'tcx>(pat: &impl std::ops::Deref<Target = Pat<'tcx>>) -> bool {
 }
 
 /// Checks if type is struct, enum or union type with the given def path.
+///
+/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead.
+/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
 pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
     match ty.kind() {
         ty::Adt(adt, _) => match_def_path(cx, adt.did, path),
@@ -138,6 +142,8 @@ pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
 }
 
 /// Checks if the type is equal to a diagnostic item
+///
+/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
 pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
     match ty.kind() {
         ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did),
@@ -748,14 +754,6 @@ pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
     }
 }
 
-/// Returns the base type for references and raw pointers.
-pub fn walk_ptrs_ty(ty: Ty<'_>) -> Ty<'_> {
-    match ty.kind() {
-        ty::Ref(_, ty, _) => walk_ptrs_ty(ty),
-        _ => ty,
-    }
-}
-
 /// Returns the base type for references and raw pointers, and count reference
 /// depth.
 pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {
diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs
index 3b031a552e5..1583afad208 100644
--- a/clippy_lints/src/utils/paths.rs
+++ b/clippy_lints/src/utils/paths.rs
@@ -113,8 +113,12 @@ pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "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: [&str; 3] = ["alloc", "string", "String"];
 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"];
+pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
+pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
 pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
 pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"];
 pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs
index 811fde388d1..ec8b7e59b59 100644
--- a/clippy_lints/src/utils/sugg.rs
+++ b/clippy_lints/src/utils/sugg.rs
@@ -49,7 +49,7 @@ impl<'a> Sugg<'a> {
     /// Convenience function around `hir_opt` for suggestions with a default
     /// text.
     pub fn hir(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
-        Self::hir_opt(cx, expr).unwrap_or_else(|| Sugg::NonParen(Cow::Borrowed(default)))
+        Self::hir_opt(cx, expr).unwrap_or(Sugg::NonParen(Cow::Borrowed(default)))
     }
 
     /// Same as `hir`, but it adapts the applicability level by following rules:
diff --git a/clippy_lints/src/utils/usage.rs b/clippy_lints/src/utils/usage.rs
index 4a64b935ac9..ea1dc3be29b 100644
--- a/clippy_lints/src/utils/usage.rs
+++ b/clippy_lints/src/utils/usage.rs
@@ -1,6 +1,8 @@
 use crate::utils::match_var;
 use rustc_data_structures::fx::FxHashSet;
+use rustc_hir as hir;
 use rustc_hir::def::Res;
+use rustc_hir::intravisit;
 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
 use rustc_hir::{Expr, HirId, Path};
 use rustc_infer::infer::TyCtxtInferExt;
@@ -108,3 +110,67 @@ pub fn is_unused<'tcx>(ident: &'tcx Ident, body: &'tcx Expr<'_>) -> bool {
     walk_expr(&mut visitor, body);
     !visitor.used
 }
+
+pub struct ParamBindingIdCollector {
+    binding_hir_ids: Vec<hir::HirId>,
+}
+impl<'tcx> ParamBindingIdCollector {
+    fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec<hir::HirId> {
+        let mut finder = ParamBindingIdCollector {
+            binding_hir_ids: Vec::new(),
+        };
+        finder.visit_body(body);
+        finder.binding_hir_ids
+    }
+}
+impl<'tcx> intravisit::Visitor<'tcx> for ParamBindingIdCollector {
+    type Map = Map<'tcx>;
+
+    fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
+        if let hir::PatKind::Binding(_, hir_id, ..) = param.pat.kind {
+            self.binding_hir_ids.push(hir_id);
+        }
+    }
+
+    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
+        intravisit::NestedVisitorMap::None
+    }
+}
+
+pub struct BindingUsageFinder<'a, 'tcx> {
+    cx: &'a LateContext<'tcx>,
+    binding_ids: Vec<hir::HirId>,
+    usage_found: bool,
+}
+impl<'a, 'tcx> BindingUsageFinder<'a, 'tcx> {
+    pub fn are_params_used(cx: &'a LateContext<'tcx>, body: &'tcx hir::Body<'tcx>) -> bool {
+        let mut finder = BindingUsageFinder {
+            cx,
+            binding_ids: ParamBindingIdCollector::collect_binding_hir_ids(body),
+            usage_found: false,
+        };
+        finder.visit_body(body);
+        finder.usage_found
+    }
+}
+impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> {
+    type Map = Map<'tcx>;
+
+    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
+        if !self.usage_found {
+            intravisit::walk_expr(self, expr);
+        }
+    }
+
+    fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) {
+        if let hir::def::Res::Local(id) = path.res {
+            if self.binding_ids.contains(&id) {
+                self.usage_found = true;
+            }
+        }
+    }
+
+    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
+        intravisit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
+    }
+}
diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs
index e653240d049..fac63bcb993 100644
--- a/clippy_lints/src/write.rs
+++ b/clippy_lints/src/write.rs
@@ -322,11 +322,15 @@ impl EarlyLintPass for Write {
 }
 
 /// Given a format string that ends in a newline and its span, calculates the span of the
-/// newline.
+/// newline, or the format string itself if the format string consists solely of a newline.
 fn newline_span(fmtstr: &StrLit) -> Span {
     let sp = fmtstr.span;
     let contents = &fmtstr.symbol.as_str();
 
+    if *contents == r"\n" {
+        return sp;
+    }
+
     let newline_sp_hi = sp.hi()
         - match fmtstr.style {
             StrStyle::Cooked => BytePos(1),