about summary refs log tree commit diff
path: root/clippy_lints/src
diff options
context:
space:
mode:
authorflip1995 <philipp.krones@embecosm.com>2021-12-17 13:40:22 +0100
committerflip1995 <philipp.krones@embecosm.com>2021-12-17 13:40:22 +0100
commitece0946d7f60bdee8b9544753a3f1a3acddcb1f7 (patch)
tree78cfdf1e08b149bae2122b496dd4cfd6fb55c4d4 /clippy_lints/src
parentb2f8a27ff2662b440d0dd65cdb609d303b2fa280 (diff)
Merge commit '23d11428de3e973b34a5090a78d62887f821c90e' into clippyup
Diffstat (limited to 'clippy_lints/src')
-rw-r--r--clippy_lints/src/asm_syntax.rs4
-rw-r--r--clippy_lints/src/assertions_on_constants.rs12
-rw-r--r--clippy_lints/src/blocks_in_if_conditions.rs8
-rw-r--r--clippy_lints/src/bytecount.rs6
-rw-r--r--clippy_lints/src/collapsible_match.rs20
-rw-r--r--clippy_lints/src/derivable_impls.rs4
-rw-r--r--clippy_lints/src/floating_point_arithmetic.rs12
-rw-r--r--clippy_lints/src/if_then_some_else_none.rs7
-rw-r--r--clippy_lints/src/implicit_saturating_sub.rs16
-rw-r--r--clippy_lints/src/lib.register_all.rs3
-rw-r--r--clippy_lints/src/lib.register_lints.rs2
-rw-r--r--clippy_lints/src/lib.register_nursery.rs1
-rw-r--r--clippy_lints/src/lib.register_perf.rs1
-rw-r--r--clippy_lints/src/lib.register_suspicious.rs2
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--clippy_lints/src/lifetimes.rs2
-rw-r--r--clippy_lints/src/loops/manual_flatten.rs112
-rw-r--r--clippy_lints/src/loops/never_loop.rs4
-rw-r--r--clippy_lints/src/manual_map.rs15
-rw-r--r--clippy_lints/src/map_clone.rs5
-rw-r--r--clippy_lints/src/matches.rs10
-rw-r--r--clippy_lints/src/methods/bind_instead_of_map.rs4
-rw-r--r--clippy_lints/src/methods/filter_map.rs4
-rw-r--r--clippy_lints/src/methods/implicit_clone.rs29
-rw-r--r--clippy_lints/src/methods/mod.rs32
-rw-r--r--clippy_lints/src/methods/option_as_ref_deref.rs4
-rw-r--r--clippy_lints/src/methods/unnecessary_fold.rs4
-rw-r--r--clippy_lints/src/methods/unnecessary_iter_cloned.rs177
-rw-r--r--clippy_lints/src/methods/unnecessary_to_owned.rs397
-rw-r--r--clippy_lints/src/needless_bool.rs97
-rw-r--r--clippy_lints/src/needless_late_init.rs17
-rw-r--r--clippy_lints/src/no_effect.rs6
-rw-r--r--clippy_lints/src/non_send_fields_in_send_ty.rs32
-rw-r--r--clippy_lints/src/option_if_let_else.rs35
-rw-r--r--clippy_lints/src/question_mark.rs65
-rw-r--r--clippy_lints/src/return_self_not_must_use.rs105
-rw-r--r--clippy_lints/src/strings.rs7
-rw-r--r--clippy_lints/src/types/mod.rs46
-rw-r--r--clippy_lints/src/unit_return_expecting_ord.rs2
-rw-r--r--clippy_lints/src/utils/internal_lints.rs10
-rw-r--r--clippy_lints/src/utils/internal_lints/metadata_collector.rs11
41 files changed, 976 insertions, 356 deletions
diff --git a/clippy_lints/src/asm_syntax.rs b/clippy_lints/src/asm_syntax.rs
index 0322698f029..f419781dbc8 100644
--- a/clippy_lints/src/asm_syntax.rs
+++ b/clippy_lints/src/asm_syntax.rs
@@ -65,6 +65,7 @@ declare_clippy_lint! {
     /// ```rust,no_run
     /// # #![feature(asm)]
     /// # unsafe { let ptr = "".as_ptr();
+    /// # use std::arch::asm;
     /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr);
     /// # }
     /// ```
@@ -72,6 +73,7 @@ declare_clippy_lint! {
     /// ```rust,no_run
     /// # #![feature(asm)]
     /// # unsafe { let ptr = "".as_ptr();
+    /// # use std::arch::asm;
     /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax));
     /// # }
     /// ```
@@ -102,6 +104,7 @@ declare_clippy_lint! {
     /// ```rust,no_run
     /// # #![feature(asm)]
     /// # unsafe { let ptr = "".as_ptr();
+    /// # use std::arch::asm;
     /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax));
     /// # }
     /// ```
@@ -109,6 +112,7 @@ declare_clippy_lint! {
     /// ```rust,no_run
     /// # #![feature(asm)]
     /// # unsafe { let ptr = "".as_ptr();
+    /// # use std::arch::asm;
     /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr);
     /// # }
     /// ```
diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs
index 521fc84ee9c..b7f414742f1 100644
--- a/clippy_lints/src/assertions_on_constants.rs
+++ b/clippy_lints/src/assertions_on_constants.rs
@@ -2,7 +2,7 @@ use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::higher;
 use clippy_utils::source::snippet_opt;
-use clippy_utils::{is_direct_expn_of, is_expn_of, match_panic_call};
+use clippy_utils::{is_direct_expn_of, is_expn_of, match_panic_call, peel_blocks};
 use if_chain::if_chain;
 use rustc_hir::{Expr, ExprKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
@@ -122,15 +122,7 @@ fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>)
         if let ExprKind::Unary(UnOp::Not, expr) = cond.kind;
         // bind the first argument of the `assert!` macro
         if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), expr);
-        // block
-        if let ExprKind::Block(block, _) = then.kind;
-        if block.stmts.is_empty();
-        if let Some(block_expr) = &block.expr;
-        // inner block is optional. unwrap it if it exists, or use the expression as is otherwise.
-        if let Some(begin_panic_call) = match block_expr.kind {
-            ExprKind::Block(inner_block, _) => &inner_block.expr,
-            _ => &block.expr,
-        };
+        let begin_panic_call = peel_blocks(then);
         // function call
         if let Some(arg) = match_panic_call(cx, begin_panic_call);
         // bind the second argument of the `assert!` macro if it exists
diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs
index b59f49357df..475fdb440d4 100644
--- a/clippy_lints/src/blocks_in_if_conditions.rs
+++ b/clippy_lints/src/blocks_in_if_conditions.rs
@@ -73,9 +73,11 @@ impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> {
 
             let body = self.cx.tcx.hir().body(eid);
             let ex = &body.value;
-            if matches!(ex.kind, ExprKind::Block(_, _)) && !body.value.span.from_expansion() {
-                self.found_block = Some(ex);
-                return;
+            if let ExprKind::Block(block, _) = ex.kind {
+                if !body.value.span.from_expansion() && !block.stmts.is_empty() {
+                    self.found_block = Some(ex);
+                    return;
+                }
             }
         }
         walk_expr(self, expr);
diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs
index afb317421d0..92336a54e27 100644
--- a/clippy_lints/src/bytecount.rs
+++ b/clippy_lints/src/bytecount.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::match_type;
 use clippy_utils::visitors::is_local_used;
-use clippy_utils::{path_to_local_id, paths, peel_ref_operators, remove_blocks, strip_pat_refs};
+use clippy_utils::{path_to_local_id, paths, peel_blocks, peel_ref_operators, strip_pat_refs};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind};
@@ -42,7 +42,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
         if_chain! {
             if let ExprKind::MethodCall(count, _, [count_recv], _) = expr.kind;
-            if count.ident.name == sym!(count);
+            if count.ident.name == sym::count;
             if let ExprKind::MethodCall(filter, _, [filter_recv, filter_arg], _) = count_recv.kind;
             if filter.ident.name == sym!(filter);
             if let ExprKind::Closure(_, _, body_id, _, _) = filter_arg.kind;
@@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount {
                        cx.typeck_results().expr_ty(filter_recv).peel_refs(),
                        &paths::SLICE_ITER);
             let operand_is_arg = |expr| {
-                let expr = peel_ref_operators(cx, remove_blocks(expr));
+                let expr = peel_ref_operators(cx, peel_blocks(expr));
                 path_to_local_id(expr, arg_id)
             };
             let needle = if operand_is_arg(l) {
diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs
index 626f9971f01..c71e9f10f79 100644
--- a/clippy_lints/src/collapsible_match.rs
+++ b/clippy_lints/src/collapsible_match.rs
@@ -1,10 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher::IfLetOrMatch;
 use clippy_utils::visitors::is_local_used;
-use clippy_utils::{is_lang_ctor, is_unit_expr, path_to_local, peel_ref_operators, SpanlessEq};
+use clippy_utils::{is_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq};
 use if_chain::if_chain;
 use rustc_hir::LangItem::OptionNone;
-use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind};
+use rustc_hir::{Arm, Expr, Guard, HirId, Pat, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::{MultiSpan, Span};
@@ -75,7 +75,7 @@ fn check_arm<'tcx>(
     outer_guard: Option<&'tcx Guard<'tcx>>,
     outer_else_body: Option<&'tcx Expr<'tcx>>,
 ) {
-    let inner_expr = strip_singleton_blocks(outer_then_body);
+    let inner_expr = peel_blocks_with_stmt(outer_then_body);
     if_chain! {
         if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr);
         if let Some((inner_scrutinee, inner_then_pat, inner_else_body)) = match inner {
@@ -138,20 +138,6 @@ fn check_arm<'tcx>(
     }
 }
 
-fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
-    while let ExprKind::Block(block, _) = expr.kind {
-        match (block.stmts, block.expr) {
-            ([stmt], None) => match stmt.kind {
-                StmtKind::Expr(e) | StmtKind::Semi(e) => expr = e,
-                _ => break,
-            },
-            ([], Some(e)) => expr = e,
-            _ => break,
-        }
-    }
-    expr
-}
-
 /// A "wild-like" arm has a wild (`_`) or `None` pattern and no guard. Such arms can be "collapsed"
 /// into a single wild arm without any significant loss in semantics or readability.
 fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs
index d0fab2b48fb..eccb18982f3 100644
--- a/clippy_lints/src/derivable_impls.rs
+++ b/clippy_lints/src/derivable_impls.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::{is_automatically_derived, is_default_equivalent, remove_blocks};
+use clippy_utils::{is_automatically_derived, is_default_equivalent, peel_blocks};
 use rustc_hir::{
     def::{DefKind, Res},
     Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind,
@@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
                         }
                     }
                 }
