about summary refs log tree commit diff
path: root/clippy_lints/src
diff options
context:
space:
mode:
authorEduardo Broto <ebroto@tutanota.com>2020-10-23 22:16:59 +0200
committerEduardo Broto <ebroto@tutanota.com>2020-10-23 22:16:59 +0200
commitcdb555f4fcdb57741ffb59bd2b0e66af69ea0a85 (patch)
tree5c9c37427427a7d7b95dc090c560f006a9b92efe /clippy_lints/src
parentfcde7683fe7ca10c83e5bc17f0969d2284affcd2 (diff)
downloadrust-cdb555f4fcdb57741ffb59bd2b0e66af69ea0a85.tar.gz
rust-cdb555f4fcdb57741ffb59bd2b0e66af69ea0a85.zip
Merge commit 'bf1c6f9871f430e284b17aa44059e0d0395e28a6' into clippyup
Diffstat (limited to 'clippy_lints/src')
-rw-r--r--clippy_lints/src/consts.rs10
-rw-r--r--clippy_lints/src/copies.rs6
-rw-r--r--clippy_lints/src/doc.rs13
-rw-r--r--clippy_lints/src/eq_op.rs37
-rw-r--r--clippy_lints/src/format.rs8
-rw-r--r--clippy_lints/src/functions.rs106
-rw-r--r--clippy_lints/src/lib.rs13
-rw-r--r--clippy_lints/src/loops.rs638
-rw-r--r--clippy_lints/src/manual_unwrap_or.rs104
-rw-r--r--clippy_lints/src/mut_key.rs7
-rw-r--r--clippy_lints/src/mutable_debug_assertion.rs66
-rw-r--r--clippy_lints/src/option_if_let_else.rs59
-rw-r--r--clippy_lints/src/ptr_eq.rs96
-rw-r--r--clippy_lints/src/transmute.rs6
-rw-r--r--clippy_lints/src/trivially_copy_pass_by_ref.rs1
-rw-r--r--clippy_lints/src/types.rs2
-rw-r--r--clippy_lints/src/utils/eager_or_lazy.rs2
-rw-r--r--clippy_lints/src/utils/higher.rs54
-rw-r--r--clippy_lints/src/utils/mod.rs2
-rw-r--r--clippy_lints/src/utils/sugg.rs59
-rw-r--r--clippy_lints/src/utils/usage.rs50
21 files changed, 998 insertions, 341 deletions
diff --git a/clippy_lints/src/consts.rs b/clippy_lints/src/consts.rs
index 062c9bd2d9e..c5e33b288a9 100644
--- a/clippy_lints/src/consts.rs
+++ b/clippy_lints/src/consts.rs
@@ -40,6 +40,8 @@ pub enum Constant {
     Tuple(Vec<Constant>),
     /// A raw pointer.
     RawPtr(u128),
+    /// A reference
+    Ref(Box<Constant>),
     /// A literal with syntax error.
     Err(Symbol),
 }
@@ -66,6 +68,7 @@ impl PartialEq for Constant {
             (&Self::Bool(l), &Self::Bool(r)) => l == r,
             (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r,
             (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv,
+            (&Self::Ref(ref lb), &Self::Ref(ref rb)) => *lb == *rb,
             // TODO: are there inter-type equalities?
             _ => false,
         }
@@ -110,6 +113,9 @@ impl Hash for Constant {
             Self::RawPtr(u) => {
                 u.hash(state);
             },
+            Self::Ref(ref r) => {
+                r.hash(state);
+            },
             Self::Err(ref s) => {
                 s.hash(state);
             },
@@ -144,6 +150,7 @@ impl Constant {
                     x => x,
                 }
             },
+            (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp(tcx, cmp_type, lb, rb),
             // TODO: are there any useful inter-type orderings?
             _ => None,
         }
@@ -239,7 +246,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
             ExprKind::Unary(op, ref operand) => self.expr(operand).and_then(|o| match op {
                 UnOp::UnNot => self.constant_not(&o, self.typeck_results.expr_ty(e)),
                 UnOp::UnNeg => self.constant_negate(&o, self.typeck_results.expr_ty(e)),
-                UnOp::UnDeref => Some(o),
+                UnOp::UnDeref => Some(if let Constant::Ref(r) = o { *r } else { o }),
             }),
             ExprKind::Binary(op, ref left, ref right) => self.binop(op, left, right),
             ExprKind::Call(ref callee, ref args) => {
@@ -269,6 +276,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
                 }
             },
             ExprKind::Index(ref arr, ref index) => self.index(arr, index),
+            ExprKind::AddrOf(_, _, ref inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))),
             // TODO: add other expressions.
             _ => None,
         }
diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs
index 10a64769585..6c969c3ead0 100644
--- a/clippy_lints/src/copies.rs
+++ b/clippy_lints/src/copies.rs
@@ -1,4 +1,4 @@
-use crate::utils::{eq_expr_value, SpanlessEq, SpanlessHash};
+use crate::utils::{eq_expr_value, in_macro, SpanlessEq, SpanlessHash};
 use crate::utils::{get_parent_expr, higher, if_sequence, snippet, span_lint_and_note, span_lint_and_then};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind};
@@ -220,6 +220,10 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
     };
 
     let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool {
+        // Do not lint if any expr originates from a macro
+        if in_macro(lhs.span) || in_macro(rhs.span) {
+            return false;
+        }
         // Do not spawn warning if `IFS_SAME_COND` already produced it.
         if eq_expr_value(cx, lhs, rhs) {
             return false;
diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs
index 62bb70af06e..07f604cf714 100644
--- a/clippy_lints/src/doc.rs
+++ b/clippy_lints/src/doc.rs
@@ -32,6 +32,11 @@ declare_clippy_lint! {
     /// **Known problems:** Lots of bad docs won’t be fixed, what the lint checks
     /// for is limited, and there are still false positives.
     ///
+    /// In addition, when writing documentation comments, including `[]` brackets
+    /// inside a link text would trip the parser. Therfore, documenting link with
+    /// `[`SmallVec<[T; INLINE_CAPACITY]>`]` and then [`SmallVec<[T; INLINE_CAPACITY]>`]: SmallVec
+    /// would fail.
+    ///
     /// **Examples:**
     /// ```rust
     /// /// Do something with the foo_bar parameter. See also
@@ -39,6 +44,14 @@ declare_clippy_lint! {
     /// // ^ `foo_bar` and `that::other::module::foo` should be ticked.
     /// fn doit(foo_bar: usize) {}
     /// ```
+    ///
+    /// ```rust
+    /// // Link text with `[]` brackets should be written as following:
+    /// /// Consume the array and return the inner
+    /// /// [`SmallVec<[T; INLINE_CAPACITY]>`][SmallVec].
+    /// /// [SmallVec]: SmallVec
+    /// fn main() {}
+    /// ```
     pub DOC_MARKDOWN,
     pedantic,
     "presence of `_`, `::` or camel-case outside backticks in documentation"
diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs
index e16ec783fab..3201adbf9a0 100644
--- a/clippy_lints/src/eq_op.rs
+++ b/clippy_lints/src/eq_op.rs
@@ -1,8 +1,10 @@
 use crate::utils::{
-    eq_expr_value, implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then,
+    eq_expr_value, higher, implements_trait, in_macro, is_copy, is_expn_of, multispan_sugg, snippet, span_lint,
+    span_lint_and_then,
 };
+use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind};
+use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
@@ -23,6 +25,12 @@ declare_clippy_lint! {
     /// # let x = 1;
     /// if x + 1 == x + 1 {}
     /// ```
+    /// or
+    /// ```rust
+    /// # let a = 3;
+    /// # let b = 4;
+    /// assert_eq!(a, a);
+    /// ```
     pub EQ_OP,
     correctness,
     "equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)"
@@ -52,9 +60,34 @@ declare_clippy_lint! {
 
 declare_lint_pass!(EqOp => [EQ_OP, OP_REF]);
 
+const ASSERT_MACRO_NAMES: [&str; 4] = ["assert_eq", "assert_ne", "debug_assert_eq", "debug_assert_ne"];
+
 impl<'tcx> LateLintPass<'tcx> for EqOp {
     #[allow(clippy::similar_names, clippy::too_many_lines)]
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+        if let ExprKind::Block(ref block, _) = e.kind {
+            for stmt in block.stmts {
+                for amn in &ASSERT_MACRO_NAMES {
+                    if_chain! {
+                        if is_expn_of(stmt.span, amn).is_some();
+                        if let StmtKind::Semi(ref matchexpr) = stmt.kind;
+                        if let Some(macro_args) = higher::extract_assert_macro_args(matchexpr);
+                        if macro_args.len() == 2;
+                        let (lhs, rhs) = (macro_args[0], macro_args[1]);
+                        if eq_expr_value(cx, lhs, rhs);
+
+                        then {
+                            span_lint(
+                                cx,
+                                EQ_OP,
+                                lhs.span.to(rhs.span),
+                                &format!("identical args used in this `{}!` macro call", amn),
+                            );
+                        }
+                    }
+                }
+            }
+        }
         if let ExprKind::Binary(op, ref left, ref right) = e.kind {
             if e.span.from_expansion() {
                 return;
diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs
index d6541010bca..26da058598e 100644
--- a/clippy_lints/src/format.rs
+++ b/clippy_lints/src/format.rs
@@ -1,6 +1,6 @@
 use crate::utils::paths;
 use crate::utils::{
-    is_expn_of, is_type_diagnostic_item, last_path_segment, match_def_path, match_function_call, snippet,
+    is_expn_of, is_type_diagnostic_item, last_path_segment, match_def_path, match_function_call, snippet, snippet_opt,
     span_lint_and_then,
 };
 use if_chain::if_chain;
@@ -132,7 +132,11 @@ fn on_new_v1<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<Strin
         then {
             // `format!("foo")` expansion contains `match () { () => [], }`
             if tup.is_empty() {
-                return Some(format!("{:?}.to_string()", s.as_str()));
+                if let Some(s_src) = snippet_opt(cx, lit.span) {
+                    // Simulate macro expansion, converting {{ and }} to { and }.
+                    let s_expand = s_src.replace("{{", "{").replace("}}", "}");
+                    return Some(format!("{}.to_string()", s_expand))
+                }
             } else if s.as_str().is_empty() {
                 return on_argumentv1_new(cx, &tup[0], arms);
             }
diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs
index 50b39cf4ea7..fd45a6da61c 100644
--- a/clippy_lints/src/functions.rs
+++ b/clippy_lints/src/functions.rs
@@ -1,8 +1,9 @@
 use crate::utils::{
-    attr_by_name, attrs::is_proc_macro, is_must_use_ty, is_trait_impl_item, iter_input_pats, match_def_path,
-    must_use_attr, qpath_res, return_ty, snippet, snippet_opt, span_lint, span_lint_and_help, span_lint_and_then,
-    trait_ref_of_method, type_is_unsafe_function,
+    attr_by_name, attrs::is_proc_macro, is_must_use_ty, is_trait_impl_item, is_type_diagnostic_item, iter_input_pats,
+    last_path_segment, match_def_path, must_use_attr, qpath_res, return_ty, snippet, snippet_opt, span_lint,
+    span_lint_and_help, span_lint_and_then, trait_ref_of_method, type_is_unsafe_function,
 };
+use if_chain::if_chain;
 use rustc_ast::ast::Attribute;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -16,6 +17,7 @@ use rustc_middle::ty::{self, Ty};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::Span;
 use rustc_target::spec::abi::Abi;
+use rustc_typeck::hir_ty_to_ty;
 
 declare_clippy_lint! {
     /// **What it does:** Checks for functions with too many parameters.
@@ -169,6 +171,52 @@ declare_clippy_lint! {
     "function or method that could take a `#[must_use]` attribute"
 }
 
+declare_clippy_lint! {
+    /// **What it does:** Checks for public functions that return a `Result`
+    /// with an `Err` type of `()`. It suggests using a custom type that
+    /// implements [`std::error::Error`].
+    ///
+    /// **Why is this bad?** Unit does not implement `Error` and carries no
+    /// further information about what went wrong.
+    ///
+    /// **Known problems:** Of course, this lint assumes that `Result` is used
+    /// for a fallible operation (which is after all the intended use). However
+    /// code may opt to (mis)use it as a basic two-variant-enum. In that case,
+    /// the suggestion is misguided, and the code should use a custom enum
+    /// instead.
+    ///
+    /// **Examples:**
+    /// ```rust
+    /// pub fn read_u8() -> Result<u8, ()> { Err(()) }
+    /// ```
+    /// should become
+    /// ```rust,should_panic
+    /// use std::fmt;
+    ///
+    /// #[derive(Debug)]
+    /// pub struct EndOfStream;
+    ///
+    /// impl fmt::Display for EndOfStream {
+    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+    ///         write!(f, "End of Stream")
+    ///     }
+    /// }
+    ///
+    /// impl std::error::Error for EndOfStream { }
+    ///
+    /// pub fn read_u8() -> Result<u8, EndOfStream> { Err(EndOfStream) }
+    ///# fn main() {
+    ///#     read_u8().unwrap();
+    ///# }
+    /// ```
+    ///
+    /// Note that there are crates that simplify creating the error type, e.g.
+    /// [`thiserror`](https://docs.rs/thiserror).
+    pub RESULT_UNIT_ERR,
+    style,
+    "public function returning `Result` with an `Err` type of `()`"
+}
+
 #[derive(Copy, Clone)]
 pub struct Functions {
     threshold: u64,
@@ -188,6 +236,7 @@ impl_lint_pass!(Functions => [
     MUST_USE_UNIT,
     DOUBLE_MUST_USE,
     MUST_USE_CANDIDATE,
+    RESULT_UNIT_ERR,
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Functions {
@@ -233,15 +282,16 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
         let attr = must_use_attr(&item.attrs);
         if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind {
+            let is_public = cx.access_levels.is_exported(item.hir_id);
+            let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
+            if is_public {
+                check_result_unit_err(cx, &sig.decl, item.span, fn_header_span);
+            }
             if let Some(attr) = attr {
-                let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
                 check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr);
                 return;
             }
-            if cx.access_levels.is_exported(item.hir_id)
-                && !is_proc_macro(cx.sess(), &item.attrs)
-                && attr_by_name(&item.attrs, "no_mangle").is_none()
-            {
+            if is_public && !is_proc_macro(cx.sess(), &item.attrs) && attr_by_name(&item.attrs, "no_mangle").is_none() {
                 check_must_use_candidate(
                     cx,
                     &sig.decl,
@@ -257,11 +307,15 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
 
     fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
         if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind {
+            let is_public = cx.access_levels.is_exported(item.hir_id);
+            let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
+            if is_public && trait_ref_of_method(cx, item.hir_id).is_none() {
+                check_result_unit_err(cx, &sig.decl, item.span, fn_header_span);
+            }
             let attr = must_use_attr(&item.attrs);
             if let Some(attr) = attr {
-                let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
                 check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr);
-            } else if cx.access_levels.is_exported(item.hir_id)
+            } else if is_public
                 && !is_proc_macro(cx.sess(), &item.attrs)
                 && trait_ref_of_method(cx, item.hir_id).is_none()
             {
@@ -284,18 +338,21 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
             if sig.header.abi == Abi::Rust {
                 self.check_arg_number(cx, &sig.decl, item.span.with_hi(sig.decl.output.span().hi()));
             }
+            let is_public = cx.access_levels.is_exported(item.hir_id);
+            let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
+            if is_public {
+                check_result_unit_err(cx, &sig.decl, item.span, fn_header_span);
+            }
 
             let attr = must_use_attr(&item.attrs);
             if let Some(attr) = attr {
-                let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
                 check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr);
             }
             if let hir::TraitFn::Provided(eid) = *eid {
                 let body = cx.tcx.hir().body(eid);
                 Self::check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id);
 
-                if attr.is_none() && cx.access_levels.is_exported(item.hir_id) && !is_proc_macro(cx.sess(), &item.attrs)
-                {
+                if attr.is_none() && is_public && !is_proc_macro(cx.sess(), &item.attrs) {
                     check_must_use_candidate(
                         cx,
                         &sig.decl,
@@ -411,6 +468,29 @@ impl<'tcx> Functions {
     }
 }
 
+fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span: Span, fn_header_span: Span) {
+    if_chain! {
+        if !in_external_macro(cx.sess(), item_span);
+        if let hir::FnRetTy::Return(ref ty) = decl.output;
+        if let hir::TyKind::Path(ref qpath) = ty.kind;
+        if is_type_diagnostic_item(cx, hir_ty_to_ty(cx.tcx, ty), sym!(result_type));
+        if let Some(ref args) = last_path_segment(qpath).args;
+        if let [_, hir::GenericArg::Type(ref err_ty)] = args.args;
+        if let hir::TyKind::Tup(t) = err_ty.kind;
+        if t.is_empty();
+        then {
+            span_lint_and_help(
+                cx,
+                RESULT_UNIT_ERR,
+                fn_header_span,
+                "this returns a `Result<_, ()>",
+                None,
+                "use a custom Error type instead",
+            );
+        }
+    }
+}
+
 fn check_needless_must_use(
     cx: &LateContext<'_>,
     decl: &hir::FnDecl<'_>,
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 93b5d9e178c..d4d2f92a6a6 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -234,6 +234,7 @@ mod main_recursion;
 mod manual_async_fn;
 mod manual_non_exhaustive;
 mod manual_strip;
+mod manual_unwrap_or;
 mod map_clone;
 mod map_err_ignore;
 mod map_identity;
@@ -281,6 +282,7 @@ mod path_buf_push_overwrite;
 mod pattern_type_mismatch;
 mod precedence;
 mod ptr;
+mod ptr_eq;
 mod ptr_offset_with_cast;
 mod question_mark;
 mod ranges;
@@ -581,6 +583,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &functions::MUST_USE_CANDIDATE,
         &functions::MUST_USE_UNIT,
         &functions::NOT_UNSAFE_PTR_ARG_DEREF,
+        &functions::RESULT_UNIT_ERR,
         &functions::TOO_MANY_ARGUMENTS,
         &functions::TOO_MANY_LINES,
         &future_not_send::FUTURE_NOT_SEND,
@@ -638,6 +641,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &manual_async_fn::MANUAL_ASYNC_FN,
         &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
         &manual_strip::MANUAL_STRIP,
+        &manual_unwrap_or::MANUAL_UNWRAP_OR,
         &map_clone::MAP_CLONE,
         &map_err_ignore::MAP_ERR_IGNORE,
         &map_identity::MAP_IDENTITY,
@@ -778,6 +782,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &ptr::CMP_NULL,
         &ptr::MUT_FROM_REF,
         &ptr::PTR_ARG,
+        &ptr_eq::PTR_EQ,
         &ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
         &question_mark::QUESTION_MARK,
         &ranges::RANGE_MINUS_ONE,
@@ -916,6 +921,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
     store.register_late_pass(move || box bit_mask::BitMask::new(verbose_bit_mask_threshold));
     store.register_late_pass(|| box ptr::Ptr);
+    store.register_late_pass(|| box ptr_eq::PtrEq);
     store.register_late_pass(|| box needless_bool::NeedlessBool);
     store.register_late_pass(|| box needless_bool::BoolComparison);
     store.register_late_pass(|| box approx_const::ApproxConstant);
@@ -1122,6 +1128,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box repeat_once::RepeatOnce);
     store.register_late_pass(|| box unwrap_in_result::UnwrapInResult);
     store.register_late_pass(|| box self_assignment::SelfAssignment);
+    store.register_late_pass(|| box manual_unwrap_or::ManualUnwrapOr);
     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);
@@ -1324,6 +1331,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&functions::DOUBLE_MUST_USE),
         LintId::of(&functions::MUST_USE_UNIT),
         LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF),
+        LintId::of(&functions::RESULT_UNIT_ERR),
         LintId::of(&functions::TOO_MANY_ARGUMENTS),
         LintId::of(&get_last_with_len::GET_LAST_WITH_LEN),
         LintId::of(&identity_op::IDENTITY_OP),
@@ -1362,6 +1370,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&manual_async_fn::MANUAL_ASYNC_FN),
         LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
         LintId::of(&manual_strip::MANUAL_STRIP),
+        LintId::of(&manual_unwrap_or::MANUAL_UNWRAP_OR),
         LintId::of(&map_clone::MAP_CLONE),
         LintId::of(&map_identity::MAP_IDENTITY),
         LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN),
@@ -1457,6 +1466,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&ptr::CMP_NULL),
         LintId::of(&ptr::MUT_FROM_REF),
         LintId::of(&ptr::PTR_ARG),
+        LintId::of(&ptr_eq::PTR_EQ),
         LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
         LintId::of(&question_mark::QUESTION_MARK),
         LintId::of(&ranges::RANGE_ZIP_WITH_LEN),
@@ -1554,6 +1564,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
         LintId::of(&functions::DOUBLE_MUST_USE),
         LintId::of(&functions::MUST_USE_UNIT),
+        LintId::of(&functions::RESULT_UNIT_ERR),
         LintId::of(&if_let_some_result::IF_LET_SOME_RESULT),
         LintId::of(&inherent_to_string::INHERENT_TO_STRING),
         LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
@@ -1611,6 +1622,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&panic_unimplemented::PANIC_PARAMS),
         LintId::of(&ptr::CMP_NULL),
         LintId::of(&ptr::PTR_ARG),
+        LintId::of(&ptr_eq::PTR_EQ),
         LintId::of(&question_mark::QUESTION_MARK),
         LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
         LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
@@ -1654,6 +1666,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&loops::MUT_RANGE_BOUND),
         LintId::of(&loops::WHILE_LET_LOOP),
         LintId::of(&manual_strip::MANUAL_STRIP),
+        LintId::of(&manual_unwrap_or::MANUAL_UNWRAP_OR),
         LintId::of(&map_identity::MAP_IDENTITY),
         LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN),
         LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN),
diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs
index 4fdcaca8f60..63d7e3176b1 100644
--- a/clippy_lints/src/loops.rs
+++ b/clippy_lints/src/loops.rs
@@ -5,9 +5,8 @@ use crate::utils::usage::{is_unused, mutated_variables};
 use crate::utils::{
     contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
     is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method,
-    match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability,
-    snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg,
-    SpanlessEq,
+    match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_with_applicability, snippet_with_macro_callsite,
+    span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq,
 };
 use if_chain::if_chain;
 use rustc_ast::ast;
@@ -770,15 +769,28 @@ fn check_for_loop<'tcx>(
     body: &'tcx Expr<'_>,
     expr: &'tcx Expr<'_>,
 ) {
-    check_for_loop_range(cx, pat, arg, body, expr);
+    let is_manual_memcpy_triggered = detect_manual_memcpy(cx, pat, arg, body, expr);
+    if !is_manual_memcpy_triggered {
+        check_for_loop_range(cx, pat, arg, body, expr);
+        check_for_loop_explicit_counter(cx, pat, arg, body, expr);
+    }
     check_for_loop_arg(cx, pat, arg, expr);
-    check_for_loop_explicit_counter(cx, pat, arg, body, expr);
     check_for_loop_over_map_kv(cx, pat, arg, body, expr);
     check_for_mut_range_bound(cx, arg, body);
-    detect_manual_memcpy(cx, pat, arg, body, expr);
     detect_same_item_push(cx, pat, arg, body, expr);
 }
 
+// this function assumes the given expression is a `for` loop.
+fn get_span_of_entire_for_loop(expr: &Expr<'_>) -> Span {
+    // for some reason this is the only way to get the `Span`
+    // of the entire `for` loop
+    if let ExprKind::Match(_, arms, _) = &expr.kind {
+        arms[0].body.span
+    } else {
+        unreachable!()
+    }
+}
+
 fn same_var<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, var: HirId) -> bool {
     if_chain! {
         if let ExprKind::Path(qpath) = &expr.kind;
@@ -794,36 +806,131 @@ fn same_var<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, var: HirId) -> bool {
     }
 }
 
-#[derive(Clone, Copy)]
-enum OffsetSign {
-    Positive,
-    Negative,
+/// a wrapper of `Sugg`. Besides what `Sugg` do, this removes unnecessary `0`;
+/// and also, it avoids subtracting a variable from the same one by replacing it with `0`.
+/// it exists for the convenience of the overloaded operators while normal functions can do the
+/// same.
+#[derive(Clone)]
+struct MinifyingSugg<'a>(Sugg<'a>);
+
+impl<'a> MinifyingSugg<'a> {
+    fn as_str(&self) -> &str {
+        let Sugg::NonParen(s) | Sugg::MaybeParen(s) | Sugg::BinOp(_, s) = &self.0;
+        s.as_ref()
+    }
+
+    fn into_sugg(self) -> Sugg<'a> {
+        self.0
+    }
+}
+
+impl<'a> From<Sugg<'a>> for MinifyingSugg<'a> {
+    fn from(sugg: Sugg<'a>) -> Self {
+        Self(sugg)
+    }
+}
+
+impl std::ops::Add for &MinifyingSugg<'static> {
+    type Output = MinifyingSugg<'static>;
+    fn add(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> {
+        match (self.as_str(), rhs.as_str()) {
+            ("0", _) => rhs.clone(),
+            (_, "0") => self.clone(),
+            (_, _) => (&self.0 + &rhs.0).into(),
+        }
+    }
+}
+
+impl std::ops::Sub for &MinifyingSugg<'static> {
+    type Output = MinifyingSugg<'static>;
+    fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> {
+        match (self.as_str(), rhs.as_str()) {
+            (_, "0") => self.clone(),
+            ("0", _) => (-rhs.0.clone()).into(),
+            (x, y) if x == y => sugg::ZERO.into(),
+            (_, _) => (&self.0 - &rhs.0).into(),
+        }
+    }
+}
+
+impl std::ops::Add<&MinifyingSugg<'static>> for MinifyingSugg<'static> {
+    type Output = MinifyingSugg<'static>;
+    fn add(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> {
+        match (self.as_str(), rhs.as_str()) {
+            ("0", _) => rhs.clone(),
+            (_, "0") => self,
+            (_, _) => (self.0 + &rhs.0).into(),
+        }
+    }
 }
 
+impl std::ops::Sub<&MinifyingSugg<'static>> for MinifyingSugg<'static> {
+    type Output = MinifyingSugg<'static>;
+    fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> {
+        match (self.as_str(), rhs.as_str()) {
+            (_, "0") => self,
+            ("0", _) => (-rhs.0.clone()).into(),
+            (x, y) if x == y => sugg::ZERO.into(),
+            (_, _) => (self.0 - &rhs.0).into(),
+        }
+    }
+}
+
+/// a wrapper around `MinifyingSugg`, which carries a operator like currying
+/// so that the suggested code become more efficient (e.g. `foo + -bar` `foo - bar`).
 struct Offset {
-    value: String,
+    value: MinifyingSugg<'static>,
     sign: OffsetSign,
 }
 
+#[derive(Clone, Copy)]
+enum OffsetSign {
+    Positive,
+    Negative,
+}
+
 impl Offset {
-    fn negative(value: String) -> Self {
+    fn negative(value: Sugg<'static>) -> Self {
         Self {
-            value,
+            value: value.into(),
             sign: OffsetSign::Negative,
         }
     }
 
-    fn positive(value: String) -> Self {
+    fn positive(value: Sugg<'static>) -> Self {
         Self {
-            value,
+            value: value.into(),
             sign: OffsetSign::Positive,
         }
     }
+
+    fn empty() -> Self {
+        Self::positive(sugg::ZERO)
+    }
 }
 
-struct FixedOffsetVar<'hir> {
-    var: &'hir Expr<'hir>,
-    offset: Offset,
+fn apply_offset(lhs: &MinifyingSugg<'static>, rhs: &Offset) -> MinifyingSugg<'static> {
+    match rhs.sign {
+        OffsetSign::Positive => lhs + &rhs.value,
+        OffsetSign::Negative => lhs - &rhs.value,
+    }
+}
+
+#[derive(Debug, Clone, Copy)]
+enum StartKind<'hir> {
+    Range,
+    Counter { initializer: &'hir Expr<'hir> },
+}
+
+struct IndexExpr<'hir> {
+    base: &'hir Expr<'hir>,
+    idx: StartKind<'hir>,
+    idx_offset: Offset,
+}
+
+struct Start<'hir> {
+    id: HirId,
+    kind: StartKind<'hir>,
 }
 
 fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool {
@@ -846,14 +953,28 @@ fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
     }
 }
 
-fn get_offset<'tcx>(cx: &LateContext<'tcx>, idx: &Expr<'_>, var: HirId) -> Option<Offset> {
-    fn extract_offset<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>, var: HirId) -> Option<String> {
+fn get_details_from_idx<'tcx>(
+    cx: &LateContext<'tcx>,
+    idx: &Expr<'_>,
+    starts: &[Start<'tcx>],
+) -> Option<(StartKind<'tcx>, Offset)> {
+    fn get_start<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>, starts: &[Start<'tcx>]) -> Option<StartKind<'tcx>> {
+        starts.iter().find_map(|start| {
+            if same_var(cx, e, start.id) {
+                Some(start.kind)
+            } else {
+                None
+            }
+        })
+    }
+
+    fn get_offset<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>, starts: &[Start<'tcx>]) -> Option<Sugg<'static>> {
         match &e.kind {
             ExprKind::Lit(l) => match l.node {
-                ast::LitKind::Int(x, _ty) => Some(x.to_string()),
+                ast::LitKind::Int(x, _ty) => Some(Sugg::NonParen(x.to_string().into())),
                 _ => None,
             },
-            ExprKind::Path(..) if !same_var(cx, e, var) => Some(snippet_opt(cx, e.span).unwrap_or_else(|| "??".into())),
+            ExprKind::Path(..) if get_start(cx, e, starts).is_none() => Some(Sugg::hir(cx, e, "???")),
             _ => None,
         }
     }
@@ -861,55 +982,89 @@ fn get_offset<'tcx>(cx: &LateContext<'tcx>, idx: &Expr<'_>, var: HirId) -> Optio
     match idx.kind {
         ExprKind::Binary(op, lhs, rhs) => match op.node {
             BinOpKind::Add => {
-                let offset_opt = if same_var(cx, lhs, var) {
-                    extract_offset(cx, rhs, var)
-                } else if same_var(cx, rhs, var) {
-                    extract_offset(cx, lhs, var)
-                } else {
-                    None
-                };
+                let offset_opt = get_start(cx, lhs, starts)
+                    .and_then(|s| get_offset(cx, rhs, starts).map(|o| (s, o)))
+                    .or_else(|| get_start(cx, rhs, starts).and_then(|s| get_offset(cx, lhs, starts).map(|o| (s, o))));
 
-                offset_opt.map(Offset::positive)
+                offset_opt.map(|(s, o)| (s, Offset::positive(o)))
+            },
+            BinOpKind::Sub => {
+                get_start(cx, lhs, starts).and_then(|s| get_offset(cx, rhs, starts).map(|o| (s, Offset::negative(o))))
             },
-            BinOpKind::Sub if same_var(cx, lhs, var) => extract_offset(cx, rhs, var).map(Offset::negative),
             _ => None,
         },
-        ExprKind::Path(..) if same_var(cx, idx, var) => Some(Offset::positive("0".into())),
+        ExprKind::Path(..) => get_start(cx, idx, starts).map(|s| (s, Offset::empty())),
         _ => None,
     }
 }
 
-fn get_assignments<'tcx>(body: &'tcx Expr<'tcx>) -> impl Iterator<Item = Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>> {
-    fn get_assignment<'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
-        if let ExprKind::Assign(lhs, rhs, _) = e.kind {
-            Some((lhs, rhs))
-        } else {
-            None
-        }
+fn get_assignment<'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
+    if let ExprKind::Assign(lhs, rhs, _) = e.kind {
+        Some((lhs, rhs))
+    } else {
+        None
     }
+}
 
-    // This is one of few ways to return different iterators
-    // derived from: https://stackoverflow.com/questions/29760668/conditionally-iterate-over-one-of-several-possible-iterators/52064434#52064434
-    let mut iter_a = None;
-    let mut iter_b = None;
+/// Get assignments from the given block.
+/// The returned iterator yields `None` if no assignment expressions are there,
+/// filtering out the increments of the given whitelisted loop counters;
+/// because its job is to make sure there's nothing other than assignments and the increments.
+fn get_assignments<'a: 'c, 'tcx: 'c, 'c>(
+    cx: &'a LateContext<'tcx>,
+    Block { stmts, expr, .. }: &'tcx Block<'tcx>,
+    loop_counters: &'c [Start<'tcx>],
+) -> impl Iterator<Item = Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>> + 'c {
+    // As the `filter` and `map` below do different things, I think putting together
+    // just increases complexity. (cc #3188 and #4193)
+    #[allow(clippy::filter_map)]
+    stmts
+        .iter()
+        .filter_map(move |stmt| match stmt.kind {
+            StmtKind::Local(..) | StmtKind::Item(..) => None,
+            StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e),
+        })
+        .chain((*expr).into_iter())
+        .filter(move |e| {
+            if let ExprKind::AssignOp(_, place, _) = e.kind {
+                !loop_counters
+                    .iter()
+                    // skip the first item which should be `StartKind::Range`
+                    // this makes it possible to use the slice with `StartKind::Range` in the same iterator loop.
+                    .skip(1)
+                    .any(|counter| same_var(cx, place, counter.id))
+            } else {
+                true
+            }
+        })
+        .map(get_assignment)
+}
 