-                let should_emit = match remove_blocks(func_expr).kind {
+                let should_emit = match peel_blocks(func_expr).kind {
                     ExprKind::Tup(fields) => fields.iter().all(|e| is_default_equivalent(cx, e)),
                     ExprKind::Call(callee, args)
                         if is_path_self(callee) => args.iter().all(|e| is_default_equivalent(cx, e)),
diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs
index 3df511ea8e7..2de2bfc040b 100644
--- a/clippy_lints/src/floating_point_arithmetic.rs
+++ b/clippy_lints/src/floating_point_arithmetic.rs
@@ -4,7 +4,7 @@ use clippy_utils::consts::{
 };
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::higher;
-use clippy_utils::{eq_expr_value, get_parent_expr, in_constant, numeric_literal, sugg};
+use clippy_utils::{eq_expr_value, get_parent_expr, in_constant, numeric_literal, peel_blocks, sugg};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
@@ -546,13 +546,9 @@ fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a
 
 fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
     if_chain! {
-        if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
-        if let ExprKind::Block(block, _) = then.kind;
-        if block.stmts.is_empty();
-        if let Some(if_body_expr) = block.expr;
-        if let Some(ExprKind::Block(else_block, _)) = r#else.map(|el| &el.kind);
-        if else_block.stmts.is_empty();
-        if let Some(else_body_expr) = else_block.expr;
+        if let Some(higher::If { cond, then, r#else: Some(r#else) }) = higher::If::hir(expr);
+        let if_body_expr = peel_blocks(then);
+        let else_body_expr = peel_blocks(r#else);
         if let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr);
         then {
             let positive_abs_sugg = (
diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs
index 30d222bd7d2..16e5c5ca603 100644
--- a/clippy_lints/src/if_then_some_else_none.rs
+++ b/clippy_lints/src/if_then_some_else_none.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::source::snippet_with_macro_callsite;
-use clippy_utils::{contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs};
+use clippy_utils::{contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs, peel_blocks};
 use if_chain::if_chain;
 use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
@@ -77,10 +77,7 @@ impl LateLintPass<'_> for IfThenSomeElseNone {
             if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind;
             if let ExprKind::Path(ref then_call_qpath) = then_call.kind;
             if is_lang_ctor(cx, then_call_qpath, OptionSome);
-            if let ExprKind::Block(els_block, _) = els.kind;
-            if els_block.stmts.is_empty();
-            if let Some(els_expr) = els_block.expr;
-            if let ExprKind::Path(ref qpath) = els_expr.kind;
+            if let ExprKind::Path(ref qpath) = peel_blocks(els).kind;
             if is_lang_ctor(cx, qpath, OptionNone);
             if !stmts_contains_early_return(then_block.stmts);
             then {
diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs
index 4088c54623d..26a196aab59 100644
--- a/clippy_lints/src/implicit_saturating_sub.rs
+++ b/clippy_lints/src/implicit_saturating_sub.rs
@@ -1,10 +1,9 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::higher;
-use clippy_utils::SpanlessEq;
+use clippy_utils::{higher, peel_blocks_with_stmt, SpanlessEq};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
-use rustc_hir::{lang_items::LangItem, BinOpKind, Expr, ExprKind, QPath, StmtKind};
+use rustc_hir::{lang_items::LangItem, BinOpKind, Expr, ExprKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
@@ -52,13 +51,8 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
             // Ensure that the binary operator is >, != and <
             if BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node;
 
-            // Check if the true condition block has only one statement
-            if let ExprKind::Block(block, _) = then.kind;
-            if block.stmts.len() == 1 && block.expr.is_none();
-
             // Check if assign operation is done
-            if let StmtKind::Semi(e) = block.stmts[0].kind;
-            if let Some(target) = subtracts_one(cx, e);
+            if let Some(target) = subtracts_one(cx, then);
 
             // Extracting out the variable name
             if let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind;
@@ -138,8 +132,8 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
     }
 }
 
-fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &Expr<'a>) -> Option<&'a Expr<'a>> {
-    match expr.kind {
+fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
+    match peel_blocks_with_stmt(expr).kind {
         ExprKind::AssignOp(ref op1, target, value) => {
             if_chain! {
                 if BinOpKind::Sub == op1.node;
diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs
index b7b5f059de6..3d3999d4cc0 100644
--- a/clippy_lints/src/lib.register_all.rs
+++ b/clippy_lints/src/lib.register_all.rs
@@ -181,6 +181,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(methods::UNNECESSARY_FILTER_MAP),
     LintId::of(methods::UNNECESSARY_FOLD),
     LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
+    LintId::of(methods::UNNECESSARY_TO_OWNED),
     LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
     LintId::of(methods::USELESS_ASREF),
     LintId::of(methods::WRONG_SELF_CONVENTION),
@@ -220,7 +221,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
     LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
     LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
-    LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
     LintId::of(octal_escapes::OCTAL_ESCAPES),
     LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
     LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
@@ -246,6 +246,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(reference::REF_IN_DEREF),
     LintId::of(regex::INVALID_REGEX),
     LintId::of(repeat_once::REPEAT_ONCE),
+    LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE),
     LintId::of(returns::LET_AND_RETURN),
     LintId::of(returns::NEEDLESS_RETURN),
     LintId::of(self_assignment::SELF_ASSIGNMENT),
diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs
index bb159e50373..766c5ba1bcb 100644
--- a/clippy_lints/src/lib.register_lints.rs
+++ b/clippy_lints/src/lib.register_lints.rs
@@ -315,6 +315,7 @@ store.register_lints(&[
     methods::UNNECESSARY_FILTER_MAP,
     methods::UNNECESSARY_FOLD,
     methods::UNNECESSARY_LAZY_EVALUATIONS,
+    methods::UNNECESSARY_TO_OWNED,
     methods::UNWRAP_OR_ELSE_DEFAULT,
     methods::UNWRAP_USED,
     methods::USELESS_ASREF,
@@ -422,6 +423,7 @@ store.register_lints(&[
     regex::INVALID_REGEX,
     regex::TRIVIAL_REGEX,
     repeat_once::REPEAT_ONCE,
+    return_self_not_must_use::RETURN_SELF_NOT_MUST_USE,
     returns::LET_AND_RETURN,
     returns::NEEDLESS_RETURN,
     same_name_method::SAME_NAME_METHOD,
diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs
index 59182fd8175..e3cf0670018 100644
--- a/clippy_lints/src/lib.register_nursery.rs
+++ b/clippy_lints/src/lib.register_nursery.rs
@@ -18,6 +18,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
     LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
     LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
     LintId::of(mutex_atomic::MUTEX_INTEGER),
+    LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
     LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
     LintId::of(option_if_let_else::OPTION_IF_LET_ELSE),
     LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
diff --git a/clippy_lints/src/lib.register_perf.rs b/clippy_lints/src/lib.register_perf.rs
index a0d5cf9418e..2ea0b696f1f 100644
--- a/clippy_lints/src/lib.register_perf.rs
+++ b/clippy_lints/src/lib.register_perf.rs
@@ -17,6 +17,7 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
     LintId::of(methods::MANUAL_STR_REPEAT),
     LintId::of(methods::OR_FUN_CALL),
     LintId::of(methods::SINGLE_CHAR_PATTERN),
+    LintId::of(methods::UNNECESSARY_TO_OWNED),
     LintId::of(misc::CMP_OWNED),
     LintId::of(mutex_atomic::MUTEX_ATOMIC),
     LintId::of(redundant_clone::REDUNDANT_CLONE),
diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs
index 414bfc42fdf..8594338ffa5 100644
--- a/clippy_lints/src/lib.register_suspicious.rs
+++ b/clippy_lints/src/lib.register_suspicious.rs
@@ -15,8 +15,8 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
     LintId::of(loops::MUT_RANGE_BOUND),
     LintId::of(methods::SUSPICIOUS_MAP),
     LintId::of(mut_key::MUTABLE_KEY_TYPE),
-    LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
     LintId::of(octal_escapes::OCTAL_ESCAPES),
+    LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE),
     LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
     LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
 ])
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 77b7fee6389..d1c7956a7a5 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -340,6 +340,7 @@ mod ref_option_ref;
 mod reference;
 mod regex;
 mod repeat_once;
+mod return_self_not_must_use;
 mod returns;
 mod same_name_method;
 mod self_assignment;
@@ -852,6 +853,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(trailing_empty_array::TrailingEmptyArray));
     store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
     store.register_late_pass(|| Box::new(needless_late_init::NeedlessLateInit));
+    store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs
index fad3343d128..0e2b78609c2 100644
--- a/clippy_lints/src/lifetimes.rs
+++ b/clippy_lints/src/lifetimes.rs
@@ -29,7 +29,7 @@ declare_clippy_lint! {
     ///
     /// ### Known problems
     /// - We bail out if the function has a `where` clause where lifetimes
-    /// are mentioned due to potenial false positives.
+    /// are mentioned due to potential false positives.
     /// - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the
     /// placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`.
     ///
diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs
index 5b6e27085d5..d276c901059 100644
--- a/clippy_lints/src/loops/manual_flatten.rs
+++ b/clippy_lints/src/loops/manual_flatten.rs
@@ -3,11 +3,11 @@ use super::MANUAL_FLATTEN;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher;
 use clippy_utils::visitors::is_local_used;
-use clippy_utils::{is_lang_ctor, path_to_local_id};
+use clippy_utils::{is_lang_ctor, path_to_local_id, peel_blocks_with_stmt};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::{OptionSome, ResultOk};
-use rustc_hir::{Expr, ExprKind, Pat, PatKind, StmtKind};
+use rustc_hir::{Expr, Pat, PatKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
 use rustc_span::source_map::Span;
@@ -21,71 +21,55 @@ pub(super) fn check<'tcx>(
     body: &'tcx Expr<'_>,
     span: Span,
 ) {
-    if let ExprKind::Block(block, _) = body.kind {
-        // Ensure the `if let` statement is the only expression or statement in the for-loop
-        let inner_expr = if block.stmts.len() == 1 && block.expr.is_none() {
-            let match_stmt = &block.stmts[0];
-            if let StmtKind::Semi(inner_expr) = match_stmt.kind {
-                Some(inner_expr)
-            } else {
-                None
-            }
-        } else if block.stmts.is_empty() {
-            block.expr
-        } else {
-            None
-        };
+    let inner_expr = peel_blocks_with_stmt(body);
+    if_chain! {
+        if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: None })
+            = higher::IfLet::hir(cx, inner_expr);
+        // Ensure match_expr in `if let` statement is the same as the pat from the for-loop
+        if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind;
+        if path_to_local_id(let_expr, pat_hir_id);
+        // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result`
+        if let PatKind::TupleStruct(ref qpath, _, _) = let_pat.kind;
+        let some_ctor = is_lang_ctor(cx, qpath, OptionSome);
+        let ok_ctor = is_lang_ctor(cx, qpath, ResultOk);
+        if some_ctor || ok_ctor;
+        // Ensure expr in `if let` is not used afterwards
+        if !is_local_used(cx, if_then, pat_hir_id);
+        then {
+            let if_let_type = if some_ctor { "Some" } else { "Ok" };
+            // Prepare the error message
+            let msg = format!("unnecessary `if let` since only the `{}` variant of the iterator element is used", if_let_type);
 
-        if_chain! {
-            if let Some(inner_expr) = inner_expr;
-            if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: None })
-                = higher::IfLet::hir(cx, inner_expr);
-            // Ensure match_expr in `if let` statement is the same as the pat from the for-loop
-            if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind;
-            if path_to_local_id(let_expr, pat_hir_id);
-            // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result`
-            if let PatKind::TupleStruct(ref qpath, _, _) = let_pat.kind;
-            let some_ctor = is_lang_ctor(cx, qpath, OptionSome);
-            let ok_ctor = is_lang_ctor(cx, qpath, ResultOk);
-            if some_ctor || ok_ctor;
-            // Ensure epxr in `if let` is not used afterwards
-            if !is_local_used(cx, if_then, pat_hir_id);
-            then {
-                let if_let_type = if some_ctor { "Some" } else { "Ok" };
-                // Prepare the error message
-                let msg = format!("unnecessary `if let` since only the `{}` variant of the iterator element is used", if_let_type);
-
-                // Prepare the help message
-                let mut applicability = Applicability::MaybeIncorrect;
-                let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability);
-                let copied = match cx.typeck_results().expr_ty(let_expr).kind() {
-                    ty::Ref(_, inner, _) => match inner.kind() {
-                        ty::Ref(..) => ".copied()",
-                        _ => ""
-                    }
+            // Prepare the help message
+            let mut applicability = Applicability::MaybeIncorrect;
+            let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability);
+            let copied = match cx.typeck_results().expr_ty(let_expr).kind() {
+                ty::Ref(_, inner, _) => match inner.kind() {
+                    ty::Ref(..) => ".copied()",
                     _ => ""
-                };
+                }
+                _ => ""
+            };
 
-                span_lint_and_then(
-                    cx,
-                    MANUAL_FLATTEN,
-                    span,
-                    &msg,
-                    |diag| {
-                        let sugg = format!("{}{}.flatten()", arg_snippet, copied);
-                        diag.span_suggestion(
-                            arg.span,
-                            "try",
-                            sugg,
-                            Applicability::MaybeIncorrect,
-                        );
-                        diag.span_help(
-                            inner_expr.span,
-                            "...and remove the `if let` statement in the for loop",
-                        );
-                    }
-                );
-            }
+            span_lint_and_then(
+                cx,
+                MANUAL_FLATTEN,
+                span,
+                &msg,
+                |diag| {
+                    let sugg = format!("{}{}.flatten()", arg_snippet, copied);
+                    diag.span_suggestion(
+                        arg.span,
+                        "try",
+                        sugg,
+                        Applicability::MaybeIncorrect,
+                    );
+                    diag.span_help(
+                        inner_expr.span,
+                        "...and remove the `if let` statement in the for loop",
+                    );
+                }
+            );
         }
     }
 }
diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs
index 86b7d6d989a..68ffcd1abfb 100644
--- a/clippy_lints/src/loops/never_loop.rs
+++ b/clippy_lints/src/loops/never_loop.rs
@@ -92,9 +92,7 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult
 }
 
 fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult {
-    let stmts = block.stmts.iter().map(stmt_to_expr);
-    let expr = once(block.expr);
-    let mut iter = stmts.chain(expr).flatten();
+    let mut iter = block.stmts.iter().filter_map(stmt_to_expr).chain(block.expr);
     never_loop_expr_seq(&mut iter, main_loop_id)
 }
 
diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs
index 4d8ad566e6b..34a70ca76c6 100644
--- a/clippy_lints/src/manual_map.rs
+++ b/clippy_lints/src/manual_map.rs
@@ -5,7 +5,7 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function};
 use clippy_utils::{
     can_move_expr_to_closure, in_constant, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id,
-    peel_hir_expr_refs, peel_hir_expr_while, CaptureKind,
+    peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, CaptureKind,
 };
 use rustc_ast::util::parser::PREC_POSTFIX;
 use rustc_errors::Applicability;
@@ -307,16 +307,5 @@ fn get_some_expr(
 
 // Checks for the `None` value.
 fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
-    match expr.kind {
-        ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
-        ExprKind::Block(
-            Block {
-                stmts: [],
-                expr: Some(expr),
-                ..
-            },
-            _,
-        ) => is_none_expr(cx, expr),
-        _ => false,
-    }
+    matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone))
 }
diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs
index c2b78e21861..174c7da28d3 100644
--- a/clippy_lints/src/map_clone.rs
+++ b/clippy_lints/src/map_clone.rs
@@ -1,8 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_trait_method;
-use clippy_utils::remove_blocks;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
+use clippy_utils::{is_trait_method, peel_blocks};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -60,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone {
             if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].kind;
             then {
                 let closure_body = cx.tcx.hir().body(body_id);
-                let closure_expr = remove_blocks(&closure_body.value);
+                let closure_expr = peel_blocks(&closure_body.value);
                 match closure_body.params[0].pat.kind {
                     hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
                         hir::BindingAnnotation::Unannotated, .., name, None
diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs
index eacbfa54cf7..acac0887226 100644
--- a/clippy_lints/src/matches.rs
+++ b/clippy_lints/src/matches.rs
@@ -9,7 +9,7 @@ use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, pe
 use clippy_utils::visitors::is_local_used;
 use clippy_utils::{
     get_parent_expr, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs,
-    path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks,
+    path_to_local, path_to_local_id, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns,
     strip_pat_refs,
 };
 use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
@@ -658,7 +658,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
                 QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind;
             if args.len() == 1;
             if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind;
-            let body = remove_blocks(arms[0].body);
+            let body = peel_blocks(arms[0].body);
             if path_to_local_id(body, arg);
 
             then {
@@ -723,7 +723,7 @@ fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp
             return;
         }
         let els = arms[1].body;
-        let els = if is_unit_expr(remove_blocks(els)) {
+        let els = if is_unit_expr(peel_blocks(els)) {
             None
         } else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind {
             if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() {
@@ -1481,7 +1481,7 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A
 
     let matched_vars = ex.span;
     let bind_names = arms[0].pat.span;
-    let match_body = remove_blocks(arms[0].body);
+    let match_body = peel_blocks(arms[0].body);
     let mut snippet_body = if match_body.span.from_expansion() {
         Sugg::hir_with_macro_callsite(cx, match_body, "..").to_string()
     } else {
@@ -1678,7 +1678,7 @@ fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<BindingAnnotat
         if is_lang_ctor(cx, qpath, OptionSome);
         if let PatKind::Binding(rb, .., ident, _) = first_pat.kind;
         if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
-        if let ExprKind::Call(e, args) = remove_blocks(arm.body).kind;
+        if let ExprKind::Call(e, args) = peel_blocks(arm.body).kind;
         if let ExprKind::Path(ref some_path) = e.kind;
         if is_lang_ctor(cx, some_path, OptionSome) && args.len() == 1;
         if let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind;
diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs
index b7690cf9222..150bafc0f5d 100644
--- a/clippy_lints/src/methods/bind_instead_of_map.rs
+++ b/clippy_lints/src/methods/bind_instead_of_map.rs
@@ -1,7 +1,7 @@
 use super::{contains_return, BIND_INSTEAD_OF_MAP};
 use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::{snippet, snippet_with_macro_callsite};
-use clippy_utils::{remove_blocks, visitors::find_all_ret_expressions};
+use clippy_utils::{peel_blocks, visitors::find_all_ret_expressions};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -152,7 +152,7 @@ pub(crate) trait BindInsteadOfMap {
         match arg.kind {
             hir::ExprKind::Closure(_, _, body_id, closure_args_span, _) => {
                 let closure_body = cx.tcx.hir().body(body_id);
-                let closure_expr = remove_blocks(&closure_body.value);
+                let closure_expr = peel_blocks(&closure_body.value);
 
                 if Self::lint_closure_autofixable(cx, expr, recv, closure_expr, closure_args_span) {
                     true
diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs
index c96c817bb8b..6d8733c08b4 100644
--- a/clippy_lints/src/methods/filter_map.rs
+++ b/clippy_lints/src/methods/filter_map.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::{indent_of, reindent_multiline, snippet};
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_trait_method, path_to_local_id, remove_blocks, SpanlessEq};
+use clippy_utils::{is_trait_method, path_to_local_id, peel_blocks, SpanlessEq};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -25,7 +25,7 @@ fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Sy
         },
         hir::ExprKind::Closure(_, _, c, _, _) => {
             let body = cx.tcx.hir().body(*c);
-            let closure_expr = remove_blocks(&body.value);
+            let closure_expr = peel_blocks(&body.value);
             let arg_id = body.params[0].pat.hir_id;
             match closure_expr.kind {
                 hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, _, args, _) => {
diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs
index 81c42de145f..90492ffda3c 100644
--- a/clippy_lints/src/methods/implicit_clone.rs
+++ b/clippy_lints/src/methods/implicit_clone.rs
@@ -12,15 +12,7 @@ use super::IMPLICIT_CLONE;
 pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, span: Span) {
     if_chain! {
         if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
-        if match method_name {
-            "to_os_string" => is_diag_item_method(cx, method_def_id, sym::OsStr),
-            "to_owned" => is_diag_trait_item(cx, method_def_id, sym::ToOwned),
-            "to_path_buf" => is_diag_item_method(cx, method_def_id, sym::Path),
-            "to_vec" => cx.tcx.impl_of_method(method_def_id)
-                .map(|impl_did| Some(impl_did) == cx.tcx.lang_items().slice_alloc_impl())
-                == Some(true),
-            _ => false,
-        };
+        if is_clone_like(cx, method_name, method_def_id);
         let return_type = cx.typeck_results().expr_ty(expr);
         let input_type = cx.typeck_results().expr_ty(recv).peel_refs();
         if let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did));
@@ -38,3 +30,22 @@ pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv
         }
     }
 }
+
+/// Returns true if the named method can be used to clone the receiver.
+/// Note that `to_string` is not flagged by `implicit_clone`. So other lints that call
+/// `is_clone_like` and that do flag `to_string` must handle it separately. See, e.g.,
+/// `is_to_owned_like` in `unnecessary_to_owned.rs`.
+pub fn is_clone_like(cx: &LateContext<'_>, method_name: &str, method_def_id: hir::def_id::DefId) -> bool {
+    match method_name {
+        "to_os_string" => is_diag_item_method(cx, method_def_id, sym::OsStr),
+        "to_owned" => is_diag_trait_item(cx, method_def_id, sym::ToOwned),
+        "to_path_buf" => is_diag_item_method(cx, method_def_id, sym::Path),
+        "to_vec" => {
+            cx.tcx
+                .impl_of_method(method_def_id)
+                .map(|impl_did| Some(impl_did) == cx.tcx.lang_items().slice_alloc_impl())
+                == Some(true)
+        },
+        _ => false,
+    }
+}
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 58ec2213535..3c43671dd34 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -56,7 +56,9 @@ mod suspicious_splitn;
 mod uninit_assumed_init;
 mod unnecessary_filter_map;
 mod unnecessary_fold;
+mod unnecessary_iter_cloned;
 mod unnecessary_lazy_eval;
+mod unnecessary_to_owned;
 mod unwrap_or_else_default;
 mod unwrap_used;
 mod useless_asref;
@@ -1885,6 +1887,32 @@ declare_clippy_lint! {
     "usages of `str::splitn` that can be replaced with `str::split`"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for unnecessary calls to [`ToOwned::to_owned`](https://doc.rust-lang.org/std/borrow/trait.ToOwned.html#tymethod.to_owned)
+    /// and other `to_owned`-like functions.
+    ///
+    /// ### Why is this bad?
+    /// The unnecessary calls result in useless allocations.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let path = std::path::Path::new("x");
+    /// foo(&path.to_string_lossy().to_string());
+    /// fn foo(s: &str) {}
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let path = std::path::Path::new("x");
+    /// foo(&path.to_string_lossy());
+    /// fn foo(s: &str) {}
+    /// ```
+    #[clippy::version = "1.58.0"]
+    pub UNNECESSARY_TO_OWNED,
+    perf,
+    "unnecessary calls to `to_owned`-like functions"
+}
+
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Option<RustcVersion>,
@@ -1964,7 +1992,8 @@ impl_lint_pass!(Methods => [
     MANUAL_STR_REPEAT,
     EXTEND_WITH_DRAIN,
     MANUAL_SPLIT_ONCE,
-    NEEDLESS_SPLITN
+    NEEDLESS_SPLITN,
+    UNNECESSARY_TO_OWNED,
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
@@ -2007,6 +2036,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
                 single_char_add_str::check(cx, expr, args);
                 into_iter_on_ref::check(cx, expr, *method_span, method_call.ident.name, args);
                 single_char_pattern::check(cx, expr, method_call.ident.name, args);
+                unnecessary_to_owned::check(cx, expr, method_call.ident.name, args);
             },
             hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
                 let mut info = BinaryExprInfo {
diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs
index d3f40d26208..fa74a8f3dc3 100644
--- a/clippy_lints/src/methods/option_as_ref_deref.rs
+++ b/clippy_lints/src/methods/option_as_ref_deref.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, remove_blocks};
+use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, peel_blocks};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -53,7 +53,7 @@ pub(super) fn check<'tcx>(
             }),
         hir::ExprKind::Closure(_, _, body_id, _, _) => {
             let closure_body = cx.tcx.hir().body(body_id);
-            let closure_expr = remove_blocks(&closure_body.value);
+            let closure_expr = peel_blocks(&closure_body.value);
 
             match &closure_expr.kind {
                 hir::ExprKind::MethodCall(_, _, args, _) => {
diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs
index 4c4034437da..47a81199608 100644
--- a/clippy_lints/src/methods/unnecessary_fold.rs
+++ b/clippy_lints/src/methods/unnecessary_fold.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{is_trait_method, path_to_local_id, remove_blocks, strip_pat_refs};
+use clippy_utils::{is_trait_method, path_to_local_id, peel_blocks, strip_pat_refs};
 use if_chain::if_chain;
 use rustc_ast::ast;
 use rustc_errors::Applicability;
@@ -31,7 +31,7 @@ pub(super) fn check(
             // Extract the body of the closure passed to fold
             if let hir::ExprKind::Closure(_, _, body_id, _, _) = acc.kind;
             let closure_body = cx.tcx.hir().body(body_id);
-            let closure_expr = remove_blocks(&closure_body.value);
+            let closure_expr = peel_blocks(&closure_body.value);
 
             // Check if the closure body is of the form `acc <op> some_expr(x)`
             if let hir::ExprKind::Binary(ref bin_op, left_expr, right_expr) = closure_expr.kind;
diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs
new file mode 100644
index 00000000000..8300df03e99
--- /dev/null
+++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs
@@ -0,0 +1,177 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::higher::ForLoop;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait};
+use clippy_utils::{fn_def_id, get_parent_expr, path_to_local_id, usage};
+use rustc_errors::Applicability;
+use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
+use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, HirId, LangItem, Mutability, Pat};
+use rustc_lint::LateContext;
+use rustc_middle::{hir::map::Map, ty};
+use rustc_span::{sym, Symbol};
+
+use super::UNNECESSARY_TO_OWNED;
+
+pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, receiver: &'tcx Expr<'tcx>) -> bool {
+    if_chain! {
+        if let Some(parent) = get_parent_expr(cx, expr);
+        if let Some(callee_def_id) = fn_def_id(cx, parent);
+        if is_into_iter(cx, callee_def_id);
+        then {
+            check_for_loop_iter(cx, parent, method_name, receiver)
+        } else {
+            false
+        }
+    }
+}
+
+/// Checks whether `expr` is an iterator in a `for` loop and, if so, determines whether the
+/// iterated-over items could be iterated over by reference. The reason why `check` above does not
+/// include this code directly is so that it can be called from
+/// `unnecessary_into_owned::check_into_iter_call_arg`.
+pub fn check_for_loop_iter(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    method_name: Symbol,
+    receiver: &'tcx Expr<'tcx>,
+) -> bool {
+    if_chain! {
+        if let Some(grandparent) = get_parent_expr(cx, expr).and_then(|parent| get_parent_expr(cx, parent));
+        if let Some(ForLoop { pat, body, .. }) = ForLoop::hir(grandparent);
+        let (clone_or_copy_needed, addr_of_exprs) = clone_or_copy_needed(cx, pat, body);
+        if !clone_or_copy_needed;
+        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
+        then {
+            let snippet = if_chain! {
+                if let ExprKind::MethodCall(maybe_iter_method_name, _, [collection], _) = receiver.kind;
+                if maybe_iter_method_name.ident.name == sym::iter;
+
+                if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
+                let receiver_ty = cx.typeck_results().expr_ty(receiver);
+                if implements_trait(cx, receiver_ty, iterator_trait_id, &[]);
+                if let Some(iter_item_ty) = get_iterator_item_ty(cx, receiver_ty);
+
+                if let Some(into_iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator);
+                let collection_ty = cx.typeck_results().expr_ty(collection);
+                if implements_trait(cx, collection_ty, into_iterator_trait_id, &[]);
+                if let Some(into_iter_item_ty) = get_associated_type(cx, collection_ty, into_iterator_trait_id, "Item");
+
+                if iter_item_ty == into_iter_item_ty;
+                if let Some(collection_snippet) = snippet_opt(cx, collection.span);
+                then {
+                    collection_snippet
+                } else {
+                    receiver_snippet
+                }
+            };
+            span_lint_and_then(
+                cx,
+                UNNECESSARY_TO_OWNED,
+                expr.span,
+                &format!("unnecessary use of `{}`", method_name),
+                |diag| {
+                    diag.span_suggestion(expr.span, "use", snippet, Applicability::MachineApplicable);
+                    for addr_of_expr in addr_of_exprs {
+                        match addr_of_expr.kind {
+                            ExprKind::AddrOf(_, _, referent) => {
+                                let span = addr_of_expr.span.with_hi(referent.span.lo());
+                                diag.span_suggestion(span, "remove this `&`", String::new(), Applicability::MachineApplicable);
+                            }
+                            _ => unreachable!(),
+                        }
+                    }
+                }
+            );
+            return true;
+        }
+    }
+    false
+}
+
+/// The core logic of `check_for_loop_iter` above, this function wraps a use of
+/// `CloneOrCopyVisitor`.
+fn clone_or_copy_needed(
+    cx: &LateContext<'tcx>,
+    pat: &Pat<'tcx>,
+    body: &'tcx Expr<'tcx>,
+) -> (bool, Vec<&'tcx Expr<'tcx>>) {
+    let mut visitor = CloneOrCopyVisitor {
+        cx,
+        binding_hir_ids: pat_bindings(pat),
+        clone_or_copy_needed: false,
+        addr_of_exprs: Vec::new(),
+    };
+    visitor.visit_expr(body);
+    (visitor.clone_or_copy_needed, visitor.addr_of_exprs)
+}
+
+/// Returns a vector of all `HirId`s bound by the pattern.
+fn pat_bindings(pat: &Pat<'_>) -> Vec<HirId> {
+    let mut collector = usage::ParamBindingIdCollector {
+        binding_hir_ids: Vec::new(),
+    };
+    collector.visit_pat(pat);
+    collector.binding_hir_ids
+}
+
+/// `clone_or_copy_needed` will be false when `CloneOrCopyVisitor` is done visiting if the only
+/// operations performed on `binding_hir_ids` are:
+/// * to take non-mutable references to them
+/// * to use them as non-mutable `&self` in method calls
+/// If any of `binding_hir_ids` is used in any other way, then `clone_or_copy_needed` will be true
+/// when `CloneOrCopyVisitor` is done visiting.
+struct CloneOrCopyVisitor<'cx, 'tcx> {
+    cx: &'cx LateContext<'tcx>,
+    binding_hir_ids: Vec<HirId>,
+    clone_or_copy_needed: bool,
+    addr_of_exprs: Vec<&'tcx Expr<'tcx>>,
+}
+
+impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> {
+    type Map = Map<'tcx>;
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
+    }
+
+    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+        walk_expr(self, expr);
+        if self.is_binding(expr) {
+            if let Some(parent) = get_parent_expr(self.cx, expr) {
+                match parent.kind {
+                    ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) => {
+                        self.addr_of_exprs.push(parent);
+                        return;
+                    },
+                    ExprKind::MethodCall(_, _, args, _) => {
+                        if_chain! {
+                            if args.iter().skip(1).all(|arg| !self.is_binding(arg));
+                            if let Some(method_def_id) = self.cx.typeck_results().type_dependent_def_id(parent.hir_id);
+                            let method_ty = self.cx.tcx.type_of(method_def_id);
+                            let self_ty = method_ty.fn_sig(self.cx.tcx).input(0).skip_binder();
+                            if matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Not));
+                            then {
+                                return;
+                            }
+                        }
+                    },
+                    _ => {},
+                }
+            }
+            self.clone_or_copy_needed = true;
+        }
+    }
+}
+
+impl<'cx, 'tcx> CloneOrCopyVisitor<'cx, 'tcx> {
+    fn is_binding(&self, expr: &Expr<'tcx>) -> bool {
+        self.binding_hir_ids
+            .iter()
+            .any(|hir_id| path_to_local_id(expr, *hir_id))
+    }
+}
+
+/// Returns true if the named method is `IntoIterator::into_iter`.
+pub fn is_into_iter(cx: &LateContext<'_>, callee_def_id: DefId) -> bool {
+    cx.tcx.lang_items().require(LangItem::IntoIterIntoIter) == Ok(callee_def_id)
+}
diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs
new file mode 100644
index 00000000000..c48bacfce0d
--- /dev/null
+++ b/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -0,0 +1,397 @@
+use super::implicit_clone::is_clone_like;
+use super::unnecessary_iter_cloned::{self, is_into_iter};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs};
+use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item};
+use rustc_errors::Applicability;
+use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::mir::Mutability;
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
+use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
+use rustc_middle::ty::{self, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
+use rustc_span::{sym, Symbol};
+use std::cmp::max;
+
+use super::UNNECESSARY_TO_OWNED;
+
+pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) {
+    if_chain! {
+        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+        if let [receiver] = args;
+        then {
+            if is_cloned_or_copied(cx, method_name, method_def_id) {
+                unnecessary_iter_cloned::check(cx, expr, method_name, receiver);
+            } else if is_to_owned_like(cx, method_name, method_def_id) {
+                // At this point, we know the call is of a `to_owned`-like function. The functions
+                // `check_addr_of_expr` and `check_call_arg` determine whether the call is unnecessary
+                // based on its context, that is, whether it is a referent in an `AddrOf` expression, an
+                // argument in a `into_iter` call, or an argument in the call of some other function.
+                if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) {
+                    return;
+                }
+                if check_into_iter_call_arg(cx, expr, method_name, receiver) {
+                    return;
+                }
+                check_other_call_arg(cx, expr, method_name, receiver);
+            }
+        }
+    }
+}
+
+/// Checks whether `expr` is a referent in an `AddrOf` expression and, if so, determines whether its
+/// call of a `to_owned`-like function is unnecessary.
+#[allow(clippy::too_many_lines)]
+fn check_addr_of_expr(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    method_name: Symbol,
+    method_def_id: DefId,
+    receiver: &'tcx Expr<'tcx>,
+) -> bool {
+    if_chain! {
+        if let Some(parent) = get_parent_expr(cx, expr);
+        if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind;
+        let adjustments = cx.typeck_results().expr_adjustments(parent).iter().collect::<Vec<_>>();
+        if let Some(target_ty) = match adjustments[..]
+        {
+            // For matching uses of `Cow::from`
+            [
+                Adjustment {
+                    kind: Adjust::Deref(None),
+                    ..
+                },
+                Adjustment {
+                    kind: Adjust::Borrow(_),
+                    target: target_ty,
+                },
+            ]
+            // For matching uses of arrays
+            | [
+                Adjustment {
+                    kind: Adjust::Deref(None),
+                    ..
+                },
+                Adjustment {
+                    kind: Adjust::Borrow(_),
+                    ..
+                },
+                Adjustment {
+                    kind: Adjust::Pointer(_),
+                    target: target_ty,
+                },
+            ]
+            // For matching everything else
+            | [
+                Adjustment {
+                    kind: Adjust::Deref(None),
+                    ..
+                },
+                Adjustment {
+                    kind: Adjust::Deref(Some(OverloadedDeref { .. })),
+                    ..
+                },
+                Adjustment {
+                    kind: Adjust::Borrow(_),
+                    target: target_ty,
+                },
+            ] => Some(target_ty),
+            _ => None,
+        };
+        let receiver_ty = cx.typeck_results().expr_ty(receiver);
+        // Only flag cases where the receiver is copyable or the method is `Cow::into_owned`. This
+        // restriction is to ensure there is not overlap between `redundant_clone` and this lint.
+        if is_copy(cx, receiver_ty) || is_cow_into_owned(cx, method_name, method_def_id);
+        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
+        then {
+            let (target_ty, n_target_refs) = peel_mid_ty_refs(target_ty);
+            let (receiver_ty, n_receiver_refs) = peel_mid_ty_refs(receiver_ty);
+            if receiver_ty == target_ty && n_target_refs >= n_receiver_refs {
+                span_lint_and_sugg(
+                    cx,
+                    UNNECESSARY_TO_OWNED,
+                    parent.span,
+                    &format!("unnecessary use of `{}`", method_name),
+                    "use",
+                    format!("{:&>width$}{}", "", receiver_snippet, width = n_target_refs - n_receiver_refs),
+                    Applicability::MachineApplicable,
+                );
+                return true;
+            }
+            if_chain! {
+                if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref);
+                if implements_trait(cx, receiver_ty, deref_trait_id, &[]);
+                if get_associated_type(cx, receiver_ty, deref_trait_id, "Target") == Some(target_ty);
+                then {
+                    if n_receiver_refs > 0 {
+                        span_lint_and_sugg(
+                            cx,
+                            UNNECESSARY_TO_OWNED,
+                            parent.span,
+                            &format!("unnecessary use of `{}`", method_name),
+                            "use",
+                            receiver_snippet,
+                            Applicability::MachineApplicable,
+                        );
+                    } else {
+                        span_lint_and_sugg(
+                            cx,
+                            UNNECESSARY_TO_OWNED,
+                            expr.span.with_lo(receiver.span.hi()),
+                            &format!("unnecessary use of `{}`", method_name),
+                            "remove this",
+                            String::new(),
+                            Applicability::MachineApplicable,
+                        );
+                    }
+                    return true;
+                }
+            }
+            if_chain! {
+                if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef);
+                if implements_trait(cx, receiver_ty, as_ref_trait_id, &[GenericArg::from(target_ty)]);
+                then {
+                    span_lint_and_sugg(
+                        cx,
+                        UNNECESSARY_TO_OWNED,
+                        parent.span,
+                        &format!("unnecessary use of `{}`", method_name),
+                        "use",
+                        format!("{}.as_ref()", receiver_snippet),
+                        Applicability::MachineApplicable,
+                    );
+                    return true;
+                }
+            }
+        }
+    }
+    false
+}
+
+/// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its
+/// call of a `to_owned`-like function is unnecessary.
+fn check_into_iter_call_arg(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    method_name: Symbol,
+    receiver: &'tcx Expr<'tcx>,
+) -> bool {
+    if_chain! {
+        if let Some(parent) = get_parent_expr(cx, expr);
+        if let Some(callee_def_id) = fn_def_id(cx, parent);
+        if is_into_iter(cx, callee_def_id);
+        if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
+        let parent_ty = cx.typeck_results().expr_ty(parent);
+        if implements_trait(cx, parent_ty, iterator_trait_id, &[]);
+        if let Some(item_ty) = get_iterator_item_ty(cx, parent_ty);
+        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
+        then {
+            if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver) {
+                return true;
+            }
+            let cloned_or_copied = if is_copy(cx, item_ty) {
+                "copied"
+            } else {
+                "cloned"
+            };
+            span_lint_and_sugg(
+                cx,
+                UNNECESSARY_TO_OWNED,
+                parent.span,
+                &format!("unnecessary use of `{}`", method_name),
+                "use",
+                format!("{}.iter().{}()", receiver_snippet, cloned_or_copied),
+                Applicability::MachineApplicable,
+            );
+            return true;
+        }
+    }
+    false
+}
+
+/// Checks whether `expr` is an argument in a function call and, if so, determines whether its call
+/// of a `to_owned`-like function is unnecessary.
+fn check_other_call_arg(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    method_name: Symbol,
+    receiver: &'tcx Expr<'tcx>,
+) -> bool {
+    if_chain! {
+        if let Some((maybe_call, maybe_arg)) = skip_addr_of_ancestors(cx, expr);
+        if let Some((callee_def_id, call_substs, call_args)) = get_callee_substs_and_args(cx, maybe_call);
+        let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
+        if let Some(i) = call_args.iter().position(|arg| arg.hir_id == maybe_arg.hir_id);
+        if let Some(input) = fn_sig.inputs().get(i);
+        let (input, n_refs) = peel_mid_ty_refs(input);
+        if let (trait_predicates, projection_predicates) = get_input_traits_and_projections(cx, callee_def_id, input);
+        if let Some(sized_def_id) = cx.tcx.lang_items().sized_trait();
+        if let [trait_predicate] = trait_predicates
+            .iter()
+            .filter(|trait_predicate| trait_predicate.def_id() != sized_def_id)
+            .collect::<Vec<_>>()[..];
+        if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref);
+        if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef);
+        let receiver_ty = cx.typeck_results().expr_ty(receiver);
+        // If the callee has type parameters, they could appear in `projection_predicate.ty` or the
+        // types of `trait_predicate.trait_ref.substs`.
+        if if trait_predicate.def_id() == deref_trait_id {
+            if let [projection_predicate] = projection_predicates[..] {
+                let normalized_ty =
+                    cx.tcx.subst_and_normalize_erasing_regions(call_substs, cx.param_env, projection_predicate.ty);
+                implements_trait(cx, receiver_ty, deref_trait_id, &[])
+                    && get_associated_type(cx, receiver_ty, deref_trait_id, "Target") == Some(normalized_ty)
+            } else {
+                false
+            }
+        } else if trait_predicate.def_id() == as_ref_trait_id {
+            let composed_substs = compose_substs(
+                cx,
+                &trait_predicate.trait_ref.substs.iter().skip(1).collect::<Vec<_>>()[..],
+                call_substs
+            );
+            implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs)
+        } else {
+            false
+        };
+        // We can't add an `&` when the trait is `Deref` because `Target = &T` won't match
+        // `Target = T`.
+        if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id;
+        let n_refs = max(n_refs, if is_copy(cx, receiver_ty) { 0 } else { 1 });
+        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
+        then {
+            span_lint_and_sugg(
+                cx,
+                UNNECESSARY_TO_OWNED,
+                maybe_arg.span,
+                &format!("unnecessary use of `{}`", method_name),
+                "use",
+                format!("{:&>width$}{}", "", receiver_snippet, width = n_refs),
+                Applicability::MachineApplicable,
+            );
+            return true;
+        }
+    }
+    false
+}
+
+/// Walks an expression's ancestors until it finds a non-`AddrOf` expression. Returns the first such
+/// expression found (if any) along with the immediately prior expression.
+fn skip_addr_of_ancestors(
+    cx: &LateContext<'tcx>,
+    mut expr: &'tcx Expr<'tcx>,
+) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
+    while let Some(parent) = get_parent_expr(cx, expr) {
+        if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind {
+            expr = parent;
+        } else {
+            return Some((parent, expr));
+        }
+    }
+    None
+}
+
+/// Checks whether an expression is a function or method call and, if so, returns its `DefId`,
+/// `Substs`, and arguments.
+fn get_callee_substs_and_args(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+) -> Option<(DefId, SubstsRef<'tcx>, &'tcx [Expr<'tcx>])> {
+    if_chain! {
+        if let ExprKind::Call(callee, args) = expr.kind;
+        let callee_ty = cx.typeck_results().expr_ty(callee);
+        if let ty::FnDef(callee_def_id, _) = callee_ty.kind();
+        then {
+            let substs = cx.typeck_results().node_substs(callee.hir_id);
+            return Some((*callee_def_id, substs, args));
+        }
+    }
+    if_chain! {
+        if let ExprKind::MethodCall(_, _, args, _) = expr.kind;
+        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+        then {
+            let substs = cx.typeck_results().node_substs(expr.hir_id);
+            return Some((method_def_id, substs, args));
+        }
+    }
+    None
+}
+
+/// Returns the `TraitPredicate`s and `ProjectionPredicate`s for a function's input type.
+fn get_input_traits_and_projections(
+    cx: &LateContext<'tcx>,
+    callee_def_id: DefId,
+    input: Ty<'tcx>,
+) -> (Vec<TraitPredicate<'tcx>>, Vec<ProjectionPredicate<'tcx>>) {
+    let mut trait_predicates = Vec::new();
+    let mut projection_predicates = Vec::new();
+    for (predicate, _) in cx.tcx.predicates_of(callee_def_id).predicates.iter() {
+        // `substs` should have 1 + n elements. The first is the type on the left hand side of an
+        // `as`. The remaining n are trait parameters.
+        let is_input_substs = |substs: SubstsRef<'tcx>| {
+            if_chain! {
+                if let Some(arg) = substs.iter().next();
+                if let GenericArgKind::Type(arg_ty) = arg.unpack();
+                if arg_ty == input;
+                then {
+                    true
+                } else {
+                    false
+                }
+            }
+        };
+        match predicate.kind().skip_binder() {
+            PredicateKind::Trait(trait_predicate) => {
+                if is_input_substs(trait_predicate.trait_ref.substs) {
+                    trait_predicates.push(trait_predicate);
+                }
+            },
+            PredicateKind::Projection(projection_predicate) => {
+                if is_input_substs(projection_predicate.projection_ty.substs) {
+                    projection_predicates.push(projection_predicate);
+                }
+            },
+            _ => {},
+        }
+    }
+    (trait_predicates, projection_predicates)
+}
+
+/// Composes two substitutions by applying the latter to the types of the former.
+fn compose_substs(cx: &LateContext<'tcx>, left: &[GenericArg<'tcx>], right: SubstsRef<'tcx>) -> Vec<GenericArg<'tcx>> {
+    left.iter()
+        .map(|arg| {
+            if let GenericArgKind::Type(arg_ty) = arg.unpack() {
+                let normalized_ty = cx.tcx.subst_and_normalize_erasing_regions(right, cx.param_env, arg_ty);
+                GenericArg::from(normalized_ty)
+            } else {
+                *arg
+            }
+        })
+        .collect()
+}
+
+/// Returns true if the named method is `Iterator::cloned` or `Iterator::copied`.
+fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
+    (method_name.as_str() == "cloned" || method_name.as_str() == "copied")
+        && is_diag_trait_item(cx, method_def_id, sym::Iterator)
+}
+
+/// Returns true if the named method can be used to convert the receiver to its "owned"
+/// representation.
+fn is_to_owned_like(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
+    is_clone_like(cx, &*method_name.as_str(), method_def_id)
+        || is_cow_into_owned(cx, method_name, method_def_id)
+        || is_to_string(cx, method_name, method_def_id)
+}
+
+/// Returns true if the named method is `Cow::into_owned`.
+fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
+    method_name.as_str() == "into_owned" && is_diag_item_method(cx, method_def_id, sym::Cow)
+}
+
+/// Returns true if the named method is `ToString::to_string`.
+fn is_to_string(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
+    method_name.as_str() == "to_string" && is_diag_trait_item(cx, method_def_id, sym::ToString)
+}
diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs
index 3709f3948c2..d391fbecf82 100644
--- a/clippy_lints/src/needless_bool.rs
+++ b/clippy_lints/src/needless_bool.rs
@@ -6,10 +6,10 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
 use clippy_utils::higher;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{is_else_clause, is_expn_of};