-    if let ExprKind::Block(b, _) = body.kind {
-        let Block { stmts, expr, .. } = *b;
+fn get_loop_counters<'a, 'tcx>(
+    cx: &'a LateContext<'tcx>,
+    body: &'tcx Block<'tcx>,
+    expr: &'tcx Expr<'_>,
+) -> Option<impl Iterator<Item = Start<'tcx>> + 'a> {
+    // Look for variables that are incremented once per loop iteration.
+    let mut increment_visitor = IncrementVisitor::new(cx);
+    walk_block(&mut increment_visitor, body);
 
-        iter_a = stmts
-            .iter()
-            .filter_map(|stmt| match stmt.kind {
-                StmtKind::Local(..) | StmtKind::Item(..) => None,
-                StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e),
+    // For each candidate, check the parent block to see if
+    // it's initialized to zero at the start of the loop.
+    get_enclosing_block(&cx, expr.hir_id).and_then(|block| {
+        increment_visitor
+            .into_results()
+            .filter_map(move |var_id| {
+                let mut initialize_visitor = InitializeVisitor::new(cx, expr, var_id);
+                walk_block(&mut initialize_visitor, block);
+
+                initialize_visitor.get_result().map(|(_, initializer)| Start {
+                    id: var_id,
+                    kind: StartKind::Counter { initializer },
+                })
             })
-            .chain(expr.into_iter())
-            .map(get_assignment)
             .into()
-    } else {
-        iter_b = Some(get_assignment(body))
-    }
-
-    iter_a.into_iter().flatten().chain(iter_b.into_iter())
+    })
 }
 
 fn build_manual_memcpy_suggestion<'tcx>(
@@ -917,80 +1072,97 @@ fn build_manual_memcpy_suggestion<'tcx>(
     start: &Expr<'_>,
     end: &Expr<'_>,
     limits: ast::RangeLimits,
-    dst_var: FixedOffsetVar<'_>,
-    src_var: FixedOffsetVar<'_>,
+    dst: &IndexExpr<'_>,
+    src: &IndexExpr<'_>,
 ) -> String {
-    fn print_sum(arg1: &str, arg2: &Offset) -> String {
-        match (arg1, &arg2.value[..], arg2.sign) {
-            ("0", "0", _) => "0".into(),
-            ("0", x, OffsetSign::Positive) | (x, "0", _) => x.into(),
-            ("0", x, OffsetSign::Negative) => format!("-{}", x),
-            (x, y, OffsetSign::Positive) => format!("({} + {})", x, y),
-            (x, y, OffsetSign::Negative) => {
-                if x == y {
-                    "0".into()
-                } else {
-                    format!("({} - {})", x, y)
-                }
-            },
-        }
-    }
-
-    fn print_offset(start_str: &str, inline_offset: &Offset) -> String {
-        let offset = print_sum(start_str, inline_offset);
+    fn print_offset(offset: MinifyingSugg<'static>) -> MinifyingSugg<'static> {
         if offset.as_str() == "0" {
-            "".into()
+            sugg::EMPTY.into()
         } else {
             offset
         }
     }
 
-    let print_limit = |end: &Expr<'_>, offset: Offset, var: &Expr<'_>| {
+    let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| {
         if_chain! {
             if let ExprKind::MethodCall(method, _, len_args, _) = end.kind;
             if method.ident.name == sym!(len);
             if len_args.len() == 1;
             if let Some(arg) = len_args.get(0);
-            if var_def_id(cx, arg) == var_def_id(cx, var);
+            if var_def_id(cx, arg) == var_def_id(cx, base);
             then {
-                match offset.sign {
-                    OffsetSign::Negative => format!("({} - {})", snippet(cx, end.span, "<src>.len()"), offset.value),
-                    OffsetSign::Positive => "".into(),
+                if sugg.as_str() == end_str {
+                    sugg::EMPTY.into()
+                } else {
+                    sugg
                 }
             } else {
-                let end_str = match limits {
+                match limits {
                     ast::RangeLimits::Closed => {
-                        let end = sugg::Sugg::hir(cx, end, "<count>");
-                        format!("{}", end + sugg::ONE)
+                        sugg + &sugg::ONE.into()
                     },
-                    ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")),
-                };
-
-                print_sum(&end_str, &offset)
+                    ast::RangeLimits::HalfOpen => sugg,
+                }
             }
         }
     };
 
-    let start_str = snippet(cx, start.span, "").to_string();
-    let dst_offset = print_offset(&start_str, &dst_var.offset);
-    let dst_limit = print_limit(end, dst_var.offset, dst_var.var);
-    let src_offset = print_offset(&start_str, &src_var.offset);
-    let src_limit = print_limit(end, src_var.offset, src_var.var);
+    let start_str = Sugg::hir(cx, start, "").into();
+    let end_str: MinifyingSugg<'_> = Sugg::hir(cx, end, "").into();
+
+    let print_offset_and_limit = |idx_expr: &IndexExpr<'_>| match idx_expr.idx {
+        StartKind::Range => (
+            print_offset(apply_offset(&start_str, &idx_expr.idx_offset)).into_sugg(),
+            print_limit(
+                end,
+                end_str.as_str(),
+                idx_expr.base,
+                apply_offset(&end_str, &idx_expr.idx_offset),
+            )
+            .into_sugg(),
+        ),
+        StartKind::Counter { initializer } => {
+            let counter_start = Sugg::hir(cx, initializer, "").into();
+            (
+                print_offset(apply_offset(&counter_start, &idx_expr.idx_offset)).into_sugg(),
+                print_limit(
+                    end,
+                    end_str.as_str(),
+                    idx_expr.base,
+                    apply_offset(&end_str, &idx_expr.idx_offset) + &counter_start - &start_str,
+                )
+                .into_sugg(),
+            )
+        },
+    };
+
+    let (dst_offset, dst_limit) = print_offset_and_limit(&dst);
+    let (src_offset, src_limit) = print_offset_and_limit(&src);
 
-    let dst_var_name = snippet_opt(cx, dst_var.var.span).unwrap_or_else(|| "???".into());
-    let src_var_name = snippet_opt(cx, src_var.var.span).unwrap_or_else(|| "???".into());
+    let dst_base_str = snippet(cx, dst.base.span, "???");
+    let src_base_str = snippet(cx, src.base.span, "???");
 
-    let dst = if dst_offset == "" && dst_limit == "" {
-        dst_var_name
+    let dst = if dst_offset == sugg::EMPTY && dst_limit == sugg::EMPTY {
+        dst_base_str
     } else {
-        format!("{}[{}..{}]", dst_var_name, dst_offset, dst_limit)
+        format!(
+            "{}[{}..{}]",
+            dst_base_str,
+            dst_offset.maybe_par(),
+            dst_limit.maybe_par()
+        )
+        .into()
     };
 
     format!(
-        "{}.clone_from_slice(&{}[{}..{}])",
-        dst, src_var_name, src_offset, src_limit
+        "{}.clone_from_slice(&{}[{}..{}]);",
+        dst,
+        src_base_str,
+        src_offset.maybe_par(),
+        src_limit.maybe_par()
     )
 }
+
 /// Checks for for loops that sequentially copy items from one slice-like
 /// object to another.
 fn detect_manual_memcpy<'tcx>(
@@ -999,7 +1171,7 @@ fn detect_manual_memcpy<'tcx>(
     arg: &'tcx Expr<'_>,
     body: &'tcx Expr<'_>,
     expr: &'tcx Expr<'_>,
-) {
+) -> bool {
     if let Some(higher::Range {
         start: Some(start),
         end: Some(end),
@@ -1008,32 +1180,53 @@ fn detect_manual_memcpy<'tcx>(
     {
         // the var must be a single name
         if let PatKind::Binding(_, canonical_id, _, _) = pat.kind {
-            // The only statements in the for loops can be indexed assignments from
-            // indexed retrievals.
-            let big_sugg = get_assignments(body)
+            let mut starts = vec![Start {
+                id: canonical_id,
+                kind: StartKind::Range,
+            }];
+
+            // This is one of few ways to return different iterators
+            // derived from: https://stackoverflow.com/questions/29760668/conditionally-iterate-over-one-of-several-possible-iterators/52064434#52064434
+            let mut iter_a = None;
+            let mut iter_b = None;
+
+            if let ExprKind::Block(block, _) = body.kind {
+                if let Some(loop_counters) = get_loop_counters(cx, block, expr) {
+                    starts.extend(loop_counters);
+                }
+                iter_a = Some(get_assignments(cx, block, &starts));
+            } else {
+                iter_b = Some(get_assignment(body));
+            }
+
+            let assignments = iter_a.into_iter().flatten().chain(iter_b.into_iter());
+
+            let big_sugg = assignments
+                // The only statements in the for loops can be indexed assignments from
+                // indexed retrievals (except increments of loop counters).
                 .map(|o| {
                     o.and_then(|(lhs, rhs)| {
                         let rhs = fetch_cloned_expr(rhs);
                         if_chain! {
-                            if let ExprKind::Index(seqexpr_left, idx_left) = lhs.kind;
-                            if let ExprKind::Index(seqexpr_right, idx_right) = rhs.kind;
-                            if is_slice_like(cx, cx.typeck_results().expr_ty(seqexpr_left))
-                                && is_slice_like(cx, cx.typeck_results().expr_ty(seqexpr_right));
-                            if let Some(offset_left) = get_offset(cx, &idx_left, canonical_id);
-                            if let Some(offset_right) = get_offset(cx, &idx_right, canonical_id);
+                            if let ExprKind::Index(base_left, idx_left) = lhs.kind;
+                            if let ExprKind::Index(base_right, idx_right) = rhs.kind;
+                            if is_slice_like(cx, cx.typeck_results().expr_ty(base_left))
+                                && is_slice_like(cx, cx.typeck_results().expr_ty(base_right));
+                            if let Some((start_left, offset_left)) = get_details_from_idx(cx, &idx_left, &starts);
+                            if let Some((start_right, offset_right)) = get_details_from_idx(cx, &idx_right, &starts);
 
                             // Source and destination must be different
-                            if var_def_id(cx, seqexpr_left) != var_def_id(cx, seqexpr_right);
+                            if var_def_id(cx, base_left) != var_def_id(cx, base_right);
                             then {
-                                Some((FixedOffsetVar { var: seqexpr_left, offset: offset_left },
-                                    FixedOffsetVar { var: seqexpr_right, offset: offset_right }))
+                                Some((IndexExpr { base: base_left, idx: start_left, idx_offset: offset_left },
+                                    IndexExpr { base: base_right, idx: start_right, idx_offset: offset_right }))
                             } else {
                                 None
                             }
                         }
                     })
                 })
-                .map(|o| o.map(|(dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, dst, src)))
+                .map(|o| o.map(|(dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, &dst, &src)))
                 .collect::<Option<Vec<_>>>()
                 .filter(|v| !v.is_empty())
                 .map(|v| v.join("\n    "));
@@ -1042,15 +1235,17 @@ fn detect_manual_memcpy<'tcx>(
                 span_lint_and_sugg(
                     cx,
                     MANUAL_MEMCPY,
-                    expr.span,
+                    get_span_of_entire_for_loop(expr),
                     "it looks like you're manually copying between slices",
                     "try replacing the loop by",
                     big_sugg,
                     Applicability::Unspecified,
                 );
+                return true;
             }
         }
     }
+    false
 }
 
 // Scans the body of the for loop and determines whether lint should be given
@@ -1533,6 +1728,9 @@ fn check_arg_type(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) {
     }
 }
 
+// To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be
+// incremented exactly once in the loop body, and initialized to zero
+// at the start of the loop.
 fn check_for_loop_explicit_counter<'tcx>(
     cx: &LateContext<'tcx>,
     pat: &'tcx Pat<'_>,
@@ -1541,40 +1739,23 @@ fn check_for_loop_explicit_counter<'tcx>(
     expr: &'tcx Expr<'_>,
 ) {
     // Look for variables that are incremented once per loop iteration.
-    let mut visitor = IncrementVisitor {
-        cx,
-        states: FxHashMap::default(),
-        depth: 0,
-        done: false,
-    };
-    walk_expr(&mut visitor, body);
+    let mut increment_visitor = IncrementVisitor::new(cx);
+    walk_expr(&mut increment_visitor, body);
 
     // For each candidate, check the parent block to see if
     // it's initialized to zero at the start of the loop.
     if let Some(block) = get_enclosing_block(&cx, expr.hir_id) {
-        for (id, _) in visitor.states.iter().filter(|&(_, v)| *v == VarState::IncrOnce) {
-            let mut visitor2 = InitializeVisitor {
-                cx,
-                end_expr: expr,
-                var_id: *id,
-                state: VarState::IncrOnce,
-                name: None,
-                depth: 0,
-                past_loop: false,
-            };
-            walk_block(&mut visitor2, block);
+        for id in increment_visitor.into_results() {
+            let mut initialize_visitor = InitializeVisitor::new(cx, expr, id);
+            walk_block(&mut initialize_visitor, block);
 
-            if visitor2.state == VarState::Warn {
-                if let Some(name) = visitor2.name {
+            if_chain! {
+                if let Some((name, initializer)) = initialize_visitor.get_result();
+                if is_integer_const(cx, initializer, 0);
+                then {
                     let mut applicability = Applicability::MachineApplicable;
 
-                    // for some reason this is the only way to get the `Span`
-                    // of the entire `for` loop
-                    let for_span = if let ExprKind::Match(_, arms, _) = &expr.kind {
-                        arms[0].body.span
-                    } else {
-                        unreachable!()
-                    };
+                    let for_span = get_span_of_entire_for_loop(expr);
 
                     span_lint_and_sugg(
                         cx,
@@ -2127,26 +2308,42 @@ fn is_simple_break_expr(expr: &Expr<'_>) -> bool {
     }
 }
 
-// To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be
-// incremented exactly once in the loop body, and initialized to zero
-// at the start of the loop.
 #[derive(Debug, PartialEq)]
-enum VarState {
+enum IncrementVisitorVarState {
     Initial,  // Not examined yet
     IncrOnce, // Incremented exactly once, may be a loop counter
-    Declared, // Declared but not (yet) initialized to zero
-    Warn,
     DontWarn,
 }
 
 /// Scan a for loop for variables that are incremented exactly once and not used after that.
 struct IncrementVisitor<'a, 'tcx> {
-    cx: &'a LateContext<'tcx>,          // context reference
-    states: FxHashMap<HirId, VarState>, // incremented variables
-    depth: u32,                         // depth of conditional expressions
+    cx: &'a LateContext<'tcx>,                          // context reference
+    states: FxHashMap<HirId, IncrementVisitorVarState>, // incremented variables
+    depth: u32,                                         // depth of conditional expressions
     done: bool,
 }
 
+impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> {
+    fn new(cx: &'a LateContext<'tcx>) -> Self {
+        Self {
+            cx,
+            states: FxHashMap::default(),
+            depth: 0,
+            done: false,
+        }
+    }
+
+    fn into_results(self) -> impl Iterator<Item = HirId> {
+        self.states.into_iter().filter_map(|(id, state)| {
+            if state == IncrementVisitorVarState::IncrOnce {
+                Some(id)
+            } else {
+                None
+            }
+        })
+    }
+}
+
 impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> {
     type Map = Map<'tcx>;
 
@@ -2158,85 +2355,118 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> {
         // If node is a variable
         if let Some(def_id) = var_def_id(self.cx, expr) {
             if let Some(parent) = get_parent_expr(self.cx, expr) {
-                let state = self.states.entry(def_id).or_insert(VarState::Initial);
-                if *state == VarState::IncrOnce {
-                    *state = VarState::DontWarn;
+                let state = self.states.entry(def_id).or_insert(IncrementVisitorVarState::Initial);
+                if *state == IncrementVisitorVarState::IncrOnce {
+                    *state = IncrementVisitorVarState::DontWarn;
                     return;
                 }
 
                 match parent.kind {
                     ExprKind::AssignOp(op, ref lhs, ref rhs) => {
                         if lhs.hir_id == expr.hir_id {
-                            if op.node == BinOpKind::Add && is_integer_const(self.cx, rhs, 1) {
-                                *state = match *state {
-                                    VarState::Initial if self.depth == 0 => VarState::IncrOnce,
-                                    _ => VarState::DontWarn,
-                                };
+                            *state = if op.node == BinOpKind::Add
+                                && is_integer_const(self.cx, rhs, 1)
+                                && *state == IncrementVisitorVarState::Initial
+                                && self.depth == 0
+                            {
+                                IncrementVisitorVarState::IncrOnce
                             } else {
-                                // Assigned some other value
-                                *state = VarState::DontWarn;
-                            }
+                                // Assigned some other value or assigned multiple times
+                                IncrementVisitorVarState::DontWarn
+                            };
                         }
                     },
-                    ExprKind::Assign(ref lhs, _, _) if lhs.hir_id == expr.hir_id => *state = VarState::DontWarn,
+                    ExprKind::Assign(ref lhs, _, _) if lhs.hir_id == expr.hir_id => {
+                        *state = IncrementVisitorVarState::DontWarn
+                    },
                     ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => {
-                        *state = VarState::DontWarn
+                        *state = IncrementVisitorVarState::DontWarn
                     },
                     _ => (),
                 }
             }
+
+            walk_expr(self, expr);
         } else if is_loop(expr) || is_conditional(expr) {
             self.depth += 1;
             walk_expr(self, expr);
             self.depth -= 1;
-            return;
         } else if let ExprKind::Continue(_) = expr.kind {
             self.done = true;
-            return;
+        } else {
+            walk_expr(self, expr);
         }
-        walk_expr(self, expr);
     }
     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
         NestedVisitorMap::None
     }
 }
 
-/// Checks whether a variable is initialized to zero at the start of a loop.
+enum InitializeVisitorState<'hir> {
+    Initial,          // Not examined yet
+    Declared(Symbol), // Declared but not (yet) initialized
+    Initialized {
+        name: Symbol,
+        initializer: &'hir Expr<'hir>,
+    },
+    DontWarn,
+}
+
+/// Checks whether a variable is initialized at the start of a loop and not modified
+/// and used after the loop.
 struct InitializeVisitor<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,  // context reference
     end_expr: &'tcx Expr<'tcx>, // the for loop. Stop scanning here.
     var_id: HirId,
-    state: VarState,
-    name: Option<Symbol>,
+    state: InitializeVisitorState<'tcx>,
     depth: u32, // depth of conditional expressions
     past_loop: bool,
 }
 
+impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> {
+    fn new(cx: &'a LateContext<'tcx>, end_expr: &'tcx Expr<'tcx>, var_id: HirId) -> Self {
+        Self {
+            cx,
+            end_expr,
+            var_id,
+            state: InitializeVisitorState::Initial,
+            depth: 0,
+            past_loop: false,
+        }
+    }
+
+    fn get_result(&self) -> Option<(Symbol, &'tcx Expr<'tcx>)> {
+        if let InitializeVisitorState::Initialized { name, initializer } = self.state {
+            Some((name, initializer))
+        } else {
+            None
+        }
+    }
+}
+
 impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
     type Map = Map<'tcx>;
 
     fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
         // Look for declarations of the variable
-        if let StmtKind::Local(ref local) = stmt.kind {
-            if local.pat.hir_id == self.var_id {
-                if let PatKind::Binding(.., ident, _) = local.pat.kind {
-                    self.name = Some(ident.name);
-
-                    self.state = local.init.as_ref().map_or(VarState::Declared, |init| {
-                        if is_integer_const(&self.cx, init, 0) {
-                            VarState::Warn
-                        } else {
-                            VarState::Declared
-                        }
-                    })
-                }
+        if_chain! {
+            if let StmtKind::Local(ref local) = stmt.kind;
+            if local.pat.hir_id == self.var_id;
+            if let PatKind::Binding(.., ident, _) = local.pat.kind;
+            then {
+                self.state = local.init.map_or(InitializeVisitorState::Declared(ident.name), |init| {
+                    InitializeVisitorState::Initialized {
+                        initializer: init,
+                        name: ident.name,
+                    }
+                })
             }
         }
         walk_stmt(self, stmt);
     }
 
     fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
-        if self.state == VarState::DontWarn {
+        if matches!(self.state, InitializeVisitorState::DontWarn) {
             return;
         }
         if expr.hir_id == self.end_expr.hir_id {
@@ -2245,45 +2475,51 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
         }
         // No need to visit expressions before the variable is
         // declared
-        if self.state == VarState::IncrOnce {
+        if matches!(self.state, InitializeVisitorState::Initial) {
             return;
         }
 
         // If node is the desired variable, see how it's used
         if var_def_id(self.cx, expr) == Some(self.var_id) {
+            if self.past_loop {
+                self.state = InitializeVisitorState::DontWarn;
+                return;
+            }
+
             if let Some(parent) = get_parent_expr(self.cx, expr) {
                 match parent.kind {
                     ExprKind::AssignOp(_, ref lhs, _) if lhs.hir_id == expr.hir_id => {
-                        self.state = VarState::DontWarn;
+                        self.state = InitializeVisitorState::DontWarn;
                     },
                     ExprKind::Assign(ref lhs, ref rhs, _) if lhs.hir_id == expr.hir_id => {
-                        self.state = if is_integer_const(&self.cx, rhs, 0) && self.depth == 0 {
-                            VarState::Warn
-                        } else {
-                            VarState::DontWarn
+                        self.state = if_chain! {
+                            if self.depth == 0;
+                            if let InitializeVisitorState::Declared(name)
+                                | InitializeVisitorState::Initialized { name, ..} = self.state;
+                            then {
+                                InitializeVisitorState::Initialized { initializer: rhs, name }
+                            } else {
+                                InitializeVisitorState::DontWarn
+                            }
                         }
                     },
                     ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => {
-                        self.state = VarState::DontWarn
+                        self.state = InitializeVisitorState::DontWarn
                     },
                     _ => (),
                 }
             }
 
-            if self.past_loop {
-                self.state = VarState::DontWarn;
-                return;
-            }
+            walk_expr(self, expr);
         } else if !self.past_loop && is_loop(expr) {
-            self.state = VarState::DontWarn;
-            return;
+            self.state = InitializeVisitorState::DontWarn;
         } else if is_conditional(expr) {
             self.depth += 1;
             walk_expr(self, expr);
             self.depth -= 1;
-            return;
+        } else {
+            walk_expr(self, expr);
         }
-        walk_expr(self, expr);
     }
 
     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs
new file mode 100644
index 00000000000..ddb8cc25077
--- /dev/null
+++ b/clippy_lints/src/manual_unwrap_or.rs
@@ -0,0 +1,104 @@
+use crate::consts::constant_simple;
+use crate::utils;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{def, Arm, Expr, ExprKind, PatKind, QPath};
+use rustc_lint::LintContext;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// **What it does:**
+    /// Finds patterns that reimplement `Option::unwrap_or`.
+    ///
+    /// **Why is this bad?**
+    /// Concise code helps focusing on behavior instead of boilerplate.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// ```rust
+    /// let foo: Option<i32> = None;
+    /// match foo {
+    ///     Some(v) => v,
+    ///     None => 1,
+    /// };
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// let foo: Option<i32> = None;
+    /// foo.unwrap_or(1);
+    /// ```
+    pub MANUAL_UNWRAP_OR,
+    complexity,
+    "finds patterns that can be encoded more concisely with `Option::unwrap_or`"
+}
+
+declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]);
+
+impl LateLintPass<'_> for ManualUnwrapOr {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+        if in_external_macro(cx.sess(), expr.span) {
+            return;
+        }
+        lint_option_unwrap_or_case(cx, expr);
+    }
+}
+
+fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+    fn applicable_none_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> {
+        if_chain! {
+            if arms.len() == 2;
+            if arms.iter().all(|arm| arm.guard.is_none());
+            if let Some((idx, none_arm)) = arms.iter().enumerate().find(|(_, arm)|
+                if let PatKind::Path(ref qpath) = arm.pat.kind {
+                    utils::match_qpath(qpath, &utils::paths::OPTION_NONE)
+                } else {
+                    false
+                }
+            );
+            let some_arm = &arms[1 - idx];
+            if let PatKind::TupleStruct(ref some_qpath, &[some_binding], _) = some_arm.pat.kind;
+            if utils::match_qpath(some_qpath, &utils::paths::OPTION_SOME);
+            if let PatKind::Binding(_, binding_hir_id, ..) = some_binding.kind;
+            if let ExprKind::Path(QPath::Resolved(_, body_path)) = some_arm.body.kind;
+            if let def::Res::Local(body_path_hir_id) = body_path.res;
+            if body_path_hir_id == binding_hir_id;
+            if !utils::usage::contains_return_break_continue_macro(none_arm.body);
+            then {
+                Some(none_arm)
+            } else {
+                None
+            }
+        }
+    }
+
+    if_chain! {
+        if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind;
+        let ty = cx.typeck_results().expr_ty(scrutinee);
+        if utils::is_type_diagnostic_item(cx, ty, sym!(option_type));
+        if let Some(none_arm) = applicable_none_arm(match_arms);
+        if let Some(scrutinee_snippet) = utils::snippet_opt(cx, scrutinee.span);
+        if let Some(none_body_snippet) = utils::snippet_opt(cx, none_arm.body.span);
+        if let Some(indent) = utils::indent_of(cx, expr.span);
+        if constant_simple(cx, cx.typeck_results(), none_arm.body).is_some();
+        then {
+            let reindented_none_body =
+                utils::reindent_multiline(none_body_snippet.into(), true, Some(indent));
+            utils::span_lint_and_sugg(
+                cx,
+                MANUAL_UNWRAP_OR, expr.span,
+                "this pattern reimplements `Option::unwrap_or`",
+                "replace with",
+                format!(
+                    "{}.unwrap_or({})",
+                    scrutinee_snippet,
+                    reindented_none_body,
+                ),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs
index 8a2dbdc50ea..4525b12689f 100644
--- a/clippy_lints/src/mut_key.rs
+++ b/clippy_lints/src/mut_key.rs
@@ -1,6 +1,7 @@
 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::TypeFoldable;
 use rustc_middle::ty::{Adt, Array, RawPtr, Ref, Slice, Tuple, Ty, TypeAndMut};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Span;
@@ -120,7 +121,11 @@ fn is_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) -> bo
             size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0) && is_mutable_type(cx, inner_ty, span)
         },
         Tuple(..) => ty.tuple_fields().any(|ty| is_mutable_type(cx, ty, span)),
-        Adt(..) => cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() && !ty.is_freeze(cx.tcx.at(span), cx.param_env),
+        Adt(..) => {
+            cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
+                && !ty.has_escaping_bound_vars()
+                && !ty.is_freeze(cx.tcx.at(span), cx.param_env)
+        },
         _ => false,
     }
 }
diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs
index cc635c2a202..76417aa7ed0 100644
--- a/clippy_lints/src/mutable_debug_assertion.rs
+++ b/clippy_lints/src/mutable_debug_assertion.rs
@@ -1,7 +1,6 @@
-use crate::utils::{is_direct_expn_of, span_lint};
-use if_chain::if_chain;
+use crate::utils::{higher, is_direct_expn_of, span_lint};
 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
-use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability, StmtKind, UnOp};
+use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::map::Map;
 use rustc_middle::ty;
@@ -39,66 +38,23 @@ impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
         for dmn in &DEBUG_MACRO_NAMES {
             if is_direct_expn_of(e.span, dmn).is_some() {
-                if let Some(span) = extract_call(cx, e) {
-                    span_lint(
-                        cx,
-                        DEBUG_ASSERT_WITH_MUT_CALL,
-                        span,
-                        &format!("do not call a function with mutable arguments inside of `{}!`", dmn),
-                    );
-                }
-            }
-        }
-    }
-}
-
-//HACK(hellow554): remove this when #4694 is implemented
-fn extract_call<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<Span> {
-    if_chain! {
-        if let ExprKind::Block(ref block, _) = e.kind;
-        if block.stmts.len() == 1;
-        if let StmtKind::Semi(ref matchexpr) = block.stmts[0].kind;
-        then {
-            // debug_assert
-            if_chain! {
-                if let ExprKind::Match(ref ifclause, _, _) = matchexpr.kind;
-                if let ExprKind::DropTemps(ref droptmp) = ifclause.kind;
-                if let ExprKind::Unary(UnOp::UnNot, ref condition) = droptmp.kind;
-                then {
-                    let mut visitor = MutArgVisitor::new(cx);
-                    visitor.visit_expr(condition);
-                    return visitor.expr_span();
-                }
-            }
-
-            // debug_assert_{eq,ne}
-            if_chain! {
-                if let ExprKind::Block(ref matchblock, _) = matchexpr.kind;
-                if let Some(ref matchheader) = matchblock.expr;
-                if let ExprKind::Match(ref headerexpr, _, _) = matchheader.kind;
-                if let ExprKind::Tup(ref conditions) = headerexpr.kind;
-                if conditions.len() == 2;
-                then {
-                    if let ExprKind::AddrOf(BorrowKind::Ref, _, ref lhs) = conditions[0].kind {
+                if let Some(macro_args) = higher::extract_assert_macro_args(e) {
+                    for arg in macro_args {
                         let mut visitor = MutArgVisitor::new(cx);
-                        visitor.visit_expr(lhs);
+                        visitor.visit_expr(arg);
                         if let Some(span) = visitor.expr_span() {
-                            return Some(span);
-                        }
-                    }
-                    if let ExprKind::AddrOf(BorrowKind::Ref, _, ref rhs) = conditions[1].kind {
-                        let mut visitor = MutArgVisitor::new(cx);
-                        visitor.visit_expr(rhs);
-                        if let Some(span) = visitor.expr_span() {
-                            return Some(span);
+                            span_lint(
+                                cx,
+                                DEBUG_ASSERT_WITH_MUT_CALL,
+                                span,
+                                &format!("do not call a function with mutable arguments inside of `{}!`", dmn),
+                            );
                         }
                     }
                 }
             }
         }
     }
-
-    None
 }
 
 struct MutArgVisitor<'a, 'tcx> {
diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs
index 4a3eb9c983a..eb7624b25a3 100644
--- a/clippy_lints/src/option_if_let_else.rs
+++ b/clippy_lints/src/option_if_let_else.rs
@@ -5,22 +5,20 @@ use crate::utils::{is_type_diagnostic_item, paths, span_lint_and_sugg};
 use if_chain::if_chain;
 
 use rustc_errors::Applicability;
-use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
 use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::hir::map::Map;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
 declare_clippy_lint! {
     /// **What it does:**
-    /// Lints usage of  `if let Some(v) = ... { y } else { x }` which is more
+    /// 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 pure
     /// expression) or `Option::map_or_else` (if the else bit is an impure
-    /// expresion).
+    /// expression).
     ///
     /// **Why is this bad?**
     /// Using the dedicated functions of the Option type is clearer and
-    /// more concise than an if let expression.
+    /// more concise than an `if let` expression.
     ///
     /// **Known problems:**
     /// This lint uses a deliberately conservative metric for checking
@@ -84,53 +82,6 @@ struct OptionIfLetElseOccurence {
     wrap_braces: bool,
 }
 
-struct ReturnBreakContinueMacroVisitor {
-    seen_return_break_continue: bool,
-}
-
-impl ReturnBreakContinueMacroVisitor {
-    fn new() -> ReturnBreakContinueMacroVisitor {
-        ReturnBreakContinueMacroVisitor {
-            seen_return_break_continue: false,
-        }
-    }
-}
-
-impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor {
-    type Map = Map<'tcx>;
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::None
-    }
-
-    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
-        if self.seen_return_break_continue {
-            // No need to look farther if we've already seen one of them
-            return;
-        }
-        match &ex.kind {
-            ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => {
-                self.seen_return_break_continue = true;
-            },
-            // Something special could be done here to handle while or for loop
-            // desugaring, as this will detect a break if there's a while loop
-            // or a for loop inside the expression.
-            _ => {
-                if utils::in_macro(ex.span) {
-                    self.seen_return_break_continue = true;
-                } else {
-                    rustc_hir::intravisit::walk_expr(self, ex);
-                }
-            },
-        }
-    }
-}
-
-fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
-    let mut recursive_visitor = ReturnBreakContinueMacroVisitor::new();
-    recursive_visitor.visit_expr(expression);
-    recursive_visitor.seen_return_break_continue
-}
-
 /// Extracts the body of a given arm. If the arm contains only an expression,
 /// then it returns the expression. Otherwise, it returns the entire block
 fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> {
@@ -208,8 +159,8 @@ fn detect_option_if_let_else<'tcx>(
         if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind;
         if utils::match_qpath(struct_qpath, &paths::OPTION_SOME);
         if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind;
-        if !contains_return_break_continue_macro(arms[0].body);
-        if !contains_return_break_continue_macro(arms[1].body);
+        if !utils::usage::contains_return_break_continue_macro(arms[0].body);
+        if !utils::usage::contains_return_break_continue_macro(arms[1].body);
         then {
             let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" };
             let some_body = extract_body_from_arm(&arms[0])?;
diff --git a/clippy_lints/src/ptr_eq.rs b/clippy_lints/src/ptr_eq.rs
new file mode 100644
index 00000000000..3be792ce5e4
--- /dev/null
+++ b/clippy_lints/src/ptr_eq.rs
@@ -0,0 +1,96 @@
+use crate::utils;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// **What it does:** Use `std::ptr::eq` when applicable
+    ///
+    /// **Why is this bad?** `ptr::eq` can be used to compare `&T` references
+    /// (which coerce to `*const T` implicitly) by their address rather than
+    /// comparing the values they point to.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// let a = &[1, 2, 3];
+    /// let b = &[1, 2, 3];
+    ///
+    /// assert!(a as *const _ as usize == b as *const _ as usize);
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let a = &[1, 2, 3];
+    /// let b = &[1, 2, 3];
+    ///
+    /// assert!(std::ptr::eq(a, b));
+    /// ```
+    pub PTR_EQ,
+    style,
+    "use `std::ptr::eq` when comparing raw pointers"
+}
+
+declare_lint_pass!(PtrEq => [PTR_EQ]);
+
+static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers";
+
+impl LateLintPass<'_> for PtrEq {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if utils::in_macro(expr.span) {
+            return;
+        }
+
+        if let ExprKind::Binary(ref op, ref left, ref right) = expr.kind {
+            if BinOpKind::Eq == op.node {
+                let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) {
+                    (Some(lhs), Some(rhs)) => (lhs, rhs),
+                    _ => (&**left, &**right),
+                };
+
+                if_chain! {
+                    if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left);
+                    if let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right);
+                    if let Some(left_snip) = utils::snippet_opt(cx, left_var.span);
+                    if let Some(right_snip) = utils::snippet_opt(cx, right_var.span);
+                    then {
+                        utils::span_lint_and_sugg(
+                            cx,
+                            PTR_EQ,
+                            expr.span,
+                            LINT_MSG,
+                            "try",
+                            format!("std::ptr::eq({}, {})", left_snip, right_snip),
+                            Applicability::MachineApplicable,
+                            );
+                    }
+                }
+            }
+        }
+    }
+}
+
+// If the given expression is a cast to an usize, return the lhs of the cast
+// E.g., `foo as *const _ as usize` returns `foo as *const _`.
+fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+    if cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize {
+        if let ExprKind::Cast(ref expr, _) = cast_expr.kind {
+            return Some(expr);
+        }
+    }
+    None
+}
+
+// If the given expression is a cast to a `*const` pointer, return the lhs of the cast
+// E.g., `foo as *const _` returns `foo`.
+fn expr_as_cast_to_raw_pointer<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+    if cx.typeck_results().expr_ty(cast_expr).is_unsafe_ptr() {
+        if let ExprKind::Cast(ref expr, _) = cast_expr.kind {
+            return Some(expr);
+        }
+    }
+    None
+}
diff --git a/clippy_lints/src/transmute.rs b/clippy_lints/src/transmute.rs
index c75adb62f25..47c650ac27d 100644
--- a/clippy_lints/src/transmute.rs
+++ b/clippy_lints/src/transmute.rs
@@ -98,7 +98,11 @@ declare_clippy_lint! {
     ///
     /// **Why is this bad?** This can always be rewritten with `&` and `*`.
     ///
-    /// **Known problems:** None.
+    /// **Known problems:**
+    /// - `mem::transmute` in statics and constants is stable from Rust 1.46.0,
+    /// while dereferencing raw pointer is not stable yet.
+    /// If you need to do this in those places,
+    /// you would have to use `transmute` instead.
     ///
     /// **Example:**
     /// ```rust,ignore
diff --git a/clippy_lints/src/trivially_copy_pass_by_ref.rs b/clippy_lints/src/trivially_copy_pass_by_ref.rs
index d92eb86fb2e..c2c86aa474a 100644
--- a/clippy_lints/src/trivially_copy_pass_by_ref.rs
+++ b/clippy_lints/src/trivially_copy_pass_by_ref.rs
@@ -14,6 +14,7 @@ use rustc_span::Span;
 use rustc_target::abi::LayoutOf;
 use rustc_target::spec::Target;
 use rustc_target::spec::abi::Abi;
+use rustc_target::spec::Target;
 
 declare_clippy_lint! {
     /// **What it does:** Checks for functions taking arguments by reference, where
diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs
index 5e83b6c81ec..9a948af8bfc 100644
--- a/clippy_lints/src/types.rs
+++ b/clippy_lints/src/types.rs
@@ -17,6 +17,7 @@ use rustc_hir::{
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::hir::map::Map;
 use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::TypeFoldable;
 use rustc_middle::ty::{self, InferTy, Ty, TyCtxt, TyS, TypeckResults};
 use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
 use rustc_span::hygiene::{ExpnKind, MacroKind};
@@ -541,6 +542,7 @@ impl Types {
                                 _ => None,
                             });
                             let ty_ty = hir_ty_to_ty(cx.tcx, boxed_ty);
+                            if !ty_ty.has_escaping_bound_vars();
                             if ty_ty.is_sized(cx.tcx.at(ty.span), cx.param_env);
                             if let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes());
                             if ty_ty_size <= self.vec_box_size_threshold;
diff --git a/clippy_lints/src/utils/eager_or_lazy.rs b/clippy_lints/src/utils/eager_or_lazy.rs
index 27e9567740d..4ceea13df37 100644
--- a/clippy_lints/src/utils/eager_or_lazy.rs
+++ b/clippy_lints/src/utils/eager_or_lazy.rs
@@ -82,7 +82,7 @@ fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool {
 /// 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 {
+fn identify_some_potentially_expensive_patterns<'tcx>(cx: &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>,
diff --git a/clippy_lints/src/utils/higher.rs b/clippy_lints/src/utils/higher.rs
index 8563b469a30..6d7c5058b4f 100644
--- a/clippy_lints/src/utils/higher.rs
+++ b/clippy_lints/src/utils/higher.rs
@@ -7,6 +7,7 @@ use crate::utils::{is_expn_of, match_def_path, paths};
 use if_chain::if_chain;
 use rustc_ast::ast;
 use rustc_hir as hir;
+use rustc_hir::{BorrowKind, Expr, ExprKind, StmtKind, UnOp};
 use rustc_lint::LateContext;
 
 /// Converts a hir binary operator to the corresponding `ast` type.
@@ -241,3 +242,56 @@ pub fn vec_macro<'e>(cx: &LateContext<'_>, expr: &'e hir::Expr<'_>) -> Option<Ve
 
     None
 }
+
+/// Extract args from an assert-like macro.
+/// Currently working with:
+/// - `assert!`, `assert_eq!` and `assert_ne!`
+/// - `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!`
+/// For example:
+/// `assert!(expr)` will return Some([expr])
+/// `debug_assert_eq!(a, b)` will return Some([a, b])
+pub fn extract_assert_macro_args<'tcx>(e: &'tcx Expr<'tcx>) -> Option<Vec<&'tcx Expr<'tcx>>> {
+    /// Try to match the AST for a pattern that contains a match, for example when two args are
+    /// compared
+    fn ast_matchblock(matchblock_expr: &'tcx Expr<'tcx>) -> Option<Vec<&Expr<'_>>> {
+        if_chain! {
+            if let ExprKind::Match(ref headerexpr, _, _) = &matchblock_expr.kind;
+            if let ExprKind::Tup([lhs, rhs]) = &headerexpr.kind;
+            if let ExprKind::AddrOf(BorrowKind::Ref, _, lhs) = lhs.kind;
+            if let ExprKind::AddrOf(BorrowKind::Ref, _, rhs) = rhs.kind;
+            then {
+                return Some(vec![lhs, rhs]);
+            }
+        }
+        None
+    }
+
+    if let ExprKind::Block(ref block, _) = e.kind {
+        if block.stmts.len() == 1 {
+            if let StmtKind::Semi(ref matchexpr) = block.stmts[0].kind {
+                // macros with unique arg: `{debug_}assert!` (e.g., `debug_assert!(some_condition)`)
+                if_chain! {
+                    if let ExprKind::Match(ref ifclause, _, _) = matchexpr.kind;
+                    if let ExprKind::DropTemps(ref droptmp) = ifclause.kind;
+                    if let ExprKind::Unary(UnOp::UnNot, condition) = droptmp.kind;
+                    then {
+                        return Some(vec![condition]);
+                    }
+                }
+
+                // debug macros with two args: `debug_assert_{ne, eq}` (e.g., `assert_ne!(a, b)`)
+                if_chain! {
+                    if let ExprKind::Block(ref matchblock,_) = matchexpr.kind;
+                    if let Some(ref matchblock_expr) = matchblock.expr;
+                    then {
+                        return ast_matchblock(matchblock_expr);
+                    }
+                }
+            }
+        } else if let Some(matchblock_expr) = block.expr {
+            // macros with two args: `assert_{ne, eq}` (e.g., `assert_ne!(a, b)`)
+            return ast_matchblock(&matchblock_expr);
+        }
+    }
+    None
+}
diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs
index 790ac4f7dd8..a9d26d48b12 100644
--- a/clippy_lints/src/utils/mod.rs
+++ b/clippy_lints/src/utils/mod.rs
@@ -708,7 +708,7 @@ fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>,
 }
 
 /// Gets the parent expression, if any –- this is useful to constrain a lint.