+use clippy_utils::{get_parent_node, is_else_clause, is_expn_of, peel_blocks, peel_blocks_with_stmt};
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
+use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Node, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Spanned;
@@ -74,6 +74,36 @@ declare_clippy_lint! {
 
 declare_lint_pass!(NeedlessBool => [NEEDLESS_BOOL]);
 
+fn condition_needs_parentheses(e: &Expr<'_>) -> bool {
+    let mut inner = e;
+    while let ExprKind::Binary(_, i, _)
+    | ExprKind::Call(i, _)
+    | ExprKind::Cast(i, _)
+    | ExprKind::Type(i, _)
+    | ExprKind::Index(i, _) = inner.kind
+    {
+        if matches!(
+            i.kind,
+            ExprKind::Block(..)
+                | ExprKind::ConstBlock(..)
+                | ExprKind::If(..)
+                | ExprKind::Loop(..)
+                | ExprKind::Match(..)
+        ) {
+            return true;
+        }
+        inner = i;
+    }
+    false
+}
+
+fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
+    matches!(
+        get_parent_node(cx.tcx, id),
+        Some(Node::Stmt(..) | Node::Block(Block { stmts: &[], .. }))
+    )
+}
+
 impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
         use self::Expression::{Bool, RetBool};
@@ -99,6 +129,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
                     snip = snip.blockify();
                 }
 