-pub fn get_parent_expr<'c>(cx: &'c LateContext<'_>, e: &Expr<'_>) -> Option<&'c Expr<'c>> {
+pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
     let map = &cx.tcx.hir();
     let hir_id = e.hir_id;
     let parent_id = map.get_parent_node(hir_id);
diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs
index a2a1d109c9a..625120b880e 100644
--- a/clippy_lints/src/utils/sugg.rs
+++ b/clippy_lints/src/utils/sugg.rs
@@ -13,8 +13,10 @@ use rustc_span::{BytePos, Pos};
 use std::borrow::Cow;
 use std::convert::TryInto;
 use std::fmt::Display;
+use std::ops::{Add, Neg, Not, Sub};
 
 /// A helper type to build suggestion correctly handling parenthesis.
+#[derive(Clone, PartialEq)]
 pub enum Sugg<'a> {
     /// An expression that never needs parenthesis such as `1337` or `[0; 42]`.
     NonParen(Cow<'a, str>),
@@ -25,8 +27,12 @@ pub enum Sugg<'a> {
     BinOp(AssocOp, Cow<'a, str>),
 }
 
+/// Literal constant `0`, for convenience.
+pub const ZERO: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("0"));
 /// Literal constant `1`, for convenience.
 pub const ONE: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("1"));
+/// a constant represents an empty string, for convenience.
+pub const EMPTY: Sugg<'static> = Sugg::NonParen(Cow::Borrowed(""));
 
 impl Display for Sugg<'_> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
@@ -269,21 +275,60 @@ impl<'a> Sugg<'a> {
     }
 }
 
-impl<'a, 'b> std::ops::Add<Sugg<'b>> for Sugg<'a> {
+// Copied from the rust standart library, and then edited
+macro_rules! forward_binop_impls_to_ref {
+    (impl $imp:ident, $method:ident for $t:ty, type Output = $o:ty) => {
+        impl $imp<$t> for &$t {
+            type Output = $o;
+
+            fn $method(self, other: $t) -> $o {
+                $imp::$method(self, &other)
+            }
+        }
+
+        impl $imp<&$t> for $t {
+            type Output = $o;
+
+            fn $method(self, other: &$t) -> $o {
+                $imp::$method(&self, other)
+            }
+        }
+
+        impl $imp for $t {
+            type Output = $o;
+
+            fn $method(self, other: $t) -> $o {
+                $imp::$method(&self, &other)
+            }
+        }
+    };
+}
+
+impl Add for &Sugg<'_> {
     type Output = Sugg<'static>;
-    fn add(self, rhs: Sugg<'b>) -> Sugg<'static> {
-        make_binop(ast::BinOpKind::Add, &self, &rhs)
+    fn add(self, rhs: &Sugg<'_>) -> Sugg<'static> {
+        make_binop(ast::BinOpKind::Add, self, rhs)
     }
 }
 
-impl<'a, 'b> std::ops::Sub<Sugg<'b>> for Sugg<'a> {
+impl Sub for &Sugg<'_> {
+    type Output = Sugg<'static>;
+    fn sub(self, rhs: &Sugg<'_>) -> Sugg<'static> {
+        make_binop(ast::BinOpKind::Sub, self, rhs)
+    }
+}
+
+forward_binop_impls_to_ref!(impl Add, add for Sugg<'_>, type Output = Sugg<'static>);
+forward_binop_impls_to_ref!(impl Sub, sub for Sugg<'_>, type Output = Sugg<'static>);
+
+impl Neg for Sugg<'_> {
     type Output = Sugg<'static>;
-    fn sub(self, rhs: Sugg<'b>) -> Sugg<'static> {
-        make_binop(ast::BinOpKind::Sub, &self, &rhs)
+    fn neg(self) -> Sugg<'static> {
+        make_unop("-", self)
     }
 }
 
-impl<'a> std::ops::Not for Sugg<'a> {
+impl Not for Sugg<'_> {
     type Output = Sugg<'static>;
     fn not(self) -> Sugg<'static> {
         make_unop("!", self)
diff --git a/clippy_lints/src/utils/usage.rs b/clippy_lints/src/utils/usage.rs
index ea1dc3be29b..2fd6046ebcf 100644
--- a/clippy_lints/src/utils/usage.rs
+++ b/clippy_lints/src/utils/usage.rs
@@ -1,10 +1,11 @@
+use crate::utils;
 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_hir::{Expr, ExprKind, HirId, Path};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
@@ -174,3 +175,50 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> {
         intravisit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
     }
 }
+
+struct ReturnBreakContinueMacroVisitor {
+    seen_return_break_continue: bool,
+}
+
+impl ReturnBreakContinueMacroVisitor {
+    fn new() -> ReturnBreakContinueMacroVisitor {
+        ReturnBreakContinueMacroVisitor {
+            seen_return_break_continue: false,
+        }
+    }
+}
+
+impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor {
+    type Map = Map<'tcx>;
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
+
+    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
+        if self.seen_return_break_continue {
+            // No need to look farther if we've already seen one of them
+            return;
+        }
+        match &ex.kind {
+            ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => {
+                self.seen_return_break_continue = true;
+            },
+            // Something special could be done here to handle while or for loop
+            // desugaring, as this will detect a break if there's a while loop
+            // or a for loop inside the expression.
+            _ => {
+                if utils::in_macro(ex.span) {
+                    self.seen_return_break_continue = true;
+                } else {
+                    rustc_hir::intravisit::walk_expr(self, ex);
+                }
+            },
+        }
+    }
+}
+
+pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
+    let mut recursive_visitor = ReturnBreakContinueMacroVisitor::new();
+    recursive_visitor.visit_expr(expression);
+    recursive_visitor.seen_return_break_continue
+}