+                if condition_needs_parentheses(cond) && is_parent_stmt(cx, e.hir_id) {
+                    snip = snip.maybe_par();
+                }
+
                 span_lint_and_sugg(
                     cx,
                     NEEDLESS_BOOL,
@@ -109,8 +143,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
                     applicability,
                 );
             };
-            if let ExprKind::Block(then, _) = then.kind {
-                match (fetch_bool_block(then), fetch_bool_expr(r#else)) {
+            if let Some((a, b)) = fetch_bool_block(then).and_then(|a| Some((a, fetch_bool_block(r#else)?))) {
+                match (a, b) {
                     (RetBool(true), RetBool(true)) | (Bool(true), Bool(true)) => {
                         span_lint(
                             cx,
@@ -133,8 +167,6 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
                     (Bool(false), Bool(true)) => reduce(false, true),
                     _ => (),
                 }
-            } else {
-                panic!("IfExpr `then` node is not an `ExprKind::Block`");
             }
         }
     }
@@ -237,8 +269,6 @@ fn check_comparison<'a, 'tcx>(
     right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
     no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &str)>,
 ) {
-    use self::Expression::{Bool, Other};
-
     if let ExprKind::Binary(op, left_side, right_side) = e.kind {
         let (l_ty, r_ty) = (
             cx.typeck_results().expr_ty(left_side),
@@ -270,19 +300,19 @@ fn check_comparison<'a, 'tcx>(
             }
 
             match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) {
-                (Bool(true), Other) => left_true.map_or((), |(h, m)| {
+                (Some(true), None) => left_true.map_or((), |(h, m)| {
                     suggest_bool_comparison(cx, e, right_side, applicability, m, h);
                 }),
-                (Other, Bool(true)) => right_true.map_or((), |(h, m)| {
+                (None, Some(true)) => right_true.map_or((), |(h, m)| {
                     suggest_bool_comparison(cx, e, left_side, applicability, m, h);
                 }),
-                (Bool(false), Other) => left_false.map_or((), |(h, m)| {
+                (Some(false), None) => left_false.map_or((), |(h, m)| {
                     suggest_bool_comparison(cx, e, right_side, applicability, m, h);
                 }),
-                (Other, Bool(false)) => right_false.map_or((), |(h, m)| {
+                (None, Some(false)) => right_false.map_or((), |(h, m)| {
                     suggest_bool_comparison(cx, e, left_side, applicability, m, h);
                 }),
-                (Other, Other) => no_literal.map_or((), |(h, m)| {
+                (None, None) => no_literal.map_or((), |(h, m)| {
                     let left_side = Sugg::hir_with_applicability(cx, left_side, "..", &mut applicability);
                     let right_side = Sugg::hir_with_applicability(cx, right_side, "..", &mut applicability);
                     span_lint_and_sugg(
@@ -331,41 +361,20 @@ fn suggest_bool_comparison<'a, 'tcx>(
 enum Expression {
     Bool(bool),
     RetBool(bool),
-    Other,
 }
 
-fn fetch_bool_block(block: &Block<'_>) -> Expression {
-    match (&*block.stmts, block.expr.as_ref()) {
-        (&[], Some(e)) => fetch_bool_expr(&**e),
-        (&[ref e], None) => {
-            if let StmtKind::Semi(e) = e.kind {
-                if let ExprKind::Ret(_) = e.kind {
-                    fetch_bool_expr(e)
-                } else {
-                    Expression::Other
-                }
-            } else {
-                Expression::Other
-            }
-        },
-        _ => Expression::Other,
+fn fetch_bool_block(expr: &Expr<'_>) -> Option<Expression> {
+    match peel_blocks_with_stmt(expr).kind {
+        ExprKind::Ret(Some(ret)) => Some(Expression::RetBool(fetch_bool_expr(ret)?)),
+        _ => Some(Expression::Bool(fetch_bool_expr(expr)?)),
     }
 }
 
-fn fetch_bool_expr(expr: &Expr<'_>) -> Expression {
-    match expr.kind {
-        ExprKind::Block(block, _) => fetch_bool_block(block),
-        ExprKind::Lit(ref lit_ptr) => {
-            if let LitKind::Bool(value) = lit_ptr.node {
-                Expression::Bool(value)
-            } else {
-                Expression::Other
-            }
-        },
-        ExprKind::Ret(Some(expr)) => match fetch_bool_expr(expr) {
-            Expression::Bool(value) => Expression::RetBool(value),
-            _ => Expression::Other,
-        },
-        _ => Expression::Other,
+fn fetch_bool_expr(expr: &Expr<'_>) -> Option<bool> {
+    if let ExprKind::Lit(ref lit_ptr) = peel_blocks(expr).kind {
+        if let LitKind::Bool(value) = lit_ptr.node {
+            return Some(value);
+        }
     }
+    None
 }
diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs
index 5b098659377..094a3f111ba 100644
--- a/clippy_lints/src/needless_late_init.rs
+++ b/clippy_lints/src/needless_late_init.rs
@@ -73,7 +73,7 @@ fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) ->
     seen
 }
 
-#[derive(Debug, Clone)]
+#[derive(Debug)]
 struct LocalAssign {
     lhs_id: HirId,
     lhs_span: Span,
@@ -154,14 +154,15 @@ fn assignment_suggestions<'tcx>(
         assignments.push(assign);
     }
 
-    let suggestions = assignments.clone()
-        .into_iter()
+    let suggestions = assignments
+        .iter()
         .map(|assignment| Some((assignment.span.until(assignment.rhs_span), String::new())))
-        .chain(
-            assignments
-                .into_iter()
-                .map(|assignment| Some((assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi()), String::new())))
-        )
+        .chain(assignments.iter().map(|assignment| {
+            Some((
+                assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi()),
+                String::new(),
+            ))
+        }))
         .collect::<Option<Vec<(Span, String)>>>()?;
 
     let applicability = if suggestions.len() > 1 {
diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs
index 6fcc9ca29b9..9d5babc5de8 100644
--- a/clippy_lints/src/no_effect.rs
+++ b/clippy_lints/src/no_effect.rs
@@ -1,5 +1,6 @@
 use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
 use clippy_utils::is_lint_allowed;
+use clippy_utils::peel_blocks;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::has_drop;
 use rustc_errors::Applicability;
@@ -114,7 +115,7 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     if expr.span.from_expansion() {
         return false;
     }
-    match expr.kind {
+    match peel_blocks(expr).kind {
         ExprKind::Lit(..) | ExprKind::Closure(..) => true,
         ExprKind::Path(..) => !has_drop(cx, cx.typeck_results().expr_ty(expr)),
         ExprKind::Index(a, b) | ExprKind::Binary(_, a, b) => has_no_effect(cx, a) && has_no_effect(cx, b),
@@ -150,9 +151,6 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
                 false
             }
         },
-        ExprKind::Block(block, _) => {
-            block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| has_no_effect(cx, expr))
-        },
         _ => false,
     }
 }
diff --git a/clippy_lints/src/non_send_fields_in_send_ty.rs b/clippy_lints/src/non_send_fields_in_send_ty.rs
index bba542ce8ca..203f03d3603 100644
--- a/clippy_lints/src/non_send_fields_in_send_ty.rs
+++ b/clippy_lints/src/non_send_fields_in_send_ty.rs
@@ -13,24 +13,28 @@ use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Warns about fields in struct implementing `Send` that are neither `Send` nor `Copy`.
+    /// This lint warns about a `Send` implementation for a type that
+    /// contains fields that are not safe to be sent across threads.
+    /// It tries to detect fields that can cause a soundness issue
+    /// when sent to another thread (e.g., `Rc`) while allowing `!Send` fields
+    /// that are expected to exist in a `Send` type, such as raw pointers.
     ///
     /// ### Why is this bad?
-    /// Sending the struct to another thread will transfer the ownership to
-    /// the new thread by dropping in the current thread during the transfer.
-    /// This causes soundness issues for non-`Send` fields, as they are also
-    /// dropped and might not be set up to handle this.
+    /// Sending the struct to another thread effectively sends all of its fields,
+    /// and the fields that do not implement `Send` can lead to soundness bugs
+    /// such as data races when accessed in a thread
+    /// that is different from the thread that created it.
     ///
     /// See:
     /// * [*The Rustonomicon* about *Send and Sync*](https://doc.rust-lang.org/nomicon/send-and-sync.html)
     /// * [The documentation of `Send`](https://doc.rust-lang.org/std/marker/trait.Send.html)
     ///
     /// ### Known Problems
-    /// Data structures that contain raw pointers may cause false positives.
-    /// They are sometimes safe to be sent across threads but do not implement
-    /// the `Send` trait. This lint has a heuristic to filter out basic cases
-    /// such as `Vec<*const T>`, but it's not perfect. Feel free to create an
-    /// issue if you have a suggestion on how this heuristic can be improved.
+    /// This lint relies on heuristics to distinguish types that are actually
+    /// unsafe to be sent across threads and `!Send` types that are expected to
+    /// exist in  `Send` type. Its rule can filter out basic cases such as
+    /// `Vec<*const T>`, but it's not perfect. Feel free to create an issue if
+    /// you have a suggestion on how this heuristic can be improved.
     ///
     /// ### Example
     /// ```rust,ignore
@@ -46,8 +50,8 @@ declare_clippy_lint! {
     /// or specify correct bounds on generic type parameters (`T: Send`).
     #[clippy::version = "1.57.0"]
     pub NON_SEND_FIELDS_IN_SEND_TY,
-    suspicious,
-    "there is field that does not implement `Send` in a `Send` struct"
+    nursery,
+    "there is a field that is not safe to be sent to another thread in a `Send` struct"
 }
 
 #[derive(Copy, Clone)]
@@ -120,14 +124,14 @@ impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy {
                         NON_SEND_FIELDS_IN_SEND_TY,
                         item.span,
                         &format!(
-                            "this implementation is unsound, as some fields in `{}` are `!Send`",
+                            "some fields in `{}` are not safe to be sent to another thread",
                             snippet(cx, hir_impl.self_ty.span, "Unknown")
                         ),
                         |diag| {
                             for field in non_send_fields {
                                 diag.span_note(
                                     field.def.span,
-                                    &format!("the type of field `{}` is `!Send`", field.def.ident.name),
+                                    &format!("it is not safe to send field `{}` to another thread", field.def.ident.name),
                                 );
 
                                 match field.generic_params.len() {
diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs
index 262be17f617..953de0f72a8 100644
--- a/clippy_lints/src/option_if_let_else.rs
+++ b/clippy_lints/src/option_if_let_else.rs
@@ -1,15 +1,14 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::higher;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{
-    can_move_expr_to_closure, eager_or_lazy, in_constant, is_else_clause, is_lang_ctor, peel_hir_expr_while,
-    CaptureKind,
+    can_move_expr_to_closure, eager_or_lazy, higher, in_constant, is_else_clause, is_lang_ctor, peel_blocks,
+    peel_hir_expr_while, CaptureKind,
 };
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::OptionSome;
-use rustc_hir::{def::Res, BindingAnnotation, Block, Expr, ExprKind, Mutability, PatKind, Path, QPath, UnOp};
+use rustc_hir::{def::Res, BindingAnnotation, Expr, ExprKind, Mutability, PatKind, Path, QPath, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::sym;
@@ -86,28 +85,6 @@ struct OptionIfLetElseOccurence {
     none_expr: String,
 }
 
-/// 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_expr<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
-    if let ExprKind::Block(
-        Block {
-            stmts: block_stmts,
-            expr: Some(block_expr),
-            ..
-        },
-        _,
-    ) = expr.kind
-    {
-        if let [] = block_stmts {
-            Some(block_expr)
-        } else {
-            Some(expr)
-        }
-    } else {
-        None
-    }
-}
-
 fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String {
     format!(
         "{}{}",
@@ -135,7 +112,7 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) ->
         if !is_result_ok(cx, let_expr); // Don't lint on Result::ok because a different lint does it already
         if let PatKind::TupleStruct(struct_qpath, [inner_pat], _) = &let_pat.kind;
         if is_lang_ctor(cx, struct_qpath, OptionSome);
-        if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind;
+        if let PatKind::Binding(bind_annotation, _, id, None) = &inner_pat.kind;
         if let Some(some_captures) = can_move_expr_to_closure(cx, if_then);
         if let Some(none_captures) = can_move_expr_to_closure(cx, if_else);
         if some_captures
@@ -145,8 +122,8 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) ->
 
         then {
             let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" };
-            let some_body = extract_body_from_expr(if_then)?;
-            let none_body = extract_body_from_expr(if_else)?;
+            let some_body = peel_blocks(if_then);
+            let none_body = peel_blocks(if_else);
             let method_sugg = if eager_or_lazy::switch_to_eager_eval(cx, none_body) { "map_or" } else { "map_or_else" };
             let capture_name = id.name.to_ident_string();
             let (as_ref, as_mut) = match &let_expr.kind {
diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs
index a5531993ee6..c765c8962cf 100644
--- a/clippy_lints/src/question_mark.rs
+++ b/clippy_lints/src/question_mark.rs
@@ -1,14 +1,13 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::higher;
-use clippy_utils::is_lang_ctor;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{eq_expr_value, path_to_local, path_to_local_id};
+use clippy_utils::{eq_expr_value, is_lang_ctor, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::{OptionNone, OptionSome, ResultOk};
-use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, PatKind, StmtKind};
+use rustc_hir::{BindingAnnotation, Expr, ExprKind, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::sym;
@@ -68,14 +67,8 @@ impl QuestionMark {
                 let receiver_str = &Sugg::hir_with_applicability(cx, subject, "..", &mut applicability);
                 let mut replacement: Option<String> = None;
                 if let Some(else_inner) = r#else {
-                    if_chain! {
-                        if let ExprKind::Block(block, None) = &else_inner.kind;
-                        if block.stmts.is_empty();
-                        if let Some(block_expr) = &block.expr;
-                        if eq_expr_value(cx, subject, block_expr);
-                        then {
-                            replacement = Some(format!("Some({}?)", receiver_str));
-                        }
+                    if eq_expr_value(cx, subject, peel_blocks(else_inner)) {
+                        replacement = Some(format!("Some({}?)", receiver_str));
                     }
                 } else if Self::moves_by_default(cx, subject)
                     && !matches!(subject.kind, ExprKind::Call(..) | ExprKind::MethodCall(..))
@@ -110,10 +103,7 @@ impl QuestionMark {
 
             if let PatKind::Binding(annot, bind_id, _, _) = fields[0].kind;
             let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut);
-            if let ExprKind::Block(block, None) = if_then.kind;
-            if block.stmts.is_empty();
-            if let Some(trailing_expr) = &block.expr;
-            if path_to_local_id(trailing_expr, bind_id);
+            if path_to_local_id(peel_blocks(if_then), bind_id);
             then {
                 let mut applicability = Applicability::MachineApplicable;
                 let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
@@ -159,14 +149,7 @@ impl QuestionMark {
     }
 
     fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
-        match expression.kind {
-            ExprKind::Block(block, _) => {
-                if let Some(return_expression) = Self::return_expression(block) {
-                    return Self::expression_returns_none(cx, return_expression);
-                }
-
-                false
-            },
+        match peel_blocks_with_stmt(expression).kind {
             ExprKind::Ret(Some(expr)) => Self::expression_returns_none(cx, expr),
             ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
             _ => false,
@@ -174,44 +157,12 @@ impl QuestionMark {
     }
 
     fn expression_returns_unmodified_err(cx: &LateContext<'_>, expr: &Expr<'_>, cond_expr: &Expr<'_>) -> bool {
-        match expr.kind {
-            ExprKind::Block(block, _) => {
-                if let Some(return_expression) = Self::return_expression(block) {
-                    return Self::expression_returns_unmodified_err(cx, return_expression, cond_expr);
-                }
-
-                false
-            },
+        match peel_blocks_with_stmt(expr).kind {
             ExprKind::Ret(Some(ret_expr)) => Self::expression_returns_unmodified_err(cx, ret_expr, cond_expr),
-            ExprKind::Path(_) => path_to_local(expr) == path_to_local(cond_expr),
+            ExprKind::Path(_) => path_to_local(expr).is_some() && path_to_local(expr) == path_to_local(cond_expr),
             _ => false,
         }
     }
-
-    fn return_expression<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
-        // Check if last expression is a return statement. Then, return the expression
-        if_chain! {
-            if block.stmts.len() == 1;
-            if let Some(expr) = block.stmts.iter().last();
-            if let StmtKind::Semi(expr) = expr.kind;
-            if let ExprKind::Ret(Some(ret_expr)) = expr.kind;
-
-            then {
-                return Some(ret_expr);
-            }
-        }
-
-        // Check for `return` without a semicolon.
-        if_chain! {
-            if block.stmts.is_empty();
-            if let Some(ExprKind::Ret(Some(ret_expr))) = block.expr.as_ref().map(|e| &e.kind);
-            then {
-                return Some(ret_expr);
-            }
-        }
-
-        None
-    }
 }
 
 impl<'tcx> LateLintPass<'tcx> for QuestionMark {
diff --git a/clippy_lints/src/return_self_not_must_use.rs b/clippy_lints/src/return_self_not_must_use.rs
new file mode 100644
index 00000000000..1118da6c8cb
--- /dev/null
+++ b/clippy_lints/src/return_self_not_must_use.rs
@@ -0,0 +1,105 @@
+use clippy_utils::{diagnostics::span_lint, must_use_attr, nth_arg, return_ty};
+use rustc_hir::def_id::LocalDefId;
+use rustc_hir::intravisit::FnKind;
+use rustc_hir::{Body, FnDecl, HirId, TraitItem, TraitItemKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// This lint warns when a method returning `Self` doesn't have the `#[must_use]` attribute.
+    ///
+    /// ### Why is this bad?
+    /// It prevents to "forget" to use the newly created value.
+    ///
+    /// ### Limitations
+    /// This lint is only applied on methods taking a `self` argument. It would be mostly noise
+    /// if it was added on constructors for example.
+    ///
+    /// ### Example
+    /// ```rust
+    /// pub struct Bar;
+    ///
+    /// impl Bar {
+    ///     // Bad
+    ///     pub fn bar(&self) -> Self {
+    ///         Self
+    ///     }
+    ///
+    ///     // Good
+    ///     #[must_use]
+    ///     pub fn foo(&self) -> Self {
+    ///         Self
+    ///     }
+    /// }
+    /// ```
+    #[clippy::version = "1.59.0"]
+    pub RETURN_SELF_NOT_MUST_USE,
+    suspicious,
+    "missing `#[must_use]` annotation on a method returning `Self`"
+}
+
+declare_lint_pass!(ReturnSelfNotMustUse => [RETURN_SELF_NOT_MUST_USE]);
+
+fn check_method(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'tcx>, fn_def: LocalDefId, span: Span, hir_id: HirId) {
+    if_chain! {
+        // If it comes from an external macro, better ignore it.
+        if !in_external_macro(cx.sess(), span);
+        if decl.implicit_self.has_implicit_self();
+        // We only show this warning for public exported methods.
+        if cx.access_levels.is_exported(fn_def);
+        if cx.tcx.visibility(fn_def.to_def_id()).is_public();
+        // No need to warn if the attribute is already present.
+        if must_use_attr(cx.tcx.hir().attrs(hir_id)).is_none();
+        let ret_ty = return_ty(cx, hir_id);
+        let self_arg = nth_arg(cx, hir_id, 0);
+        // If `Self` has the same type as the returned type, then we want to warn.
+        //
+        // For this check, we don't want to remove the reference on the returned type because if
+        // there is one, we shouldn't emit a warning!
+        if self_arg.peel_refs() == ret_ty;
+
+        then {
+            span_lint(
+                cx,
+                RETURN_SELF_NOT_MUST_USE,
+                span,
+                "missing `#[must_use]` attribute on a method returning `Self`",
+            );
+        }
+    }
+}
+
+impl<'tcx> LateLintPass<'tcx> for ReturnSelfNotMustUse {
+    fn check_fn(
+        &mut self,
+        cx: &LateContext<'tcx>,
+        kind: FnKind<'tcx>,
+        decl: &'tcx FnDecl<'tcx>,
+        _: &'tcx Body<'tcx>,
+        span: Span,
+        hir_id: HirId,
+    ) {
+        if_chain! {
+            // We are only interested in methods, not in functions or associated functions.
+            if matches!(kind, FnKind::Method(_, _, _));
+            if let Some(fn_def) = cx.tcx.hir().opt_local_def_id(hir_id);
+            if let Some(impl_def) = cx.tcx.impl_of_method(fn_def.to_def_id());
+            // We don't want this method to be te implementation of a trait because the
+            // `#[must_use]` should be put on the trait definition directly.
+            if cx.tcx.trait_id_of_impl(impl_def).is_none();
+
+            then {
+                check_method(cx, decl, fn_def, span, hir_id);
+            }
+        }
+    }
+
+    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tcx>) {
+        if let TraitItemKind::Fn(ref sig, _) = item.kind {
+            check_method(cx, sig.decl, item.def_id, item.span, item.hir_id());
+        }
+    }
+}
diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs
index c2163a24b7f..73600a8a087 100644
--- a/clippy_lints/src/strings.rs
+++ b/clippy_lints/src/strings.rs
@@ -1,8 +1,8 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg};
 use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::SpanlessEq;
 use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method_calls, paths};
+use clippy_utils::{peel_blocks, SpanlessEq};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath};
@@ -201,7 +201,7 @@ fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 }
 
 fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool {
-    match src.kind {
+    match peel_blocks(src).kind {
         ExprKind::Binary(
             Spanned {
                 node: BinOpKind::Add, ..
@@ -209,9 +209,6 @@ fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool {
             left,
             _,
         ) => SpanlessEq::new(cx).eq_expr(target, left),
-        ExprKind::Block(block, _) => {
-            block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| is_add(cx, expr, target))
-        },
         _ => false,
     }
 }
diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs
index 5a7ef760a30..481e5957435 100644
--- a/clippy_lints/src/types/mod.rs
+++ b/clippy_lints/src/types/mod.rs
@@ -350,16 +350,28 @@ impl<'tcx> LateLintPass<'tcx> for Types {
 
     fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
         match item.kind {
-            ImplItemKind::Const(ty, _) | ImplItemKind::TyAlias(ty) => self.check_ty(
-                cx,
-                ty,
-                CheckTyContext {
-                    is_in_trait_impl: true,
-                    ..CheckTyContext::default()
-                },
-            ),
-            // methods are covered by check_fn
-            ImplItemKind::Fn(..) => (),
+            ImplItemKind::Const(ty, _) => {
+                let is_in_trait_impl = if let Some(hir::Node::Item(item)) =
+                    cx.tcx.hir().find(cx.tcx.hir().get_parent_item(item.hir_id()))
+                {
+                    matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
+                } else {
+                    false
+                };
+
+                self.check_ty(
+                    cx,
+                    ty,
+                    CheckTyContext {
+                        is_in_trait_impl,
+                        ..CheckTyContext::default()
+                    },
+                );
+            },
+            // Methods are covered by check_fn.
+            // Type aliases are ignored because oftentimes it's impossible to
+            // make type alias declaration in trait simpler, see #1013
+            ImplItemKind::Fn(..) | ImplItemKind::TyAlias(..) => (),
         }
     }
 
@@ -417,6 +429,14 @@ impl Types {
     }
 
     fn check_fn_decl(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>, context: CheckTyContext) {
+        // Ignore functions in trait implementations as they are usually forced by the trait definition.
+        //
+        // FIXME: idially we would like to warn *if the compicated type can be simplified*, but it's hard to
+        // check.
+        if context.is_in_trait_impl {
+            return;
+        }
+
         for input in decl.inputs {
             self.check_ty(cx, input, context);
         }
@@ -435,12 +455,12 @@ impl Types {
             return;
         }
 
-        if !context.is_nested_call && type_complexity::check(cx, hir_ty, self.type_complexity_threshold) {
+        // Skip trait implementations; see issue #605.
+        if context.is_in_trait_impl {
             return;
         }
 
-        // Skip trait implementations; see issue #605.
-        if context.is_in_trait_impl {
+        if !context.is_nested_call && type_complexity::check(cx, hir_ty, self.type_complexity_threshold) {
             return;
         }
 
diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs
index 9fb8f236899..fe35ff33d35 100644
--- a/clippy_lints/src/unit_return_expecting_ord.rs
+++ b/clippy_lints/src/unit_return_expecting_ord.rs
@@ -169,7 +169,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd {
                                     trait_name
                                 ),
                                 Some(last_semi),
-                                &"probably caused by this trailing semicolon".to_string(),
+                                "probably caused by this trailing semicolon",
                             );
                         },
                         None => {},
diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs
index 1f97c8ba7e6..81e9c2e15c9 100644
--- a/clippy_lints/src/utils/internal_lints.rs
+++ b/clippy_lints/src/utils/internal_lints.rs
@@ -1,11 +1,10 @@
 use clippy_utils::consts::{constant_simple, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::higher;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::match_type;
 use clippy_utils::{
-    is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path, method_calls, path_to_res,
-    paths, SpanlessEq,
+    higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path, method_calls,
+    path_to_res, paths, peel_blocks_with_stmt, SpanlessEq,
 };
 use if_chain::if_chain;
 use rustc_ast as ast;
@@ -662,10 +661,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
             if and_then_args.len() == 5;
             if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind;
             let body = cx.tcx.hir().body(*body_id);
-            if let ExprKind::Block(block, _) = &body.value.kind;
-            let stmts = &block.stmts;
-            if stmts.len() == 1 && block.expr.is_none();
-            if let StmtKind::Semi(only_expr) = &stmts[0].kind;
+            let only_expr = peel_blocks_with_stmt(&body.value);
             if let ExprKind::MethodCall(ps, _, span_call_args, _) = &only_expr.kind;
             then {
                 let and_then_snippets = get_and_then_snippets(cx, and_then_args);
diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs
index 8051c58bad7..7707eebd622 100644
--- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs
+++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs
@@ -8,6 +8,11 @@
 //! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such
 //! a simple mistake)
 
+use crate::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type};
+
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::{match_type, walk_ptrs_ty_depth};
+use clippy_utils::{last_path_segment, match_def_path, match_function_call, match_path, paths};
 use if_chain::if_chain;
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashMap;
@@ -25,12 +30,6 @@ use std::fs::{self, OpenOptions};
 use std::io::prelude::*;
 use std::path::Path;
 
-use crate::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type};
-use clippy_utils::{
-    diagnostics::span_lint, last_path_segment, match_def_path, match_function_call, match_path, paths, ty::match_type,
-    ty::walk_ptrs_ty_depth,
-};
-
 /// This is the output file of the lint collector.
 const OUTPUT_FILE: &str = "../util/gh-pages/lints.json";
 /// These lints are excluded from the export.