about summary refs log tree commit diff
diff options
context:
space:
mode:
authorflip1995 <philipp.krones@embecosm.com>2021-09-08 16:31:47 +0200
committerflip1995 <philipp.krones@embecosm.com>2021-09-08 16:31:47 +0200
commit7636fbce7eb25b3178842499588bb121b781b4f5 (patch)
tree8088ff374214eccc1eabdccda8085ae16b2ddbd5
parent434cb437b55d61bcb54a01921de7ac752e6dee13 (diff)
parent27afd6ade4bb1123a8bf82001629b69d23d62aff (diff)
downloadrust-7636fbce7eb25b3178842499588bb121b781b4f5.tar.gz
rust-7636fbce7eb25b3178842499588bb121b781b4f5.zip
Merge commit '27afd6ade4bb1123a8bf82001629b69d23d62aff' into clippyup
-rw-r--r--src/tools/clippy/.cargo/config3
-rw-r--r--src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.md14
-rw-r--r--src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.md40
-rw-r--r--src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.md22
-rw-r--r--src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.md31
-rw-r--r--src/tools/clippy/.github/ISSUE_TEMPLATE/ice.md21
-rw-r--r--src/tools/clippy/.github/workflows/clippy_dev.yml3
-rw-r--r--src/tools/clippy/CHANGELOG.md12
-rw-r--r--src/tools/clippy/Cargo.toml15
-rw-r--r--src/tools/clippy/README.md8
-rw-r--r--src/tools/clippy/clippy_dev/src/lib.rs1
-rw-r--r--src/tools/clippy/clippy_dev/src/main.rs9
-rw-r--r--src/tools/clippy/clippy_dev/src/stderr_length_check.rs51
-rw-r--r--src/tools/clippy/clippy_lints/Cargo.toml4
-rw-r--r--src/tools/clippy/clippy_lints/src/approx_const.rs124
-rw-r--r--src/tools/clippy/clippy_lints/src/assertions_on_constants.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs90
-rw-r--r--src/tools/clippy/clippy_lints/src/bytecount.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/collapsible_match.rs39
-rw-r--r--src/tools/clippy/clippy_lints/src/copies.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/derivable_impls.rs108
-rw-r--r--src/tools/clippy/clippy_lints/src/derive.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/entry.rs31
-rw-r--r--src/tools/clippy/clippy_lints/src/eval_order_dependence.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/feature_name.rs164
-rw-r--r--src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs32
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/must_use.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/if_let_mutex.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/if_let_some_result.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/let_if_seq.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs41
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/mod.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs98
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/needless_collect.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs29
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/never_loop.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/macro_use.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_map.rs322
-rw-r--r--src/tools/clippy/clippy_lints/src/matches.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/mem_forget.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/mem_replace.rs85
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/filter_next.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_split_once.rs213
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs42
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/utils.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/misc.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/module_style.rs178
-rw-r--r--src/tools/clippy/clippy_lints/src/mut_mutex_lock.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_option_as_deref.rs66
-rw-r--r--src/tools/clippy/clippy_lints/src/no_effect.rs69
-rw-r--r--src/tools/clippy/clippy_lints/src/open_options.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/option_if_let_else.rs46
-rw-r--r--src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/question_mark.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_clone.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_closure_call.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/returns.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/strings.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/to_string_in_display.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/types/mod.rs81
-rw-r--r--src/tools/clippy/clippy_lints/src/types/rc_mutex.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_async.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_io_amount.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_self.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/unwrap.rs133
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/conf.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/inspector.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints.rs28
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/vec.rs2
-rw-r--r--src/tools/clippy/clippy_utils/Cargo.toml6
-rw-r--r--src/tools/clippy/clippy_utils/src/diagnostics.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/higher.rs165
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs6
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs357
-rw-r--r--src/tools/clippy/clippy_utils/src/msrvs.rs3
-rw-r--r--src/tools/clippy/clippy_utils/src/paths.rs1
-rw-r--r--src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/sugg.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs9
-rw-r--r--src/tools/clippy/clippy_utils/src/usage.rs11
-rw-r--r--src/tools/clippy/clippy_utils/src/visitors.rs117
-rw-r--r--src/tools/clippy/doc/adding_lints.md9
-rw-r--r--src/tools/clippy/doc/common_tools_writing_lints.md27
-rw-r--r--src/tools/clippy/lintcheck/Cargo.toml1
-rw-r--r--src/tools/clippy/lintcheck/src/main.rs46
-rw-r--r--src/tools/clippy/rust-toolchain2
-rw-r--r--src/tools/clippy/tests/compile-test.rs159
-rw-r--r--src/tools/clippy/tests/dogfood.rs2
-rw-r--r--src/tools/clippy/tests/fmt.rs3
-rw-r--r--src/tools/clippy/tests/integration.rs2
-rw-r--r--src/tools/clippy/tests/lint_message_convention.rs3
-rw-r--r--src/tools/clippy/tests/missing-test-files.rs2
-rw-r--r--src/tools/clippy/tests/ui-cargo/feature_name/fail/Cargo.toml21
-rw-r--r--src/tools/clippy/tests/ui-cargo/feature_name/fail/src/main.rs7
-rw-r--r--src/tools/clippy/tests/ui-cargo/feature_name/fail/src/main.stderr44
-rw-r--r--src/tools/clippy/tests/ui-cargo/feature_name/pass/Cargo.toml9
-rw-r--r--src/tools/clippy/tests/ui-cargo/feature_name/pass/src/main.rs7
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_mod/Cargo.toml8
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner.rs1
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff.rs3
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff/most.rs1
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/mod.rs3
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.rs9
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.stderr19
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.toml8
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/bad/mod.rs1
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.rs7
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr11
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/pass_mod/Cargo.toml8
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/bad/mod.rs1
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/main.rs10
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/foo.rs1
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/inner/mod.rs1
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/mod.rs2
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/Cargo.toml8
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/src/good.rs1
-rw-r--r--src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/src/main.rs7
-rw-r--r--src/tools/clippy/tests/ui/approx_const.rs4
-rw-r--r--src/tools/clippy/tests/ui/approx_const.stderr101
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/option_helpers.rs9
-rw-r--r--src/tools/clippy/tests/ui/bool_assert_comparison.rs63
-rw-r--r--src/tools/clippy/tests/ui/bool_assert_comparison.stderr62
-rw-r--r--src/tools/clippy/tests/ui/box_vec.rs28
-rw-r--r--src/tools/clippy/tests/ui/box_vec.stderr6
-rw-r--r--src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr39
-rw-r--r--src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr4
-rw-r--r--src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs4
-rw-r--r--src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr90
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls.rs170
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls.stderr77
-rw-r--r--src/tools/clippy/tests/ui/entry.fixed15
-rw-r--r--src/tools/clippy/tests/ui/entry.rs9
-rw-r--r--src/tools/clippy/tests/ui/entry.stderr16
-rw-r--r--src/tools/clippy/tests/ui/entry_btree.fixed18
-rw-r--r--src/tools/clippy/tests/ui/entry_btree.rs18
-rw-r--r--src/tools/clippy/tests/ui/entry_btree.stderr20
-rw-r--r--src/tools/clippy/tests/ui/linkedlist.rs31
-rw-r--r--src/tools/clippy/tests/ui/linkedlist.stderr24
-rw-r--r--src/tools/clippy/tests/ui/manual_flatten.rs13
-rw-r--r--src/tools/clippy/tests/ui/manual_map_option_2.fixed50
-rw-r--r--src/tools/clippy/tests/ui/manual_map_option_2.rs56
-rw-r--r--src/tools/clippy/tests/ui/manual_map_option_2.stderr43
-rw-r--r--src/tools/clippy/tests/ui/manual_split_once.fixed50
-rw-r--r--src/tools/clippy/tests/ui/manual_split_once.rs50
-rw-r--r--src/tools/clippy/tests/ui/manual_split_once.stderr82
-rw-r--r--src/tools/clippy/tests/ui/mem_replace.fixed20
-rw-r--r--src/tools/clippy/tests/ui/mem_replace.rs20
-rw-r--r--src/tools/clippy/tests/ui/mem_replace.stderr20
-rw-r--r--src/tools/clippy/tests/ui/methods.rs5
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_attr.rs5
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_attr.stderr8
-rw-r--r--src/tools/clippy/tests/ui/missing-doc-crate.stderr0
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.stderr0
-rw-r--r--src/tools/clippy/tests/ui/mut_range_bound.rs39
-rw-r--r--src/tools/clippy/tests/ui/mut_range_bound.stderr47
-rw-r--r--src/tools/clippy/tests/ui/needless_option_as_deref.fixed13
-rw-r--r--src/tools/clippy/tests/ui/needless_option_as_deref.rs13
-rw-r--r--src/tools/clippy/tests/ui/needless_option_as_deref.stderr16
-rw-r--r--src/tools/clippy/tests/ui/option_if_let_else.fixed62
-rw-r--r--src/tools/clippy/tests/ui/option_if_let_else.rs68
-rw-r--r--src/tools/clippy/tests/ui/option_if_let_else.stderr72
-rw-r--r--src/tools/clippy/tests/ui/proc_macro.stderr3
-rw-r--r--src/tools/clippy/tests/ui/rc_buffer_redefined_string.stderr0
-rw-r--r--src/tools/clippy/tests/ui/rc_mutex.rs32
-rw-r--r--src/tools/clippy/tests/ui/rc_mutex.stderr33
-rw-r--r--src/tools/clippy/tests/ui/redundant_allocation.rs20
-rw-r--r--src/tools/clippy/tests/ui/redundant_allocation.stderr11
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.fixed4
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.stderr80
-rw-r--r--src/tools/clippy/tests/versioncheck.rs3
183 files changed, 4143 insertions, 1285 deletions
diff --git a/src/tools/clippy/.cargo/config b/src/tools/clippy/.cargo/config
index e95ea224cb6..84ae36a46d7 100644
--- a/src/tools/clippy/.cargo/config
+++ b/src/tools/clippy/.cargo/config
@@ -5,4 +5,5 @@ lintcheck = "run --target-dir lintcheck/target --package lintcheck --bin lintche
 collect-metadata = "test --test dogfood --features metadata-collector-lint -- run_metadata_collection_lint --ignored"
 
 [build]
-rustflags = ["-Zunstable-options"]
+# -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests
+rustflags = ["-Zunstable-options", "-Zbinary-dep-depinfo"]
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.md b/src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.md
index 9aef3ebe637..2891d5e5da1 100644
--- a/src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.md
+++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.md
@@ -2,3 +2,17 @@
 name: Blank Issue
 about: Create a blank issue.
 ---
+
+
+<!--
+Additional labels can be added to this issue by including the following command
+(without the space after the @ symbol):
+
+`@rustbot label +<label>`
+
+Common labels for this issue type are:
+* C-an-interesting-project
+* C-enhancement
+* C-question
+* C-tracking-issue
+-->
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.md b/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.md
index 2bc87db123d..87c18cdee66 100644
--- a/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.md
@@ -20,28 +20,24 @@ Instead, this happened: *explanation*
 
 ### Meta
 
-- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20)
-- `rustc -Vv`:
-  ```
-  rustc 1.46.0-nightly (f455e46ea 2020-06-20)
-  binary: rustc
-  commit-hash: f455e46eae1a227d735091091144601b467e1565
-  commit-date: 2020-06-20
-  host: x86_64-unknown-linux-gnu
-  release: 1.46.0-nightly
-  LLVM version: 10.0
-  ```
+**Rust version (`rustc -Vv`):**
+
+```
+rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+binary: rustc
+commit-hash: f455e46eae1a227d735091091144601b467e1565
+commit-date: 2020-06-20
+host: x86_64-unknown-linux-gnu
+release: 1.46.0-nightly
+LLVM version: 10.0
+```
 
 <!--
-Include a backtrace in the code block by setting `RUST_BACKTRACE=1` in your
-environment. E.g. `RUST_BACKTRACE=1 cargo clippy`.
+Additional labels can be added to this issue by including the following command
+(without the space after the @ symbol):
+
+`@rustbot label +<label>`
+
+Common labels for this issue type are:
+* `I-suggestion-causes-error`
 -->
-<details><summary>Backtrace</summary>
-  <p>
-  
-  ```
-  <backtrace>
-  ```
-  
-  </p>
-</details>
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.md b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.md
index 53341c7a928..d9ea2db34ed 100644
--- a/src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.md
+++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.md
@@ -22,14 +22,14 @@ Instead, this happened: *explanation*
 
 ### Meta
 
-- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20)
-- `rustc -Vv`:
-  ```
-  rustc 1.46.0-nightly (f455e46ea 2020-06-20)
-  binary: rustc
-  commit-hash: f455e46eae1a227d735091091144601b467e1565
-  commit-date: 2020-06-20
-  host: x86_64-unknown-linux-gnu
-  release: 1.46.0-nightly
-  LLVM version: 10.0
-  ```
+**Rust version (`rustc -Vv`):**
+
+```
+rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+binary: rustc
+commit-hash: f455e46eae1a227d735091091144601b467e1565
+commit-date: 2020-06-20
+host: x86_64-unknown-linux-gnu
+release: 1.46.0-nightly
+LLVM version: 10.0
+```
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.md b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.md
index 34fd6f4bdb7..4170b9ff2db 100644
--- a/src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.md
+++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.md
@@ -22,14 +22,23 @@ Instead, this happened: *explanation*
 
 ### Meta
 
-- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20)
-- `rustc -Vv`:
-  ```
-  rustc 1.46.0-nightly (f455e46ea 2020-06-20)
-  binary: rustc
-  commit-hash: f455e46eae1a227d735091091144601b467e1565
-  commit-date: 2020-06-20
-  host: x86_64-unknown-linux-gnu
-  release: 1.46.0-nightly
-  LLVM version: 10.0
-  ```
+**Rust version (`rustc -Vv`):**
+```
+rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+binary: rustc
+commit-hash: f455e46eae1a227d735091091144601b467e1565
+commit-date: 2020-06-20
+host: x86_64-unknown-linux-gnu
+release: 1.46.0-nightly
+LLVM version: 10.0
+```
+
+<!--
+Additional labels can be added to this issue by including the following command
+(without the space after the @ symbol):
+
+`@rustbot label +<label>`
+
+Common labels for this issue type are:
+* I-suggestion-causes-error
+-->
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/ice.md b/src/tools/clippy/.github/ISSUE_TEMPLATE/ice.md
index 0b7cd1ed0fb..6c1bed663c6 100644
--- a/src/tools/clippy/.github/ISSUE_TEMPLATE/ice.md
+++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/ice.md
@@ -20,17 +20,16 @@ http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/
 
 ### Meta
 
-- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20)
-- `rustc -Vv`:
-  ```
-  rustc 1.46.0-nightly (f455e46ea 2020-06-20)
-  binary: rustc
-  commit-hash: f455e46eae1a227d735091091144601b467e1565
-  commit-date: 2020-06-20
-  host: x86_64-unknown-linux-gnu
-  release: 1.46.0-nightly
-  LLVM version: 10.0
-  ```
+**Rust version (`rustc -Vv`):**
+```
+rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+binary: rustc
+commit-hash: f455e46eae1a227d735091091144601b467e1565
+commit-date: 2020-06-20
+host: x86_64-unknown-linux-gnu
+release: 1.46.0-nightly
+LLVM version: 10.0
+```
 
 ### Error output
 
diff --git a/src/tools/clippy/.github/workflows/clippy_dev.yml b/src/tools/clippy/.github/workflows/clippy_dev.yml
index 95da775b7bc..9a5416153ab 100644
--- a/src/tools/clippy/.github/workflows/clippy_dev.yml
+++ b/src/tools/clippy/.github/workflows/clippy_dev.yml
@@ -42,9 +42,6 @@ jobs:
       run: cargo build --features deny-warnings
       working-directory: clippy_dev
 
-    - name: Test limit_stderr_length
-      run: cargo dev limit_stderr_length
-
     - name: Test update_lints
       run: cargo dev update_lints --check
 
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index 2b89170073b..f5ac2f7c9f8 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -964,7 +964,7 @@ Released 2020-11-19
   [#5907](https://github.com/rust-lang/rust-clippy/pull/5907)
 * [`suspicious_arithmetic_impl`]: extend to implementations of `BitAnd`, `BitOr`, `BitXor`, `Rem`, `Shl`, and `Shr`
   [#5884](https://github.com/rust-lang/rust-clippy/pull/5884)
-* [`invalid_atomic_ordering`]: detect misuse of `compare_exchange`, `compare_exchange_weak`, and `fetch_update`
+* `invalid_atomic_ordering`: detect misuse of `compare_exchange`, `compare_exchange_weak`, and `fetch_update`
   [#6025](https://github.com/rust-lang/rust-clippy/pull/6025)
 * Avoid [`redundant_pattern_matching`] triggering in macros
   [#6069](https://github.com/rust-lang/rust-clippy/pull/6069)
@@ -1451,7 +1451,7 @@ Released 2020-03-12
 * [`option_as_ref_deref`] [#4945](https://github.com/rust-lang/rust-clippy/pull/4945)
 * [`wildcard_in_or_patterns`] [#4960](https://github.com/rust-lang/rust-clippy/pull/4960)
 * [`iter_nth_zero`] [#4966](https://github.com/rust-lang/rust-clippy/pull/4966)
-* [`invalid_atomic_ordering`] [#4999](https://github.com/rust-lang/rust-clippy/pull/4999)
+* `invalid_atomic_ordering` [#4999](https://github.com/rust-lang/rust-clippy/pull/4999)
 * [`skip_while_next`] [#5067](https://github.com/rust-lang/rust-clippy/pull/5067)
 
 ### Moves and Deprecations
@@ -2613,6 +2613,7 @@ Released 2018-09-13
 [`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr
 [`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver
 [`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof
+[`derivable_impls`]: https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls
 [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
 [`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord
 [`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method
@@ -2712,7 +2713,6 @@ Released 2018-09-13
 [`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic
 [`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division
 [`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
-[`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering
 [`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage
 [`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
 [`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
@@ -2754,6 +2754,7 @@ Released 2018-09-13
 [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
 [`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
 [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
+[`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once
 [`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat
 [`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
 [`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
@@ -2795,6 +2796,7 @@ Released 2018-09-13
 [`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
 [`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes
 [`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals
+[`mod_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#mod_module_files
 [`module_inception`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_inception
 [`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions
 [`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic
@@ -2821,6 +2823,7 @@ Released 2018-09-13
 [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
 [`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each
 [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
+[`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref
 [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
 [`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
 [`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop
@@ -2828,6 +2831,7 @@ Released 2018-09-13
 [`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update
 [`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord
 [`neg_multiply`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_multiply
+[`negative_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#negative_feature_names
 [`never_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#never_loop
 [`new_ret_no_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self
 [`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default
@@ -2881,6 +2885,7 @@ Released 2018-09-13
 [`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
 [`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls
 [`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else
+[`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names
 [`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names
 [`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern
 [`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching
@@ -2903,6 +2908,7 @@ Released 2018-09-13
 [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
 [`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment
 [`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors
+[`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files
 [`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned
 [`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse
 [`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
index 16b6c207a5f..2310370fb9f 100644
--- a/src/tools/clippy/Cargo.toml
+++ b/src/tools/clippy/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy"
-version = "0.1.56"
+version = "0.1.57"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
@@ -32,11 +32,7 @@ tempfile = { version = "3.1.0", optional = true }
 cargo_metadata = "0.12"
 compiletest_rs = { version = "0.6.0", features = ["tmp"] }
 tester = "0.9"
-serde = { version = "1.0", features = ["derive"] }
-derive-new = "0.5"
 regex = "1.4"
-quote = "1"
-syn = { version = "1", features = ["full"] }
 # This is used by the `collect-metadata` alias.
 filetime = "0.2"
 
@@ -45,6 +41,15 @@ filetime = "0.2"
 # for more information.
 rustc-workspace-hack = "1.0.0"
 
+# UI test dependencies
+clippy_utils = { path = "clippy_utils" }
+derive-new = "0.5"
+if_chain = "1.0"
+itertools = "0.10.1"
+quote = "1"
+serde = { version = "1.0", features = ["derive"] }
+syn = { version = "1", features = ["full"] }
+
 [build-dependencies]
 rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" }
 
diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md
index e1c968273cd..aaf404eadea 100644
--- a/src/tools/clippy/README.md
+++ b/src/tools/clippy/README.md
@@ -45,13 +45,13 @@ or in Travis CI.
 One way to use Clippy is by installing Clippy through rustup as a cargo
 subcommand.
 
-#### Step 1: Install rustup
+#### Step 1: Install Rustup
 
-You can install [rustup](https://rustup.rs/) on supported platforms. This will help
+You can install [Rustup](https://rustup.rs/) on supported platforms. This will help
 us install Clippy and its dependencies.
 
-If you already have rustup installed, update to ensure you have the latest
-rustup and compiler:
+If you already have Rustup installed, update to ensure you have the latest
+Rustup and compiler:
 
 ```terminal
 rustup update
diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs
index 72bdaf8d592..e05db7af586 100644
--- a/src/tools/clippy/clippy_dev/src/lib.rs
+++ b/src/tools/clippy/clippy_dev/src/lib.rs
@@ -17,7 +17,6 @@ pub mod fmt;
 pub mod new_lint;
 pub mod serve;
 pub mod setup;
-pub mod stderr_length_check;
 pub mod update_lints;
 
 static DEC_CLIPPY_LINT_RE: SyncLazy<Regex> = SyncLazy::new(|| {
diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs
index ff324ff6ee6..8fdeba9842a 100644
--- a/src/tools/clippy/clippy_dev/src/main.rs
+++ b/src/tools/clippy/clippy_dev/src/main.rs
@@ -3,7 +3,7 @@
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
 use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
-use clippy_dev::{bless, fmt, new_lint, serve, setup, stderr_length_check, update_lints};
+use clippy_dev::{bless, fmt, new_lint, serve, setup, update_lints};
 fn main() {
     let matches = get_clap_config();
 
@@ -33,9 +33,6 @@ fn main() {
                 Err(e) => eprintln!("Unable to create lint: {}", e),
             }
         },
-        ("limit_stderr_length", _) => {
-            stderr_length_check::check();
-        },
         ("setup", Some(sub_command)) => match sub_command.subcommand() {
             ("intellij", Some(matches)) => setup::intellij::setup_rustc_src(
                 matches
@@ -153,10 +150,6 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
                 ),
         )
         .subcommand(
-            SubCommand::with_name("limit_stderr_length")
-                .about("Ensures that stderr files do not grow longer than a certain amount of lines."),
-        )
-        .subcommand(
             SubCommand::with_name("setup")
                 .about("Support for setting up your personal development environment")
                 .setting(AppSettings::ArgRequiredElseHelp)
diff --git a/src/tools/clippy/clippy_dev/src/stderr_length_check.rs b/src/tools/clippy/clippy_dev/src/stderr_length_check.rs
deleted file mode 100644
index e02b6f7da5f..00000000000
--- a/src/tools/clippy/clippy_dev/src/stderr_length_check.rs
+++ /dev/null
@@ -1,51 +0,0 @@
-use crate::clippy_project_root;
-use std::ffi::OsStr;
-use std::fs;
-use std::path::{Path, PathBuf};
-use walkdir::WalkDir;
-
-// The maximum length allowed for stderr files.
-//
-// We limit this because small files are easier to deal with than bigger files.
-const LENGTH_LIMIT: usize = 200;
-
-pub fn check() {
-    let exceeding_files: Vec<_> = exceeding_stderr_files();
-
-    if !exceeding_files.is_empty() {
-        eprintln!("Error: stderr files exceeding limit of {} lines:", LENGTH_LIMIT);
-        for (path, count) in exceeding_files {
-            println!("{}: {}", path.display(), count);
-        }
-        std::process::exit(1);
-    }
-}
-
-fn exceeding_stderr_files() -> Vec<(PathBuf, usize)> {
-    // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories.
-    WalkDir::new(clippy_project_root().join("tests/ui"))
-        .into_iter()
-        .filter_map(Result::ok)
-        .filter(|f| !f.file_type().is_dir())
-        .filter_map(|e| {
-            let p = e.into_path();
-            let count = count_linenumbers(&p);
-            if p.extension() == Some(OsStr::new("stderr")) && count > LENGTH_LIMIT {
-                Some((p, count))
-            } else {
-                None
-            }
-        })
-        .collect()
-}
-
-#[must_use]
-fn count_linenumbers(filepath: &Path) -> usize {
-    match fs::read(filepath) {
-        Ok(content) => bytecount::count(&content, b'\n'),
-        Err(e) => {
-            eprintln!("Failed to read file: {}", e);
-            0
-        },
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml
index a3b92e1faa1..3c28024bf92 100644
--- a/src/tools/clippy/clippy_lints/Cargo.toml
+++ b/src/tools/clippy/clippy_lints/Cargo.toml
@@ -1,8 +1,6 @@
 [package]
 name = "clippy_lints"
-# begin automatic update
-version = "0.1.56"
-# end automatic update
+version = "0.1.57"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
diff --git a/src/tools/clippy/clippy_lints/src/approx_const.rs b/src/tools/clippy/clippy_lints/src/approx_const.rs
index 6100f4e435a..fb54ac1ec51 100644
--- a/src/tools/clippy/clippy_lints/src/approx_const.rs
+++ b/src/tools/clippy/clippy_lints/src/approx_const.rs
@@ -1,8 +1,10 @@
-use clippy_utils::diagnostics::span_lint;
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::{meets_msrv, msrvs};
 use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
 use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::symbol;
 use std::f64::consts as f64;
 
@@ -36,68 +38,82 @@ declare_clippy_lint! {
     "the approximate of a known float constant (in `std::fXX::consts`)"
 }
 
-// Tuples are of the form (constant, name, min_digits)
-const KNOWN_CONSTS: [(f64, &str, usize); 18] = [
-    (f64::E, "E", 4),
-    (f64::FRAC_1_PI, "FRAC_1_PI", 4),
-    (f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5),
-    (f64::FRAC_2_PI, "FRAC_2_PI", 5),
-    (f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5),
-    (f64::FRAC_PI_2, "FRAC_PI_2", 5),
-    (f64::FRAC_PI_3, "FRAC_PI_3", 5),
-    (f64::FRAC_PI_4, "FRAC_PI_4", 5),
-    (f64::FRAC_PI_6, "FRAC_PI_6", 5),
-    (f64::FRAC_PI_8, "FRAC_PI_8", 5),
-    (f64::LN_10, "LN_10", 5),
-    (f64::LN_2, "LN_2", 5),
-    (f64::LOG10_E, "LOG10_E", 5),
-    (f64::LOG2_E, "LOG2_E", 5),
-    (f64::LOG2_10, "LOG2_10", 5),
-    (f64::LOG10_2, "LOG10_2", 5),
-    (f64::PI, "PI", 3),
-    (f64::SQRT_2, "SQRT_2", 5),
+// Tuples are of the form (constant, name, min_digits, msrv)
+const KNOWN_CONSTS: [(f64, &str, usize, Option<RustcVersion>); 19] = [
+    (f64::E, "E", 4, None),
+    (f64::FRAC_1_PI, "FRAC_1_PI", 4, None),
+    (f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5, None),
+    (f64::FRAC_2_PI, "FRAC_2_PI", 5, None),
+    (f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5, None),
+    (f64::FRAC_PI_2, "FRAC_PI_2", 5, None),
+    (f64::FRAC_PI_3, "FRAC_PI_3", 5, None),
+    (f64::FRAC_PI_4, "FRAC_PI_4", 5, None),
+    (f64::FRAC_PI_6, "FRAC_PI_6", 5, None),
+    (f64::FRAC_PI_8, "FRAC_PI_8", 5, None),
+    (f64::LN_2, "LN_2", 5, None),
+    (f64::LN_10, "LN_10", 5, None),
+    (f64::LOG2_10, "LOG2_10", 5, Some(msrvs::LOG2_10)),
+    (f64::LOG2_E, "LOG2_E", 5, None),
+    (f64::LOG10_2, "LOG10_2", 5, Some(msrvs::LOG10_2)),
+    (f64::LOG10_E, "LOG10_E", 5, None),
+    (f64::PI, "PI", 3, None),
+    (f64::SQRT_2, "SQRT_2", 5, None),
+    (f64::TAU, "TAU", 3, Some(msrvs::TAU)),
 ];
 
-declare_lint_pass!(ApproxConstant => [APPROX_CONSTANT]);
+pub struct ApproxConstant {
+    msrv: Option<RustcVersion>,
+}
 
-impl<'tcx> LateLintPass<'tcx> for ApproxConstant {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-        if let ExprKind::Lit(lit) = &e.kind {
-            check_lit(cx, &lit.node, e);
+impl ApproxConstant {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        Self { msrv }
+    }
+
+    fn check_lit(&self, cx: &LateContext<'_>, lit: &LitKind, e: &Expr<'_>) {
+        match *lit {
+            LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty {
+                FloatTy::F32 => self.check_known_consts(cx, e, s, "f32"),
+                FloatTy::F64 => self.check_known_consts(cx, e, s, "f64"),
+            },
+            LitKind::Float(s, LitFloatType::Unsuffixed) => self.check_known_consts(cx, e, s, "f{32, 64}"),
+            _ => (),
         }
     }
-}
 
-fn check_lit(cx: &LateContext<'_>, lit: &LitKind, e: &Expr<'_>) {
-    match *lit {
-        LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty {
-            FloatTy::F32 => check_known_consts(cx, e, s, "f32"),
-            FloatTy::F64 => check_known_consts(cx, e, s, "f64"),
-        },
-        LitKind::Float(s, LitFloatType::Unsuffixed) => check_known_consts(cx, e, s, "f{32, 64}"),
-        _ => (),
+    fn check_known_consts(&self, cx: &LateContext<'_>, e: &Expr<'_>, s: symbol::Symbol, module: &str) {
+        let s = s.as_str();
+        if s.parse::<f64>().is_ok() {
+            for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS {
+                if is_approx_const(constant, &s, min_digits)
+                    && msrv.as_ref().map_or(true, |msrv| meets_msrv(self.msrv.as_ref(), msrv))
+                {
+                    span_lint_and_help(
+                        cx,
+                        APPROX_CONSTANT,
+                        e.span,
+                        &format!("approximate value of `{}::consts::{}` found", module, &name),
+                        None,
+                        "consider using the constant directly",
+                    );
+                    return;
+                }
+            }
+        }
     }
 }
 
-fn check_known_consts(cx: &LateContext<'_>, e: &Expr<'_>, s: symbol::Symbol, module: &str) {
-    let s = s.as_str();
-    if s.parse::<f64>().is_ok() {
-        for &(constant, name, min_digits) in &KNOWN_CONSTS {
-            if is_approx_const(constant, &s, min_digits) {
-                span_lint(
-                    cx,
-                    APPROX_CONSTANT,
-                    e.span,
-                    &format!(
-                        "approximate value of `{}::consts::{}` found. \
-                         Consider using it directly",
-                        module, &name
-                    ),
-                );
-                return;
-            }
+impl_lint_pass!(ApproxConstant => [APPROX_CONSTANT]);
+
+impl<'tcx> LateLintPass<'tcx> for ApproxConstant {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+        if let ExprKind::Lit(lit) = &e.kind {
+            self.check_lit(cx, &lit.node, e);
         }
     }
+
+    extract_msrv_attr!(LateContext);
 }
 
 /// Returns `false` if the number of significant figures in `value` are
diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
index 891e865b245..d834a1d317a 100644
--- a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
+++ b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
@@ -118,7 +118,7 @@ enum AssertKind {
 fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<AssertKind> {
     if_chain! {
         if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr);
-        if let ExprKind::Unary(UnOp::Not, ref expr) = cond.kind;
+        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
diff --git a/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs b/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs
index 51d95cc6f0b..47e5b0d583d 100644
--- a/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs
+++ b/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs
@@ -61,8 +61,8 @@ impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> {
             // do not lint if the closure is called using an iterator (see #1141)
             if_chain! {
                 if let Some(parent) = get_parent_expr(self.cx, expr);
-                if let ExprKind::MethodCall(_, _, args, _) = parent.kind;
-                let caller = self.cx.typeck_results().expr_ty(&args[0]);
+                if let ExprKind::MethodCall(_, _, [self_arg, ..], _) = &parent.kind;
+                let caller = self.cx.typeck_results().expr_ty(self_arg);
                 if let Some(iter_id) = self.cx.tcx.get_diagnostic_item(sym::Iterator);
                 if implements_trait(self.cx, caller, iter_id, &[]);
                 then {
diff --git a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
index 8d3f68565b2..cdc192a47e4 100644
--- a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
+++ b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
@@ -1,9 +1,11 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::{ast_utils, is_direct_expn_of};
-use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind};
+use clippy_utils::{diagnostics::span_lint_and_sugg, higher, is_direct_expn_of, ty::implements_trait};
+use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
-use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_hir::{Expr, ExprKind, Lit};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::Ident;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -28,45 +30,77 @@ declare_clippy_lint! {
 
 declare_lint_pass!(BoolAssertComparison => [BOOL_ASSERT_COMPARISON]);
 
-fn is_bool_lit(e: &Expr) -> bool {
+fn is_bool_lit(e: &Expr<'_>) -> bool {
     matches!(
         e.kind,
         ExprKind::Lit(Lit {
-            kind: LitKind::Bool(_),
+            node: LitKind::Bool(_),
             ..
         })
     ) && !e.span.from_expansion()
 }
 
-impl EarlyLintPass for BoolAssertComparison {
-    fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
+fn is_impl_not_trait_with_bool_out(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
+    let ty = cx.typeck_results().expr_ty(e);
+
+    cx.tcx
+        .lang_items()
+        .not_trait()
+        .filter(|trait_id| implements_trait(cx, ty, *trait_id, &[]))
+        .and_then(|trait_id| {
+            cx.tcx.associated_items(trait_id).find_by_name_and_kind(
+                cx.tcx,
+                Ident::from_str("Output"),
+                ty::AssocKind::Type,
+                trait_id,
+            )
+        })
+        .map_or(false, |assoc_item| {
+            let proj = cx.tcx.mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(ty, &[]));
+            let nty = cx.tcx.normalize_erasing_regions(cx.param_env, proj);
+
+            nty.is_bool()
+        })
+}
+
+impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         let macros = ["assert_eq", "debug_assert_eq"];
         let inverted_macros = ["assert_ne", "debug_assert_ne"];
 
         for mac in macros.iter().chain(inverted_macros.iter()) {
-            if let Some(span) = is_direct_expn_of(e.span, mac) {
-                if let Some([a, b]) = ast_utils::extract_assert_macro_args(e) {
-                    let nb_bool_args = is_bool_lit(a) as usize + is_bool_lit(b) as usize;
+            if let Some(span) = is_direct_expn_of(expr.span, mac) {
+                if let Some(args) = higher::extract_assert_macro_args(expr) {
+                    if let [a, b, ..] = args[..] {
+                        let nb_bool_args = is_bool_lit(a) as usize + is_bool_lit(b) as usize;
+
+                        if nb_bool_args != 1 {
+                            // If there are two boolean arguments, we definitely don't understand
+                            // what's going on, so better leave things as is...
+                            //
+                            // Or there is simply no boolean and then we can leave things as is!
+                            return;
+                        }
 
-                    if nb_bool_args != 1 {
-                        // If there are two boolean arguments, we definitely don't understand
-                        // what's going on, so better leave things as is...
-                        //
-                        // Or there is simply no boolean and then we can leave things as is!
+                        if !is_impl_not_trait_with_bool_out(cx, a) || !is_impl_not_trait_with_bool_out(cx, b) {
+                            // At this point the expression which is not a boolean
+                            // literal does not implement Not trait with a bool output,
+                            // so we cannot suggest to rewrite our code
+                            return;
+                        }
+
+                        let non_eq_mac = &mac[..mac.len() - 3];
+                        span_lint_and_sugg(
+                            cx,
+                            BOOL_ASSERT_COMPARISON,
+                            span,
+                            &format!("used `{}!` with a literal bool", mac),
+                            "replace it with",
+                            format!("{}!(..)", non_eq_mac),
+                            Applicability::MaybeIncorrect,
+                        );
                         return;
                     }
-
-                    let non_eq_mac = &mac[..mac.len() - 3];
-                    span_lint_and_sugg(
-                        cx,
-                        BOOL_ASSERT_COMPARISON,
-                        span,
-                        &format!("used `{}!` with a literal bool", mac),
-                        "replace it with",
-                        format!("{}!(..)", non_eq_mac),
-                        Applicability::MaybeIncorrect,
-                    );
-                    return;
                 }
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/bytecount.rs b/src/tools/clippy/clippy_lints/src/bytecount.rs
index c444984bc13..a07cd5e5f4e 100644
--- a/src/tools/clippy/clippy_lints/src/bytecount.rs
+++ b/src/tools/clippy/clippy_lints/src/bytecount.rs
@@ -1,7 +1,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::LocalUsedVisitor;
+use clippy_utils::visitors::is_local_used;
 use clippy_utils::{path_to_local_id, paths, peel_ref_operators, remove_blocks, strip_pat_refs};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -65,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount {
                 return;
             };
             if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
-            if !LocalUsedVisitor::new(cx, arg_id).check_expr(needle);
+            if !is_local_used(cx, needle, arg_id);
             then {
                 let haystack = if let ExprKind::MethodCall(path, _, args, _) =
                         filter_recv.kind {
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
index 5dcf1824ef0..248b35b024e 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
@@ -19,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             cx.typeck_results().expr_ty(expr),
         );
         lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
-    } else if let ExprKind::MethodCall(method_path, _, args, _) = expr.kind {
+    } else if let ExprKind::MethodCall(method_path, _, [self_arg, ..], _) = &expr.kind {
         if_chain! {
             if method_path.ident.name == sym!(cast);
             if let Some(generic_args) = method_path.args;
@@ -28,7 +28,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             if !is_hir_ty_cfg_dependant(cx, cast_to);
             then {
                 let (cast_from, cast_to) =
-                    (cx.typeck_results().expr_ty(&args[0]), cx.typeck_results().expr_ty(expr));
+                    (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr));
                 lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/collapsible_match.rs
index a42eee53459..a4693fa213b 100644
--- a/src/tools/clippy/clippy_lints/src/collapsible_match.rs
+++ b/src/tools/clippy/clippy_lints/src/collapsible_match.rs
@@ -1,9 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::visitors::LocalUsedVisitor;
-use clippy_utils::{higher, is_lang_ctor, is_unit_expr, path_to_local, peel_ref_operators, SpanlessEq};
+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 if_chain::if_chain;
 use rustc_hir::LangItem::OptionNone;
-use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, MatchSource, Pat, PatKind, StmtKind};
+use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::{MultiSpan, Span};
@@ -56,11 +57,11 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch {
                         check_arm(cx, true, arm.pat, arm.body, arm.guard.as_ref(), Some(els_arm.body));
                     }
                 }
-            }
+            },
             Some(IfLetOrMatch::IfLet(_, pat, body, els)) => {
                 check_arm(cx, false, pat, body, None, els);
-            }
-            None => {}
+            },
+            None => {},
         }
     }
 }
@@ -71,7 +72,7 @@ fn check_arm<'tcx>(
     outer_pat: &'tcx Pat<'tcx>,
     outer_then_body: &'tcx Expr<'tcx>,
     outer_guard: Option<&'tcx Guard<'tcx>>,
-    outer_else_body: Option<&'tcx Expr<'tcx>>
+    outer_else_body: Option<&'tcx Expr<'tcx>>,
 ) {
     let inner_expr = strip_singleton_blocks(outer_then_body);
     if_chain! {
@@ -106,14 +107,13 @@ fn check_arm<'tcx>(
             (Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b),
         };
         // the binding must not be used in the if guard
-        let mut used_visitor = LocalUsedVisitor::new(cx, binding_id);
-        if outer_guard.map_or(true, |(Guard::If(e) | Guard::IfLet(_, e))| !used_visitor.check_expr(e));
+        if outer_guard.map_or(true, |(Guard::If(e) | Guard::IfLet(_, e))| !is_local_used(cx, *e, binding_id));
         // ...or anywhere in the inner expression
         if match inner {
             IfLetOrMatch::IfLet(_, _, body, els) => {
-                !used_visitor.check_expr(body) && els.map_or(true, |e| !used_visitor.check_expr(e))
+                !is_local_used(cx, body, binding_id) && els.map_or(true, |e| !is_local_used(cx, e, binding_id))
             },
-            IfLetOrMatch::Match(_, arms, ..) => !arms.iter().any(|arm| used_visitor.check_arm(arm)),
+            IfLetOrMatch::Match(_, arms, ..) => !arms.iter().any(|arm| is_local_used(cx, arm, binding_id)),
         };
         then {
             let msg = format!(
@@ -151,23 +151,6 @@ fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir>
     expr
 }
 
-enum IfLetOrMatch<'hir> {
-    Match(&'hir Expr<'hir>, &'hir [Arm<'hir>], MatchSource),
-    /// scrutinee, pattern, then block, else block
-    IfLet(&'hir Expr<'hir>, &'hir Pat<'hir>, &'hir Expr<'hir>, Option<&'hir Expr<'hir>>),
-}
-
-impl<'hir> IfLetOrMatch<'hir> {
-    fn parse(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option<Self> {
-        match expr.kind {
-            ExprKind::Match(expr, arms, source) => Some(Self::Match(expr, arms, source)),
-            _ => higher::IfLet::hir(cx, expr).map(|higher::IfLet { let_expr, let_pat, if_then, if_else }| {
-                Self::IfLet(let_expr, let_pat, if_then, if_else)
-            })
-        }
-    }
-}
-
 /// 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/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs
index 5eb99cfe24f..d58e4949120 100644
--- a/src/tools/clippy/clippy_lints/src/copies.rs
+++ b/src/tools/clippy/clippy_lints/src/copies.rs
@@ -148,7 +148,7 @@ declare_clippy_lint! {
     /// };
     /// ```
     pub BRANCHES_SHARING_CODE,
-    complexity,
+    nursery,
     "`if` statement with shared code in all blocks"
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
new file mode 100644
index 00000000000..b4c4ca016aa
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
@@ -0,0 +1,108 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::{in_macro, is_automatically_derived, is_default_equivalent, remove_blocks};
+use rustc_hir::{
+    def::{DefKind, Res},
+    Body, Expr, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node, QPath,
+};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::TypeFoldable;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Detects manual `std::default::Default` implementations that are identical to a derived implementation.
+    ///
+    /// ### Why is this bad?
+    /// It is less concise.
+    ///
+    /// ### Example
+    /// ```rust
+    /// struct Foo {
+    ///     bar: bool
+    /// }
+    ///
+    /// impl std::default::Default for Foo {
+    ///     fn default() -> Self {
+    ///         Self {
+    ///             bar: false
+    ///         }
+    ///     }
+    /// }
+    /// ```
+    ///
+    /// Could be written as:
+    ///
+    /// ```rust
+    /// #[derive(Default)]
+    /// struct Foo {
+    ///     bar: bool
+    /// }
+    /// ```
+    ///
+    /// ### Known problems
+    /// Derive macros [sometimes use incorrect bounds](https://github.com/rust-lang/rust/issues/26925)
+    /// in generic types and the user defined `impl` maybe is more generalized or
+    /// specialized than what derive will produce. This lint can't detect the manual `impl`
+    /// has exactly equal bounds, and therefore this lint is disabled for types with
+    /// generic parameters.
+    ///
+    pub DERIVABLE_IMPLS,
+    complexity,
+    "manual implementation of the `Default` trait which is equal to a derive"
+}
+
+declare_lint_pass!(DerivableImpls => [DERIVABLE_IMPLS]);
+
+fn is_path_self(e: &Expr<'_>) -> bool {
+    if let ExprKind::Path(QPath::Resolved(_, p)) = e.kind {
+        matches!(p.res, Res::SelfCtor(..) | Res::Def(DefKind::Ctor(..), _))
+    } else {
+        false
+    }
+}
+
+impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
+    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
+        if_chain! {
+            if let ItemKind::Impl(Impl {
+                of_trait: Some(ref trait_ref),
+                items: [child],
+                ..
+            }) = item.kind;
+            if let attrs = cx.tcx.hir().attrs(item.hir_id());
+            if !is_automatically_derived(attrs);
+            if !in_macro(item.span);
+            if let Some(def_id) = trait_ref.trait_def_id();
+            if cx.tcx.is_diagnostic_item(sym::Default, def_id);
+            if let impl_item_hir = child.id.hir_id();
+            if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir);
+            if let ImplItemKind::Fn(_, b) = &impl_item.kind;
+            if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b);
+            if let Some(adt_def) = cx.tcx.type_of(item.def_id).ty_adt_def();
+            then {
+                if cx.tcx.type_of(item.def_id).definitely_has_param_types_or_consts(cx.tcx) {
+                    return;
+                }
+                let should_emit = match remove_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)),
+                    ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_equivalent(cx, ef.expr)),
+                    _ => false,
+                };
+                if should_emit {
+                    let path_string = cx.tcx.def_path_str(adt_def.did);
+                    span_lint_and_help(
+                        cx,
+                        DERIVABLE_IMPLS,
+                        item.span,
+                        "this `impl` can be derived",
+                        None,
+                        &format!("try annotating `{}` with `#[derive(Default)]`", path_string),
+                    );
+                }
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs
index dcfa5253f83..8416b8440df 100644
--- a/src/tools/clippy/clippy_lints/src/derive.rs
+++ b/src/tools/clippy/clippy_lints/src/derive.rs
@@ -105,9 +105,6 @@ declare_clippy_lint! {
     /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]`
     /// gets you.
     ///
-    /// ### Known problems
-    /// Bounds of generic types are sometimes wrong: https://github.com/rust-lang/rust/issues/26925
-    ///
     /// ### Example
     /// ```rust,ignore
     /// #[derive(Copy)]
diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs
index 627f746ec99..ac6824672f6 100644
--- a/src/tools/clippy/clippy_lints/src/entry.rs
+++ b/src/tools/clippy/clippy_lints/src/entry.rs
@@ -9,8 +9,9 @@ use clippy_utils::{
 use core::fmt::Write;
 use rustc_errors::Applicability;
 use rustc_hir::{
+    hir_id::HirIdSet,
     intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
-    Block, Expr, ExprKind, Guard, HirId, Local, Stmt, StmtKind, UnOp,
+    Block, Expr, ExprKind, Guard, HirId, Pat, Stmt, StmtKind, UnOp,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -338,6 +339,8 @@ struct InsertSearcher<'cx, 'tcx> {
     edits: Vec<Edit<'tcx>>,
     /// A stack of loops the visitor is currently in.
     loops: Vec<HirId>,
+    /// Local variables created in the expression. These don't need to be captured.
+    locals: HirIdSet,
 }
 impl<'tcx> InsertSearcher<'_, 'tcx> {
     /// Visit the expression as a branch in control flow. Multiple insert calls can be used, but
@@ -385,13 +388,16 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
                 }
             },
             StmtKind::Expr(e) => self.visit_expr(e),
-            StmtKind::Local(Local { init: Some(e), .. }) => {
-                self.allow_insert_closure &= !self.in_tail_pos;
-                self.in_tail_pos = false;
-                self.is_single_insert = false;
-                self.visit_expr(e);
+            StmtKind::Local(l) => {
+                self.visit_pat(l.pat);
+                if let Some(e) = l.init {
+                    self.allow_insert_closure &= !self.in_tail_pos;
+                    self.in_tail_pos = false;
+                    self.is_single_insert = false;
+                    self.visit_expr(e);
+                }
             },
-            _ => {
+            StmtKind::Item(_) => {
                 self.allow_insert_closure &= !self.in_tail_pos;
                 self.is_single_insert = false;
             },
@@ -473,6 +479,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
                     // Each branch may contain it's own insert expression.
                     let mut is_map_used = self.is_map_used;
                     for arm in arms {
+                        self.visit_pat(arm.pat);
                         if let Some(Guard::If(guard) | Guard::IfLet(_, guard)) = arm.guard {
                             self.visit_non_tail_expr(guard);
                         }
@@ -498,7 +505,8 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
                 },
                 _ => {
                     self.allow_insert_closure &= !self.in_tail_pos;
-                    self.allow_insert_closure &= can_move_expr_to_closure_no_visit(self.cx, expr, &self.loops);
+                    self.allow_insert_closure &=
+                        can_move_expr_to_closure_no_visit(self.cx, expr, &self.loops, &self.locals);
                     // Sub expressions are no longer in the tail position.
                     self.is_single_insert = false;
                     self.in_tail_pos = false;
@@ -507,6 +515,12 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
             },
         }
     }
+
+    fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
+        p.each_binding_or_first(&mut |_, id, _, _| {
+            self.locals.insert(id);
+        });
+    }
 }
 
 struct InsertSearchResults<'tcx> {
@@ -632,6 +646,7 @@ fn find_insert_calls(
         in_tail_pos: true,
         is_single_insert: true,
         loops: Vec::new(),
+        locals: HirIdSet::default(),
     };
     s.visit_expr(expr);
     let allow_insert_closure = s.allow_insert_closure;
diff --git a/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs b/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs
index f72a1e446d5..8714ce90164 100644
--- a/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs
+++ b/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs
@@ -15,8 +15,8 @@ declare_clippy_lint! {
     /// order of sub-expressions.
     ///
     /// ### Why is this bad?
-    /// It is often confusing to read. In addition, the
-    /// sub-expression evaluation order for Rust is not well documented.
+    /// It is often confusing to read. As described [here](https://doc.rust-lang.org/reference/expressions.html?highlight=subexpression#evaluation-order-of-operands),
+    /// the operands of these expressions are evaluated before applying the effects of the expression.
     ///
     /// ### Known problems
     /// Code which intentionally depends on the evaluation
diff --git a/src/tools/clippy/clippy_lints/src/feature_name.rs b/src/tools/clippy/clippy_lints/src/feature_name.rs
new file mode 100644
index 00000000000..eef1407a80c
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/feature_name.rs
@@ -0,0 +1,164 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::{diagnostics::span_lint, is_lint_allowed};
+use rustc_hir::{Crate, CRATE_HIR_ID};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::DUMMY_SP;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for feature names with prefix `use-`, `with-` or suffix `-support`
+    ///
+    /// ### Why is this bad?
+    /// These prefixes and suffixes have no significant meaning.
+    ///
+    /// ### Example
+    /// ```toml
+    /// # The `Cargo.toml` with feature name redundancy
+    /// [features]
+    /// default = ["use-abc", "with-def", "ghi-support"]
+    /// use-abc = []  // redundant
+    /// with-def = []   // redundant
+    /// ghi-support = []   // redundant
+    /// ```
+    ///
+    /// Use instead:
+    /// ```toml
+    /// [features]
+    /// default = ["abc", "def", "ghi"]
+    /// abc = []
+    /// def = []
+    /// ghi = []
+    /// ```
+    ///
+    pub REDUNDANT_FEATURE_NAMES,
+    cargo,
+    "usage of a redundant feature name"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for negative feature names with prefix `no-` or `not-`
+    ///
+    /// ### Why is this bad?
+    /// Features are supposed to be additive, and negatively-named features violate it.
+    ///
+    /// ### Example
+    /// ```toml
+    /// # The `Cargo.toml` with negative feature names
+    /// [features]
+    /// default = []
+    /// no-abc = []
+    /// not-def = []
+    ///
+    /// ```
+    /// Use instead:
+    /// ```toml
+    /// [features]
+    /// default = ["abc", "def"]
+    /// abc = []
+    /// def = []
+    ///
+    /// ```
+    pub NEGATIVE_FEATURE_NAMES,
+    cargo,
+    "usage of a negative feature name"
+}
+
+declare_lint_pass!(FeatureName => [REDUNDANT_FEATURE_NAMES, NEGATIVE_FEATURE_NAMES]);
+
+static PREFIXES: [&str; 8] = ["no-", "no_", "not-", "not_", "use-", "use_", "with-", "with_"];
+static SUFFIXES: [&str; 2] = ["-support", "_support"];
+
+fn is_negative_prefix(s: &str) -> bool {
+    s.starts_with("no")
+}
+
+fn lint(cx: &LateContext<'_>, feature: &str, substring: &str, is_prefix: bool) {
+    let is_negative = is_prefix && is_negative_prefix(substring);
+    span_lint_and_help(
+        cx,
+        if is_negative {
+            NEGATIVE_FEATURE_NAMES
+        } else {
+            REDUNDANT_FEATURE_NAMES
+        },
+        DUMMY_SP,
+        &format!(
+            "the \"{}\" {} in the feature name \"{}\" is {}",
+            substring,
+            if is_prefix { "prefix" } else { "suffix" },
+            feature,
+            if is_negative { "negative" } else { "redundant" }
+        ),
+        None,
+        &format!(
+            "consider renaming the feature to \"{}\"{}",
+            if is_prefix {
+                feature.strip_prefix(substring)
+            } else {
+                feature.strip_suffix(substring)
+            }
+            .unwrap(),
+            if is_negative {
+                ", but make sure the feature adds functionality"
+            } else {
+                ""
+            }
+        ),
+    );
+}
+
+impl LateLintPass<'_> for FeatureName {
+    fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
+        if is_lint_allowed(cx, REDUNDANT_FEATURE_NAMES, CRATE_HIR_ID)
+            && is_lint_allowed(cx, NEGATIVE_FEATURE_NAMES, CRATE_HIR_ID)
+        {
+            return;
+        }
+
+        let metadata = unwrap_cargo_metadata!(cx, REDUNDANT_FEATURE_NAMES, false);
+
+        for package in metadata.packages {
+            let mut features: Vec<&String> = package.features.keys().collect();
+            features.sort();
+            for feature in features {
+                let prefix_opt = {
+                    let i = PREFIXES.partition_point(|prefix| prefix < &feature.as_str());
+                    if i > 0 && feature.starts_with(PREFIXES[i - 1]) {
+                        Some(PREFIXES[i - 1])
+                    } else {
+                        None
+                    }
+                };
+                if let Some(prefix) = prefix_opt {
+                    lint(cx, feature, prefix, true);
+                }
+
+                let suffix_opt: Option<&str> = {
+                    let i = SUFFIXES.partition_point(|suffix| {
+                        suffix.bytes().rev().cmp(feature.bytes().rev()) == std::cmp::Ordering::Less
+                    });
+                    if i > 0 && feature.ends_with(SUFFIXES[i - 1]) {
+                        Some(SUFFIXES[i - 1])
+                    } else {
+                        None
+                    }
+                };
+                if let Some(suffix) = suffix_opt {
+                    lint(cx, feature, suffix, false);
+                }
+            }
+        }
+    }
+}
+
+#[test]
+fn test_prefixes_sorted() {
+    let mut sorted_prefixes = PREFIXES;
+    sorted_prefixes.sort_unstable();
+    assert_eq!(PREFIXES, sorted_prefixes);
+    let mut sorted_suffixes = SUFFIXES;
+    sorted_suffixes.sort_by(|a, b| a.bytes().rev().cmp(b.bytes().rev()));
+    assert_eq!(SUFFIXES, sorted_suffixes);
+}
diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
index d12482e7b7b..eda611117ba 100644
--- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
+++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
@@ -332,8 +332,6 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
                         ),
                         Applicability::MachineApplicable,
                     );
-
-                    return;
                 }
             }
         }
@@ -364,22 +362,22 @@ fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option<String> {
         if_chain! {
             if let ExprKind::MethodCall(
                 PathSegment { ident: lmethod_name, .. },
-                ref _lspan,
-                largs,
+                _lspan,
+                [largs_0, largs_1, ..],
                 _
-            ) = add_lhs.kind;
+            ) = &add_lhs.kind;
             if let ExprKind::MethodCall(
                 PathSegment { ident: rmethod_name, .. },
-                ref _rspan,
-                rargs,
+                _rspan,
+                [rargs_0, rargs_1, ..],
                 _
-            ) = add_rhs.kind;
+            ) = &add_rhs.kind;
             if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi";
-            if let Some((lvalue, _)) = constant(cx, cx.typeck_results(), &largs[1]);
-            if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), &rargs[1]);
+            if let Some((lvalue, _)) = constant(cx, cx.typeck_results(), largs_1);
+            if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), rargs_1);
             if Int(2) == lvalue && Int(2) == rvalue;
             then {
-                return Some(format!("{}.hypot({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], "..")));
+                return Some(format!("{}.hypot({})", Sugg::hir(cx, largs_0, ".."), Sugg::hir(cx, rargs_0, "..")));
             }
         }
     }
@@ -409,8 +407,8 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
         if cx.typeck_results().expr_ty(lhs).is_floating_point();
         if let Some((value, _)) = constant(cx, cx.typeck_results(), rhs);
         if F32(1.0) == value || F64(1.0) == value;
-        if let ExprKind::MethodCall(path, _, method_args, _) = lhs.kind;
-        if cx.typeck_results().expr_ty(&method_args[0]).is_floating_point();
+        if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &lhs.kind;
+        if cx.typeck_results().expr_ty(self_arg).is_floating_point();
         if path.ident.name.as_str() == "exp";
         then {
             span_lint_and_sugg(
@@ -421,7 +419,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
                 "consider using",
                 format!(
                     "{}.exp_m1()",
-                    Sugg::hir(cx, &method_args[0], "..")
+                    Sugg::hir(cx, self_arg, "..")
                 ),
                 Applicability::MachineApplicable,
             );
@@ -619,8 +617,8 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) {
             rhs,
         ) = &expr.kind;
         if are_same_base_logs(cx, lhs, rhs);
-        if let ExprKind::MethodCall(_, _, largs, _) = lhs.kind;
-        if let ExprKind::MethodCall(_, _, rargs, _) = rhs.kind;
+        if let ExprKind::MethodCall(_, _, [largs_self, ..], _) = &lhs.kind;
+        if let ExprKind::MethodCall(_, _, [rargs_self, ..], _) = &rhs.kind;
         then {
             span_lint_and_sugg(
                 cx,
@@ -628,7 +626,7 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) {
                 expr.span,
                 "log base can be expressed more clearly",
                 "consider using",
-                format!("{}.log({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], ".."),),
+                format!("{}.log({})", Sugg::hir(cx, largs_self, ".."), Sugg::hir(cx, rargs_self, ".."),),
                 Applicability::MachineApplicable,
             );
         }
diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs
index ea6193acbe8..77d08081c07 100644
--- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs
@@ -26,7 +26,6 @@ pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
         let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
         if let Some(attr) = attr {
             check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
-            return;
         } else if is_public && !is_proc_macro(cx.sess(), attrs) && !attrs.iter().any(|a| a.has_name(sym::no_mangle)) {
             check_must_use_candidate(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
index 7dad1c31150..ef72b88b3c7 100644
--- a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
+++ b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
@@ -138,12 +138,12 @@ impl<'tcx, 'l> ArmVisitor<'tcx, 'l> {
 
 fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
     if_chain! {
-        if let ExprKind::MethodCall(path, _span, args, _) = &expr.kind;
+        if let ExprKind::MethodCall(path, _span, [self_arg, ..], _) = &expr.kind;
         if path.ident.as_str() == "lock";
-        let ty = cx.typeck_results().expr_ty(&args[0]);
+        let ty = cx.typeck_results().expr_ty(self_arg);
         if is_type_diagnostic_item(cx, ty, sym!(mutex_type));
         then {
-            Some(&args[0])
+            Some(self_arg)
         } else {
             None
         }
diff --git a/src/tools/clippy/clippy_lints/src/if_let_some_result.rs b/src/tools/clippy/clippy_lints/src/if_let_some_result.rs
index fb5637fcec1..adcd78ed0d4 100644
--- a/src/tools/clippy/clippy_lints/src/if_let_some_result.rs
+++ b/src/tools/clippy/clippy_lints/src/if_let_some_result.rs
@@ -46,10 +46,10 @@ impl<'tcx> LateLintPass<'tcx> for OkIfLet {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if_chain! { //begin checking variables
             if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr);
-            if let ExprKind::MethodCall(_, ok_span, ref result_types, _) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
-            if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _)  = let_pat.kind; //get operation
+            if let ExprKind::MethodCall(_, ok_span, [ref result_types_0, ..], _) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
+            if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _)  = let_pat.kind; //get operation
             if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized;
-            if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&result_types[0]), sym::result_type);
+            if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::result_type);
             if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";
 
             then {
diff --git a/src/tools/clippy/clippy_lints/src/let_if_seq.rs b/src/tools/clippy/clippy_lints/src/let_if_seq.rs
index 0594b73dd38..7f2c7b707f0 100644
--- a/src/tools/clippy/clippy_lints/src/let_if_seq.rs
+++ b/src/tools/clippy/clippy_lints/src/let_if_seq.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
-use clippy_utils::{path_to_local_id, visitors::LocalUsedVisitor};
+use clippy_utils::{path_to_local_id, visitors::is_local_used};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -65,11 +65,10 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
                 if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind;
                 if let hir::StmtKind::Expr(if_) = expr.kind;
                 if let hir::ExprKind::If(hir::Expr { kind: hir::ExprKind::DropTemps(cond), ..}, then, else_) = if_.kind;
-                let mut used_visitor = LocalUsedVisitor::new(cx, canonical_id);
-                if !used_visitor.check_expr(cond);
+                if !is_local_used(cx, *cond, canonical_id);
                 if let hir::ExprKind::Block(then, _) = then.kind;
                 if let Some(value) = check_assign(cx, canonical_id, &*then);
-                if !used_visitor.check_expr(value);
+                if !is_local_used(cx, value, canonical_id);
                 then {
                     let span = stmt.span.to(if_.span);
 
@@ -148,15 +147,13 @@ fn check_assign<'tcx>(
         if let hir::ExprKind::Assign(var, value, _) = expr.kind;
         if path_to_local_id(var, decl);
         then {
-            let mut v = LocalUsedVisitor::new(cx, decl);
-
-            if block.stmts.iter().take(block.stmts.len()-1).any(|stmt| v.check_stmt(stmt)) {
-                return None;
+            if block.stmts.iter().take(block.stmts.len()-1).any(|stmt| is_local_used(cx, stmt, decl)) {
+                None
+            } else {
+                Some(value)
             }
-
-            return Some(value);
+        } else {
+            None
         }
     }
-
-    None
 }
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 19719502870..92a13be81f4 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -187,6 +187,7 @@ mod dbg_macro;
 mod default;
 mod default_numeric_fallback;
 mod dereference;
+mod derivable_impls;
 mod derive;
 mod disallowed_method;
 mod disallowed_script_idents;
@@ -211,6 +212,7 @@ mod exhaustive_items;
 mod exit;
 mod explicit_write;
 mod fallible_impl_from;
+mod feature_name;
 mod float_equality_without_abs;
 mod float_literal;
 mod floating_point_arithmetic;
@@ -272,6 +274,7 @@ mod missing_const_for_fn;
 mod missing_doc;
 mod missing_enforced_import_rename;
 mod missing_inline;
+mod module_style;
 mod modulo_arithmetic;
 mod multiple_crate_versions;
 mod mut_key;
@@ -287,6 +290,7 @@ mod needless_borrow;
 mod needless_borrowed_ref;
 mod needless_continue;
 mod needless_for_each;
+mod needless_option_as_deref;
 mod needless_pass_by_value;
 mod needless_question_mark;
 mod needless_update;
@@ -584,6 +588,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         default::FIELD_REASSIGN_WITH_DEFAULT,
         default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK,
         dereference::EXPLICIT_DEREF_METHODS,
+        derivable_impls::DERIVABLE_IMPLS,
         derive::DERIVE_HASH_XOR_EQ,
         derive::DERIVE_ORD_XOR_PARTIAL_ORD,
         derive::EXPL_IMPL_CLONE_ON_COPY,
@@ -625,6 +630,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         exit::EXIT,
         explicit_write::EXPLICIT_WRITE,
         fallible_impl_from::FALLIBLE_IMPL_FROM,
+        feature_name::NEGATIVE_FEATURE_NAMES,
+        feature_name::REDUNDANT_FEATURE_NAMES,
         float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS,
         float_literal::EXCESSIVE_PRECISION,
         float_literal::LOSSY_FLOAT_LITERAL,
@@ -770,6 +777,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         methods::MANUAL_FILTER_MAP,
         methods::MANUAL_FIND_MAP,
         methods::MANUAL_SATURATING_ARITHMETIC,
+        methods::MANUAL_SPLIT_ONCE,
         methods::MANUAL_STR_REPEAT,
         methods::MAP_COLLECT_RESULT_UNIT,
         methods::MAP_FLATTEN,
@@ -822,6 +830,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS,
         missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES,
         missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS,
+        module_style::MOD_MODULE_FILES,
+        module_style::SELF_NAMED_MODULE_FILES,
         modulo_arithmetic::MODULO_ARITHMETIC,
         multiple_crate_versions::MULTIPLE_CRATE_VERSIONS,
         mut_key::MUTABLE_KEY_TYPE,
@@ -840,6 +850,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
         needless_continue::NEEDLESS_CONTINUE,
         needless_for_each::NEEDLESS_FOR_EACH,
+        needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF,
         needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
         needless_question_mark::NEEDLESS_QUESTION_MARK,
         needless_update::NEEDLESS_UPDATE,
@@ -1031,6 +1042,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS),
         LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES),
         LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
+        LintId::of(module_style::MOD_MODULE_FILES),
+        LintId::of(module_style::SELF_NAMED_MODULE_FILES),
         LintId::of(modulo_arithmetic::MODULO_ARITHMETIC),
         LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN),
         LintId::of(panic_unimplemented::PANIC),
@@ -1122,7 +1135,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(needless_for_each::NEEDLESS_FOR_EACH),
         LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
         LintId::of(non_expressive_names::SIMILAR_NAMES),
-        LintId::of(option_if_let_else::OPTION_IF_LET_ELSE),
         LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE),
         LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
         LintId::of(ranges::RANGE_MINUS_ONE),
@@ -1193,10 +1205,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(collapsible_if::COLLAPSIBLE_IF),
         LintId::of(collapsible_match::COLLAPSIBLE_MATCH),
         LintId::of(comparison_chain::COMPARISON_CHAIN),
-        LintId::of(copies::BRANCHES_SHARING_CODE),
         LintId::of(copies::IFS_SAME_COND),
         LintId::of(copies::IF_SAME_THEN_ELSE),
         LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
+        LintId::of(derivable_impls::DERIVABLE_IMPLS),
         LintId::of(derive::DERIVE_HASH_XOR_EQ),
         LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
         LintId::of(doc::MISSING_SAFETY_DOC),
@@ -1316,6 +1328,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(methods::MANUAL_FILTER_MAP),
         LintId::of(methods::MANUAL_FIND_MAP),
         LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
+        LintId::of(methods::MANUAL_SPLIT_ONCE),
         LintId::of(methods::MANUAL_STR_REPEAT),
         LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
         LintId::of(methods::MAP_IDENTITY),
@@ -1366,6 +1379,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(needless_bool::NEEDLESS_BOOL),
         LintId::of(needless_borrow::NEEDLESS_BORROW),
         LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
+        LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF),
         LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
         LintId::of(needless_update::NEEDLESS_UPDATE),
         LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
@@ -1581,7 +1595,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(booleans::NONMINIMAL_BOOL),
         LintId::of(casts::CHAR_LIT_AS_U8),
         LintId::of(casts::UNNECESSARY_CAST),
-        LintId::of(copies::BRANCHES_SHARING_CODE),
+        LintId::of(derivable_impls::DERIVABLE_IMPLS),
         LintId::of(double_comparison::DOUBLE_COMPARISONS),
         LintId::of(double_parens::DOUBLE_PARENS),
         LintId::of(duration_subsec::DURATION_SUBSEC),
@@ -1614,6 +1628,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(methods::ITER_COUNT),
         LintId::of(methods::MANUAL_FILTER_MAP),
         LintId::of(methods::MANUAL_FIND_MAP),
+        LintId::of(methods::MANUAL_SPLIT_ONCE),
         LintId::of(methods::MAP_IDENTITY),
         LintId::of(methods::OPTION_AS_REF_DEREF),
         LintId::of(methods::OPTION_FILTER_MAP),
@@ -1628,6 +1643,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(needless_bool::BOOL_COMPARISON),
         LintId::of(needless_bool::NEEDLESS_BOOL),
         LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
+        LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF),
         LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
         LintId::of(needless_update::NEEDLESS_UPDATE),
         LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
@@ -1779,6 +1795,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
 
     store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![
         LintId::of(cargo_common_metadata::CARGO_COMMON_METADATA),
+        LintId::of(feature_name::NEGATIVE_FEATURE_NAMES),
+        LintId::of(feature_name::REDUNDANT_FEATURE_NAMES),
         LintId::of(multiple_crate_versions::MULTIPLE_CRATE_VERSIONS),
         LintId::of(wildcard_dependencies::WILDCARD_DEPENDENCIES),
     ]);
@@ -1786,6 +1804,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
         LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
         LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY),
+        LintId::of(copies::BRANCHES_SHARING_CODE),
         LintId::of(disallowed_method::DISALLOWED_METHOD),
         LintId::of(disallowed_type::DISALLOWED_TYPE),
         LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM),
@@ -1797,6 +1816,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
         LintId::of(mutex_atomic::MUTEX_INTEGER),
         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),
         LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE),
         LintId::of(regex::TRIVIAL_REGEX),
@@ -1835,7 +1855,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(serde_api::SerdeApi));
     let vec_box_size_threshold = conf.vec_box_size_threshold;
     let type_complexity_threshold = conf.type_complexity_threshold;
-    store.register_late_pass(move || Box::new(types::Types::new(vec_box_size_threshold, type_complexity_threshold)));
+    let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
+    store.register_late_pass(move || Box::new(types::Types::new(
+        vec_box_size_threshold,
+        type_complexity_threshold,
+        avoid_breaking_exported_api,
+    )));
     store.register_late_pass(|| Box::new(booleans::NonminimalBool));
     store.register_late_pass(|| Box::new(needless_bitwise_bool::NeedlessBitwiseBool));
     store.register_late_pass(|| Box::new(eq_op::EqOp));
@@ -1846,9 +1871,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(ptr::Ptr));
     store.register_late_pass(|| Box::new(ptr_eq::PtrEq));
     store.register_late_pass(|| Box::new(needless_bool::NeedlessBool));
+    store.register_late_pass(|| Box::new(needless_option_as_deref::OptionNeedlessDeref));
     store.register_late_pass(|| Box::new(needless_bool::BoolComparison));
     store.register_late_pass(|| Box::new(needless_for_each::NeedlessForEach));
-    store.register_late_pass(|| Box::new(approx_const::ApproxConstant));
     store.register_late_pass(|| Box::new(misc::MiscLints));
     store.register_late_pass(|| Box::new(eta_reduction::EtaReduction));
     store.register_late_pass(|| Box::new(identity_op::IdentityOp));
@@ -1877,6 +1902,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     });
 
     let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
+    store.register_late_pass(move || Box::new(approx_const::ApproxConstant::new(msrv)));
     store.register_late_pass(move || Box::new(methods::Methods::new(avoid_breaking_exported_api, msrv)));
     store.register_late_pass(move || Box::new(matches::Matches::new(msrv)));
     store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustive::new(msrv)));
@@ -1920,6 +1946,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(panic_unimplemented::PanicUnimplemented));
     store.register_late_pass(|| Box::new(strings::StringLitAsBytes));
     store.register_late_pass(|| Box::new(derive::Derive));
+    store.register_late_pass(|| Box::new(derivable_impls::DerivableImpls));
     store.register_late_pass(|| Box::new(get_last_with_len::GetLastWithLen));
     store.register_late_pass(|| Box::new(drop_forget_ref::DropForgetRef));
     store.register_late_pass(|| Box::new(empty_enum::EmptyEnum));
@@ -2092,7 +2119,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10));
     store.register_late_pass(|| Box::new(manual_map::ManualMap));
     store.register_late_pass(move || Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv)));
-    store.register_early_pass(|| Box::new(bool_assert_comparison::BoolAssertComparison));
+    store.register_late_pass(|| Box::new(bool_assert_comparison::BoolAssertComparison));
+    store.register_early_pass(move || Box::new(module_style::ModStyle));
     store.register_late_pass(|| Box::new(unused_async::UnusedAsync));
     let disallowed_types = conf.disallowed_types.iter().cloned().collect::<FxHashSet<_>>();
     store.register_late_pass(move || Box::new(disallowed_type::DisallowedType::new(&disallowed_types)));
@@ -2102,6 +2130,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(&scripts)));
     store.register_late_pass(|| Box::new(strlen_on_c_strings::StrlenOnCStrings));
     store.register_late_pass(move || Box::new(self_named_constructors::SelfNamedConstructors));
+    store.register_late_pass(move || Box::new(feature_name::FeatureName));
 }
 
 #[rustfmt::skip]
diff --git a/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs b/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs
index 82bf49f5b49..68bef2f4c8b 100644
--- a/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs
@@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
 use clippy_utils::source::snippet;
 use clippy_utils::sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::visitors::LocalUsedVisitor;
+use clippy_utils::visitors::is_local_used;
 use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
@@ -66,9 +66,7 @@ pub(super) fn check<'tcx>(
 fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool {
     match *pat {
         PatKind::Wild => true,
-        PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
-            !LocalUsedVisitor::new(cx, id).check_expr(body)
-        },
+        PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => !is_local_used(cx, body, id),
         _ => false,
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
index 5852674da57..5b6e27085d5 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
@@ -2,6 +2,7 @@ use super::utils::make_iterator_snippet;
 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 if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -37,7 +38,8 @@ pub(super) fn check<'tcx>(
 
         if_chain! {
             if let Some(inner_expr) = inner_expr;
-            if let Some(higher::IfLet { let_pat, let_expr, if_else: None, .. }) = higher::IfLet::hir(cx, 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);
@@ -46,6 +48,8 @@ pub(super) fn check<'tcx>(
             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
diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs
index bd9de5e08d7..2860cb68f42 100644
--- a/src/tools/clippy/clippy_lints/src/loops/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs
@@ -397,6 +397,21 @@ declare_clippy_lint! {
     /// ### Why is this bad?
     /// One might think that modifying the mutable variable changes the loop bounds
     ///
+    /// ### Known problems
+    /// False positive when mutation is followed by a `break`, but the `break` is not immediately
+    /// after the mutation:
+    ///
+    /// ```rust
+    /// let mut x = 5;
+    /// for _ in 0..x {
+    ///     x += 1; // x is a range bound that is mutated
+    ///     ..; // some other expression
+    ///     break; // leaves the loop, so mutation is not an issue
+    /// }
+    /// ```
+    ///
+    /// False positive on nested loops ([#6072](https://github.com/rust-lang/rust-clippy/issues/6072))
+    ///
     /// ### Example
     /// ```rust
     /// let mut foo = 42;
@@ -580,8 +595,8 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
 
         while_let_on_iterator::check(cx, expr);
 
-        if let Some(higher::While { if_cond, if_then, .. }) = higher::While::hir(&expr) {
-            while_immutable_condition::check(cx, if_cond, if_then);
+        if let Some(higher::While { condition, body }) = higher::While::hir(expr) {
+            while_immutable_condition::check(cx, condition, body);
         }
 
         needless_collect::check(expr, cx);
diff --git a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
index 344dc5074d3..358d53e8859 100644
--- a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
@@ -1,24 +1,27 @@
 use super::MUT_RANGE_BOUND;
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{higher, path_to_local};
+use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::{get_enclosing_block, higher, path_to_local};
 use if_chain::if_chain;
-use rustc_hir::{BindingAnnotation, Expr, HirId, Node, PatKind};
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Node, PatKind};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::LateContext;
+use rustc_middle::hir::map::Map;
 use rustc_middle::{mir::FakeReadCause, ty};
 use rustc_span::source_map::Span;
 use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 
 pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) {
-    if let Some(higher::Range {
-        start: Some(start),
-        end: Some(end),
-        ..
-    }) = higher::Range::hir(arg)
-    {
-        let mut_ids = vec![check_for_mutability(cx, start), check_for_mutability(cx, end)];
-        if mut_ids[0].is_some() || mut_ids[1].is_some() {
-            let (span_low, span_high) = check_for_mutation(cx, body, &mut_ids);
+    if_chain! {
+        if let Some(higher::Range {
+            start: Some(start),
+            end: Some(end),
+            ..
+        }) = higher::Range::hir(arg);
+        let (mut_id_start, mut_id_end) = (check_for_mutability(cx, start), check_for_mutability(cx, end));
+        if mut_id_start.is_some() || mut_id_end.is_some();
+        then {
+            let (span_low, span_high) = check_for_mutation(cx, body, mut_id_start, mut_id_end);
             mut_warn_with_span(cx, span_low);
             mut_warn_with_span(cx, span_high);
         }
@@ -27,11 +30,13 @@ pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) {
 
 fn mut_warn_with_span(cx: &LateContext<'_>, span: Option<Span>) {
     if let Some(sp) = span {
-        span_lint(
+        span_lint_and_note(
             cx,
             MUT_RANGE_BOUND,
             sp,
-            "attempt to mutate range bound within loop; note that the range of the loop is unchanged",
+            "attempt to mutate range bound within loop",
+            None,
+            "the range of the loop is unchanged",
         );
     }
 }
@@ -51,12 +56,13 @@ fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option<HirId>
 fn check_for_mutation<'tcx>(
     cx: &LateContext<'tcx>,
     body: &Expr<'_>,
-    bound_ids: &[Option<HirId>],
+    bound_id_start: Option<HirId>,
+    bound_id_end: Option<HirId>,
 ) -> (Option<Span>, Option<Span>) {
     let mut delegate = MutatePairDelegate {
         cx,
-        hir_id_low: bound_ids[0],
-        hir_id_high: bound_ids[1],
+        hir_id_low: bound_id_start,
+        hir_id_high: bound_id_end,
         span_low: None,
         span_high: None,
     };
@@ -70,6 +76,7 @@ fn check_for_mutation<'tcx>(
         )
         .walk_expr(body);
     });
+
     delegate.mutation_span()
 }
 
@@ -87,10 +94,10 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> {
     fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) {
         if let ty::BorrowKind::MutBorrow = bk {
             if let PlaceBase::Local(id) = cmt.place.base {
-                if Some(id) == self.hir_id_low {
+                if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
                     self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id));
                 }
-                if Some(id) == self.hir_id_high {
+                if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
                     self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id));
                 }
             }
@@ -99,10 +106,10 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> {
 
     fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
         if let PlaceBase::Local(id) = cmt.place.base {
-            if Some(id) == self.hir_id_low {
+            if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
                 self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id));
             }
-            if Some(id) == self.hir_id_high {
+            if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
                 self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id));
             }
         }
@@ -116,3 +123,52 @@ impl MutatePairDelegate<'_, '_> {
         (self.span_low, self.span_high)
     }
 }
+
+struct BreakAfterExprVisitor {
+    hir_id: HirId,
+    past_expr: bool,
+    past_candidate: bool,
+    break_after_expr: bool,
+}
+
+impl BreakAfterExprVisitor {
+    pub fn is_found(cx: &LateContext<'_>, hir_id: HirId) -> bool {
+        let mut visitor = BreakAfterExprVisitor {
+            hir_id,
+            past_expr: false,
+            past_candidate: false,
+            break_after_expr: false,
+        };
+
+        get_enclosing_block(cx, hir_id).map_or(false, |block| {
+            visitor.visit_block(block);
+            visitor.break_after_expr
+        })
+    }
+}
+
+impl intravisit::Visitor<'tcx> for BreakAfterExprVisitor {
+    type Map = Map<'tcx>;
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
+
+    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+        if self.past_candidate {
+            return;
+        }
+
+        if expr.hir_id == self.hir_id {
+            self.past_expr = true;
+        } else if self.past_expr {
+            if matches!(&expr.kind, ExprKind::Break(..)) {
+                self.break_after_expr = true;
+            }
+
+            self.past_candidate = true;
+        } else {
+            intravisit::walk_expr(self, expr);
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs b/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs
index 51d7def137e..f90ed7397e1 100644
--- a/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs
@@ -26,7 +26,7 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont
         if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator);
         then {
             let ty = cx.typeck_results().expr_ty(&args[0]);
-            let mut applicability = Applicability::MachineApplicable;
+            let mut applicability = Applicability::MaybeIncorrect;
             let is_empty_sugg = "next().is_none()".to_string();
             let method_name = &*method.ident.name.as_str();
             let sugg = if is_type_diagnostic_item(cx, ty, sym::vec_type) ||
@@ -113,7 +113,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo
                                     (stmt.span, String::new()),
                                     (iter_call.span, iter_replacement)
                                 ],
-                                Applicability::MachineApplicable,// MaybeIncorrect,
+                                Applicability::MaybeIncorrect,
                             );
                         },
                     );
diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
index 3f77e7af927..e8f3550283a 100644
--- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
@@ -2,10 +2,8 @@ use super::NEEDLESS_RANGE_LOOP;
 use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
 use clippy_utils::source::snippet;
 use clippy_utils::ty::has_iter_method;
-use clippy_utils::visitors::LocalUsedVisitor;
-use clippy_utils::{
-    contains_name, higher, is_integer_const, match_trait_method, path_to_local_id, paths, sugg, SpanlessEq,
-};
+use clippy_utils::visitors::is_local_used;
+use clippy_utils::{contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq};
 use if_chain::if_chain;
 use rustc_ast::ast;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@@ -256,43 +254,36 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
             if let ExprKind::Path(ref seqpath) = seqexpr.kind;
             if let QPath::Resolved(None, seqvar) = *seqpath;
             if seqvar.segments.len() == 1;
-            let index_used_directly = path_to_local_id(idx, self.var);
-            let indexed_indirectly = {
-                let mut used_visitor = LocalUsedVisitor::new(self.cx, self.var);
-                walk_expr(&mut used_visitor, idx);
-                used_visitor.used
-            };
-            if indexed_indirectly || index_used_directly;
+            if is_local_used(self.cx, idx, self.var);
             then {
                 if self.prefer_mutable {
                     self.indexed_mut.insert(seqvar.segments[0].ident.name);
                 }
+                let index_used_directly = matches!(idx.kind, ExprKind::Path(_));
                 let res = self.cx.qpath_res(seqpath, seqexpr.hir_id);
                 match res {
                     Res::Local(hir_id) => {
                         let parent_id = self.cx.tcx.hir().get_parent_item(expr.hir_id);
                         let parent_def_id = self.cx.tcx.hir().local_def_id(parent_id);
                         let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id);
-                        if indexed_indirectly {
-                            self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent));
-                        }
                         if index_used_directly {
                             self.indexed_directly.insert(
                                 seqvar.segments[0].ident.name,
                                 (Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)),
                             );
+                        } else {
+                            self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent));
                         }
                         return false;  // no need to walk further *on the variable*
                     }
                     Res::Def(DefKind::Static | DefKind::Const, ..) => {
-                        if indexed_indirectly {
-                            self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None);
-                        }
                         if index_used_directly {
                             self.indexed_directly.insert(
                                 seqvar.segments[0].ident.name,
                                 (None, self.cx.typeck_results().node_type(seqexpr.hir_id)),
                             );
+                        } else {
+                            self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None);
                         }
                         return false;  // no need to walk further *on the variable*
                     }
@@ -310,10 +301,10 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
     fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
         if_chain! {
             // a range index op
-            if let ExprKind::MethodCall(meth, _, args, _) = expr.kind;
+            if let ExprKind::MethodCall(meth, _, [args_0, args_1, ..], _) = &expr.kind;
             if (meth.ident.name == sym::index && match_trait_method(self.cx, expr, &paths::INDEX))
                 || (meth.ident.name == sym::index_mut && match_trait_method(self.cx, expr, &paths::INDEX_MUT));
-            if !self.check(&args[1], &args[0], expr);
+            if !self.check(args_1, args_0, expr);
             then { return }
         }
 
diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
index 2c46971d5f7..41956650c9f 100644
--- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
@@ -87,7 +87,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.as_deref());
+    let expr = once(block.expr);
     let mut iter = stmts.chain(expr).flatten();
     never_loop_expr_seq(&mut iter, main_loop_id)
 }
@@ -100,7 +100,7 @@ fn never_loop_expr_seq<'a, T: Iterator<Item = &'a Expr<'a>>>(es: &mut T, main_lo
 fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> {
     match stmt.kind {
         StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some(e),
-        StmtKind::Local(local) => local.init.as_deref(),
+        StmtKind::Local(local) => local.init,
         StmtKind::Item(..) => None,
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs
index d6d3315e0a8..1848f5b5de2 100644
--- a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs
@@ -24,13 +24,13 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'
             }
         }
 
-        if let ExprKind::Match(ref matchexpr, ref arms, MatchSource::Normal) = inner.kind {
+        if let ExprKind::Match(matchexpr, arms, MatchSource::Normal) = inner.kind {
             if arms.len() == 2
                 && arms[0].guard.is_none()
                 && arms[1].guard.is_none()
-                && is_simple_break_expr(&arms[1].body)
+                && is_simple_break_expr(arms[1].body)
             {
-                could_be_while_let(cx, expr, &arms[0].pat, matchexpr);
+                could_be_while_let(cx, expr, arms[0].pat, matchexpr);
             }
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs
index 0757d329125..79527e3bfa9 100644
--- a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs
@@ -14,12 +14,7 @@ use rustc_span::{symbol::sym, Span, Symbol};
 
 pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
     let (scrutinee_expr, iter_expr, some_pat, loop_expr) = if_chain! {
-        if let Some(higher::WhileLet {
-            if_then,
-            let_pat,
-            let_expr,
-            ..
-        }) = higher::WhileLet::hir(expr);
+        if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr);
         // check for `Some(..)` pattern
         if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = let_pat.kind;
         if let Res::Def(_, pat_did) = pat_path.res;
diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs
index 39f7ade3f81..7627e0fb289 100644
--- a/src/tools/clippy/clippy_lints/src/macro_use.rs
+++ b/src/tools/clippy/clippy_lints/src/macro_use.rs
@@ -48,8 +48,7 @@ pub struct MacroRefData {
 impl MacroRefData {
     pub fn new(name: String, callee: Span, cx: &LateContext<'_>) -> Self {
         let sm = cx.sess().source_map();
-        let mut path = sm.filename_for_diagnostics(&sm.span_to_filename(callee))
-            .to_string();
+        let mut path = sm.filename_for_diagnostics(&sm.span_to_filename(callee)).to_string();
 
         // std lib paths are <::std::module::file type>
         // so remove brackets, space and type.
diff --git a/src/tools/clippy/clippy_lints/src/manual_map.rs b/src/tools/clippy/clippy_lints/src/manual_map.rs
index 161d8841490..b5f573cb104 100644
--- a/src/tools/clippy/clippy_lints/src/manual_map.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_map.rs
@@ -1,16 +1,18 @@
 use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF};
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::higher;
+use clippy_utils::higher::IfLetOrMatch;
 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
 use clippy_utils::{
     can_move_expr_to_closure, in_constant, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id,
-    peel_hir_expr_refs,
+    peel_hir_expr_refs, peel_hir_expr_while, CaptureKind,
 };
 use rustc_ast::util::parser::PREC_POSTFIX;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::{OptionNone, OptionSome};
-use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, Mutability, Pat, PatKind};
+use rustc_hir::{
+    def::Res, Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath,
+};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -44,168 +46,169 @@ declare_lint_pass!(ManualMap => [MANUAL_MAP]);
 impl LateLintPass<'_> for ManualMap {
     #[allow(clippy::too_many_lines)]
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if let Some(higher::IfLet {
-            let_pat,
-            let_expr,
-            if_then,
-            if_else: Some(if_else),
-        }) = higher::IfLet::hir(cx, expr)
-        {
-            manage_lint(cx, expr, (&let_pat.kind, if_then), (&PatKind::Wild, if_else), let_expr);
+        let (scrutinee, then_pat, then_body, else_pat, else_body) = match IfLetOrMatch::parse(cx, expr) {
+            Some(IfLetOrMatch::IfLet(scrutinee, pat, body, Some(r#else))) => (scrutinee, pat, body, None, r#else),
+            Some(IfLetOrMatch::Match(
+                scrutinee,
+                [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }],
+                _,
+            )) => (scrutinee, arm1.pat, arm1.body, Some(arm2.pat), arm2.body),
+            _ => return,
+        };
+        if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) {
+            return;
         }
 
-        if let ExprKind::Match(scrutinee, [then @ Arm { guard: None, .. }, r#else @ Arm { guard: None, .. }], _) =
-            expr.kind
+        let (scrutinee_ty, ty_ref_count, ty_mutability) =
+            peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee));
+        if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::option_type)
+            && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::option_type))
         {
-            manage_lint(
-                cx,
-                expr,
-                (&then.pat.kind, then.body),
-                (&r#else.pat.kind, r#else.body),
-                scrutinee,
-            );
+            return;
         }
-    }
-}
-
-fn manage_lint<'tcx>(
-    cx: &LateContext<'tcx>,
-    expr: &'tcx Expr<'_>,
-    then: (&'tcx PatKind<'_>, &'tcx Expr<'_>),
-    r#else: (&'tcx PatKind<'_>, &'tcx Expr<'_>),
-    scrut: &'tcx Expr<'_>,
-) {
-    if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) {
-        return;
-    }
 
-    let (scrutinee_ty, ty_ref_count, ty_mutability) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrut));
-    if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::option_type)
-        && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::option_type))
-    {
-        return;
-    }
+        let expr_ctxt = expr.span.ctxt();
+        let (some_expr, some_pat, pat_ref_count, is_wild_none) = match (
+            try_parse_pattern(cx, then_pat, expr_ctxt),
+            else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)),
+        ) {
+            (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => {
+                (else_body, pattern, ref_count, true)
+            },
+            (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => {
+                (else_body, pattern, ref_count, false)
+            },
+            (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => {
+                (then_body, pattern, ref_count, true)
+            },
+            (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => {
+                (then_body, pattern, ref_count, false)
+            },
+            _ => return,
+        };
 
-    let (then_pat, then_expr) = then;
-    let (else_pat, else_expr) = r#else;
+        // Top level or patterns aren't allowed in closures.
+        if matches!(some_pat.kind, PatKind::Or(_)) {
+            return;
+        }
 
-    let expr_ctxt = expr.span.ctxt();
-    let (some_expr, some_pat, pat_ref_count, is_wild_none) = match (
-        try_parse_pattern(cx, then_pat, expr_ctxt),
-        try_parse_pattern(cx, else_pat, expr_ctxt),
-    ) {
-        (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_expr) => {
-            (else_expr, pattern, ref_count, true)
-        },
-        (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_expr) => {
-            (else_expr, pattern, ref_count, false)
-        },
-        (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_expr) => {
-            (then_expr, pattern, ref_count, true)
-        },
-        (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_expr) => {
-            (then_expr, pattern, ref_count, false)
-        },
-        _ => return,
-    };
+        let some_expr = match get_some_expr(cx, some_expr, expr_ctxt) {
+            Some(expr) => expr,
+            None => return,
+        };
 
-    // Top level or patterns aren't allowed in closures.
-    if matches!(some_pat.kind, PatKind::Or(_)) {
-        return;
-    }
+        // These two lints will go back and forth with each other.
+        if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit
+            && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
+        {
+            return;
+        }
 
-    let some_expr = match get_some_expr(cx, some_expr, expr_ctxt) {
-        Some(expr) => expr,
-        None => return,
-    };
+        // `map` won't perform any adjustments.
+        if !cx.typeck_results().expr_adjustments(some_expr).is_empty() {
+            return;
+        }
 
-    if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) {
-        return;
-    }
+        // Determine which binding mode to use.
+        let explicit_ref = some_pat.contains_explicit_ref_binding();
+        let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability));
 
-    // `map` won't perform any adjustments.
-    if !cx.typeck_results().expr_adjustments(some_expr).is_empty() {
-        return;
-    }
+        let as_ref_str = match binding_ref {
+            Some(Mutability::Mut) => ".as_mut()",
+            Some(Mutability::Not) => ".as_ref()",
+            None => "",
+        };
 
-    if !can_move_expr_to_closure(cx, some_expr) {
-        return;
-    }
-
-    // Determine which binding mode to use.
-    let explicit_ref = some_pat.contains_explicit_ref_binding();
-    let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability));
+        match can_move_expr_to_closure(cx, some_expr) {
+            Some(captures) => {
+                // Check if captures the closure will need conflict with borrows made in the scrutinee.
+                // TODO: check all the references made in the scrutinee expression. This will require interacting
+                // with the borrow checker. Currently only `<local>[.<field>]*` is checked for.
+                if let Some(binding_ref_mutability) = binding_ref {
+                    let e = peel_hir_expr_while(scrutinee, |e| match e.kind {
+                        ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e),
+                        _ => None,
+                    });
+                    if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind {
+                        match captures.get(l) {
+                            Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return,
+                            Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => {
+                                return;
+                            },
+                            Some(CaptureKind::Ref(Mutability::Not)) | None => (),
+                        }
+                    }
+                }
+            },
+            None => return,
+        };
 
-    let as_ref_str = match binding_ref {
-        Some(Mutability::Mut) => ".as_mut()",
-        Some(Mutability::Not) => ".as_ref()",
-        None => "",
-    };
+        let mut app = Applicability::MachineApplicable;
 
-    let mut app = Applicability::MachineApplicable;
+        // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or
+        // it's being passed by value.
+        let scrutinee = peel_hir_expr_refs(scrutinee).0;
+        let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
+        let scrutinee_str =
+            if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX {
+                format!("({})", scrutinee_str)
+            } else {
+                scrutinee_str.into()
+            };
 
-    // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or
-    // it's being passed by value.
-    let scrutinee = peel_hir_expr_refs(scrut).0;
-    let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
-    let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX {
-        format!("({})", scrutinee_str)
-    } else {
-        scrutinee_str.into()
-    };
+        let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind {
+            match can_pass_as_func(cx, id, some_expr) {
+                Some(func) if func.span.ctxt() == some_expr.span.ctxt() => {
+                    snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
+                },
+                _ => {
+                    if path_to_local_id(some_expr, id)
+                        && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id)
+                        && binding_ref.is_some()
+                    {
+                        return;
+                    }
 
-    let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind {
-        match can_pass_as_func(cx, id, some_expr) {
-            Some(func) if func.span.ctxt() == some_expr.span.ctxt() => {
-                snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
-            },
-            _ => {
-                if path_to_local_id(some_expr, id)
-                    && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id)
-                    && binding_ref.is_some()
-                {
-                    return;
-                }
+                    // `ref` and `ref mut` annotations were handled earlier.
+                    let annotation = if matches!(annotation, BindingAnnotation::Mutable) {
+                        "mut "
+                    } else {
+                        ""
+                    };
+                    format!(
+                        "|{}{}| {}",
+                        annotation,
+                        some_binding,
+                        snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0
+                    )
+                },
+            }
+        } else if !is_wild_none && explicit_ref.is_none() {
+            // TODO: handle explicit reference annotations.
+            format!(
+                "|{}| {}",
+                snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0,
+                snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0
+            )
+        } else {
+            // Refutable bindings and mixed reference annotations can't be handled by `map`.
+            return;
+        };
 
-                // `ref` and `ref mut` annotations were handled earlier.
-                let annotation = if matches!(annotation, BindingAnnotation::Mutable) {
-                    "mut "
-                } else {
-                    ""
-                };
-                format!(
-                    "|{}{}| {}",
-                    annotation,
-                    some_binding,
-                    snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0
-                )
+        span_lint_and_sugg(
+            cx,
+            MANUAL_MAP,
+            expr.span,
+            "manual implementation of `Option::map`",
+            "try this",
+            if else_pat.is_none() && is_else_clause(cx.tcx, expr) {
+                format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str)
+            } else {
+                format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str)
             },
-        }
-    } else if !is_wild_none && explicit_ref.is_none() {
-        // TODO: handle explicit reference annotations.
-        format!(
-            "|{}| {}",
-            snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0,
-            snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0
-        )
-    } else {
-        // Refutable bindings and mixed reference annotations can't be handled by `map`.
-        return;
-    };
-
-    span_lint_and_sugg(
-        cx,
-        MANUAL_MAP,
-        expr.span,
-        "manual implementation of `Option::map`",
-        "try this",
-        if is_else_clause(cx.tcx, expr) {
-            format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str)
-        } else {
-            format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str)
-        },
-        app,
-    );
+            app,
+        );
+    }
 }
 
 // Checks whether the expression could be passed as a function, or whether a closure is needed.
@@ -213,7 +216,7 @@ fn manage_lint<'tcx>(
 fn can_pass_as_func(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
     match expr.kind {
         ExprKind::Call(func, [arg])
-            if path_to_local_id (arg, binding) && cx.typeck_results().expr_adjustments(arg).is_empty() =>
+            if path_to_local_id(arg, binding) && cx.typeck_results().expr_adjustments(arg).is_empty() =>
         {
             Some(func)
         },
@@ -235,28 +238,21 @@ enum OptionPat<'a> {
 
 // Try to parse into a recognized `Option` pattern.
 // i.e. `_`, `None`, `Some(..)`, or a reference to any of those.
-fn try_parse_pattern(
-    cx: &LateContext<'tcx>,
-    pat_kind: &'tcx PatKind<'_>,
-    ctxt: SyntaxContext,
-) -> Option<OptionPat<'tcx>> {
-    fn f(
-        cx: &LateContext<'tcx>,
-        pat_kind: &'tcx PatKind<'_>,
-        ref_count: usize,
-        ctxt: SyntaxContext,
-    ) -> Option<OptionPat<'tcx>> {
-        match pat_kind {
+fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
+    fn f(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ref_count: usize, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
+        match pat.kind {
             PatKind::Wild => Some(OptionPat::Wild),
-            PatKind::Ref(ref_pat, _) => f(cx, &ref_pat.kind, ref_count + 1, ctxt),
+            PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt),
             PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None),
-            PatKind::TupleStruct(ref qpath, [pattern], _) if is_lang_ctor(cx, qpath, OptionSome) => {
+            PatKind::TupleStruct(ref qpath, [pattern], _)
+                if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt =>
+            {
                 Some(OptionPat::Some { pattern, ref_count })
             },
             _ => None,
         }
     }
-    f(cx, pat_kind, 0, ctxt)
+    f(cx, pat, 0, ctxt)
 }
 
 // Checks for an expression wrapped by the `Some` constructor. Returns the contained expression.
diff --git a/src/tools/clippy/clippy_lints/src/matches.rs b/src/tools/clippy/clippy_lints/src/matches.rs
index 3d0da472ddc..2f1ff567e84 100644
--- a/src/tools/clippy/clippy_lints/src/matches.rs
+++ b/src/tools/clippy/clippy_lints/src/matches.rs
@@ -6,7 +6,7 @@ use clippy_utils::higher;
 use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
-use clippy_utils::visitors::LocalUsedVisitor;
+use clippy_utils::visitors::is_local_used;
 use clippy_utils::{
     get_parent_expr, in_macro, 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,
@@ -631,7 +631,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
                 check_match_single_binding(cx, ex, arms, expr);
             }
         }
-        if let ExprKind::Match(ref ex, ref arms, _) = expr.kind {
+        if let ExprKind::Match(ex, arms, _) = expr.kind {
             check_match_ref_pats(cx, ex, arms.iter().map(|el| el.pat), expr);
         }
         if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr) {
@@ -959,9 +959,7 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm
                         // Looking for unused bindings (i.e.: `_e`)
                         for pat in inner.iter() {
                             if let PatKind::Binding(_, id, ident, None) = pat.kind {
-                                if ident.as_str().starts_with('_')
-                                    && !LocalUsedVisitor::new(cx, id).check_expr(arm.body)
-                                {
+                                if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) {
                                     ident_bind_name = (&ident.name.as_str()).to_string();
                                     matching_wild = true;
                                 }
@@ -1196,7 +1194,7 @@ where
 
     let (first_sugg, msg, title);
     let span = ex.span.source_callsite();
-    if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = ex.kind {
+    if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind {
         first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
         msg = "try";
         title = "you don't need to add `&` to both the expression and the patterns";
@@ -1207,7 +1205,7 @@ where
     }
 
     let remaining_suggs = pats.filter_map(|pat| {
-        if let PatKind::Ref(ref refp, _) = pat.kind {
+        if let PatKind::Ref(refp, _) = pat.kind {
             Some((pat.span, snippet(cx, refp.span, "..").to_string()))
         } else {
             None
@@ -1367,7 +1365,7 @@ where
                 find_bool_lit(&arm.2.kind, is_if_let).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty()
             });
         then {
-            if let Some(ref last_pat) = last_pat_opt {
+            if let Some(last_pat) = last_pat_opt {
                 if !is_wild(last_pat) {
                     return false;
                 }
@@ -1829,13 +1827,13 @@ mod redundant_pattern_match {
             ..
         }) = higher::IfLet::hir(cx, expr)
         {
-            find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some())
+            find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some());
         }
         if let ExprKind::Match(op, arms, MatchSource::Normal) = &expr.kind {
-            find_sugg_for_match(cx, expr, op, arms)
+            find_sugg_for_match(cx, expr, op, arms);
         }
         if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
-            find_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false)
+            find_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false);
         }
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/mem_forget.rs b/src/tools/clippy/clippy_lints/src/mem_forget.rs
index 07202a59c4b..eb437dc47af 100644
--- a/src/tools/clippy/clippy_lints/src/mem_forget.rs
+++ b/src/tools/clippy/clippy_lints/src/mem_forget.rs
@@ -28,11 +28,11 @@ declare_lint_pass!(MemForget => [MEM_FORGET]);
 
 impl<'tcx> LateLintPass<'tcx> for MemForget {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-        if let ExprKind::Call(path_expr, args) = e.kind {
+        if let ExprKind::Call(path_expr, [ref first_arg, ..]) = e.kind {
             if let ExprKind::Path(ref qpath) = path_expr.kind {
                 if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id() {
                     if match_def_path(cx, def_id, &paths::MEM_FORGET) {
-                        let forgot_ty = cx.typeck_results().expr_ty(&args[0]);
+                        let forgot_ty = cx.typeck_results().expr_ty(first_arg);
 
                         if forgot_ty.ty_adt_def().map_or(false, |def| def.has_dtor(cx.tcx)) {
                             span_lint(cx, MEM_FORGET, e.span, "usage of `mem::forget` on `Drop` type");
diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs
index 3d071c9081b..1e6057a8fe9 100644
--- a/src/tools/clippy/clippy_lints/src/mem_replace.rs
+++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs
@@ -1,9 +1,9 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::{snippet, snippet_with_applicability};
-use clippy_utils::{in_macro, is_diag_trait_item, is_lang_ctor, match_def_path, meets_msrv, msrvs, paths};
+use clippy_utils::ty::is_non_aggregate_primitive_type;
+use clippy_utils::{in_macro, is_default_equivalent, is_lang_ctor, match_def_path, meets_msrv, msrvs, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::def_id::DefId;
 use rustc_hir::LangItem::OptionNone;
 use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -194,64 +194,37 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'
     }
 }
 
-/// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent"
-/// constructor from the std library
-fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
-    let std_types_symbols = &[
-        sym::string_type,
-        sym::vec_type,
-        sym::vecdeque_type,
-        sym::LinkedList,
-        sym::hashmap_type,
-        sym::BTreeMap,
-        sym::hashset_type,
-        sym::BTreeSet,
-        sym::BinaryHeap,
-    ];
-
-    if let QPath::TypeRelative(_, method) = path {
-        if method.ident.name == sym::new {
-            if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
-                if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
-                    return std_types_symbols
-                        .iter()
-                        .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did));
-                }
-            }
+fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
+    // disable lint for primitives
+    let expr_type = cx.typeck_results().expr_ty_adjusted(src);
+    if is_non_aggregate_primitive_type(expr_type) {
+        return;
+    }
+    // disable lint for Option since it is covered in another lint
+    if let ExprKind::Path(q) = &src.kind {
+        if is_lang_ctor(cx, q, OptionNone) {
+            return;
         }
     }
-    false
-}
+    if is_default_equivalent(cx, src) && !in_external_macro(cx.tcx.sess, expr_span) {
+        span_lint_and_then(
+            cx,
+            MEM_REPLACE_WITH_DEFAULT,
+            expr_span,
+            "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`",
+            |diag| {
+                if !in_macro(expr_span) {
+                    let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, ""));
 
-fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
-    if_chain! {
-        if let ExprKind::Call(repl_func, _) = src.kind;
-        if !in_external_macro(cx.tcx.sess, expr_span);
-        if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
-        if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
-        if is_diag_trait_item(cx, repl_def_id, sym::Default)
-            || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
-
-        then {
-            span_lint_and_then(
-                cx,
-                MEM_REPLACE_WITH_DEFAULT,
-                expr_span,
-                "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`",
-                |diag| {
-                    if !in_macro(expr_span) {
-                        let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, ""));
-
-                        diag.span_suggestion(
-                            expr_span,
-                            "consider using",
-                            suggestion,
-                            Applicability::MachineApplicable
-                        );
-                    }
+                    diag.span_suggestion(
+                        expr_span,
+                        "consider using",
+                        suggestion,
+                        Applicability::MachineApplicable,
+                    );
                 }
-            );
-        }
+            },
+        );
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_next.rs b/src/tools/clippy/clippy_lints/src/methods/filter_next.rs
index 172714f6b01..bcf8d93b602 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filter_next.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_next.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
-use clippy_utils::is_trait_method;
 use clippy_utils::source::snippet;
+use clippy_utils::ty::implements_trait;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -16,7 +16,10 @@ pub(super) fn check<'tcx>(
     filter_arg: &'tcx hir::Expr<'_>,
 ) {
     // lint if caller of `.filter().next()` is an Iterator
-    if is_trait_method(cx, expr, sym::Iterator) {
+    let recv_impls_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| {
+        implements_trait(cx, cx.typeck_results().expr_ty(recv), id, &[])
+    });
+    if recv_impls_iterator {
         let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \
                    `.find(..)` instead";
         let filter_snippet = snippet(cx, filter_arg.span, "..");
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_split_once.rs b/src/tools/clippy/clippy_lints/src/methods/manual_split_once.rs
new file mode 100644
index 00000000000..e273186d051
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_split_once.rs
@@ -0,0 +1,213 @@
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_context;
+use clippy_utils::{is_diag_item_method, match_def_path, paths};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, adjustment::Adjust};
+use rustc_span::{symbol::sym, Span, SyntaxContext};
+
+use super::MANUAL_SPLIT_ONCE;
+
+pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>) {
+    if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
+        return;
+    }
+
+    let ctxt = expr.span.ctxt();
+    let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id)) {
+        Some(x) => x,
+        None => return,
+    };
+    let (method_name, msg) = if method_name == "splitn" {
+        ("split_once", "manual implementation of `split_once`")
+    } else {
+        ("rsplit_once", "manual implementation of `rsplit_once`")
+    };
+
+    let mut app = Applicability::MachineApplicable;
+    let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
+    let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0;
+
+    match usage.kind {
+        IterUsageKind::NextTuple => {
+            span_lint_and_sugg(
+                cx,
+                MANUAL_SPLIT_ONCE,
+                usage.span,
+                msg,
+                "try this",
+                format!("{}.{}({})", self_snip, method_name, pat_snip),
+                app,
+            );
+        },
+        IterUsageKind::Next => {
+            let self_deref = {
+                let adjust = cx.typeck_results().expr_adjustments(self_arg);
+                if adjust.is_empty() {
+                    String::new()
+                } else if cx.typeck_results().expr_ty(self_arg).is_box()
+                    || adjust
+                        .iter()
+                        .any(|a| matches!(a.kind, Adjust::Deref(Some(_))) || a.target.is_box())
+                {
+                    format!("&{}", "*".repeat(adjust.len() - 1))
+                } else {
+                    "*".repeat(adjust.len() - 2)
+                }
+            };
+            let sugg = if usage.unwrap_kind.is_some() {
+                format!(
+                    "{}.{}({}).map_or({}{}, |x| x.0)",
+                    &self_snip, method_name, pat_snip, self_deref, &self_snip
+                )
+            } else {
+                format!(
+                    "Some({}.{}({}).map_or({}{}, |x| x.0))",
+                    &self_snip, method_name, pat_snip, self_deref, &self_snip
+                )
+            };
+
+            span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
+        },
+        IterUsageKind::Second => {
+            let access_str = match usage.unwrap_kind {
+                Some(UnwrapKind::Unwrap) => ".unwrap().1",
+                Some(UnwrapKind::QuestionMark) => "?.1",
+                None => ".map(|x| x.1)",
+            };
+            span_lint_and_sugg(
+                cx,
+                MANUAL_SPLIT_ONCE,
+                usage.span,
+                msg,
+                "try this",
+                format!("{}.{}({}){}", self_snip, method_name, pat_snip, access_str),
+                app,
+            );
+        },
+    }
+}
+
+enum IterUsageKind {
+    Next,
+    Second,
+    NextTuple,
+}
+
+enum UnwrapKind {
+    Unwrap,
+    QuestionMark,
+}
+
+struct IterUsage {
+    kind: IterUsageKind,
+    unwrap_kind: Option<UnwrapKind>,
+    span: Span,
+}
+
+fn parse_iter_usage(
+    cx: &LateContext<'tcx>,
+    ctxt: SyntaxContext,
+    mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
+) -> Option<IterUsage> {
+    let (kind, span) = match iter.next() {
+        Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
+            let (name, args) = if let ExprKind::MethodCall(name, _, [_, args @ ..], _) = e.kind {
+                (name, args)
+            } else {
+                return None;
+            };
+            let did = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
+            let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?;
+
+            match (&*name.ident.as_str(), args) {
+                ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Next, e.span),
+                ("next_tuple", []) => {
+                    if_chain! {
+                        if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE);
+                        if let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind();
+                        if cx.tcx.is_diagnostic_item(sym::option_type, adt_def.did);
+                        if let ty::Tuple(subs) = subs.type_at(0).kind();
+                        if subs.len() == 2;
+                        then {
+                            return Some(IterUsage { kind: IterUsageKind::NextTuple, span: e.span, unwrap_kind: None });
+                        } else {
+                            return None;
+                        }
+                    }
+                },
+                ("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
+                    if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) {
+                        let span = if name.ident.as_str() == "nth" {
+                            e.span
+                        } else {
+                            if_chain! {
+                                if let Some((_, Node::Expr(next_expr))) = iter.next();
+                                if let ExprKind::MethodCall(next_name, _, [_], _) = next_expr.kind;
+                                if next_name.ident.name == sym::next;
+                                if next_expr.span.ctxt() == ctxt;
+                                if let Some(next_id) = cx.typeck_results().type_dependent_def_id(next_expr.hir_id);
+                                if cx.tcx.trait_of_item(next_id) == Some(iter_id);
+                                then {
+                                    next_expr.span
+                                } else {
+                                    return None;
+                                }
+                            }
+                        };
+                        match idx {
+                            0 => (IterUsageKind::Next, span),
+                            1 => (IterUsageKind::Second, span),
+                            _ => return None,
+                        }
+                    } else {
+                        return None;
+                    }
+                },
+                _ => return None,
+            }
+        },
+        _ => return None,
+    };
+
+    let (unwrap_kind, span) = if let Some((_, Node::Expr(e))) = iter.next() {
+        match e.kind {
+            ExprKind::Call(
+                Expr {
+                    kind: ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, _)),
+                    ..
+                },
+                _,
+            ) => {
+                let parent_span = e.span.parent().unwrap();
+                if parent_span.ctxt() == ctxt {
+                    (Some(UnwrapKind::QuestionMark), parent_span)
+                } else {
+                    (None, span)
+                }
+            },
+            _ if e.span.ctxt() != ctxt => (None, span),
+            ExprKind::MethodCall(name, _, [_], _)
+                if name.ident.name == sym::unwrap
+                    && cx
+                        .typeck_results()
+                        .type_dependent_def_id(e.hir_id)
+                        .map_or(false, |id| is_diag_item_method(cx, id, sym::option_type)) =>
+            {
+                (Some(UnwrapKind::Unwrap), e.span)
+            },
+            _ => (None, span),
+        }
+    } else {
+        (None, span)
+    };
+
+    Some(IterUsage {
+        kind,
+        unwrap_kind,
+        span,
+    })
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 9626cf79dc1..e89b2d295b9 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -33,6 +33,7 @@ mod iter_nth_zero;
 mod iter_skip_next;
 mod iterator_step_by_zero;
 mod manual_saturating_arithmetic;
+mod manual_split_once;
 mod manual_str_repeat;
 mod map_collect_result_unit;
 mod map_flatten;
@@ -64,6 +65,7 @@ mod wrong_self_convention;
 mod zst_offset;
 
 use bind_instead_of_map::BindInsteadOfMap;
+use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item};
 use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, meets_msrv, msrvs, paths, return_ty};
@@ -1771,6 +1773,29 @@ declare_clippy_lint! {
     "manual implementation of `str::repeat`"
 }
 
+declare_clippy_lint! {
+    /// **What it does:** Checks for usages of `str::splitn(2, _)`
+    ///
+    /// **Why is this bad?** `split_once` is both clearer in intent and slightly more efficient.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust,ignore
+    /// // Bad
+    ///  let (key, value) = _.splitn(2, '=').next_tuple()?;
+    ///  let value = _.splitn(2, '=').nth(1)?;
+    ///
+    /// // Good
+    /// let (key, value) = _.split_once('=')?;
+    /// let value = _.split_once('=')?.1;
+    /// ```
+    pub MANUAL_SPLIT_ONCE,
+    complexity,
+    "replace `.splitn(2, pat)` with `.split_once(pat)`"
+}
+
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Option<RustcVersion>,
@@ -1848,7 +1873,8 @@ impl_lint_pass!(Methods => [
     IMPLICIT_CLONE,
     SUSPICIOUS_SPLITN,
     MANUAL_STR_REPEAT,
-    EXTEND_WITH_DRAIN
+    EXTEND_WITH_DRAIN,
+    MANUAL_SPLIT_ONCE
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
@@ -2176,8 +2202,18 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                     unnecessary_lazy_eval::check(cx, expr, recv, arg, "or");
                 }
             },
-            ("splitn" | "splitn_mut" | "rsplitn" | "rsplitn_mut", [count_arg, _]) => {
-                suspicious_splitn::check(cx, name, expr, recv, count_arg);
+            ("splitn" | "rsplitn", [count_arg, pat_arg]) => {
+                if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
+                    suspicious_splitn::check(cx, name, expr, recv, count);
+                    if count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE) {
+                        manual_split_once::check(cx, name, expr, recv, pat_arg);
+                    }
+                }
+            },
+            ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => {
+                if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
+                    suspicious_splitn::check(cx, name, expr, recv, count);
+                }
             },
             ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
             ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
index c1d22e5d72c..30ed1d665a9 100644
--- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
@@ -96,9 +96,9 @@ pub(super) fn check<'tcx>(
             (&paths::RESULT, true, &["or", "unwrap_or"], "else"),
         ];
 
-        if let hir::ExprKind::MethodCall(path, _, args, _) = &arg.kind {
+        if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &arg.kind {
             if path.ident.name == sym::len {
-                let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
+                let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
 
                 match ty.kind() {
                     ty::Slice(_) | ty::Array(_, _) | ty::Str => return,
diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs
index a271df60572..1c546a15bf6 100644
--- a/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs
@@ -1,4 +1,3 @@
-use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::span_lint_and_note;
 use if_chain::if_chain;
 use rustc_ast::LitKind;
@@ -8,15 +7,8 @@ use rustc_span::source_map::Spanned;
 
 use super::SUSPICIOUS_SPLITN;
 
-pub(super) fn check(
-    cx: &LateContext<'_>,
-    method_name: &str,
-    expr: &Expr<'_>,
-    self_arg: &Expr<'_>,
-    count_arg: &Expr<'_>,
-) {
+pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, count: u128) {
     if_chain! {
-        if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg);
         if count <= 1;
         if let Some(call_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
         if let Some(impl_id) = cx.tcx.impl_of_method(call_id);
@@ -24,9 +16,9 @@ pub(super) fn check(
         if lang_items.slice_impl() == Some(impl_id) || lang_items.str_impl() == Some(impl_id);
         then {
             // Ignore empty slice and string literals when used with a literal count.
-            if (matches!(self_arg.kind, ExprKind::Array([]))
+            if matches!(self_arg.kind, ExprKind::Array([]))
                 || matches!(self_arg.kind, ExprKind::Lit(Spanned { node: LitKind::Str(s, _), .. }) if s.is_empty())
-            ) && matches!(count_arg.kind, ExprKind::Lit(_))
+
             {
                 return;
             }
diff --git a/src/tools/clippy/clippy_lints/src/methods/utils.rs b/src/tools/clippy/clippy_lints/src/methods/utils.rs
index 0daea47816a..30d6665a920 100644
--- a/src/tools/clippy/clippy_lints/src/methods/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/utils.rs
@@ -24,9 +24,9 @@ pub(super) fn derefs_to_slice<'tcx>(
         }
     }
 
-    if let hir::ExprKind::MethodCall(path, _, args, _) = expr.kind {
-        if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) {
-            Some(&args[0])
+    if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind {
+        if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(self_arg)) {
+            Some(self_arg)
         } else {
             None
         }
diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs
index c796abe9815..538fa4e1678 100644
--- a/src/tools/clippy/clippy_lints/src/misc.rs
+++ b/src/tools/clippy/clippy_lints/src/misc.rs
@@ -513,12 +513,12 @@ fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     }
 
     if_chain! {
-        if let ExprKind::MethodCall(method_name, _, expressions, _) = expr.kind;
+        if let ExprKind::MethodCall(method_name, _, [ref self_arg, ..], _) = expr.kind;
         if sym!(signum) == method_name.ident.name;
         // Check that the receiver of the signum() is a float (expressions[0] is the receiver of
         // the method call)
         then {
-            return is_float(cx, &expressions[0]);
+            return is_float(cx, self_arg);
         }
     }
     false
diff --git a/src/tools/clippy/clippy_lints/src/module_style.rs b/src/tools/clippy/clippy_lints/src/module_style.rs
new file mode 100644
index 00000000000..80a930d0c54
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/module_style.rs
@@ -0,0 +1,178 @@
+use std::{
+    ffi::OsString,
+    path::{Component, Path},
+};
+
+use rustc_ast::ast;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::{FileName, RealFileName, SourceFile, Span, SyntaxContext};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks that module layout uses only self named module files, bans mod.rs files.
+    ///
+    /// ### Why is this bad?
+    /// Having multiple module layout styles in a project can be confusing.
+    ///
+    /// ### Example
+    /// ```text
+    /// src/
+    ///   stuff/
+    ///     stuff_files.rs
+    ///     mod.rs
+    ///   lib.rs
+    /// ```
+    /// Use instead:
+    /// ```text
+    /// src/
+    ///   stuff/
+    ///     stuff_files.rs
+    ///   stuff.rs
+    ///   lib.rs
+    /// ```
+    pub MOD_MODULE_FILES,
+    restriction,
+    "checks that module layout is consistent"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks that module layout uses only mod.rs files.
+    ///
+    /// ### Why is this bad?
+    /// Having multiple module layout styles in a project can be confusing.
+    ///
+    /// ### Example
+    /// ```text
+    /// src/
+    ///   stuff/
+    ///     stuff_files.rs
+    ///   stuff.rs
+    ///   lib.rs
+    /// ```
+    /// Use instead:
+    /// ```text
+    /// src/
+    ///   stuff/
+    ///     stuff_files.rs
+    ///     mod.rs
+    ///   lib.rs
+    /// ```
+
+    pub SELF_NAMED_MODULE_FILES,
+    restriction,
+    "checks that module layout is consistent"
+}
+
+pub struct ModStyle;
+
+impl_lint_pass!(ModStyle => [MOD_MODULE_FILES, SELF_NAMED_MODULE_FILES]);
+
+impl EarlyLintPass for ModStyle {
+    fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
+        if cx.builder.lint_level(MOD_MODULE_FILES).0 == Level::Allow
+            && cx.builder.lint_level(SELF_NAMED_MODULE_FILES).0 == Level::Allow
+        {
+            return;
+        }
+
+        let files = cx.sess.source_map().files();
+
+        let trim_to_src = if let RealFileName::LocalPath(p) = &cx.sess.opts.working_dir {
+            p.to_string_lossy()
+        } else {
+            return;
+        };
+
+        // `folder_segments` is all unique folder path segments `path/to/foo.rs` gives
+        // `[path, to]` but not foo
+        let mut folder_segments = FxHashSet::default();
+        // `mod_folders` is all the unique folder names that contain a mod.rs file
+        let mut mod_folders = FxHashSet::default();
+        // `file_map` maps file names to the full path including the file name
+        // `{ foo => path/to/foo.rs, .. }
+        let mut file_map = FxHashMap::default();
+        for file in files.iter() {
+            match &file.name {
+                FileName::Real(RealFileName::LocalPath(lp))
+                    if lp.to_string_lossy().starts_with(trim_to_src.as_ref()) =>
+                {
+                    let p = lp.to_string_lossy();
+                    let path = Path::new(p.trim_start_matches(trim_to_src.as_ref()));
+                    if let Some(stem) = path.file_stem() {
+                        file_map.insert(stem.to_os_string(), (file, path.to_owned()));
+                    }
+                    process_paths_for_mod_files(path, &mut folder_segments, &mut mod_folders);
+                    check_self_named_mod_exists(cx, path, file);
+                }
+                _ => {},
+            }
+        }
+
+        for folder in &folder_segments {
+            if !mod_folders.contains(folder) {
+                if let Some((file, path)) = file_map.get(folder) {
+                    let mut correct = path.clone();
+                    correct.pop();
+                    correct.push(folder);
+                    correct.push("mod.rs");
+                    cx.struct_span_lint(
+                        SELF_NAMED_MODULE_FILES,
+                        Span::new(file.start_pos, file.start_pos, SyntaxContext::root()),
+                        |build| {
+                            let mut lint =
+                                build.build(&format!("`mod.rs` files are required, found `{}`", path.display()));
+                            lint.help(&format!("move `{}` to `{}`", path.display(), correct.display(),));
+                            lint.emit();
+                        },
+                    );
+                }
+            }
+        }
+    }
+}
+
+/// For each `path` we add each folder component to `folder_segments` and if the file name
+/// is `mod.rs` we add it's parent folder to `mod_folders`.
+fn process_paths_for_mod_files(
+    path: &Path,
+    folder_segments: &mut FxHashSet<OsString>,
+    mod_folders: &mut FxHashSet<OsString>,
+) {
+    let mut comp = path.components().rev().peekable();
+    let _ = comp.next();
+    if path.ends_with("mod.rs") {
+        mod_folders.insert(comp.peek().map(|c| c.as_os_str().to_owned()).unwrap_or_default());
+    }
+    let folders = comp
+        .filter_map(|c| {
+            if let Component::Normal(s) = c {
+                Some(s.to_os_string())
+            } else {
+                None
+            }
+        })
+        .collect::<Vec<_>>();
+    folder_segments.extend(folders);
+}
+
+/// Checks every path for the presence of `mod.rs` files and emits the lint if found.
+fn check_self_named_mod_exists(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) {
+    if path.ends_with("mod.rs") {
+        let mut mod_file = path.to_path_buf();
+        mod_file.pop();
+        mod_file.set_extension("rs");
+
+        cx.struct_span_lint(
+            MOD_MODULE_FILES,
+            Span::new(file.start_pos, file.start_pos, SyntaxContext::root()),
+            |build| {
+                let mut lint = build.build(&format!("`mod.rs` files are not allowed, found `{}`", path.display()));
+                lint.help(&format!("move `{}` to `{}`", path.display(), mod_file.display(),));
+                lint.emit();
+            },
+        );
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/mut_mutex_lock.rs b/src/tools/clippy/clippy_lints/src/mut_mutex_lock.rs
index 85e870632a5..e9dcc7b227d 100644
--- a/src/tools/clippy/clippy_lints/src/mut_mutex_lock.rs
+++ b/src/tools/clippy/clippy_lints/src/mut_mutex_lock.rs
@@ -47,9 +47,9 @@ declare_lint_pass!(MutMutexLock => [MUT_MUTEX_LOCK]);
 impl<'tcx> LateLintPass<'tcx> for MutMutexLock {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) {
         if_chain! {
-            if let ExprKind::MethodCall(path, method_span, args, _) = &ex.kind;
+            if let ExprKind::MethodCall(path, method_span, [self_arg, ..], _) = &ex.kind;
             if path.ident.name == sym!(lock);
-            let ty = cx.typeck_results().expr_ty(&args[0]);
+            let ty = cx.typeck_results().expr_ty(self_arg);
             if let ty::Ref(_, inner_ty, Mutability::Mut) = ty.kind();
             if is_type_diagnostic_item(cx, inner_ty, sym!(mutex_type));
             then {
diff --git a/src/tools/clippy/clippy_lints/src/needless_option_as_deref.rs b/src/tools/clippy/clippy_lints/src/needless_option_as_deref.rs
new file mode 100644
index 00000000000..5024a881d2a
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/needless_option_as_deref.rs
@@ -0,0 +1,66 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_macro;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::is_type_diagnostic_item;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::TyS;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::sym;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for no-op uses of Option::{as_deref,as_deref_mut},
+    /// for example, `Option<&T>::as_deref()` returns the same type.
+    ///
+    /// ### Why is this bad?
+    /// Redundant code and improving readability.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let a = Some(&1);
+    /// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32>
+    /// ```
+    /// Could be written as:
+    /// ```rust
+    /// let a = Some(&1);
+    /// let b = a;
+    /// ```
+    pub NEEDLESS_OPTION_AS_DEREF,
+    complexity,
+    "no-op use of `deref` or `deref_mut` method to `Option`."
+}
+
+declare_lint_pass!(OptionNeedlessDeref=> [
+    NEEDLESS_OPTION_AS_DEREF,
+]);
+
+impl<'tcx> LateLintPass<'tcx> for OptionNeedlessDeref {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if expr.span.from_expansion() || in_macro(expr.span) {
+            return;
+        }
+        let typeck = cx.typeck_results();
+        let outer_ty = typeck.expr_ty(expr);
+
+        if_chain! {
+            if is_type_diagnostic_item(cx,outer_ty,sym::option_type);
+            if let ExprKind::MethodCall(path, _, [sub_expr], _) = expr.kind;
+            let symbol = path.ident.as_str();
+            if symbol=="as_deref" || symbol=="as_deref_mut";
+            if TyS::same_type( outer_ty, typeck.expr_ty(sub_expr) );
+            then{
+                span_lint_and_sugg(
+                    cx,
+                    NEEDLESS_OPTION_AS_DEREF,
+                    expr.span,
+                    "derefed type is same as origin",
+                    "try this",
+                    snippet_opt(cx,sub_expr.span).unwrap(),
+                    Applicability::MachineApplicable
+                );
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs
index 28e9e6f438e..c5a5cde4b11 100644
--- a/src/tools/clippy/clippy_lints/src/no_effect.rs
+++ b/src/tools/clippy/clippy_lints/src/no_effect.rs
@@ -96,28 +96,63 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect {
             if has_no_effect(cx, expr) {
                 span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect");
             } else if let Some(reduced) = reduce_expression(cx, expr) {
-                let mut snippet = String::new();
-                for e in reduced {
+                for e in &reduced {
                     if e.span.from_expansion() {
                         return;
                     }
-                    if let Some(snip) = snippet_opt(cx, e.span) {
-                        snippet.push_str(&snip);
-                        snippet.push(';');
-                    } else {
-                        return;
+                }
+                if let ExprKind::Index(..) = &expr.kind {
+                    let snippet;
+                    if_chain! {
+                        if let Some(arr) = snippet_opt(cx, reduced[0].span);
+                        if let Some(func) = snippet_opt(cx, reduced[1].span);
+                        then {
+                            snippet = format!("assert!({}.len() > {});", &arr, &func);
+                        } else {
+                            return;
+                        }
+                    }
+                    span_lint_hir_and_then(
+                        cx,
+                        UNNECESSARY_OPERATION,
+                        expr.hir_id,
+                        stmt.span,
+                        "unnecessary operation",
+                        |diag| {
+                            diag.span_suggestion(
+                                stmt.span,
+                                "statement can be written as",
+                                snippet,
+                                Applicability::MaybeIncorrect,
+                            );
+                        },
+                    );
+                } else {
+                    let mut snippet = String::new();
+                    for e in reduced {
+                        if let Some(snip) = snippet_opt(cx, e.span) {
+                            snippet.push_str(&snip);
+                            snippet.push(';');
+                        } else {
+                            return;
+                        }
                     }
+                    span_lint_hir_and_then(
+                        cx,
+                        UNNECESSARY_OPERATION,
+                        expr.hir_id,
+                        stmt.span,
+                        "unnecessary operation",
+                        |diag| {
+                            diag.span_suggestion(
+                                stmt.span,
+                                "statement can be reduced to",
+                                snippet,
+                                Applicability::MachineApplicable,
+                            );
+                        },
+                    );
                 }
-                span_lint_hir_and_then(
-                    cx,
-                    UNNECESSARY_OPERATION,
-                    expr.hir_id,
-                    stmt.span,
-                    "statement can be reduced",
-                    |diag| {
-                        diag.span_suggestion(stmt.span, "replace it with", snippet, Applicability::MachineApplicable);
-                    },
-                );
             }
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/open_options.rs b/src/tools/clippy/clippy_lints/src/open_options.rs
index 4064d94da2a..5752342cf62 100644
--- a/src/tools/clippy/clippy_lints/src/open_options.rs
+++ b/src/tools/clippy/clippy_lints/src/open_options.rs
@@ -31,11 +31,11 @@ declare_lint_pass!(OpenOptions => [NONSENSICAL_OPEN_OPTIONS]);
 
 impl<'tcx> LateLintPass<'tcx> for OpenOptions {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-        if let ExprKind::MethodCall(path, _, arguments, _) = e.kind {
-            let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs();
+        if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &e.kind {
+            let obj_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
             if path.ident.name == sym!(open) && match_type(cx, obj_ty, &paths::OPEN_OPTIONS) {
                 let mut options = Vec::new();
-                get_open_options(cx, &arguments[0], &mut options);
+                get_open_options(cx, self_arg, &mut options);
                 check_open_options(cx, &options, e.span);
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
index eff3d3abff8..15f6dcae887 100644
--- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
+++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
@@ -2,12 +2,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::usage::contains_return_break_continue_macro;
-use clippy_utils::{eager_or_lazy, in_macro, is_else_clause, is_lang_ctor};
+use clippy_utils::{
+    can_move_expr_to_closure, eager_or_lazy, in_constant, in_macro, is_else_clause, is_lang_ctor, peel_hir_expr_while,
+    CaptureKind,
+};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::OptionSome;
-use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, Mutability, PatKind, UnOp};
+use rustc_hir::{def::Res, BindingAnnotation, Block, 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;
@@ -58,7 +60,7 @@ declare_clippy_lint! {
     /// }, |foo| foo);
     /// ```
     pub OPTION_IF_LET_ELSE,
-    pedantic,
+    nursery,
     "reimplementation of Option::map_or"
 }
 
@@ -125,20 +127,30 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo
 fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionIfLetElseOccurence> {
     if_chain! {
         if !in_macro(expr.span); // Don't lint macros, because it behaves weirdly
-        if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(cx, expr);
+        if !in_constant(cx, expr.hir_id);
+        if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) })
+            = higher::IfLet::hir(cx, expr);
         if !is_else_clause(cx.tcx, expr);
         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 !contains_return_break_continue_macro(if_then);
-        if !contains_return_break_continue_macro(if_else);
+        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
+            .iter()
+            .filter_map(|(id, &c)| none_captures.get(id).map(|&c2| (c, c2)))
+            .all(|(x, y)| x.is_imm_ref() && y.is_imm_ref());
 
         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 method_sugg = if eager_or_lazy::is_eagerness_candidate(cx, none_body) { "map_or" } else { "map_or_else" };
+            let method_sugg = if eager_or_lazy::is_eagerness_candidate(cx, none_body) {
+                "map_or"
+            } else {
+                "map_or_else"
+            };
             let capture_name = id.name.to_ident_string();
             let (as_ref, as_mut) = match &let_expr.kind {
                 ExprKind::AddrOf(_, Mutability::Not, _) => (true, false),
@@ -150,6 +162,24 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) ->
                 ExprKind::Unary(UnOp::Deref, expr) | ExprKind::AddrOf(_, _, expr) => expr,
                 _ => let_expr,
             };
+            // Check if captures the closure will need conflict with borrows made in the scrutinee.
+            // TODO: check all the references made in the scrutinee expression. This will require interacting
+            // with the borrow checker. Currently only `<local>[.<field>]*` is checked for.
+            if as_ref || as_mut {
+                let e = peel_hir_expr_while(cond_expr, |e| match e.kind {
+                    ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e),
+                    _ => None,
+                });
+                if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(local_id), .. })) = e.kind {
+                    match some_captures.get(local_id)
+                        .or_else(|| (method_sugg == "map_or_else").then(|| ()).and_then(|_| none_captures.get(local_id)))
+                    {
+                        Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None,
+                        Some(CaptureKind::Ref(Mutability::Not)) if as_mut => return None,
+                        Some(CaptureKind::Ref(Mutability::Not)) | None => (),
+                    }
+                }
+            }
             Some(OptionIfLetElseOccurence {
                 option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut),
                 method_sugg: method_sugg.to_string(),
diff --git a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
index 35cff4141a9..e7bc2446590 100644
--- a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
+++ b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
@@ -118,7 +118,7 @@ impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch {
             }
         }
         if let ExprKind::Let(let_pat, let_expr, _) = expr.kind {
-            if let Some(ref expr_ty) = cx.typeck_results().node_type_opt(let_expr.hir_id) {
+            if let Some(expr_ty) = cx.typeck_results().node_type_opt(let_expr.hir_id) {
                 if in_external_macro(cx.sess(), let_pat.span) {
                     return;
                 }
diff --git a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs
index f1975056ddc..cfb5287c667 100644
--- a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs
@@ -92,13 +92,13 @@ fn expr_as_ptr_offset_call<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &'tcx Expr<'_>,
 ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> {
-    if let ExprKind::MethodCall(path_segment, _, args, _) = expr.kind {
-        if is_expr_ty_raw_ptr(cx, &args[0]) {
+    if let ExprKind::MethodCall(path_segment, _, [arg_0, arg_1, ..], _) = &expr.kind {
+        if is_expr_ty_raw_ptr(cx, arg_0) {
             if path_segment.ident.name == sym::offset {
-                return Some((&args[0], &args[1], Method::Offset));
+                return Some((arg_0, arg_1, Method::Offset));
             }
             if path_segment.ident.name == sym!(wrapping_offset) {
-                return Some((&args[0], &args[1], Method::WrappingOffset));
+                return Some((arg_0, arg_1, Method::WrappingOffset));
             }
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs
index 7b6a0894e6d..e79cd7ed4ec 100644
--- a/src/tools/clippy/clippy_lints/src/question_mark.rs
+++ b/src/tools/clippy/clippy_lints/src/question_mark.rs
@@ -97,7 +97,8 @@ impl QuestionMark {
 
     fn check_if_let_some_and_early_return_none(cx: &LateContext<'_>, expr: &Expr<'_>) {
         if_chain! {
-            if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(cx, expr);
+            if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) })
+                = higher::IfLet::hir(cx, expr);
             if Self::is_option(cx, let_expr);
 
             if let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind;
@@ -105,7 +106,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(ref block, None) = if_then.kind;
+            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);
diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
index f5e43264a5c..cfa12ef3a32 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
@@ -625,7 +625,10 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> {
                 .flat_map(HybridBitSet::iter)
                 .collect();
 
-            if ContainsRegion(self.cx.tcx).visit_ty(self.body.local_decls[*dest].ty).is_break() {
+            if ContainsRegion(self.cx.tcx)
+                .visit_ty(self.body.local_decls[*dest].ty)
+                .is_break()
+            {
                 mutable_variables.push(*dest);
             }
 
diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
index 7314bce83e0..90e3c3f4b3e 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
@@ -51,9 +51,7 @@ impl ReturnVisitor {
 
 impl<'ast> ast_visit::Visitor<'ast> for ReturnVisitor {
     fn visit_expr(&mut self, ex: &'ast ast::Expr) {
-        if let ast::ExprKind::Ret(_) = ex.kind {
-            self.found_return = true;
-        } else if let ast::ExprKind::Try(_) = ex.kind {
+        if let ast::ExprKind::Ret(_) | ast::ExprKind::Try(_) = ex.kind {
             self.found_return = true;
         }
 
diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs
index 681baed8c36..341b5a61631 100644
--- a/src/tools/clippy/clippy_lints/src/returns.rs
+++ b/src/tools/clippy/clippy_lints/src/returns.rs
@@ -206,13 +206,10 @@ fn check_final_expr<'tcx>(
         // an if/if let expr, check both exprs
         // note, if without else is going to be a type checking error anyways
         // (except for unit type functions) so we don't match it
-        ExprKind::Match(_, arms, source) => match source {
-            MatchSource::Normal => {
-                for arm in arms.iter() {
-                    check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Block);
-                }
-            },
-            _ => (),
+        ExprKind::Match(_, arms, MatchSource::Normal) => {
+            for arm in arms.iter() {
+                check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Block);
+            }
         },
         ExprKind::DropTemps(expr) => check_final_expr(cx, expr, None, RetReplacement::Empty),
         _ => (),
diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs
index 1a78a4968e5..13d8f954c44 100644
--- a/src/tools/clippy/clippy_lints/src/strings.rs
+++ b/src/tools/clippy/clippy_lints/src/strings.rs
@@ -345,9 +345,9 @@ declare_lint_pass!(StrToString => [STR_TO_STRING]);
 impl LateLintPass<'_> for StrToString {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
         if_chain! {
-            if let ExprKind::MethodCall(path, _, args, _) = &expr.kind;
+            if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind;
             if path.ident.name == sym!(to_string);
-            let ty = cx.typeck_results().expr_ty(&args[0]);
+            let ty = cx.typeck_results().expr_ty(self_arg);
             if let ty::Ref(_, ty, ..) = ty.kind();
             if *ty.kind() == ty::Str;
             then {
@@ -394,9 +394,9 @@ declare_lint_pass!(StringToString => [STRING_TO_STRING]);
 impl LateLintPass<'_> for StringToString {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
         if_chain! {
-            if let ExprKind::MethodCall(path, _, args, _) = &expr.kind;
+            if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind;
             if path.ident.name == sym!(to_string);
-            let ty = cx.typeck_results().expr_ty(&args[0]);
+            let ty = cx.typeck_results().expr_ty(self_arg);
             if is_type_diagnostic_item(cx, ty, sym::string_type);
             then {
                 span_lint_and_help(
diff --git a/src/tools/clippy/clippy_lints/src/to_string_in_display.rs b/src/tools/clippy/clippy_lints/src/to_string_in_display.rs
index b036ed9a3d2..b7414cec87c 100644
--- a/src/tools/clippy/clippy_lints/src/to_string_in_display.rs
+++ b/src/tools/clippy/clippy_lints/src/to_string_in_display.rs
@@ -92,11 +92,11 @@ impl LateLintPass<'_> for ToStringInDisplay {
         if_chain! {
             if self.in_display_impl;
             if let Some(self_hir_id) = self.self_hir_id;
-            if let ExprKind::MethodCall(path, _, args, _) = expr.kind;
+            if let ExprKind::MethodCall(path, _, [ref self_arg, ..], _) = expr.kind;
             if path.ident.name == sym!(to_string);
             if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
             if is_diag_trait_item(cx, expr_def_id, sym::ToString);
-            if path_to_local_id(&args[0], self_hir_id);
+            if path_to_local_id(self_arg, self_hir_id);
             then {
                 span_lint(
                     cx,
diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs
index 371bb8b445a..9588de8459c 100644
--- a/src/tools/clippy/clippy_lints/src/types/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/types/mod.rs
@@ -295,6 +295,7 @@ declare_clippy_lint! {
 pub struct Types {
     vec_box_size_threshold: u64,
     type_complexity_threshold: u64,
+    avoid_breaking_exported_api: bool,
 }
 
 impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, RC_MUTEX, TYPE_COMPLEXITY]);
@@ -308,19 +309,31 @@ impl<'tcx> LateLintPass<'tcx> for Types {
             false
         };
 
+        let is_exported = cx.access_levels.is_exported(cx.tcx.hir().local_def_id(id));
+
         self.check_fn_decl(
             cx,
             decl,
             CheckTyContext {
                 is_in_trait_impl,
+                is_exported,
                 ..CheckTyContext::default()
             },
         );
     }
 
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
+        let is_exported = cx.access_levels.is_exported(item.def_id);
+
         match item.kind {
-            ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _) => self.check_ty(cx, ty, CheckTyContext::default()),
+            ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _) => self.check_ty(
+                cx,
+                ty,
+                CheckTyContext {
+                    is_exported,
+                    ..CheckTyContext::default()
+                },
+            ),
             // functions, enums, structs, impls and traits are covered
             _ => (),
         }
@@ -342,15 +355,31 @@ impl<'tcx> LateLintPass<'tcx> for Types {
     }
 
     fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) {
-        self.check_ty(cx, field.ty, CheckTyContext::default());
+        let is_exported = cx.access_levels.is_exported(cx.tcx.hir().local_def_id(field.hir_id));
+
+        self.check_ty(
+            cx,
+            field.ty,
+            CheckTyContext {
+                is_exported,
+                ..CheckTyContext::default()
+            },
+        );
     }
 
-    fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) {
+    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &TraitItem<'_>) {
+        let is_exported = cx.access_levels.is_exported(item.def_id);
+
+        let context = CheckTyContext {
+            is_exported,
+            ..CheckTyContext::default()
+        };
+
         match item.kind {
             TraitItemKind::Const(ty, _) | TraitItemKind::Type(_, Some(ty)) => {
-                self.check_ty(cx, ty, CheckTyContext::default());
+                self.check_ty(cx, ty, context);
             },
-            TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, sig.decl, CheckTyContext::default()),
+            TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, sig.decl, context),
             TraitItemKind::Type(..) => (),
         }
     }
@@ -370,10 +399,11 @@ impl<'tcx> LateLintPass<'tcx> for Types {
 }
 
 impl Types {
-    pub fn new(vec_box_size_threshold: u64, type_complexity_threshold: u64) -> Self {
+    pub fn new(vec_box_size_threshold: u64, type_complexity_threshold: u64, avoid_breaking_exported_api: bool) -> Self {
         Self {
             vec_box_size_threshold,
             type_complexity_threshold,
+            avoid_breaking_exported_api,
         }
     }
 
@@ -410,17 +440,24 @@ impl Types {
                 let hir_id = hir_ty.hir_id;
                 let res = cx.qpath_res(qpath, hir_id);
                 if let Some(def_id) = res.opt_def_id() {
-                    let mut triggered = false;
-                    triggered |= box_vec::check(cx, hir_ty, qpath, def_id);
-                    triggered |= redundant_allocation::check(cx, hir_ty, qpath, def_id);
-                    triggered |= rc_buffer::check(cx, hir_ty, qpath, def_id);
-                    triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold);
-                    triggered |= option_option::check(cx, hir_ty, qpath, def_id);
-                    triggered |= linked_list::check(cx, hir_ty, def_id);
-                    triggered |= rc_mutex::check(cx, hir_ty, qpath, def_id);
-
-                    if triggered {
-                        return;
+                    if self.is_type_change_allowed(context) {
+                        // All lints that are being checked in this block are guarded by
+                        // the `avoid_breaking_exported_api` configuration. When adding a
+                        // new lint, please also add the name to the configuration documentation
+                        // in `clippy_lints::utils::conf.rs`
+
+                        let mut triggered = false;
+                        triggered |= box_vec::check(cx, hir_ty, qpath, def_id);
+                        triggered |= redundant_allocation::check(cx, hir_ty, qpath, def_id);
+                        triggered |= rc_buffer::check(cx, hir_ty, qpath, def_id);
+                        triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold);
+                        triggered |= option_option::check(cx, hir_ty, qpath, def_id);
+                        triggered |= linked_list::check(cx, hir_ty, def_id);
+                        triggered |= rc_mutex::check(cx, hir_ty, qpath, def_id);
+
+                        if triggered {
+                            return;
+                        }
                     }
                 }
                 match *qpath {
@@ -487,11 +524,21 @@ impl Types {
             _ => {},
         }
     }
+
+    /// This function checks if the type is allowed to change in the current context
+    /// based on the `avoid_breaking_exported_api` configuration
+    fn is_type_change_allowed(&self, context: CheckTyContext) -> bool {
+        !(context.is_exported && self.avoid_breaking_exported_api)
+    }
 }
 
+#[allow(clippy::struct_excessive_bools)]
 #[derive(Clone, Copy, Default)]
 struct CheckTyContext {
     is_in_trait_impl: bool,
+    /// `true` for types on local variables.
     is_local: bool,
+    /// `true` for types that are part of the public API.
+    is_exported: bool,
     is_nested_call: bool,
 }
diff --git a/src/tools/clippy/clippy_lints/src/types/rc_mutex.rs b/src/tools/clippy/clippy_lints/src/types/rc_mutex.rs
index bd7a0ee6408..12db7afb81c 100644
--- a/src/tools/clippy/clippy_lints/src/types/rc_mutex.rs
+++ b/src/tools/clippy/clippy_lints/src/types/rc_mutex.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint;
+use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::is_ty_param_diagnostic_item;
 use if_chain::if_chain;
 use rustc_hir::{self as hir, def_id::DefId, QPath};
@@ -11,13 +11,14 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
     if_chain! {
         if cx.tcx.is_diagnostic_item(sym::Rc, def_id) ;
         if let Some(_) = is_ty_param_diagnostic_item(cx, qpath, sym!(mutex_type)) ;
-
-        then{
-            span_lint(
+        then {
+            span_lint_and_help(
                 cx,
                 RC_MUTEX,
                 hir_ty.span,
-                "found `Rc<Mutex<_>>`. Consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead",
+                "usage of `Rc<Mutex<_>>`",
+                None,
+                "consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead",
             );
             return true;
         }
diff --git a/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs b/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs
index 8e83dcbf704..ac7bdd6a1eb 100644
--- a/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs
+++ b/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs
@@ -54,7 +54,13 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
         _ => return false,
     };
     let inner_span = match get_qpath_generic_tys(inner_qpath).next() {
-        Some(ty) => ty.span,
+        Some(ty) => {
+            // Box<Box<dyn T>> is smaller than Box<dyn T> because of wide pointers
+            if matches!(ty.kind, TyKind::TraitObject(..)) {
+                return false;
+            }
+            ty.span
+        },
         None => return false,
     };
     if inner_sym == outer_sym {
diff --git a/src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs b/src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs
index 47571e608c7..09570616593 100644
--- a/src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs
+++ b/src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs
@@ -37,8 +37,8 @@ declare_lint_pass!(UndroppedManuallyDrops => [UNDROPPED_MANUALLY_DROPS]);
 
 impl LateLintPass<'tcx> for UndroppedManuallyDrops {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if let Some(args) = match_function_call(cx, expr, &paths::DROP) {
-            let ty = cx.typeck_results().expr_ty(&args[0]);
+        if let Some([arg_0, ..]) = match_function_call(cx, expr, &paths::DROP) {
+            let ty = cx.typeck_results().expr_ty(arg_0);
             if is_type_lang_item(cx, ty, lang_items::LangItem::ManuallyDrop) {
                 span_lint_and_help(
                     cx,
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs
index 97b1b2dae3c..61670fe124e 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs
@@ -218,7 +218,10 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
 
 fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     let ty = cx.typeck_results().expr_ty(expr);
-    matches!(ty.kind(), ty::Ref(..)) || ty.walk(cx.tcx).any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
+    matches!(ty.kind(), ty::Ref(..))
+        || ty
+            .walk(cx.tcx)
+            .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
 }
 
 impl LateLintPass<'_> for UnnecessarySortBy {
diff --git a/src/tools/clippy/clippy_lints/src/unused_async.rs b/src/tools/clippy/clippy_lints/src/unused_async.rs
index 3a6a07c5226..f4808682b69 100644
--- a/src/tools/clippy/clippy_lints/src/unused_async.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_async.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor};
-use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnHeader, HirId, IsAsync, Item, ItemKind, YieldSource};
+use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnHeader, HirId, IsAsync, YieldSource};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::map::Map;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -57,11 +57,6 @@ impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> {
 }
 
 impl<'tcx> LateLintPass<'tcx> for UnusedAsync {
-    fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
-        if let ItemKind::Trait(..) = item.kind {
-            return;
-        }
-    }
     fn check_fn(
         &mut self,
         cx: &LateContext<'tcx>,
diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
index 82bc4a6d153..031b182bd2f 100644
--- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
@@ -45,20 +45,20 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
 
         match expr.kind {
             hir::ExprKind::Match(res, _, _) if is_try(cx, expr).is_some() => {
-                if let hir::ExprKind::Call(func, args) = res.kind {
+                if let hir::ExprKind::Call(func, [ref arg_0, ..]) = res.kind {
                     if matches!(
                         func.kind,
                         hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, _))
                     ) {
-                        check_map_error(cx, &args[0], expr);
+                        check_map_error(cx, arg_0, expr);
                     }
                 } else {
                     check_map_error(cx, res, expr);
                 }
             },
-            hir::ExprKind::MethodCall(path, _, args, _) => match &*path.ident.as_str() {
+            hir::ExprKind::MethodCall(path, _, [ref arg_0, ..], _) => match &*path.ident.as_str() {
                 "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => {
-                    check_map_error(cx, &args[0], expr);
+                    check_map_error(cx, arg_0, expr);
                 },
                 _ => (),
             },
diff --git a/src/tools/clippy/clippy_lints/src/unused_self.rs b/src/tools/clippy/clippy_lints/src/unused_self.rs
index 658ac81f6ea..e7e249c79a2 100644
--- a/src/tools/clippy/clippy_lints/src/unused_self.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_self.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::visitors::LocalUsedVisitor;
+use clippy_utils::visitors::is_local_used;
 use if_chain::if_chain;
 use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -50,8 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf {
             if let ImplItemKind::Fn(.., body_id) = &impl_item.kind;
             let body = cx.tcx.hir().body(*body_id);
             if let [self_param, ..] = body.params;
-            let self_hir_id = self_param.pat.hir_id;
-            if !LocalUsedVisitor::new(cx, self_hir_id).check_body(body);
+            if !is_local_used(cx, body, self_param.pat.hir_id);
             then {
                 span_lint_and_help(
                     cx,
diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs
index bffd9f3612b..b2ab300c2e9 100644
--- a/src/tools/clippy/clippy_lints/src/unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/unwrap.rs
@@ -1,10 +1,11 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{differing_macro_contexts, usage::is_potentially_mutated};
+use clippy_utils::{differing_macro_contexts, path_to_local, usage::is_potentially_mutated};
 use if_chain::if_chain;
+use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor};
-use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Path, QPath, UnOp};
+use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, PathSegment, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::map::Map;
 use rustc_middle::lint::in_external_macro;
@@ -74,26 +75,61 @@ struct UnwrappableVariablesVisitor<'a, 'tcx> {
     unwrappables: Vec<UnwrapInfo<'tcx>>,
     cx: &'a LateContext<'tcx>,
 }
+
+/// What kind of unwrappable this is.
+#[derive(Copy, Clone, Debug)]
+enum UnwrappableKind {
+    Option,
+    Result,
+}
+
+impl UnwrappableKind {
+    fn success_variant_pattern(self) -> &'static str {
+        match self {
+            UnwrappableKind::Option => "Some(..)",
+            UnwrappableKind::Result => "Ok(..)",
+        }
+    }
+
+    fn error_variant_pattern(self) -> &'static str {
+        match self {
+            UnwrappableKind::Option => "None",
+            UnwrappableKind::Result => "Err(..)",
+        }
+    }
+}
+
 /// Contains information about whether a variable can be unwrapped.
 #[derive(Copy, Clone, Debug)]
 struct UnwrapInfo<'tcx> {
     /// The variable that is checked
-    ident: &'tcx Path<'tcx>,
+    local_id: HirId,
+    /// The if itself
+    if_expr: &'tcx Expr<'tcx>,
     /// The check, like `x.is_ok()`
     check: &'tcx Expr<'tcx>,
+    /// The check's name, like `is_ok`
+    check_name: &'tcx PathSegment<'tcx>,
     /// The branch where the check takes place, like `if x.is_ok() { .. }`
     branch: &'tcx Expr<'tcx>,
     /// Whether `is_some()` or `is_ok()` was called (as opposed to `is_err()` or `is_none()`).
     safe_to_unwrap: bool,
+    /// What kind of unwrappable this is.
+    kind: UnwrappableKind,
+    /// If the check is the entire condition (`if x.is_ok()`) or only a part of it (`foo() &&
+    /// x.is_ok()`)
+    is_entire_condition: bool,
 }
 
 /// Collects the information about unwrappable variables from an if condition
 /// The `invert` argument tells us whether the condition is negated.
 fn collect_unwrap_info<'tcx>(
     cx: &LateContext<'tcx>,
+    if_expr: &'tcx Expr<'_>,
     expr: &'tcx Expr<'_>,
     branch: &'tcx Expr<'_>,
     invert: bool,
+    is_entire_condition: bool,
 ) -> Vec<UnwrapInfo<'tcx>> {
     fn is_relevant_option_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool {
         is_type_diagnostic_item(cx, ty, sym::option_type) && ["is_some", "is_none"].contains(&method_name)
@@ -106,18 +142,18 @@ fn collect_unwrap_info<'tcx>(
     if let ExprKind::Binary(op, left, right) = &expr.kind {
         match (invert, op.node) {
             (false, BinOpKind::And | BinOpKind::BitAnd) | (true, BinOpKind::Or | BinOpKind::BitOr) => {
-                let mut unwrap_info = collect_unwrap_info(cx, left, branch, invert);
-                unwrap_info.append(&mut collect_unwrap_info(cx, right, branch, invert));
+                let mut unwrap_info = collect_unwrap_info(cx, if_expr, left, branch, invert, false);
+                unwrap_info.append(&mut collect_unwrap_info(cx, if_expr, right, branch, invert, false));
                 return unwrap_info;
             },
             _ => (),
         }
     } else if let ExprKind::Unary(UnOp::Not, expr) = &expr.kind {
-        return collect_unwrap_info(cx, expr, branch, !invert);
+        return collect_unwrap_info(cx, if_expr, expr, branch, !invert, false);
     } else {
         if_chain! {
             if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind;
-            if let ExprKind::Path(QPath::Resolved(None, path)) = &args[0].kind;
+            if let Some(local_id) = path_to_local(&args[0]);
             let ty = cx.typeck_results().expr_ty(&args[0]);
             let name = method_name.ident.as_str();
             if is_relevant_option_call(cx, ty, &name) || is_relevant_result_call(cx, ty, &name);
@@ -129,7 +165,24 @@ fn collect_unwrap_info<'tcx>(
                     _ => unreachable!(),
                 };
                 let safe_to_unwrap = unwrappable != invert;
-                return vec![UnwrapInfo { ident: path, check: expr, branch, safe_to_unwrap }];
+                let kind = if is_type_diagnostic_item(cx, ty, sym::option_type) {
+                    UnwrappableKind::Option
+                } else {
+                    UnwrappableKind::Result
+                };
+
+                return vec![
+                    UnwrapInfo {
+                        local_id,
+                        if_expr,
+                        check: expr,
+                        check_name: method_name,
+                        branch,
+                        safe_to_unwrap,
+                        kind,
+                        is_entire_condition,
+                    }
+                ]
             }
         }
     }
@@ -137,11 +190,17 @@ fn collect_unwrap_info<'tcx>(
 }
 
 impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> {
-    fn visit_branch(&mut self, cond: &'tcx Expr<'_>, branch: &'tcx Expr<'_>, else_branch: bool) {
+    fn visit_branch(
+        &mut self,
+        if_expr: &'tcx Expr<'_>,
+        cond: &'tcx Expr<'_>,
+        branch: &'tcx Expr<'_>,
+        else_branch: bool,
+    ) {
         let prev_len = self.unwrappables.len();
-        for unwrap_info in collect_unwrap_info(self.cx, cond, branch, else_branch) {
-            if is_potentially_mutated(unwrap_info.ident, cond, self.cx)
-                || is_potentially_mutated(unwrap_info.ident, branch, self.cx)
+        for unwrap_info in collect_unwrap_info(self.cx, if_expr, cond, branch, else_branch, true) {
+            if is_potentially_mutated(unwrap_info.local_id, cond, self.cx)
+                || is_potentially_mutated(unwrap_info.local_id, branch, self.cx)
             {
                 // if the variable is mutated, we don't know whether it can be unwrapped:
                 continue;
@@ -163,32 +222,62 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
         }
         if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr) {
             walk_expr(self, cond);
-            self.visit_branch(cond, then, false);
+            self.visit_branch(expr, cond, then, false);
             if let Some(else_inner) = r#else {
-                self.visit_branch(cond, else_inner, true);
+                self.visit_branch(expr, cond, else_inner, true);
             }
         } else {
             // find `unwrap[_err]()` calls:
             if_chain! {
                 if let ExprKind::MethodCall(method_name, _, args, _) = expr.kind;
-                if let ExprKind::Path(QPath::Resolved(None, path)) = args[0].kind;
-                if [sym::unwrap, sym!(unwrap_err)].contains(&method_name.ident.name);
-                let call_to_unwrap = method_name.ident.name == sym::unwrap;
+                if let Some(id) = path_to_local(&args[0]);
+                if [sym::unwrap, sym::expect, sym!(unwrap_err)].contains(&method_name.ident.name);
+                let call_to_unwrap = [sym::unwrap, sym::expect].contains(&method_name.ident.name);
                 if let Some(unwrappable) = self.unwrappables.iter()
-                    .find(|u| u.ident.res == path.res);
+                    .find(|u| u.local_id == id);
                 // Span contexts should not differ with the conditional branch
                 if !differing_macro_contexts(unwrappable.branch.span, expr.span);
                 if !differing_macro_contexts(unwrappable.branch.span, unwrappable.check.span);
                 then {
                     if call_to_unwrap == unwrappable.safe_to_unwrap {
+                        let is_entire_condition = unwrappable.is_entire_condition;
+                        let unwrappable_variable_name = self.cx.tcx.hir().name(unwrappable.local_id);
+                        let suggested_pattern = if call_to_unwrap {
+                            unwrappable.kind.success_variant_pattern()
+                        } else {
+                            unwrappable.kind.error_variant_pattern()
+                        };
+
                         span_lint_and_then(
                             self.cx,
                             UNNECESSARY_UNWRAP,
                             expr.span,
-                            &format!("you checked before that `{}()` cannot fail, \
-                            instead of checking and unwrapping, it's better to use `if let` or `match`",
-                            method_name.ident.name),
-                            |diag| { diag.span_label(unwrappable.check.span, "the check is happening here"); },
+                            &format!(
+                                "called `{}` on `{}` after checking its variant with `{}`",
+                                method_name.ident.name,
+                                unwrappable_variable_name,
+                                unwrappable.check_name.ident.as_str(),
+                            ),
+                            |diag| {
+                                if is_entire_condition {
+                                    diag.span_suggestion(
+                                        unwrappable.check.span.with_lo(unwrappable.if_expr.span.lo()),
+                                        "try",
+                                        format!(
+                                            "if let {} = {}",
+                                            suggested_pattern,
+                                            unwrappable_variable_name,
+                                        ),
+                                        // We don't track how the unwrapped value is used inside the
+                                        // block or suggest deleting the unwrap, so we can't offer a
+                                        // fixable solution.
+                                        Applicability::Unspecified,
+                                    );
+                                } else {
+                                    diag.span_label(unwrappable.check.span, "the check is happening here");
+                                    diag.help("try using `if let` or `match`");
+                                }
+                            },
                         );
                     } else {
                         span_lint_and_then(
diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs
index c192f9094a8..8651fa6fcf9 100644
--- a/src/tools/clippy/clippy_lints/src/utils/conf.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs
@@ -31,9 +31,6 @@ impl TryConf {
     }
 }
 
-/// Note that the configuration parsing currently doesn't support documentation that will
-/// that spans over several lines. This will be possible with the new implementation
-/// See (rust-clippy#7172)
 macro_rules! define_Conf {
     ($(
         $(#[doc = $doc:literal])+
@@ -130,13 +127,12 @@ macro_rules! define_Conf {
     };
 }
 
-// N.B., this macro is parsed by util/lintlib.py
 define_Conf! {
-    /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION.
+    /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_VEC, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
     ///
     /// Suppress lints whenever the suggested change would cause breakage for other crates.
     (avoid_breaking_exported_api: bool = true),
-    /// Lint: MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE.
+    /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT.
     ///
     /// The minimum rust version that the project supports
     (msrv: Option<String> = None),
diff --git a/src/tools/clippy/clippy_lints/src/utils/inspector.rs b/src/tools/clippy/clippy_lints/src/utils/inspector.rs
index e97983a2e14..43590cc7862 100644
--- a/src/tools/clippy/clippy_lints/src/utils/inspector.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/inspector.rs
@@ -142,7 +142,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) {
                 print_expr(cx, arg, indent + 1);
             }
         },
-        hir::ExprKind::Let(ref pat, ref expr, _) => {
+        hir::ExprKind::Let(pat, expr, _) => {
             print_pat(cx, pat, indent + 1);
             print_expr(cx, expr, indent + 1);
         },
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
index d660008e7d1..756c33d70c2 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
@@ -1,5 +1,6 @@
 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::{
@@ -17,8 +18,8 @@ use rustc_hir::def_id::DefId;
 use rustc_hir::hir_id::CRATE_HIR_ID;
 use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
 use rustc_hir::{
-    BinOpKind, Block, Crate, Expr, ExprKind, HirId, Item, Local, MatchSource, MutTy, Mutability, Node, Path, Stmt,
-    StmtKind, Ty, TyKind, UnOp,
+    BinOpKind, Block, Crate, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, Ty,
+    TyKind, UnOp,
 };
 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 use rustc_middle::hir::map::Map;
@@ -503,10 +504,10 @@ impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions {
         }
 
         if_chain! {
-            if let ExprKind::MethodCall(path, _, args, _) = expr.kind;
+            if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind;
             let fn_name = path.ident;
             if let Some(sugg) = self.map.get(&*fn_name.as_str());
-            let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
+            let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
             if match_type(cx, ty, &paths::EARLY_CONTEXT)
                 || match_type(cx, ty, &paths::LATE_CONTEXT);
             then {
@@ -1106,16 +1107,10 @@ impl<'tcx> LateLintPass<'tcx> for IfChainStyle {
     }
 
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        let (cond, then, els) = match expr.kind {
-            ExprKind::If(cond, then, els) => (Some(cond), then, els.is_some()),
-            ExprKind::Match(
-                _,
-                [arm, ..],
-                MatchSource::IfLetDesugar {
-                    contains_else_clause: els,
-                },
-            ) => (None, arm.body, els),
-            _ => return,
+        let (cond, then, els) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) {
+            (cond, then, r#else.is_some())
+        } else {
+            return;
         };
         let then_block = match then.kind {
             ExprKind::Block(block, _) => block,
@@ -1131,7 +1126,6 @@ impl<'tcx> LateLintPass<'tcx> for IfChainStyle {
         };
         // check for `if a && b;`
         if_chain! {
-            if let Some(cond) = cond;
             if let ExprKind::Binary(op, _, _) = cond.kind;
             if op.node == BinOpKind::And;
             if cx.sess().source_map().is_multiline(cond.span);
@@ -1166,9 +1160,7 @@ fn check_nested_if_chains(
         _ => return,
     };
     if_chain! {
-        if matches!(tail.kind,
-            ExprKind::If(_, _, None)
-            | ExprKind::Match(.., MatchSource::IfLetDesugar { contains_else_clause: false }));
+        if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail);
         let sm = cx.sess().source_map();
         if head
             .iter()
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
index a48a5385083..188d0419c39 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
@@ -82,7 +82,7 @@ This lint has the following configuration variables:
 /// `default`
 macro_rules! CONFIGURATION_VALUE_TEMPLATE {
     () => {
-        "* {name}: `{ty}`: {doc} (defaults to `{default}`)\n"
+        "* `{name}`: `{ty}`: {doc} (defaults to `{default}`)\n"
     };
 }
 
@@ -786,8 +786,6 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> {
             }
         };
 
-        // TODO xFrednet 2021-03-01: support function arguments?
-
         intravisit::walk_expr(self, expr);
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs
index e76d5f81c96..d124d948b5e 100644
--- a/src/tools/clippy/clippy_lints/src/vec.rs
+++ b/src/tools/clippy/clippy_lints/src/vec.rs
@@ -49,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
         if_chain! {
             if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(expr).kind();
             if let ty::Slice(..) = ty.kind();
-            if let ExprKind::AddrOf(BorrowKind::Ref, mutability, ref addressee) = expr.kind;
+            if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind;
             if let Some(vec_args) = higher::VecArgs::hir(cx, addressee);
             then {
                 self.check_vec_macro(cx, &vec_args, mutability, expr.span);
diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml
index c65b2958ec5..4c038a99795 100644
--- a/src/tools/clippy/clippy_utils/Cargo.toml
+++ b/src/tools/clippy/clippy_utils/Cargo.toml
@@ -1,15 +1,11 @@
 [package]
 name = "clippy_utils"
-version = "0.1.56"
+version = "0.1.57"
 edition = "2018"
 publish = false
 
 [dependencies]
 if_chain = "1.0.0"
-itertools = "0.9"
-regex-syntax = "0.6"
-serde = { version = "1.0", features = ["derive"] }
-unicode-normalization = "0.1"
 rustc-semver="1.1.0"
 
 [features]
diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs
index 71cfa196fc3..9302e5c21fa 100644
--- a/src/tools/clippy/clippy_utils/src/diagnostics.rs
+++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs
@@ -21,7 +21,7 @@ fn docs_link(diag: &mut DiagnosticBuilder<'_>, lint: &'static Lint) {
                 "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{}",
                 &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| {
                     // extract just major + minor version and ignore patch versions
-                    format!("rust-{}", n.rsplitn(2, '.').nth(1).unwrap())
+                    format!("rust-{}", n.rsplit_once('.').unwrap().1)
                 }),
                 lint
             ));
diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs
index c06b894a738..05a4a014319 100644
--- a/src/tools/clippy/clippy_utils/src/higher.rs
+++ b/src/tools/clippy/clippy_utils/src/higher.rs
@@ -6,33 +6,38 @@ use crate::{is_expn_of, match_def_path, paths};
 use if_chain::if_chain;
 use rustc_ast::ast::{self, LitKind};
 use rustc_hir as hir;
-use rustc_hir::{Block, BorrowKind, Expr, ExprKind, LoopSource, Node, Pat, StmtKind, UnOp};
+use rustc_hir::{Arm, Block, BorrowKind, Expr, ExprKind, LoopSource, MatchSource, Node, Pat, StmtKind, UnOp};
 use rustc_lint::LateContext;
 use rustc_span::{sym, ExpnKind, Span, Symbol};
 
 /// The essential nodes of a desugared for loop as well as the entire span:
 /// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`.
 pub struct ForLoop<'tcx> {
+    /// `for` loop item
     pub pat: &'tcx hir::Pat<'tcx>,
+    /// `IntoIterator` argument
     pub arg: &'tcx hir::Expr<'tcx>,
+    /// `for` loop body
     pub body: &'tcx hir::Expr<'tcx>,
+    /// entire `for` loop span
     pub span: Span,
 }
 
 impl<'tcx> ForLoop<'tcx> {
     #[inline]
+    /// Parses a desugared `for` loop
     pub fn hir(expr: &Expr<'tcx>) -> Option<Self> {
         if_chain! {
-            if let hir::ExprKind::Match(ref iterexpr, ref arms, hir::MatchSource::ForLoopDesugar) = expr.kind;
+            if let hir::ExprKind::Match(iterexpr, arms, hir::MatchSource::ForLoopDesugar) = expr.kind;
             if let Some(first_arm) = arms.get(0);
-            if let hir::ExprKind::Call(_, ref iterargs) = iterexpr.kind;
+            if let hir::ExprKind::Call(_, iterargs) = iterexpr.kind;
             if let Some(first_arg) = iterargs.get(0);
             if iterargs.len() == 1 && arms.len() == 1 && first_arm.guard.is_none();
-            if let hir::ExprKind::Loop(ref block, ..) = first_arm.body.kind;
+            if let hir::ExprKind::Loop(block, ..) = first_arm.body.kind;
             if block.expr.is_none();
             if let [ _, _, ref let_stmt, ref body ] = *block.stmts;
-            if let hir::StmtKind::Local(ref local) = let_stmt.kind;
-            if let hir::StmtKind::Expr(ref body_expr) = body.kind;
+            if let hir::StmtKind::Local(local) = let_stmt.kind;
+            if let hir::StmtKind::Expr(body_expr) = body.kind;
             then {
                 return Some(Self {
                     pat: &*local.pat,
@@ -46,14 +51,19 @@ impl<'tcx> ForLoop<'tcx> {
     }
 }
 
+/// An `if` expression without `DropTemps`
 pub struct If<'hir> {
+    /// `if` condition
     pub cond: &'hir Expr<'hir>,
-    pub r#else: Option<&'hir Expr<'hir>>,
+    /// `if` then expression
     pub then: &'hir Expr<'hir>,
+    /// `else` expression
+    pub r#else: Option<&'hir Expr<'hir>>,
 }
 
 impl<'hir> If<'hir> {
     #[inline]
+    /// Parses an `if` expression
     pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
         if let ExprKind::If(
             Expr {
@@ -64,21 +74,27 @@ impl<'hir> If<'hir> {
             r#else,
         ) = expr.kind
         {
-            Some(Self { cond, r#else, then })
+            Some(Self { cond, then, r#else })
         } else {
             None
         }
     }
 }
 
+/// An `if let` expression
 pub struct IfLet<'hir> {
+    /// `if let` pattern
     pub let_pat: &'hir Pat<'hir>,
+    /// `if let` scrutinee
     pub let_expr: &'hir Expr<'hir>,
+    /// `if let` then expression
     pub if_then: &'hir Expr<'hir>,
+    /// `if let` else expression
     pub if_else: Option<&'hir Expr<'hir>>,
 }
 
 impl<'hir> IfLet<'hir> {
+    /// Parses an `if let` expression
     pub fn hir(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option<Self> {
         if let ExprKind::If(
             Expr {
@@ -92,7 +108,14 @@ impl<'hir> IfLet<'hir> {
             let hir = cx.tcx.hir();
             let mut iter = hir.parent_iter(expr.hir_id);
             if let Some((_, Node::Block(Block { stmts: [], .. }))) = iter.next() {
-                if let Some((_, Node::Expr(Expr { kind: ExprKind::Loop(_, _, LoopSource::While, _), .. }))) = iter.next() {
+                if let Some((
+                    _,
+                    Node::Expr(Expr {
+                        kind: ExprKind::Loop(_, _, LoopSource::While, _),
+                        ..
+                    }),
+                )) = iter.next()
+                {
                     // while loop desugar
                     return None;
                 }
@@ -108,14 +131,49 @@ impl<'hir> IfLet<'hir> {
     }
 }
 
+/// An `if let` or `match` expression. Useful for lints that trigger on one or the other.
+pub enum IfLetOrMatch<'hir> {
+    /// Any `match` expression
+    Match(&'hir Expr<'hir>, &'hir [Arm<'hir>], MatchSource),
+    /// scrutinee, pattern, then block, else block
+    IfLet(
+        &'hir Expr<'hir>,
+        &'hir Pat<'hir>,
+        &'hir Expr<'hir>,
+        Option<&'hir Expr<'hir>>,
+    ),
+}
+
+impl<'hir> IfLetOrMatch<'hir> {
+    /// Parses an `if let` or `match` expression
+    pub fn parse(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option<Self> {
+        match expr.kind {
+            ExprKind::Match(expr, arms, source) => Some(Self::Match(expr, arms, source)),
+            _ => IfLet::hir(cx, expr).map(
+                |IfLet {
+                     let_expr,
+                     let_pat,
+                     if_then,
+                     if_else,
+                 }| { Self::IfLet(let_expr, let_pat, if_then, if_else) },
+            ),
+        }
+    }
+}
+
+/// An `if` or `if let` expression
 pub struct IfOrIfLet<'hir> {
+    /// `if` condition that is maybe a `let` expression
     pub cond: &'hir Expr<'hir>,
-    pub r#else: Option<&'hir Expr<'hir>>,
+    /// `if` then expression
     pub then: &'hir Expr<'hir>,
+    /// `else` expression
+    pub r#else: Option<&'hir Expr<'hir>>,
 }
 
 impl<'hir> IfOrIfLet<'hir> {
     #[inline]
+    /// Parses an `if` or `if let` expression
     pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
         if let ExprKind::If(cond, then, r#else) = expr.kind {
             if let ExprKind::DropTemps(new_cond) = cond.kind {
@@ -126,7 +184,7 @@ impl<'hir> IfOrIfLet<'hir> {
                 });
             }
             if let ExprKind::Let(..) = cond.kind {
-                return Some(Self { cond, r#else, then });
+                return Some(Self { cond, then, r#else });
             }
         }
         None
@@ -155,7 +213,7 @@ impl<'a> Range<'a> {
         }
 
         match expr.kind {
-            hir::ExprKind::Call(ref path, ref args)
+            hir::ExprKind::Call(path, args)
                 if matches!(
                     path.kind,
                     hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, _))
@@ -167,7 +225,7 @@ impl<'a> Range<'a> {
                     limits: ast::RangeLimits::Closed,
                 })
             },
-            hir::ExprKind::Struct(ref path, ref fields, None) => match path {
+            hir::ExprKind::Struct(path, fields, None) => match &path {
                 hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range {
                     start: None,
                     end: None,
@@ -213,7 +271,7 @@ impl<'a> VecArgs<'a> {
     /// from `vec!`.
     pub fn hir(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> Option<VecArgs<'a>> {
         if_chain! {
-            if let hir::ExprKind::Call(ref fun, ref args) = expr.kind;
+            if let hir::ExprKind::Call(fun, args) = expr.kind;
             if let hir::ExprKind::Path(ref qpath) = fun.kind;
             if is_expn_of(fun.span, "vec").is_some();
             if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
@@ -225,10 +283,10 @@ impl<'a> VecArgs<'a> {
                 else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 {
                     // `vec![a, b, c]` case
                     if_chain! {
-                        if let hir::ExprKind::Box(ref boxed) = args[0].kind;
-                        if let hir::ExprKind::Array(ref args) = boxed.kind;
+                        if let hir::ExprKind::Box(boxed) = args[0].kind;
+                        if let hir::ExprKind::Array(args) = boxed.kind;
                         then {
-                            return Some(VecArgs::Vec(&*args));
+                            return Some(VecArgs::Vec(args));
                         }
                     }
 
@@ -247,14 +305,17 @@ impl<'a> VecArgs<'a> {
     }
 }
 
+/// A desugared `while` loop
 pub struct While<'hir> {
-    pub if_cond: &'hir Expr<'hir>,
-    pub if_then: &'hir Expr<'hir>,
-    pub if_else: Option<&'hir Expr<'hir>>,
+    /// `while` loop condition
+    pub condition: &'hir Expr<'hir>,
+    /// `while` loop body
+    pub body: &'hir Expr<'hir>,
 }
 
 impl<'hir> While<'hir> {
     #[inline]
+    /// Parses a desugared `while` loop
     pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
         if let ExprKind::Loop(
             Block {
@@ -263,11 +324,11 @@ impl<'hir> While<'hir> {
                         kind:
                             ExprKind::If(
                                 Expr {
-                                    kind: ExprKind::DropTemps(if_cond),
+                                    kind: ExprKind::DropTemps(condition),
                                     ..
                                 },
-                                if_then,
-                                if_else_ref,
+                                body,
+                                _,
                             ),
                         ..
                     }),
@@ -278,59 +339,53 @@ impl<'hir> While<'hir> {
             _,
         ) = expr.kind
         {
-            let if_else = *if_else_ref;
-            return Some(Self {
-                if_cond,
-                if_then,
-                if_else,
-            });
+            return Some(Self { condition, body });
         }
         None
     }
 }
 
+/// A desugared `while let` loop
 pub struct WhileLet<'hir> {
-    pub if_expr: &'hir Expr<'hir>,
+    /// `while let` loop item pattern
     pub let_pat: &'hir Pat<'hir>,
+    /// `while let` loop scrutinee
     pub let_expr: &'hir Expr<'hir>,
+    /// `while let` loop body
     pub if_then: &'hir Expr<'hir>,
-    pub if_else: Option<&'hir Expr<'hir>>,
 }
 
 impl<'hir> WhileLet<'hir> {
     #[inline]
+    /// Parses a desugared `while let` loop
     pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
         if let ExprKind::Loop(
             Block {
-                expr: Some(if_expr), ..
+                expr:
+                    Some(Expr {
+                        kind:
+                            ExprKind::If(
+                                Expr {
+                                    kind: ExprKind::Let(let_pat, let_expr, _),
+                                    ..
+                                },
+                                if_then,
+                                _,
+                            ),
+                        ..
+                    }),
+                ..
             },
             _,
             LoopSource::While,
             _,
         ) = expr.kind
         {
-            if let Expr {
-                kind:
-                    ExprKind::If(
-                        Expr {
-                            kind: ExprKind::Let(let_pat, let_expr, _),
-                            ..
-                        },
-                        if_then,
-                        if_else_ref,
-                    ),
-                ..
-            } = if_expr
-            {
-                let if_else = *if_else_ref;
-                return Some(Self {
-                    if_expr,
-                    let_pat,
-                    let_expr,
-                    if_then,
-                    if_else,
-                });
-            }
+            return Some(Self {
+                let_pat,
+                let_expr,
+                if_then,
+            });
         }
         None
     }
@@ -532,7 +587,7 @@ pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool {
     // }
     // ```
     if_chain! {
-        if let Some(ref expr) = local.init;
+        if let Some(expr) = local.init;
         if let hir::ExprKind::Match(_, _, hir::MatchSource::ForLoopDesugar) = expr.kind;
         then {
             return true;
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index a44f2df2fd6..6e9a1de21ee 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -232,9 +232,7 @@ impl HirEqInterExpr<'_, '_, '_> {
             (&ExprKind::If(lc, lt, ref le), &ExprKind::If(rc, rt, ref re)) => {
                 self.eq_expr(lc, rc) && self.eq_expr(&**lt, &**rt) && both(le, re, |l, r| self.eq_expr(l, r))
             },
-            (&ExprKind::Let(ref lp, ref le, _), &ExprKind::Let(ref rp, ref re, _)) => {
-                self.eq_pat(lp, rp) && self.eq_expr(le, re)
-            },
+            (&ExprKind::Let(lp, le, _), &ExprKind::Let(rp, re, _)) => self.eq_pat(lp, rp) && self.eq_expr(le, re),
             (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node,
             (&ExprKind::Loop(lb, ref ll, ref lls, _), &ExprKind::Loop(rb, ref rl, ref rls, _)) => {
                 lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name)
@@ -668,7 +666,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                     }
                 }
             },
-            ExprKind::Let(ref pat, ref expr, _) => {
+            ExprKind::Let(pat, expr, _) => {
                 self.hash_expr(expr);
                 self.hash_pat(pat);
             },
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index ddff1686ba2..757485d19d2 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -2,6 +2,7 @@
 #![feature(in_band_lifetimes)]
 #![feature(iter_zip)]
 #![feature(rustc_private)]
+#![feature(control_flow_enum)]
 #![recursion_limit = "512"]
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
 #![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
@@ -62,23 +63,27 @@ use std::collections::hash_map::Entry;
 use std::hash::BuildHasherDefault;
 
 use if_chain::if_chain;
-use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind};
+use rustc_ast::ast::{self, Attribute, LitKind};
 use rustc_data_structures::unhash::UnhashMap;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
+use rustc_hir::hir_id::{HirIdMap, HirIdSet};
 use rustc_hir::intravisit::{self, walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor};
-use rustc_hir::LangItem::{ResultErr, ResultOk};
+use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
 use rustc_hir::{
     def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl,
-    ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path,
-    PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp,
+    ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node, Param, Pat,
+    PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp,
 };
 use rustc_lint::{LateContext, Level, Lint, LintContext};
 use rustc_middle::hir::exports::Export;
 use rustc_middle::hir::map::Map;
+use rustc_middle::hir::place::PlaceBase;
 use rustc_middle::ty as rustc_ty;
-use rustc_middle::ty::{layout::IntegerExt, DefIdTree, Ty, TyCtxt, TypeFoldable};
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
+use rustc_middle::ty::binding::BindingMode;
+use rustc_middle::ty::{layout::IntegerExt, BorrowKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeFoldable, UpvarCapture};
 use rustc_semver::RustcVersion;
 use rustc_session::Session;
 use rustc_span::hygiene::{ExpnKind, MacroKind};
@@ -89,7 +94,7 @@ use rustc_span::{Span, DUMMY_SP};
 use rustc_target::abi::Integer;
 
 use crate::consts::{constant, Constant};
-use crate::ty::{can_partially_move_ty, is_recursively_primitive_type};
+use crate::ty::{can_partially_move_ty, is_copy, is_recursively_primitive_type};
 
 pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
     if let Ok(version) = RustcVersion::parse(msrv) {
@@ -255,7 +260,17 @@ pub fn in_macro(span: Span) -> bool {
 }
 
 pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
-    matches!(expr.kind, ExprKind::Block(Block { stmts: [], expr: None, .. }, _) | ExprKind::Tup([]))
+    matches!(
+        expr.kind,
+        ExprKind::Block(
+            Block {
+                stmts: [],
+                expr: None,
+                ..
+            },
+            _
+        ) | ExprKind::Tup([])
+    )
 }
 
 /// Checks if given pattern is a wildcard (`_`)
@@ -629,12 +644,106 @@ pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -
     false
 }
 
+/// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent"
+/// constructor from the std library
+fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
+    let std_types_symbols = &[
+        sym::string_type,
+        sym::vec_type,
+        sym::vecdeque_type,
+        sym::LinkedList,
+        sym::hashmap_type,
+        sym::BTreeMap,
+        sym::hashset_type,
+        sym::BTreeSet,
+        sym::BinaryHeap,
+    ];
+
+    if let QPath::TypeRelative(_, method) = path {
+        if method.ident.name == sym::new {
+            if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
+                if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
+                    return std_types_symbols
+                        .iter()
+                        .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did));
+                }
+            }
+        }
+    }
+    false
+}
+
+/// Returns true if the expr is equal to `Default::default()` of it's type when evaluated.
+/// It doesn't cover all cases, for example indirect function calls (some of std
+/// functions are supported) but it is the best we have.
+pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+    match &e.kind {
+        ExprKind::Lit(lit) => match lit.node {
+            LitKind::Bool(false) | LitKind::Int(0, _) => true,
+            LitKind::Str(s, _) => s.is_empty(),
+            _ => false,
+        },
+        ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
+        ExprKind::Repeat(x, _) => is_default_equivalent(cx, x),
+        ExprKind::Call(repl_func, _) => if_chain! {
+            if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
+            if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
+            if is_diag_trait_item(cx, repl_def_id, sym::Default)
+                || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
+            then {
+                true
+            }
+            else {
+                false
+            }
+        },
+        ExprKind::Path(qpath) => is_lang_ctor(cx, qpath, OptionNone),
+        ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
+        _ => false,
+    }
+}
+
 /// Checks if the top level expression can be moved into a closure as is.
-pub fn can_move_expr_to_closure_no_visit(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, jump_targets: &[HirId]) -> bool {
+/// Currently checks for:
+/// * Break/Continue outside the given loop HIR ids.
+/// * Yield/Return statments.
+/// * Inline assembly.
+/// * Usages of a field of a local where the type of the local can be partially moved.
+///
+/// For example, given the following function:
+///
+/// ```
+/// fn f<'a>(iter: &mut impl Iterator<Item = (usize, &'a mut String)>) {
+///     for item in iter {
+///         let s = item.1;
+///         if item.0 > 10 {
+///             continue;
+///         } else {
+///             s.clear();
+///         }
+///     }
+/// }
+/// ```
+///
+/// When called on the expression `item.0` this will return false unless the local `item` is in the
+/// `ignore_locals` set. The type `(usize, &mut String)` can have the second element moved, so it
+/// isn't always safe to move into a closure when only a single field is needed.
+///
+/// When called on the `continue` expression this will return false unless the outer loop expression
+/// is in the `loop_ids` set.
+///
+/// Note that this check is not recursive, so passing the `if` expression will always return true
+/// even though sub-expressions might return false.
+pub fn can_move_expr_to_closure_no_visit(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    loop_ids: &[HirId],
+    ignore_locals: &HirIdSet,
+) -> bool {
     match expr.kind {
         ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
         | ExprKind::Continue(Destination { target_id: Ok(id), .. })
-            if jump_targets.contains(&id) =>
+            if loop_ids.contains(&id) =>
         {
             true
         },
@@ -646,25 +755,170 @@ pub fn can_move_expr_to_closure_no_visit(cx: &LateContext<'tcx>, expr: &'tcx Exp
         | ExprKind::LlvmInlineAsm(_) => false,
         // Accessing a field of a local value can only be done if the type isn't
         // partially moved.
-        ExprKind::Field(base_expr, _)
-            if matches!(
-                base_expr.kind,
-                ExprKind::Path(QPath::Resolved(_, Path { res: Res::Local(_), .. }))
-            ) && can_partially_move_ty(cx, cx.typeck_results().expr_ty(base_expr)) =>
-        {
+        ExprKind::Field(
+            &Expr {
+                hir_id,
+                kind:
+                    ExprKind::Path(QPath::Resolved(
+                        _,
+                        Path {
+                            res: Res::Local(local_id),
+                            ..
+                        },
+                    )),
+                ..
+            },
+            _,
+        ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
             // TODO: check if the local has been partially moved. Assume it has for now.
             false
-        }
+        },
         _ => true,
     }
 }
 
-/// Checks if the expression can be moved into a closure as is.
-pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+/// How a local is captured by a closure
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum CaptureKind {
+    Value,
+    Ref(Mutability),
+}
+impl CaptureKind {
+    pub fn is_imm_ref(self) -> bool {
+        self == Self::Ref(Mutability::Not)
+    }
+}
+impl std::ops::BitOr for CaptureKind {
+    type Output = Self;
+    fn bitor(self, rhs: Self) -> Self::Output {
+        match (self, rhs) {
+            (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
+            (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
+            | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
+            (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
+        }
+    }
+}
+impl std::ops::BitOrAssign for CaptureKind {
+    fn bitor_assign(&mut self, rhs: Self) {
+        *self = *self | rhs;
+    }
+}
+
+/// Given an expression referencing a local, determines how it would be captured in a closure.
+/// Note as this will walk up to parent expressions until the capture can be determined it should
+/// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or
+/// function argument (other than a receiver).
+pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind {
+    fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
+        let mut capture = CaptureKind::Ref(Mutability::Not);
+        pat.each_binding_or_first(&mut |_, id, span, _| match cx
+            .typeck_results()
+            .extract_binding_mode(cx.sess(), id, span)
+            .unwrap()
+        {
+            BindingMode::BindByValue(_) if !is_copy(cx, cx.typeck_results().node_type(id)) => {
+                capture = CaptureKind::Value;
+            },
+            BindingMode::BindByReference(Mutability::Mut) if capture != CaptureKind::Value => {
+                capture = CaptureKind::Ref(Mutability::Mut);
+            },
+            _ => (),
+        });
+        capture
+    }
+
+    debug_assert!(matches!(
+        e.kind,
+        ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
+    ));
+
+    let map = cx.tcx.hir();
+    let mut child_id = e.hir_id;
+    let mut capture = CaptureKind::Value;
+    let mut capture_expr_ty = e;
+
+    for (parent_id, parent) in map.parent_iter(e.hir_id) {
+        if let [Adjustment {
+            kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
+            target,
+        }, ref adjust @ ..] = *cx
+            .typeck_results()
+            .adjustments()
+            .get(child_id)
+            .map_or(&[][..], |x| &**x)
+        {
+            if let rustc_ty::RawPtr(TypeAndMut { mutbl: mutability, .. }) | rustc_ty::Ref(_, _, mutability) =
+                *adjust.last().map_or(target, |a| a.target).kind()
+            {
+                return CaptureKind::Ref(mutability);
+            }
+        }
+
+        match parent {
+            Node::Expr(e) => match e.kind {
+                ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
+                ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
+                ExprKind::Assign(lhs, ..) | ExprKind::Assign(_, lhs, _) if lhs.hir_id == child_id => {
+                    return CaptureKind::Ref(Mutability::Mut);
+                },
+                ExprKind::Field(..) => {
+                    if capture == CaptureKind::Value {
+                        capture_expr_ty = e;
+                    }
+                },
+                ExprKind::Let(pat, ..) => {
+                    let mutability = match pat_capture_kind(cx, pat) {
+                        CaptureKind::Value => Mutability::Not,
+                        CaptureKind::Ref(m) => m,
+                    };
+                    return CaptureKind::Ref(mutability);
+                },
+                ExprKind::Match(_, arms, _) => {
+                    let mut mutability = Mutability::Not;
+                    for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
+                        match capture {
+                            CaptureKind::Value => break,
+                            CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
+                            CaptureKind::Ref(Mutability::Not) => (),
+                        }
+                    }
+                    return CaptureKind::Ref(mutability);
+                },
+                _ => break,
+            },
+            Node::Local(l) => match pat_capture_kind(cx, l.pat) {
+                CaptureKind::Value => break,
+                capture @ CaptureKind::Ref(_) => return capture,
+            },
+            _ => break,
+        }
+
+        child_id = parent_id;
+    }
+
+    if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
+        // Copy types are never automatically captured by value.
+        CaptureKind::Ref(Mutability::Not)
+    } else {
+        capture
+    }
+}
+
+/// Checks if the expression can be moved into a closure as is. This will return a list of captures
+/// if so, otherwise, `None`.
+pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
     struct V<'cx, 'tcx> {
         cx: &'cx LateContext<'tcx>,
+        // Stack of potential break targets contained in the expression.
         loops: Vec<HirId>,
+        /// Local variables created in the expression. These don't need to be captured.
+        locals: HirIdSet,
+        /// Whether this expression can be turned into a closure.
         allow_closure: bool,
+        /// Locals which need to be captured, and whether they need to be by value, reference, or
+        /// mutable reference.
+        captures: HirIdMap<CaptureKind>,
     }
     impl Visitor<'tcx> for V<'_, 'tcx> {
         type Map = ErasedMap<'tcx>;
@@ -676,24 +930,67 @@ pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) ->
             if !self.allow_closure {
                 return;
             }
-            if let ExprKind::Loop(b, ..) = e.kind {
-                self.loops.push(e.hir_id);
-                self.visit_block(b);
-                self.loops.pop();
-            } else {
-                self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops);
-                walk_expr(self, e);
+
+            match e.kind {
+                ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
+                    if !self.locals.contains(&l) {
+                        let cap = capture_local_usage(self.cx, e);
+                        self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
+                    }
+                },
+                ExprKind::Closure(..) => {
+                    let closure_id = self.cx.tcx.hir().local_def_id(e.hir_id).to_def_id();
+                    for capture in self.cx.typeck_results().closure_min_captures_flattened(closure_id) {
+                        let local_id = match capture.place.base {
+                            PlaceBase::Local(id) => id,
+                            PlaceBase::Upvar(var) => var.var_path.hir_id,
+                            _ => continue,
+                        };
+                        if !self.locals.contains(&local_id) {
+                            let capture = match capture.info.capture_kind {
+                                UpvarCapture::ByValue(_) => CaptureKind::Value,
+                                UpvarCapture::ByRef(borrow) => match borrow.kind {
+                                    BorrowKind::ImmBorrow => CaptureKind::Ref(Mutability::Not),
+                                    BorrowKind::UniqueImmBorrow | BorrowKind::MutBorrow => {
+                                        CaptureKind::Ref(Mutability::Mut)
+                                    },
+                                },
+                            };
+                            self.captures
+                                .entry(local_id)
+                                .and_modify(|e| *e |= capture)
+                                .or_insert(capture);
+                        }
+                    }
+                },
+                ExprKind::Loop(b, ..) => {
+                    self.loops.push(e.hir_id);
+                    self.visit_block(b);
+                    self.loops.pop();
+                },
+                _ => {
+                    self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
+                    walk_expr(self, e);
+                },
             }
         }
+
+        fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
+            p.each_binding_or_first(&mut |_, id, _, _| {
+                self.locals.insert(id);
+            });
+        }
     }
 
     let mut v = V {
         cx,
         allow_closure: true,
         loops: Vec::new(),
+        locals: HirIdSet::default(),
+        captures: HirIdMap::default(),
     };
     v.visit_expr(expr);
-    v.allow_closure
+    v.allow_closure.then(|| v.captures)
 }
 
 /// Returns the method names and argument list of nested method call expressions that make up
@@ -1365,13 +1662,13 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>,
 
     while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
         conds.push(&*cond);
-        if let ExprKind::Block(ref block, _) = then.kind {
+        if let ExprKind::Block(block, _) = then.kind {
             blocks.push(block);
         } else {
             panic!("ExprKind::If node is not an ExprKind::Block");
         }
 
-        if let Some(ref else_expr) = r#else {
+        if let Some(else_expr) = r#else {
             expr = else_expr;
         } else {
             break;
@@ -1708,7 +2005,7 @@ pub fn peel_hir_expr_while<'tcx>(
 pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
     let mut remaining = count;
     let e = peel_hir_expr_while(expr, |e| match e.kind {
-        ExprKind::AddrOf(BorrowKind::Ref, _, e) if remaining != 0 => {
+        ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
             remaining -= 1;
             Some(e)
         },
@@ -1722,7 +2019,7 @@ pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>,
 pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
     let mut count = 0;
     let e = peel_hir_expr_while(expr, |e| match e.kind {
-        ExprKind::AddrOf(BorrowKind::Ref, _, e) => {
+        ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
             count += 1;
             Some(e)
         },
diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs
index 4a9c4fd0276..fa57dfbb57e 100644
--- a/src/tools/clippy/clippy_utils/src/msrvs.rs
+++ b/src/tools/clippy/clippy_utils/src/msrvs.rs
@@ -13,9 +13,12 @@ macro_rules! msrv_aliases {
 // names may refer to stabilized feature flags or library items
 msrv_aliases! {
     1,53,0 { OR_PATTERNS }
+    1,52,0 { STR_SPLIT_ONCE }
     1,50,0 { BOOL_THEN }
+    1,47,0 { TAU }
     1,46,0 { CONST_IF_MATCH }
     1,45,0 { STR_STRIP_PREFIX }
+    1,43,0 { LOG2_10, LOG10_2 }
     1,42,0 { MATCHES_MACRO }
     1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE }
     1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF }
diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs
index b0c3fe1e5a7..d7e46c2d3eb 100644
--- a/src/tools/clippy/clippy_utils/src/paths.rs
+++ b/src/tools/clippy/clippy_utils/src/paths.rs
@@ -68,6 +68,7 @@ pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
 pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"];
 pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
 pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
+pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
 #[cfg(feature = "internal-lints")]
 pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
 #[cfg(feature = "internal-lints")]
diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
index e5bbf75c3b0..6cb2bd7f6ef 100644
--- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
+++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
@@ -36,9 +36,7 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&Ru
                 ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate),
                 ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate),
                 ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate),
-                ty::PredicateKind::Coerce(_) => {
-                    panic!("coerce predicate on function: {:#?}", predicate)
-                },
+                ty::PredicateKind::Coerce(_) => panic!("coerce predicate on function: {:#?}", predicate),
                 ty::PredicateKind::Trait(pred) => {
                     if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
                         continue;
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index 65d93e8f86e..ab05a0b4238 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -329,7 +329,7 @@ fn has_enclosing_paren(sugg: impl AsRef<str>) -> bool {
     }
 }
 
-// Copied from the rust standart library, and then edited
+/// Copied from the rust standard library, and then edited
 macro_rules! forward_binop_impls_to_ref {
     (impl $imp:ident, $method:ident for $t:ty, type Output = $o:ty) => {
         impl $imp<$t> for &$t {
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index 3cd8ed5aa2c..d6f9ebe89bc 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -10,7 +10,7 @@ use rustc_hir::{TyKind, Unsafety};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::LateContext;
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
-use rustc_middle::ty::{self, TyCtxt, AdtDef, IntTy, Ty, TypeFoldable, UintTy};
+use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TyCtxt, TypeFoldable, UintTy};
 use rustc_span::sym;
 use rustc_span::symbol::{Ident, Symbol};
 use rustc_span::DUMMY_SP;
@@ -224,6 +224,13 @@ fn is_normalizable_helper<'tcx>(
     result
 }
 
+/// Returns true iff the given type is a non aggregate primitive (a bool or char, any integer or
+/// floating-point number type). For checking aggregation of primitive types (e.g. tuples and slices
+/// of primitive type) see `is_recursively_primitive_type`
+pub fn is_non_aggregate_primitive_type(ty: Ty<'_>) -> bool {
+    matches!(ty.kind(), ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_))
+}
+
 /// Returns true iff the given type is a primitive (a bool or char, any integer or floating-point
 /// number type, a str, or an array, slice, or tuple of those types).
 pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs
index ac885e99944..098ec175fe2 100644
--- a/src/tools/clippy/clippy_utils/src/usage.rs
+++ b/src/tools/clippy/clippy_utils/src/usage.rs
@@ -1,10 +1,9 @@
 use crate as utils;
 use rustc_hir as hir;
-use rustc_hir::def::Res;
 use rustc_hir::intravisit;
 use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
 use rustc_hir::HirIdSet;
-use rustc_hir::{Expr, ExprKind, HirId, Path};
+use rustc_hir::{Expr, ExprKind, HirId};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
@@ -35,12 +34,8 @@ pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) ->
     Some(delegate.used_mutably)
 }
 
-pub fn is_potentially_mutated<'tcx>(variable: &'tcx Path<'_>, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
-    if let Res::Local(id) = variable.res {
-        mutated_variables(expr, cx).map_or(true, |mutated| mutated.contains(&id))
-    } else {
-        true
-    }
+pub fn is_potentially_mutated<'tcx>(variable: HirId, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
+    mutated_variables(expr, cx).map_or(true, |mutated| mutated.contains(&variable))
 }
 
 struct MutVarsDelegate {
diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs
index ce00106dd4d..503effbdad5 100644
--- a/src/tools/clippy/clippy_utils/src/visitors.rs
+++ b/src/tools/clippy/clippy_utils/src/visitors.rs
@@ -4,6 +4,7 @@ use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visito
 use rustc_hir::{def::Res, Arm, Block, Body, BodyId, Destination, Expr, ExprKind, HirId, Stmt};
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
+use std::ops::ControlFlow;
 
 /// returns `true` if expr contains match expr desugared from try
 fn contains_try(expr: &hir::Expr<'_>) -> bool {
@@ -133,62 +134,6 @@ where
     }
 }
 
-pub struct LocalUsedVisitor<'hir> {
-    hir: Map<'hir>,
-    pub local_hir_id: HirId,
-    pub used: bool,
-}
-
-impl<'hir> LocalUsedVisitor<'hir> {
-    pub fn new(cx: &LateContext<'hir>, local_hir_id: HirId) -> Self {
-        Self {
-            hir: cx.tcx.hir(),
-            local_hir_id,
-            used: false,
-        }
-    }
-
-    fn check<T>(&mut self, t: T, visit: fn(&mut Self, T)) -> bool {
-        visit(self, t);
-        std::mem::replace(&mut self.used, false)
-    }
-
-    pub fn check_arm(&mut self, arm: &'hir Arm<'_>) -> bool {
-        self.check(arm, Self::visit_arm)
-    }
-
-    pub fn check_body(&mut self, body: &'hir Body<'_>) -> bool {
-        self.check(body, Self::visit_body)
-    }
-
-    pub fn check_expr(&mut self, expr: &'hir Expr<'_>) -> bool {
-        self.check(expr, Self::visit_expr)
-    }
-
-    pub fn check_stmt(&mut self, stmt: &'hir Stmt<'_>) -> bool {
-        self.check(stmt, Self::visit_stmt)
-    }
-}
-
-impl<'v> Visitor<'v> for LocalUsedVisitor<'v> {
-    type Map = Map<'v>;
-
-    fn visit_expr(&mut self, expr: &'v Expr<'v>) {
-        if self.used {
-            return;
-        }
-        if path_to_local_id(expr, self.local_hir_id) {
-            self.used = true;
-        } else {
-            walk_expr(self, expr);
-        }
-    }
-
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::OnlyBodies(self.hir)
-    }
-}
-
 /// A type which can be visited.
 pub trait Visitable<'tcx> {
     /// Calls the corresponding `visit_*` function on the visitor.
@@ -203,7 +148,22 @@ macro_rules! visitable_ref {
         }
     };
 }
+visitable_ref!(Arm, visit_arm);
 visitable_ref!(Block, visit_block);
+visitable_ref!(Body, visit_body);
+visitable_ref!(Expr, visit_expr);
+visitable_ref!(Stmt, visit_stmt);
+
+// impl<'tcx, I: IntoIterator> Visitable<'tcx> for I
+// where
+//     I::Item: Visitable<'tcx>,
+// {
+//     fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
+//         for x in self {
+//             x.visit(visitor);
+//         }
+//     }
+// }
 
 /// Calls the given function for each break expression.
 pub fn visit_break_exprs<'tcx>(
@@ -260,3 +220,48 @@ pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
     v.visit_expr(&cx.tcx.hir().body(body).value);
     v.found
 }
+
+/// Calls the given function for each usage of the given local.
+pub fn for_each_local_usage<'tcx, B>(
+    cx: &LateContext<'tcx>,
+    visitable: impl Visitable<'tcx>,
+    id: HirId,
+    f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
+) -> ControlFlow<B> {
+    struct V<'tcx, B, F> {
+        map: Map<'tcx>,
+        id: HirId,
+        f: F,
+        res: ControlFlow<B>,
+    }
+    impl<'tcx, B, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>> Visitor<'tcx> for V<'tcx, B, F> {
+        type Map = Map<'tcx>;
+        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+            NestedVisitorMap::OnlyBodies(self.map)
+        }
+
+        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
+            if self.res.is_continue() {
+                if path_to_local_id(e, self.id) {
+                    self.res = (self.f)(e);
+                } else {
+                    walk_expr(self, e);
+                }
+            }
+        }
+    }
+
+    let mut v = V {
+        map: cx.tcx.hir(),
+        id,
+        f,
+        res: ControlFlow::CONTINUE,
+    };
+    visitable.visit(&mut v);
+    v.res
+}
+
+/// Checks if the given local is used.
+pub fn is_local_used(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
+    for_each_local_usage(cx, visitable, id, |_| ControlFlow::BREAK).is_break()
+}
diff --git a/src/tools/clippy/doc/adding_lints.md b/src/tools/clippy/doc/adding_lints.md
index f2260c3d1a2..004eb28b446 100644
--- a/src/tools/clippy/doc/adding_lints.md
+++ b/src/tools/clippy/doc/adding_lints.md
@@ -556,14 +556,15 @@ directory. Adding a configuration to a lint can be useful for thresholds or to c
 behavior that can be seen as a false positive for some users. Adding a configuration is done
 in the following steps:
 
-1. Adding a new configuration entry to [clippy_utils::conf](/clippy_utils/src/conf.rs)
+1. Adding a new configuration entry to [clippy_lints::utils::conf](/clippy_lints/src/utils/conf.rs)
     like this:
     ```rust
-    /// Lint: LINT_NAME. <The configuration field doc comment>
+    /// Lint: LINT_NAME.
+    ///
+    /// <The configuration field doc comment>
     (configuration_ident: Type = DefaultValue),
     ```
-    The configuration value and identifier should usually be the same. The doc comment will be
-    automatically added to the lint documentation.
+    The doc comment will be automatically added to the lint documentation.
 2. Adding the configuration value to the lint impl struct:
     1. This first requires the definition of a lint impl struct. Lint impl structs are usually
         generated with the `declare_lint_pass!` macro. This struct needs to be defined manually
diff --git a/src/tools/clippy/doc/common_tools_writing_lints.md b/src/tools/clippy/doc/common_tools_writing_lints.md
index 0a85f650011..1a6b7c8cb47 100644
--- a/src/tools/clippy/doc/common_tools_writing_lints.md
+++ b/src/tools/clippy/doc/common_tools_writing_lints.md
@@ -11,6 +11,7 @@ You may need following tooltips to catch up with common operations.
 
 Useful Rustc dev guide links:
 - [Stages of compilation](https://rustc-dev-guide.rust-lang.org/compiler-src.html#the-main-stages-of-compilation)
+- [Diagnostic items](https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html)
 - [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html)
 - [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html)
 
@@ -75,20 +76,21 @@ impl LateLintPass<'_> for MyStructLint {
 
 # Checking if a type implements a specific trait
 
-There are two ways to do this, depending if the target trait is part of lang items.
+There are three ways to do this, depending on if the target trait has a diagnostic item, lang item or neither.
 
 ```rust
-use clippy_utils::{implements_trait, match_trait_method, paths};
+use clippy_utils::{implements_trait, is_trait_method, match_trait_method, paths};
+use rustc_span::symbol::sym;
 
 impl LateLintPass<'_> for MyStructLint {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
-        // 1. Using expression and Clippy's convenient method
-        // we use `match_trait_method` function from Clippy's toolbox
-        if match_trait_method(cx, expr, &paths::INTO) {
-            // `expr` implements `Into` trait
+        // 1. Using diagnostic items with the expression
+        // we use `is_trait_method` function from Clippy's utils
+        if is_trait_method(cx, expr, sym::Iterator) {
+            // method call in `expr` belongs to `Iterator` trait
         }
 
-        // 2. Using type context `TyCtxt`
+        // 2. Using lang items with the expression type
         let ty = cx.typeck_results().expr_ty(expr);
         if cx.tcx.lang_items()
             // we are looking for the `DefId` of `Drop` trait in lang items
@@ -97,15 +99,20 @@ impl LateLintPass<'_> for MyStructLint {
             .map_or(false, |id| implements_trait(cx, ty, id, &[])) {
                 // `expr` implements `Drop` trait
             }
+
+        // 3. Using the type path with the expression
+        // we use `match_trait_method` function from Clippy's utils
+        if match_trait_method(cx, expr, &paths::INTO) {
+            // `expr` implements `Into` trait
+        }
     }
 }
 ```
 
-> Prefer using lang items, if the target trait is available there.
-
-A list of defined paths for Clippy can be found in [paths.rs][paths]
+> Prefer using diagnostic and lang items, if the target trait has one.
 
 We access lang items through the type context `tcx`. `tcx` is of type [`TyCtxt`][TyCtxt] and is defined in the `rustc_middle` crate.
+A list of defined paths for Clippy can be found in [paths.rs][paths]
 
 # Checking if a type defines a specific method
 
diff --git a/src/tools/clippy/lintcheck/Cargo.toml b/src/tools/clippy/lintcheck/Cargo.toml
index 8c33fa372ec..ada033de6e3 100644
--- a/src/tools/clippy/lintcheck/Cargo.toml
+++ b/src/tools/clippy/lintcheck/Cargo.toml
@@ -19,6 +19,7 @@ serde_json = {version = "1.0"}
 tar = {version = "0.4.30"}
 toml = {version = "0.5"}
 ureq = {version = "2.0.0-rc3"}
+walkdir = {version = "2.3.2"}
 
 [features]
 deny-warnings = []
diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs
index 7d2f3173fb0..f1e03ba4296 100644
--- a/src/tools/clippy/lintcheck/src/main.rs
+++ b/src/tools/clippy/lintcheck/src/main.rs
@@ -21,6 +21,7 @@ use clap::{App, Arg, ArgMatches};
 use rayon::prelude::*;
 use serde::{Deserialize, Serialize};
 use serde_json::Value;
+use walkdir::{DirEntry, WalkDir};
 
 #[cfg(not(windows))]
 const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver";
@@ -193,32 +194,41 @@ impl CrateSource {
                 }
             },
             CrateSource::Path { name, path, options } => {
-                use fs_extra::dir;
-
-                // simply copy the entire directory into our target dir
-                let copy_dest = PathBuf::from(format!("{}/", LINTCHECK_SOURCES));
+                // copy path into the dest_crate_root but skip directories that contain a CACHEDIR.TAG file.
+                // The target/ directory contains a CACHEDIR.TAG file so it is the most commonly skipped directory
+                // as a result of this filter.
+                let dest_crate_root = PathBuf::from(LINTCHECK_SOURCES).join(name);
+                if dest_crate_root.exists() {
+                    println!("Deleting existing directory at {:?}", dest_crate_root);
+                    std::fs::remove_dir_all(&dest_crate_root).unwrap();
+                }
 
-                // the source path of the crate we copied,  ${copy_dest}/crate_name
-                let crate_root = copy_dest.join(name); // .../crates/local_crate
+                println!("Copying {:?} to {:?}", path, dest_crate_root);
 
-                if crate_root.exists() {
-                    println!(
-                        "Not copying {} to {}, destination already exists",
-                        path.display(),
-                        crate_root.display()
-                    );
-                } else {
-                    println!("Copying {} to {}", path.display(), copy_dest.display());
+                fn is_cache_dir(entry: &DirEntry) -> bool {
+                    std::fs::read(entry.path().join("CACHEDIR.TAG"))
+                        .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55"))
+                        .unwrap_or(false)
+                }
 
-                    dir::copy(path, &copy_dest, &dir::CopyOptions::new()).unwrap_or_else(|_| {
-                        panic!("Failed to copy from {}, to  {}", path.display(), crate_root.display())
-                    });
+                for entry in WalkDir::new(path).into_iter().filter_entry(|e| !is_cache_dir(e)) {
+                    let entry = entry.unwrap();
+                    let entry_path = entry.path();
+                    let relative_entry_path = entry_path.strip_prefix(path).unwrap();
+                    let dest_path = dest_crate_root.join(relative_entry_path);
+                    let metadata = entry_path.symlink_metadata().unwrap();
+
+                    if metadata.is_dir() {
+                        std::fs::create_dir(dest_path).unwrap();
+                    } else if metadata.is_file() {
+                        std::fs::copy(entry_path, dest_path).unwrap();
+                    }
                 }
 
                 Crate {
                     version: String::from("local"),
                     name: name.clone(),
-                    path: crate_root,
+                    path: dest_crate_root,
                     options: options.clone(),
                 }
             },
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index 23887f17845..92bde3423a2 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2021-08-12"
+channel = "nightly-2021-09-08"
 components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs
index 0a82377a10e..c63c47690d5 100644
--- a/src/tools/clippy/tests/compile-test.rs
+++ b/src/tools/clippy/tests/compile-test.rs
@@ -1,10 +1,12 @@
 #![feature(test)] // compiletest_rs requires this attribute
 #![feature(once_cell)]
-#![feature(try_blocks)]
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+#![warn(rust_2018_idioms, unused_lifetimes)]
 
 use compiletest_rs as compiletest;
 use compiletest_rs::common::Mode as TestMode;
 
+use std::collections::HashMap;
 use std::env::{self, remove_var, set_var, var_os};
 use std::ffi::{OsStr, OsString};
 use std::fs;
@@ -16,6 +18,34 @@ mod cargo;
 // whether to run internal tests or not
 const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal-lints");
 
+/// All crates used in UI tests are listed here
+static TEST_DEPENDENCIES: &[&str] = &[
+    "clippy_utils",
+    "derive_new",
+    "if_chain",
+    "itertools",
+    "quote",
+    "regex",
+    "serde",
+    "serde_derive",
+    "syn",
+];
+
+// Test dependencies may need an `extern crate` here to ensure that they show up
+// in the depinfo file (otherwise cargo thinks they are unused)
+#[allow(unused_extern_crates)]
+extern crate clippy_utils;
+#[allow(unused_extern_crates)]
+extern crate derive_new;
+#[allow(unused_extern_crates)]
+extern crate if_chain;
+#[allow(unused_extern_crates)]
+extern crate itertools;
+#[allow(unused_extern_crates)]
+extern crate quote;
+#[allow(unused_extern_crates)]
+extern crate syn;
+
 fn host_lib() -> PathBuf {
     option_env!("HOST_LIBS").map_or(cargo::CARGO_TARGET_DIR.join(env!("PROFILE")), PathBuf::from)
 }
@@ -24,71 +54,58 @@ fn clippy_driver_path() -> PathBuf {
     option_env!("CLIPPY_DRIVER_PATH").map_or(cargo::TARGET_LIB.join("clippy-driver"), PathBuf::from)
 }
 
-// When we'll want to use `extern crate ..` for a dependency that is used
-// both by the crate and the compiler itself, we can't simply pass -L flags
-// as we'll get a duplicate matching versions. Instead, disambiguate with
-// `--extern dep=path`.
-// See https://github.com/rust-lang/rust-clippy/issues/4015.
-//
-// FIXME: We cannot use `cargo build --message-format=json` to resolve to dependency files.
-//        Because it would force-rebuild if the options passed to `build` command is not the same
-//        as what we manually pass to `cargo` invocation
-fn third_party_crates() -> String {
-    use std::collections::HashMap;
-    static CRATES: &[&str] = &[
-        "clippy_lints",
-        "clippy_utils",
-        "if_chain",
-        "quote",
-        "regex",
-        "serde",
-        "serde_derive",
-        "syn",
-    ];
-    let dep_dir = cargo::TARGET_LIB.join("deps");
-    let mut crates: HashMap<&str, Vec<PathBuf>> = HashMap::with_capacity(CRATES.len());
-    let mut flags = String::new();
-    for entry in fs::read_dir(dep_dir).unwrap().flatten() {
-        let path = entry.path();
-        if let Some(name) = try {
-            let name = path.file_name()?.to_str()?;
-            let (name, _) = name.strip_suffix(".rlib")?.strip_prefix("lib")?.split_once('-')?;
-            CRATES.iter().copied().find(|&c| c == name)?
-        } {
-            flags += &format!(" --extern {}={}", name, path.display());
-            crates.entry(name).or_default().push(path.clone());
+/// Produces a string with an `--extern` flag for all UI test crate
+/// dependencies.
+///
+/// The dependency files are located by parsing the depinfo file for this test
+/// module. This assumes the `-Z binary-dep-depinfo` flag is enabled. All test
+/// dependencies must be added to Cargo.toml at the project root. Test
+/// dependencies that are not *directly* used by this test module require an
+/// `extern crate` declaration.
+fn extern_flags() -> String {
+    let current_exe_depinfo = {
+        let mut path = env::current_exe().unwrap();
+        path.set_extension("d");
+        std::fs::read_to_string(path).unwrap()
+    };
+    let mut crates: HashMap<&str, &str> = HashMap::with_capacity(TEST_DEPENDENCIES.len());
+    for line in current_exe_depinfo.lines() {
+        // each dependency is expected to have a Makefile rule like `/path/to/crate-hash.rlib:`
+        let parse_name_path = || {
+            if line.starts_with(char::is_whitespace) {
+                return None;
+            }
+            let path_str = line.strip_suffix(':')?;
+            let path = Path::new(path_str);
+            if !matches!(path.extension()?.to_str()?, "rlib" | "so" | "dylib" | "dll") {
+                return None;
+            }
+            let (name, _hash) = path.file_stem()?.to_str()?.rsplit_once('-')?;
+            // the "lib" prefix is not present for dll files
+            let name = name.strip_prefix("lib").unwrap_or(name);
+            Some((name, path_str))
+        };
+        if let Some((name, path)) = parse_name_path() {
+            if TEST_DEPENDENCIES.contains(&name) {
+                // A dependency may be listed twice if it is available in sysroot,
+                // and the sysroot dependencies are listed first. As of the writing,
+                // this only seems to apply to if_chain.
+                crates.insert(name, path);
+            }
         }
     }
-    crates.retain(|_, paths| paths.len() > 1);
-    if !crates.is_empty() {
-        let crate_names = crates.keys().map(|s| format!("`{}`", s)).collect::<Vec<_>>().join(", ");
-        // add backslashes for an easy copy-paste `rm` command
-        let paths = crates
-            .into_values()
-            .flatten()
-            .map(|p| strip_current_dir(&p).display().to_string())
-            .collect::<Vec<_>>()
-            .join(" \\\n");
-        // Check which action should be done in order to remove compiled deps.
-        // If pre-installed version of compiler is used, `cargo clean` will do.
-        // Otherwise (for bootstrapped compiler), the dependencies directory
-        // must be removed manually.
-        let suggested_action = if std::env::var_os("RUSTC_BOOTSTRAP").is_some() {
-            "removing the stageN-tools directory"
-        } else {
-            "running `cargo clean`"
-        };
-
-        panic!(
-            "\n----------------------------------------------------------------------\n\
-            ERROR: Found multiple rlibs for crates: {}\n\
-            Try {} or remove the following files:\n\n{}\n\n\
-            For details on this error see https://github.com/rust-lang/rust-clippy/issues/7343\n\
-            ----------------------------------------------------------------------\n",
-            crate_names, suggested_action, paths
-        );
+    let not_found: Vec<&str> = TEST_DEPENDENCIES
+        .iter()
+        .copied()
+        .filter(|n| !crates.contains_key(n))
+        .collect();
+    if !not_found.is_empty() {
+        panic!("dependencies not found in depinfo: {:?}", not_found);
     }
-    flags
+    crates
+        .into_iter()
+        .map(|(name, path)| format!("--extern {}={} ", name, path))
+        .collect()
 }
 
 fn default_config() -> compiletest::Config {
@@ -104,11 +121,14 @@ fn default_config() -> compiletest::Config {
         config.compile_lib_path = path;
     }
 
+    // Using `-L dependency={}` enforces that external dependencies are added with `--extern`.
+    // This is valuable because a) it allows us to monitor what external dependencies are used
+    // and b) it ensures that conflicting rlibs are resolved properly.
     config.target_rustcflags = Some(format!(
-        "--emit=metadata -L {0} -L {1} -Dwarnings -Zui-testing {2}",
+        "--emit=metadata -L dependency={} -L dependency={} -Dwarnings -Zui-testing {}",
         host_lib().join("deps").display(),
         cargo::TARGET_LIB.join("deps").display(),
-        third_party_crates(),
+        extern_flags(),
     ));
 
     config.build_base = host_lib().join("test_build_base");
@@ -315,12 +335,3 @@ impl Drop for VarGuard {
         }
     }
 }
-
-fn strip_current_dir(path: &Path) -> &Path {
-    if let Ok(curr) = env::current_dir() {
-        if let Ok(stripped) = path.strip_prefix(curr) {
-            return stripped;
-        }
-    }
-    path
-}
diff --git a/src/tools/clippy/tests/dogfood.rs b/src/tools/clippy/tests/dogfood.rs
index 4ede20c5258..54f452172de 100644
--- a/src/tools/clippy/tests/dogfood.rs
+++ b/src/tools/clippy/tests/dogfood.rs
@@ -6,6 +6,8 @@
 // Dogfood cannot run on Windows
 #![cfg(not(windows))]
 #![feature(once_cell)]
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+#![warn(rust_2018_idioms, unused_lifetimes)]
 
 use std::lazy::SyncLazy;
 use std::path::PathBuf;
diff --git a/src/tools/clippy/tests/fmt.rs b/src/tools/clippy/tests/fmt.rs
index 7616d8001e8..be42f1fbb20 100644
--- a/src/tools/clippy/tests/fmt.rs
+++ b/src/tools/clippy/tests/fmt.rs
@@ -1,3 +1,6 @@
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+#![warn(rust_2018_idioms, unused_lifetimes)]
+
 use std::path::PathBuf;
 use std::process::Command;
 
diff --git a/src/tools/clippy/tests/integration.rs b/src/tools/clippy/tests/integration.rs
index 1718089e8bd..7e3eff3c732 100644
--- a/src/tools/clippy/tests/integration.rs
+++ b/src/tools/clippy/tests/integration.rs
@@ -1,4 +1,6 @@
 #![cfg(feature = "integration")]
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+#![warn(rust_2018_idioms, unused_lifetimes)]
 
 use std::env;
 use std::ffi::OsStr;
diff --git a/src/tools/clippy/tests/lint_message_convention.rs b/src/tools/clippy/tests/lint_message_convention.rs
index 2f8989c8e11..b4d94dc983f 100644
--- a/src/tools/clippy/tests/lint_message_convention.rs
+++ b/src/tools/clippy/tests/lint_message_convention.rs
@@ -1,3 +1,6 @@
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+#![warn(rust_2018_idioms, unused_lifetimes)]
+
 use std::ffi::OsStr;
 use std::path::PathBuf;
 
diff --git a/src/tools/clippy/tests/missing-test-files.rs b/src/tools/clippy/tests/missing-test-files.rs
index 9cef7438d22..bd342e390f5 100644
--- a/src/tools/clippy/tests/missing-test-files.rs
+++ b/src/tools/clippy/tests/missing-test-files.rs
@@ -1,3 +1,5 @@
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+#![warn(rust_2018_idioms, unused_lifetimes)]
 #![allow(clippy::assertions_on_constants)]
 
 use std::fs::{self, DirEntry};
diff --git a/src/tools/clippy/tests/ui-cargo/feature_name/fail/Cargo.toml b/src/tools/clippy/tests/ui-cargo/feature_name/fail/Cargo.toml
new file mode 100644
index 00000000000..97d51462a94
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/feature_name/fail/Cargo.toml
@@ -0,0 +1,21 @@
+
+# Content that triggers the lint goes here
+
+[package]
+name = "feature_name"
+version = "0.1.0"
+publish = false
+
+[workspace]
+
+[features]
+use-qwq = []
+use_qwq = []
+with-owo = []
+with_owo = []
+qvq-support = []
+qvq_support = []
+no-qaq = []
+no_qaq = []
+not-orz = []
+not_orz = []
diff --git a/src/tools/clippy/tests/ui-cargo/feature_name/fail/src/main.rs b/src/tools/clippy/tests/ui-cargo/feature_name/fail/src/main.rs
new file mode 100644
index 00000000000..64f01a98c90
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/feature_name/fail/src/main.rs
@@ -0,0 +1,7 @@
+// compile-flags: --crate-name=feature_name
+#![warn(clippy::redundant_feature_names)]
+#![warn(clippy::negative_feature_names)]
+
+fn main() {
+    // test code goes here
+}
diff --git a/src/tools/clippy/tests/ui-cargo/feature_name/fail/src/main.stderr b/src/tools/clippy/tests/ui-cargo/feature_name/fail/src/main.stderr
new file mode 100644
index 00000000000..b9e6cb49bc9
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/feature_name/fail/src/main.stderr
@@ -0,0 +1,44 @@
+error: the "no-" prefix in the feature name "no-qaq" is negative
+   |
+   = note: `-D clippy::negative-feature-names` implied by `-D warnings`
+   = help: consider renaming the feature to "qaq", but make sure the feature adds functionality
+
+error: the "no_" prefix in the feature name "no_qaq" is negative
+   |
+   = help: consider renaming the feature to "qaq", but make sure the feature adds functionality
+
+error: the "not-" prefix in the feature name "not-orz" is negative
+   |
+   = help: consider renaming the feature to "orz", but make sure the feature adds functionality
+
+error: the "not_" prefix in the feature name "not_orz" is negative
+   |
+   = help: consider renaming the feature to "orz", but make sure the feature adds functionality
+
+error: the "-support" suffix in the feature name "qvq-support" is redundant
+   |
+   = note: `-D clippy::redundant-feature-names` implied by `-D warnings`
+   = help: consider renaming the feature to "qvq"
+
+error: the "_support" suffix in the feature name "qvq_support" is redundant
+   |
+   = help: consider renaming the feature to "qvq"
+
+error: the "use-" prefix in the feature name "use-qwq" is redundant
+   |
+   = help: consider renaming the feature to "qwq"
+
+error: the "use_" prefix in the feature name "use_qwq" is redundant
+   |
+   = help: consider renaming the feature to "qwq"
+
+error: the "with-" prefix in the feature name "with-owo" is redundant
+   |
+   = help: consider renaming the feature to "owo"
+
+error: the "with_" prefix in the feature name "with_owo" is redundant
+   |
+   = help: consider renaming the feature to "owo"
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui-cargo/feature_name/pass/Cargo.toml b/src/tools/clippy/tests/ui-cargo/feature_name/pass/Cargo.toml
new file mode 100644
index 00000000000..cf947312bd4
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/feature_name/pass/Cargo.toml
@@ -0,0 +1,9 @@
+
+# This file should not trigger the lint
+
+[package]
+name = "feature_name"
+version = "0.1.0"
+publish = false
+
+[workspace]
diff --git a/src/tools/clippy/tests/ui-cargo/feature_name/pass/src/main.rs b/src/tools/clippy/tests/ui-cargo/feature_name/pass/src/main.rs
new file mode 100644
index 00000000000..64f01a98c90
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/feature_name/pass/src/main.rs
@@ -0,0 +1,7 @@
+// compile-flags: --crate-name=feature_name
+#![warn(clippy::redundant_feature_names)]
+#![warn(clippy::negative_feature_names)]
+
+fn main() {
+    // test code goes here
+}
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/Cargo.toml b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/Cargo.toml
new file mode 100644
index 00000000000..27b61c09fb4
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "fail"
+version = "0.1.0"
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner.rs b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner.rs
new file mode 100644
index 00000000000..91cd540a28f
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner.rs
@@ -0,0 +1 @@
+pub mod stuff;
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff.rs b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff.rs
new file mode 100644
index 00000000000..7713fa9d35c
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff.rs
@@ -0,0 +1,3 @@
+pub mod most;
+
+pub struct Inner;
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff/most.rs b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff/most.rs
new file mode 100644
index 00000000000..5a5eaf9670f
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff/most.rs
@@ -0,0 +1 @@
+pub struct Snarks;
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/mod.rs b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/mod.rs
new file mode 100644
index 00000000000..a12734db7cb
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/bad/mod.rs
@@ -0,0 +1,3 @@
+pub mod inner;
+
+pub struct Thing;
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.rs b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.rs
new file mode 100644
index 00000000000..3e985d4e904
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.rs
@@ -0,0 +1,9 @@
+#![warn(clippy::self_named_module_files)]
+
+mod bad;
+
+fn main() {
+    let _ = bad::Thing;
+    let _ = bad::inner::stuff::Inner;
+    let _ = bad::inner::stuff::most::Snarks;
+}
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.stderr b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.stderr
new file mode 100644
index 00000000000..af4c298b310
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/src/main.stderr
@@ -0,0 +1,19 @@
+error: `mod.rs` files are required, found `/bad/inner.rs`
+  --> $DIR/bad/inner.rs:1:1
+   |
+LL | pub mod stuff;
+   | ^
+   |
+   = note: `-D clippy::self-named-module-files` implied by `-D warnings`
+   = help: move `/bad/inner.rs` to `/bad/inner/mod.rs`
+
+error: `mod.rs` files are required, found `/bad/inner/stuff.rs`
+  --> $DIR/bad/inner/stuff.rs:1:1
+   |
+LL | pub mod most;
+   | ^
+   |
+   = help: move `/bad/inner/stuff.rs` to `/bad/inner/stuff/mod.rs`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.toml b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.toml
new file mode 100644
index 00000000000..27b61c09fb4
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "fail"
+version = "0.1.0"
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/bad/mod.rs b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/bad/mod.rs
new file mode 100644
index 00000000000..f19ab10d5fb
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/bad/mod.rs
@@ -0,0 +1 @@
+pub struct Thing;
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.rs b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.rs
new file mode 100644
index 00000000000..c6e9045b8dc
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.rs
@@ -0,0 +1,7 @@
+#![warn(clippy::mod_module_files)]
+
+mod bad;
+
+fn main() {
+    let _ = bad::Thing;
+}
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr
new file mode 100644
index 00000000000..11e15db7fb9
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr
@@ -0,0 +1,11 @@
+error: `mod.rs` files are not allowed, found `/bad/mod.rs`
+  --> $DIR/bad/mod.rs:1:1
+   |
+LL | pub struct Thing;
+   | ^
+   |
+   = note: `-D clippy::mod-module-files` implied by `-D warnings`
+   = help: move `/bad/mod.rs` to `/bad.rs`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/Cargo.toml b/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/Cargo.toml
new file mode 100644
index 00000000000..27b61c09fb4
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "fail"
+version = "0.1.0"
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/bad/mod.rs b/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/bad/mod.rs
new file mode 100644
index 00000000000..f19ab10d5fb
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/bad/mod.rs
@@ -0,0 +1 @@
+pub struct Thing;
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/main.rs b/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/main.rs
new file mode 100644
index 00000000000..9e08715fc05
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/main.rs
@@ -0,0 +1,10 @@
+#![warn(clippy::self_named_module_files)]
+
+mod bad;
+mod more;
+
+fn main() {
+    let _ = bad::Thing;
+    let _ = more::foo::Foo;
+    let _ = more::inner::Inner;
+}
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/foo.rs b/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/foo.rs
new file mode 100644
index 00000000000..4a835673a59
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/foo.rs
@@ -0,0 +1 @@
+pub struct Foo;
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/inner/mod.rs b/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/inner/mod.rs
new file mode 100644
index 00000000000..aa84f78cc2c
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/inner/mod.rs
@@ -0,0 +1 @@
+pub struct Inner;
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/mod.rs b/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/mod.rs
new file mode 100644
index 00000000000..d79569f78ff
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/pass_mod/src/more/mod.rs
@@ -0,0 +1,2 @@
+pub mod foo;
+pub mod inner;
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/Cargo.toml b/src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/Cargo.toml
new file mode 100644
index 00000000000..3c0896dd2cd
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "pass"
+version = "0.1.0"
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/src/good.rs b/src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/src/good.rs
new file mode 100644
index 00000000000..f19ab10d5fb
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/src/good.rs
@@ -0,0 +1 @@
+pub struct Thing;
diff --git a/src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/src/main.rs b/src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/src/main.rs
new file mode 100644
index 00000000000..50211a340b9
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/module_style/pass_no_mod/src/main.rs
@@ -0,0 +1,7 @@
+#![warn(clippy::mod_module_files)]
+
+mod good;
+
+fn main() {
+    let _ = good::Thing;
+}
diff --git a/src/tools/clippy/tests/ui/approx_const.rs b/src/tools/clippy/tests/ui/approx_const.rs
index fb57a0becbb..2ae4d613507 100644
--- a/src/tools/clippy/tests/ui/approx_const.rs
+++ b/src/tools/clippy/tests/ui/approx_const.rs
@@ -57,4 +57,8 @@ fn main() {
 
     let my_sq2 = 1.4142;
     let no_sq2 = 1.414;
+
+    let my_tau = 6.2832;
+    let almost_tau = 6.28;
+    let no_tau = 6.3;
 }
diff --git a/src/tools/clippy/tests/ui/approx_const.stderr b/src/tools/clippy/tests/ui/approx_const.stderr
index 98b85443f0b..4da1b8215ae 100644
--- a/src/tools/clippy/tests/ui/approx_const.stderr
+++ b/src/tools/clippy/tests/ui/approx_const.stderr
@@ -1,130 +1,187 @@
-error: approximate value of `f{32, 64}::consts::E` found. Consider using it directly
+error: approximate value of `f{32, 64}::consts::E` found
   --> $DIR/approx_const.rs:4:16
    |
 LL |     let my_e = 2.7182;
    |                ^^^^^^
    |
    = note: `-D clippy::approx-constant` implied by `-D warnings`
+   = help: consider using the constant directly
 
-error: approximate value of `f{32, 64}::consts::E` found. Consider using it directly
+error: approximate value of `f{32, 64}::consts::E` found
   --> $DIR/approx_const.rs:5:20
    |
 LL |     let almost_e = 2.718;
    |                    ^^^^^
+   |
+   = help: consider using the constant directly
 
-error: approximate value of `f{32, 64}::consts::FRAC_1_PI` found. Consider using it directly
+error: approximate value of `f{32, 64}::consts::FRAC_1_PI` found
   --> $DIR/approx_const.rs:8:24
    |
 LL |     let my_1_frac_pi = 0.3183;
    |                        ^^^^^^
+   |
+   = help: consider using the constant directly
 
-error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found. Consider using it directly
+error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found
   --> $DIR/approx_const.rs:11:28
    |
 LL |     let my_frac_1_sqrt_2 = 0.70710678;
    |                            ^^^^^^^^^^
+   |
+   = help: consider using the constant directly
 
-error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found. Consider using it directly
+error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found
   --> $DIR/approx_const.rs:12:32
    |
 LL |     let almost_frac_1_sqrt_2 = 0.70711;
    |                                ^^^^^^^
+   |
+   = help: consider using the constant directly
 
-error: approximate value of `f{32, 64}::consts::FRAC_2_PI` found. Consider using it directly
+error: approximate value of `f{32, 64}::consts::FRAC_2_PI` found
   --> $DIR/approx_const.rs:15:24
    |
 LL |     let my_frac_2_pi = 0.63661977;
    |                        ^^^^^^^^^^
+   |
+   = help: consider using the constant directly
 
-error: approximate value of `f{32, 64}::consts::FRAC_2_SQRT_PI` found. Consider using it directly
+error: approximate value of `f{32, 64}::consts::FRAC_2_SQRT_PI` found
   --> $DIR/approx_const.rs:18:27
    |
 LL |     let my_frac_2_sq_pi = 1.128379;
    |                           ^^^^^^^^
+   |
+   = help: consider using the constant directly
 
-error: approximate value of `f{32, 64}::consts::FRAC_PI_2` found. Consider using it directly
+error: approximate value of `f{32, 64}::consts::FRAC_PI_2` found
   --> $DIR/approx_const.rs:21:24
    |
 LL |     let my_frac_pi_2 = 1.57079632679;
    |                        ^^^^^^^^^^^^^
+   |
+   = help: consider using the constant directly
 
-error: approximate value of `f{32, 64}::consts::FRAC_PI_3` found. Consider using it directly
+error: approximate value of `f{32, 64}::consts::FRAC_PI_3` found
   --> $DIR/approx_const.rs:24:24
    |
 LL |     let my_frac_pi_3 = 1.04719755119;
    |                        ^^^^^^^^^^^^^
+   |
+   = help: consider using the constant directly
 
-error: approximate value of `f{32, 64}::consts::FRAC_PI_4` found. Consider using it directly
+error: approximate value of `f{32, 64}::consts::FRAC_PI_4` found
   --> $DIR/approx_const.rs:27:24
    |
 LL |     let my_frac_pi_4 = 0.785398163397;
    |                        ^^^^^^^^^^^^^^
+   |
+   = help: consider using the constant directly
 
-error: approximate value of `f{32, 64}::consts::FRAC_PI_6` found. Consider using it directly
+error: approximate value of `f{32, 64}::consts::FRAC_PI_6` found
   --> $DIR/approx_const.rs:30:24
    |
 LL |     let my_frac_pi_6 = 0.523598775598;
    |                        ^^^^^^^^^^^^^^
+   |
+   = help: consider using the constant directly
 
-error: approximate value of `f{32, 64}::consts::FRAC_PI_8` found. Consider using it directly
+error: approximate value of `f{32, 64}::consts::FRAC_PI_8` found
   --> $DIR/approx_const.rs:33:24
    |
 LL |     let my_frac_pi_8 = 0.3926990816987;
    |                        ^^^^^^^^^^^^^^^
+   |
+   = help: consider using the constant directly
 
-error: approximate value of `f{32, 64}::consts::LN_10` found. Consider using it directly
+error: approximate value of `f{32, 64}::consts::LN_10` found
   --> $DIR/approx_const.rs:36:20
    |
 LL |     let my_ln_10 = 2.302585092994046;
    |                    ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using the constant directly
 
-error: approximate value of `f{32, 64}::consts::LN_2` found. Consider using it directly
+error: approximate value of `f{32, 64}::consts::LN_2` found
   --> $DIR/approx_const.rs:39:19
    |
 LL |     let my_ln_2 = 0.6931471805599453;
    |                   ^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using the constant directly
 
-error: approximate value of `f{32, 64}::consts::LOG10_E` found. Consider using it directly
+error: approximate value of `f{32, 64}::consts::LOG10_E` found
   --> $DIR/approx_const.rs:42:22
    |
 LL |     let my_log10_e = 0.4342944819032518;
    |                      ^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using the constant directly
 
-error: approximate value of `f{32, 64}::consts::LOG2_E` found. Consider using it directly
+error: approximate value of `f{32, 64}::consts::LOG2_E` found
   --> $DIR/approx_const.rs:45:21
    |
 LL |     let my_log2_e = 1.4426950408889634;
    |                     ^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using the constant directly
 
-error: approximate value of `f{32, 64}::consts::LOG2_10` found. Consider using it directly
+error: approximate value of `f{32, 64}::consts::LOG2_10` found
   --> $DIR/approx_const.rs:48:19
    |
 LL |     let log2_10 = 3.321928094887362;
    |                   ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using the constant directly
 
-error: approximate value of `f{32, 64}::consts::LOG10_2` found. Consider using it directly
+error: approximate value of `f{32, 64}::consts::LOG10_2` found
   --> $DIR/approx_const.rs:51:19
    |
 LL |     let log10_2 = 0.301029995663981;
    |                   ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using the constant directly
 
-error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
+error: approximate value of `f{32, 64}::consts::PI` found
   --> $DIR/approx_const.rs:54:17
    |
 LL |     let my_pi = 3.1415;
    |                 ^^^^^^
+   |
+   = help: consider using the constant directly
 
-error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
+error: approximate value of `f{32, 64}::consts::PI` found
   --> $DIR/approx_const.rs:55:21
    |
 LL |     let almost_pi = 3.14;
    |                     ^^^^
+   |
+   = help: consider using the constant directly
 
-error: approximate value of `f{32, 64}::consts::SQRT_2` found. Consider using it directly
+error: approximate value of `f{32, 64}::consts::SQRT_2` found
   --> $DIR/approx_const.rs:58:18
    |
 LL |     let my_sq2 = 1.4142;
    |                  ^^^^^^
+   |
+   = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::TAU` found
+  --> $DIR/approx_const.rs:61:18
+   |
+LL |     let my_tau = 6.2832;
+   |                  ^^^^^^
+   |
+   = help: consider using the constant directly
+
+error: approximate value of `f{32, 64}::consts::TAU` found
+  --> $DIR/approx_const.rs:62:22
+   |
+LL |     let almost_tau = 6.28;
+   |                      ^^^^
+   |
+   = help: consider using the constant directly
 
-error: aborting due to 21 previous errors
+error: aborting due to 23 previous errors
 
diff --git a/src/tools/clippy/tests/ui/auxiliary/option_helpers.rs b/src/tools/clippy/tests/ui/auxiliary/option_helpers.rs
index 7dc3f4ebd4d..86a637ce309 100644
--- a/src/tools/clippy/tests/ui/auxiliary/option_helpers.rs
+++ b/src/tools/clippy/tests/ui/auxiliary/option_helpers.rs
@@ -53,3 +53,12 @@ impl IteratorFalsePositives {
         self.foo as usize
     }
 }
+
+#[derive(Copy, Clone)]
+pub struct IteratorMethodFalsePositives;
+
+impl IteratorMethodFalsePositives {
+    pub fn filter(&self, _s: i32) -> std::vec::IntoIter<i32> {
+        unimplemented!();
+    }
+}
diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.rs b/src/tools/clippy/tests/ui/bool_assert_comparison.rs
index 2de402fae8c..ec4d6f3ff84 100644
--- a/src/tools/clippy/tests/ui/bool_assert_comparison.rs
+++ b/src/tools/clippy/tests/ui/bool_assert_comparison.rs
@@ -1,5 +1,7 @@
 #![warn(clippy::bool_assert_comparison)]
 
+use std::ops::Not;
+
 macro_rules! a {
     () => {
         true
@@ -11,7 +13,58 @@ macro_rules! b {
     };
 }
 
+// Implements the Not trait but with an output type
+// that's not bool. Should not suggest a rewrite
+#[derive(Debug)]
+enum ImplNotTraitWithoutBool {
+    VariantX(bool),
+    VariantY(u32),
+}
+
+impl PartialEq<bool> for ImplNotTraitWithoutBool {
+    fn eq(&self, other: &bool) -> bool {
+        match *self {
+            ImplNotTraitWithoutBool::VariantX(b) => b == *other,
+            _ => false,
+        }
+    }
+}
+
+impl Not for ImplNotTraitWithoutBool {
+    type Output = Self;
+
+    fn not(self) -> Self::Output {
+        match self {
+            ImplNotTraitWithoutBool::VariantX(b) => ImplNotTraitWithoutBool::VariantX(!b),
+            ImplNotTraitWithoutBool::VariantY(0) => ImplNotTraitWithoutBool::VariantY(1),
+            ImplNotTraitWithoutBool::VariantY(_) => ImplNotTraitWithoutBool::VariantY(0),
+        }
+    }
+}
+
+// This type implements the Not trait with an Output of
+// type bool. Using assert!(..) must be suggested
+#[derive(Debug)]
+struct ImplNotTraitWithBool;
+
+impl PartialEq<bool> for ImplNotTraitWithBool {
+    fn eq(&self, other: &bool) -> bool {
+        false
+    }
+}
+
+impl Not for ImplNotTraitWithBool {
+    type Output = bool;
+
+    fn not(self) -> Self::Output {
+        true
+    }
+}
+
 fn main() {
+    let a = ImplNotTraitWithoutBool::VariantX(true);
+    let b = ImplNotTraitWithBool;
+
     assert_eq!("a".len(), 1);
     assert_eq!("a".is_empty(), false);
     assert_eq!("".is_empty(), true);
@@ -19,6 +72,8 @@ fn main() {
     assert_eq!(a!(), b!());
     assert_eq!(a!(), "".is_empty());
     assert_eq!("".is_empty(), b!());
+    assert_eq!(a, true);
+    assert_eq!(b, true);
 
     assert_ne!("a".len(), 1);
     assert_ne!("a".is_empty(), false);
@@ -27,6 +82,8 @@ fn main() {
     assert_ne!(a!(), b!());
     assert_ne!(a!(), "".is_empty());
     assert_ne!("".is_empty(), b!());
+    assert_ne!(a, true);
+    assert_ne!(b, true);
 
     debug_assert_eq!("a".len(), 1);
     debug_assert_eq!("a".is_empty(), false);
@@ -35,6 +92,8 @@ fn main() {
     debug_assert_eq!(a!(), b!());
     debug_assert_eq!(a!(), "".is_empty());
     debug_assert_eq!("".is_empty(), b!());
+    debug_assert_eq!(a, true);
+    debug_assert_eq!(b, true);
 
     debug_assert_ne!("a".len(), 1);
     debug_assert_ne!("a".is_empty(), false);
@@ -43,6 +102,8 @@ fn main() {
     debug_assert_ne!(a!(), b!());
     debug_assert_ne!(a!(), "".is_empty());
     debug_assert_ne!("".is_empty(), b!());
+    debug_assert_ne!(a, true);
+    debug_assert_ne!(b, true);
 
     // assert with error messages
     assert_eq!("a".len(), 1, "tadam {}", 1);
@@ -50,10 +111,12 @@ fn main() {
     assert_eq!("a".is_empty(), false, "tadam {}", 1);
     assert_eq!("a".is_empty(), false, "tadam {}", true);
     assert_eq!(false, "a".is_empty(), "tadam {}", true);
+    assert_eq!(a, true, "tadam {}", false);
 
     debug_assert_eq!("a".len(), 1, "tadam {}", 1);
     debug_assert_eq!("a".len(), 1, "tadam {}", true);
     debug_assert_eq!("a".is_empty(), false, "tadam {}", 1);
     debug_assert_eq!("a".is_empty(), false, "tadam {}", true);
     debug_assert_eq!(false, "a".is_empty(), "tadam {}", true);
+    debug_assert_eq!(a, true, "tadam {}", false);
 }
diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.stderr b/src/tools/clippy/tests/ui/bool_assert_comparison.stderr
index f57acf520d5..da9b56aa779 100644
--- a/src/tools/clippy/tests/ui/bool_assert_comparison.stderr
+++ b/src/tools/clippy/tests/ui/bool_assert_comparison.stderr
@@ -1,5 +1,5 @@
 error: used `assert_eq!` with a literal bool
-  --> $DIR/bool_assert_comparison.rs:16:5
+  --> $DIR/bool_assert_comparison.rs:69:5
    |
 LL |     assert_eq!("a".is_empty(), false);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
@@ -7,106 +7,130 @@ LL |     assert_eq!("a".is_empty(), false);
    = note: `-D clippy::bool-assert-comparison` implied by `-D warnings`
 
 error: used `assert_eq!` with a literal bool
-  --> $DIR/bool_assert_comparison.rs:17:5
+  --> $DIR/bool_assert_comparison.rs:70:5
    |
 LL |     assert_eq!("".is_empty(), true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
 
 error: used `assert_eq!` with a literal bool
-  --> $DIR/bool_assert_comparison.rs:18:5
+  --> $DIR/bool_assert_comparison.rs:71:5
    |
 LL |     assert_eq!(true, "".is_empty());
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
 
+error: used `assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:76:5
+   |
+LL |     assert_eq!(b, true);
+   |     ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
 error: used `assert_ne!` with a literal bool
-  --> $DIR/bool_assert_comparison.rs:24:5
+  --> $DIR/bool_assert_comparison.rs:79:5
    |
 LL |     assert_ne!("a".is_empty(), false);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
 
 error: used `assert_ne!` with a literal bool
-  --> $DIR/bool_assert_comparison.rs:25:5
+  --> $DIR/bool_assert_comparison.rs:80:5
    |
 LL |     assert_ne!("".is_empty(), true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
 
 error: used `assert_ne!` with a literal bool
-  --> $DIR/bool_assert_comparison.rs:26:5
+  --> $DIR/bool_assert_comparison.rs:81:5
    |
 LL |     assert_ne!(true, "".is_empty());
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
 
+error: used `assert_ne!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:86:5
+   |
+LL |     assert_ne!(b, true);
+   |     ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+
 error: used `debug_assert_eq!` with a literal bool
-  --> $DIR/bool_assert_comparison.rs:32:5
+  --> $DIR/bool_assert_comparison.rs:89:5
    |
 LL |     debug_assert_eq!("a".is_empty(), false);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
 
 error: used `debug_assert_eq!` with a literal bool
-  --> $DIR/bool_assert_comparison.rs:33:5
+  --> $DIR/bool_assert_comparison.rs:90:5
    |
 LL |     debug_assert_eq!("".is_empty(), true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
 
 error: used `debug_assert_eq!` with a literal bool
-  --> $DIR/bool_assert_comparison.rs:34:5
+  --> $DIR/bool_assert_comparison.rs:91:5
    |
 LL |     debug_assert_eq!(true, "".is_empty());
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
 
+error: used `debug_assert_eq!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:96:5
+   |
+LL |     debug_assert_eq!(b, true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
 error: used `debug_assert_ne!` with a literal bool
-  --> $DIR/bool_assert_comparison.rs:40:5
+  --> $DIR/bool_assert_comparison.rs:99:5
    |
 LL |     debug_assert_ne!("a".is_empty(), false);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
 
 error: used `debug_assert_ne!` with a literal bool
-  --> $DIR/bool_assert_comparison.rs:41:5
+  --> $DIR/bool_assert_comparison.rs:100:5
    |
 LL |     debug_assert_ne!("".is_empty(), true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
 
 error: used `debug_assert_ne!` with a literal bool
-  --> $DIR/bool_assert_comparison.rs:42:5
+  --> $DIR/bool_assert_comparison.rs:101:5
    |
 LL |     debug_assert_ne!(true, "".is_empty());
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
 
+error: used `debug_assert_ne!` with a literal bool
+  --> $DIR/bool_assert_comparison.rs:106:5
+   |
+LL |     debug_assert_ne!(b, true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+
 error: used `assert_eq!` with a literal bool
-  --> $DIR/bool_assert_comparison.rs:50:5
+  --> $DIR/bool_assert_comparison.rs:111:5
    |
 LL |     assert_eq!("a".is_empty(), false, "tadam {}", 1);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
 
 error: used `assert_eq!` with a literal bool
-  --> $DIR/bool_assert_comparison.rs:51:5
+  --> $DIR/bool_assert_comparison.rs:112:5
    |
 LL |     assert_eq!("a".is_empty(), false, "tadam {}", true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
 
 error: used `assert_eq!` with a literal bool
-  --> $DIR/bool_assert_comparison.rs:52:5
+  --> $DIR/bool_assert_comparison.rs:113:5
    |
 LL |     assert_eq!(false, "a".is_empty(), "tadam {}", true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
 
 error: used `debug_assert_eq!` with a literal bool
-  --> $DIR/bool_assert_comparison.rs:56:5
+  --> $DIR/bool_assert_comparison.rs:118:5
    |
 LL |     debug_assert_eq!("a".is_empty(), false, "tadam {}", 1);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
 
 error: used `debug_assert_eq!` with a literal bool
-  --> $DIR/bool_assert_comparison.rs:57:5
+  --> $DIR/bool_assert_comparison.rs:119:5
    |
 LL |     debug_assert_eq!("a".is_empty(), false, "tadam {}", true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
 
 error: used `debug_assert_eq!` with a literal bool
-  --> $DIR/bool_assert_comparison.rs:58:5
+  --> $DIR/bool_assert_comparison.rs:120:5
    |
 LL |     debug_assert_eq!(false, "a".is_empty(), "tadam {}", true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
 
-error: aborting due to 18 previous errors
+error: aborting due to 22 previous errors
 
diff --git a/src/tools/clippy/tests/ui/box_vec.rs b/src/tools/clippy/tests/ui/box_vec.rs
index 87b67c23704..1d6366972da 100644
--- a/src/tools/clippy/tests/ui/box_vec.rs
+++ b/src/tools/clippy/tests/ui/box_vec.rs
@@ -1,6 +1,10 @@
 #![warn(clippy::all)]
-#![allow(clippy::boxed_local, clippy::needless_pass_by_value)]
-#![allow(clippy::blacklisted_name)]
+#![allow(
+    clippy::boxed_local,
+    clippy::needless_pass_by_value,
+    clippy::blacklisted_name,
+    unused
+)]
 
 macro_rules! boxit {
     ($init:expr, $x:ty) => {
@@ -11,22 +15,22 @@ macro_rules! boxit {
 fn test_macro() {
     boxit!(Vec::new(), Vec<u8>);
 }
-pub fn test(foo: Box<Vec<bool>>) {
-    println!("{:?}", foo.get(0))
-}
+fn test(foo: Box<Vec<bool>>) {}
 
-pub fn test2(foo: Box<dyn Fn(Vec<u32>)>) {
+fn test2(foo: Box<dyn Fn(Vec<u32>)>) {
     // pass if #31 is fixed
     foo(vec![1, 2, 3])
 }
 
-pub fn test_local_not_linted() {
+fn test_local_not_linted() {
     let _: Box<Vec<bool>>;
 }
 
-fn main() {
-    test(Box::new(Vec::new()));
-    test2(Box::new(|v| println!("{:?}", v)));
-    test_macro();
-    test_local_not_linted();
+// All of these test should be allowed because they are part of the
+// public api and `avoid_breaking_exported_api` is `false` by default.
+pub fn pub_test(foo: Box<Vec<bool>>) {}
+pub fn pub_test_ret() -> Box<Vec<bool>> {
+    Box::new(Vec::new())
 }
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/box_vec.stderr b/src/tools/clippy/tests/ui/box_vec.stderr
index 9b789334bae..58c1f13fb87 100644
--- a/src/tools/clippy/tests/ui/box_vec.stderr
+++ b/src/tools/clippy/tests/ui/box_vec.stderr
@@ -1,8 +1,8 @@
 error: you seem to be trying to use `Box<Vec<T>>`. Consider using just `Vec<T>`
-  --> $DIR/box_vec.rs:14:18
+  --> $DIR/box_vec.rs:18:14
    |
-LL | pub fn test(foo: Box<Vec<bool>>) {
-   |                  ^^^^^^^^^^^^^^
+LL | fn test(foo: Box<Vec<bool>>) {}
+   |              ^^^^^^^^^^^^^^
    |
    = note: `-D clippy::box-vec` implied by `-D warnings`
    = help: `Vec<T>` is already on the heap, `Box<Vec<T>>` makes an extra allocation
diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr
index 33bb5136ef8..46c6f69708e 100644
--- a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr
+++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr
@@ -1,4 +1,4 @@
-error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
+error: called `unwrap` on `x` after checking its variant with `is_ok`
   --> $DIR/complex_conditionals.rs:8:9
    |
 LL |     if x.is_ok() && y.is_err() {
@@ -11,6 +11,7 @@ note: the lint level is defined here
    |
 LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = help: try using `if let` or `match`
 
 error: this call to `unwrap_err()` will always panic
   --> $DIR/complex_conditionals.rs:9:9
@@ -36,7 +37,7 @@ LL |     if x.is_ok() && y.is_err() {
 LL |         y.unwrap(); // will panic
    |         ^^^^^^^^^^
 
-error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
+error: called `unwrap_err` on `y` after checking its variant with `is_err`
   --> $DIR/complex_conditionals.rs:11:9
    |
 LL |     if x.is_ok() && y.is_err() {
@@ -44,6 +45,8 @@ LL |     if x.is_ok() && y.is_err() {
 ...
 LL |         y.unwrap_err(); // unnecessary
    |         ^^^^^^^^^^^^^^
+   |
+   = help: try using `if let` or `match`
 
 error: this call to `unwrap()` will always panic
   --> $DIR/complex_conditionals.rs:25:9
@@ -54,7 +57,7 @@ LL |     if x.is_ok() || y.is_ok() {
 LL |         x.unwrap(); // will panic
    |         ^^^^^^^^^^
 
-error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
+error: called `unwrap_err` on `x` after checking its variant with `is_ok`
   --> $DIR/complex_conditionals.rs:26:9
    |
 LL |     if x.is_ok() || y.is_ok() {
@@ -62,6 +65,8 @@ LL |     if x.is_ok() || y.is_ok() {
 ...
 LL |         x.unwrap_err(); // unnecessary
    |         ^^^^^^^^^^^^^^
+   |
+   = help: try using `if let` or `match`
 
 error: this call to `unwrap()` will always panic
   --> $DIR/complex_conditionals.rs:27:9
@@ -72,7 +77,7 @@ LL |     if x.is_ok() || y.is_ok() {
 LL |         y.unwrap(); // will panic
    |         ^^^^^^^^^^
 
-error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
+error: called `unwrap_err` on `y` after checking its variant with `is_ok`
   --> $DIR/complex_conditionals.rs:28:9
    |
 LL |     if x.is_ok() || y.is_ok() {
@@ -80,14 +85,18 @@ LL |     if x.is_ok() || y.is_ok() {
 ...
 LL |         y.unwrap_err(); // unnecessary
    |         ^^^^^^^^^^^^^^
+   |
+   = help: try using `if let` or `match`
 
-error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
+error: called `unwrap` on `x` after checking its variant with `is_ok`
   --> $DIR/complex_conditionals.rs:32:9
    |
 LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
    |        --------- the check is happening here
 LL |         x.unwrap(); // unnecessary
    |         ^^^^^^^^^^
+   |
+   = help: try using `if let` or `match`
 
 error: this call to `unwrap_err()` will always panic
   --> $DIR/complex_conditionals.rs:33:9
@@ -107,7 +116,7 @@ LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
 LL |         y.unwrap(); // will panic
    |         ^^^^^^^^^^
 
-error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
+error: called `unwrap_err` on `y` after checking its variant with `is_ok`
   --> $DIR/complex_conditionals.rs:35:9
    |
 LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
@@ -115,8 +124,10 @@ LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
 ...
 LL |         y.unwrap_err(); // unnecessary
    |         ^^^^^^^^^^^^^^
+   |
+   = help: try using `if let` or `match`
 
-error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
+error: called `unwrap` on `z` after checking its variant with `is_err`
   --> $DIR/complex_conditionals.rs:36:9
    |
 LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
@@ -124,6 +135,8 @@ LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
 ...
 LL |         z.unwrap(); // unnecessary
    |         ^^^^^^^^^^
+   |
+   = help: try using `if let` or `match`
 
 error: this call to `unwrap_err()` will always panic
   --> $DIR/complex_conditionals.rs:37:9
@@ -143,7 +156,7 @@ LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
 LL |         x.unwrap(); // will panic
    |         ^^^^^^^^^^
 
-error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
+error: called `unwrap_err` on `x` after checking its variant with `is_ok`
   --> $DIR/complex_conditionals.rs:46:9
    |
 LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
@@ -151,8 +164,10 @@ LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
 ...
 LL |         x.unwrap_err(); // unnecessary
    |         ^^^^^^^^^^^^^^
+   |
+   = help: try using `if let` or `match`
 
-error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
+error: called `unwrap` on `y` after checking its variant with `is_ok`
   --> $DIR/complex_conditionals.rs:47:9
    |
 LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
@@ -160,6 +175,8 @@ LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
 ...
 LL |         y.unwrap(); // unnecessary
    |         ^^^^^^^^^^
+   |
+   = help: try using `if let` or `match`
 
 error: this call to `unwrap_err()` will always panic
   --> $DIR/complex_conditionals.rs:48:9
@@ -179,7 +196,7 @@ LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
 LL |         z.unwrap(); // will panic
    |         ^^^^^^^^^^
 
-error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
+error: called `unwrap_err` on `z` after checking its variant with `is_err`
   --> $DIR/complex_conditionals.rs:50:9
    |
 LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
@@ -187,6 +204,8 @@ LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
 ...
 LL |         z.unwrap_err(); // unnecessary
    |         ^^^^^^^^^^^^^^
+   |
+   = help: try using `if let` or `match`
 
 error: aborting due to 20 previous errors
 
diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr
index a01f7f956f6..542ab53300c 100644
--- a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr
+++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr
@@ -1,8 +1,8 @@
-error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
+error: called `unwrap` on `x` after checking its variant with `is_some`
   --> $DIR/complex_conditionals_nested.rs:8:13
    |
 LL |         if x.is_some() {
-   |            ----------- the check is happening here
+   |         -------------- help: try: `if let Some(..) = x`
 LL |             x.unwrap(); // unnecessary
    |             ^^^^^^^^^^
    |
diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs
index 8f23fb28827..ee3fdfabe9d 100644
--- a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs
+++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs
@@ -37,8 +37,10 @@ fn main() {
     let x = Some(());
     if x.is_some() {
         x.unwrap(); // unnecessary
+        x.expect("an error message"); // unnecessary
     } else {
         x.unwrap(); // will panic
+        x.expect("an error message"); // will panic
     }
     if x.is_none() {
         x.unwrap(); // will panic
@@ -52,9 +54,11 @@ fn main() {
     let mut x: Result<(), ()> = Ok(());
     if x.is_ok() {
         x.unwrap(); // unnecessary
+        x.expect("an error message"); // unnecessary
         x.unwrap_err(); // will panic
     } else {
         x.unwrap(); // will panic
+        x.expect("an error message"); // will panic
         x.unwrap_err(); // unnecessary
     }
     if x.is_err() {
diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr
index a4bc058fe20..82f26954380 100644
--- a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr
+++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr
@@ -1,8 +1,8 @@
-error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
+error: called `unwrap` on `x` after checking its variant with `is_some`
   --> $DIR/simple_conditionals.rs:39:9
    |
 LL |     if x.is_some() {
-   |        ----------- the check is happening here
+   |     -------------- help: try: `if let Some(..) = x`
 LL |         x.unwrap(); // unnecessary
    |         ^^^^^^^^^^
    |
@@ -12,8 +12,17 @@ note: the lint level is defined here
 LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+error: called `expect` on `x` after checking its variant with `is_some`
+  --> $DIR/simple_conditionals.rs:40:9
+   |
+LL |     if x.is_some() {
+   |     -------------- help: try: `if let Some(..) = x`
+LL |         x.unwrap(); // unnecessary
+LL |         x.expect("an error message"); // unnecessary
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
 error: this call to `unwrap()` will always panic
-  --> $DIR/simple_conditionals.rs:41:9
+  --> $DIR/simple_conditionals.rs:42:9
    |
 LL |     if x.is_some() {
    |        ----------- because of this check
@@ -27,28 +36,37 @@ note: the lint level is defined here
 LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^
 
+error: this call to `expect()` will always panic
+  --> $DIR/simple_conditionals.rs:43:9
+   |
+LL |     if x.is_some() {
+   |        ----------- because of this check
+...
+LL |         x.expect("an error message"); // will panic
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
 error: this call to `unwrap()` will always panic
-  --> $DIR/simple_conditionals.rs:44:9
+  --> $DIR/simple_conditionals.rs:46:9
    |
 LL |     if x.is_none() {
    |        ----------- because of this check
 LL |         x.unwrap(); // will panic
    |         ^^^^^^^^^^
 
-error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
-  --> $DIR/simple_conditionals.rs:46:9
+error: called `unwrap` on `x` after checking its variant with `is_none`
+  --> $DIR/simple_conditionals.rs:48:9
    |
 LL |     if x.is_none() {
-   |        ----------- the check is happening here
+   |     -------------- help: try: `if let Some(..) = x`
 ...
 LL |         x.unwrap(); // unnecessary
    |         ^^^^^^^^^^
 
-error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
+error: called `unwrap` on `x` after checking its variant with `is_some`
   --> $DIR/simple_conditionals.rs:7:13
    |
 LL |         if $a.is_some() {
-   |            ------------ the check is happening here
+   |         --------------- help: try: `if let Some(..) = x`
 LL |             $a.unwrap(); // unnecessary
    |             ^^^^^^^^^^^
 ...
@@ -57,25 +75,34 @@ LL |     m!(x);
    |
    = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
-  --> $DIR/simple_conditionals.rs:54:9
+error: called `unwrap` on `x` after checking its variant with `is_ok`
+  --> $DIR/simple_conditionals.rs:56:9
    |
 LL |     if x.is_ok() {
-   |        --------- the check is happening here
+   |     ------------ help: try: `if let Ok(..) = x`
 LL |         x.unwrap(); // unnecessary
    |         ^^^^^^^^^^
 
+error: called `expect` on `x` after checking its variant with `is_ok`
+  --> $DIR/simple_conditionals.rs:57:9
+   |
+LL |     if x.is_ok() {
+   |     ------------ help: try: `if let Ok(..) = x`
+LL |         x.unwrap(); // unnecessary
+LL |         x.expect("an error message"); // unnecessary
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
 error: this call to `unwrap_err()` will always panic
-  --> $DIR/simple_conditionals.rs:55:9
+  --> $DIR/simple_conditionals.rs:58:9
    |
 LL |     if x.is_ok() {
    |        --------- because of this check
-LL |         x.unwrap(); // unnecessary
+...
 LL |         x.unwrap_err(); // will panic
    |         ^^^^^^^^^^^^^^
 
 error: this call to `unwrap()` will always panic
-  --> $DIR/simple_conditionals.rs:57:9
+  --> $DIR/simple_conditionals.rs:60:9
    |
 LL |     if x.is_ok() {
    |        --------- because of this check
@@ -83,43 +110,52 @@ LL |     if x.is_ok() {
 LL |         x.unwrap(); // will panic
    |         ^^^^^^^^^^
 
-error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
-  --> $DIR/simple_conditionals.rs:58:9
+error: this call to `expect()` will always panic
+  --> $DIR/simple_conditionals.rs:61:9
+   |
+LL |     if x.is_ok() {
+   |        --------- because of this check
+...
+LL |         x.expect("an error message"); // will panic
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: called `unwrap_err` on `x` after checking its variant with `is_ok`
+  --> $DIR/simple_conditionals.rs:62:9
    |
 LL |     if x.is_ok() {
-   |        --------- the check is happening here
+   |     ------------ help: try: `if let Err(..) = x`
 ...
 LL |         x.unwrap_err(); // unnecessary
    |         ^^^^^^^^^^^^^^
 
 error: this call to `unwrap()` will always panic
-  --> $DIR/simple_conditionals.rs:61:9
+  --> $DIR/simple_conditionals.rs:65:9
    |
 LL |     if x.is_err() {
    |        ---------- because of this check
 LL |         x.unwrap(); // will panic
    |         ^^^^^^^^^^
 
-error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
-  --> $DIR/simple_conditionals.rs:62:9
+error: called `unwrap_err` on `x` after checking its variant with `is_err`
+  --> $DIR/simple_conditionals.rs:66:9
    |
 LL |     if x.is_err() {
-   |        ---------- the check is happening here
+   |     ------------- help: try: `if let Err(..) = x`
 LL |         x.unwrap(); // will panic
 LL |         x.unwrap_err(); // unnecessary
    |         ^^^^^^^^^^^^^^
 
-error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
-  --> $DIR/simple_conditionals.rs:64:9
+error: called `unwrap` on `x` after checking its variant with `is_err`
+  --> $DIR/simple_conditionals.rs:68:9
    |
 LL |     if x.is_err() {
-   |        ---------- the check is happening here
+   |     ------------- help: try: `if let Ok(..) = x`
 ...
 LL |         x.unwrap(); // unnecessary
    |         ^^^^^^^^^^
 
 error: this call to `unwrap_err()` will always panic
-  --> $DIR/simple_conditionals.rs:65:9
+  --> $DIR/simple_conditionals.rs:69:9
    |
 LL |     if x.is_err() {
    |        ---------- because of this check
@@ -127,5 +163,5 @@ LL |     if x.is_err() {
 LL |         x.unwrap_err(); // will panic
    |         ^^^^^^^^^^^^^^
 
-error: aborting due to 13 previous errors
+error: aborting due to 17 previous errors
 
diff --git a/src/tools/clippy/tests/ui/derivable_impls.rs b/src/tools/clippy/tests/ui/derivable_impls.rs
new file mode 100644
index 00000000000..336a743de72
--- /dev/null
+++ b/src/tools/clippy/tests/ui/derivable_impls.rs
@@ -0,0 +1,170 @@
+use std::collections::HashMap;
+
+struct FooDefault<'a> {
+    a: bool,
+    b: i32,
+    c: u64,
+    d: Vec<i32>,
+    e: FooND1,
+    f: FooND2,
+    g: HashMap<i32, i32>,
+    h: (i32, Vec<i32>),
+    i: [Vec<i32>; 3],
+    j: [i32; 5],
+    k: Option<i32>,
+    l: &'a [i32],
+}
+
+impl std::default::Default for FooDefault<'_> {
+    fn default() -> Self {
+        Self {
+            a: false,
+            b: 0,
+            c: 0u64,
+            d: vec![],
+            e: Default::default(),
+            f: FooND2::default(),
+            g: HashMap::new(),
+            h: (0, vec![]),
+            i: [vec![], vec![], vec![]],
+            j: [0; 5],
+            k: None,
+            l: &[],
+        }
+    }
+}
+
+struct TupleDefault(bool, i32, u64);
+
+impl std::default::Default for TupleDefault {
+    fn default() -> Self {
+        Self(false, 0, 0u64)
+    }
+}
+
+struct FooND1 {
+    a: bool,
+}
+
+impl std::default::Default for FooND1 {
+    fn default() -> Self {
+        Self { a: true }
+    }
+}
+
+struct FooND2 {
+    a: i32,
+}
+
+impl std::default::Default for FooND2 {
+    fn default() -> Self {
+        Self { a: 5 }
+    }
+}
+
+struct FooNDNew {
+    a: bool,
+}
+
+impl FooNDNew {
+    fn new() -> Self {
+        Self { a: true }
+    }
+}
+
+impl Default for FooNDNew {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+struct FooNDVec(Vec<i32>);
+
+impl Default for FooNDVec {
+    fn default() -> Self {
+        Self(vec![5, 12])
+    }
+}
+
+struct StrDefault<'a>(&'a str);
+
+impl Default for StrDefault<'_> {
+    fn default() -> Self {
+        Self("")
+    }
+}
+
+#[derive(Default)]
+struct AlreadyDerived(i32, bool);
+
+macro_rules! mac {
+    () => {
+        0
+    };
+    ($e:expr) => {
+        struct X(u32);
+        impl Default for X {
+            fn default() -> Self {
+                Self($e)
+            }
+        }
+    };
+}
+
+mac!(0);
+
+struct Y(u32);
+impl Default for Y {
+    fn default() -> Self {
+        Self(mac!())
+    }
+}
+
+struct RustIssue26925<T> {
+    a: Option<T>,
+}
+
+// We should watch out for cases where a manual impl is needed because a
+// derive adds different type bounds (https://github.com/rust-lang/rust/issues/26925).
+// For example, a struct with Option<T> does not require T: Default, but a derive adds
+// that type bound anyways. So until #26925 get fixed we should disable lint
+// for the following case
+impl<T> Default for RustIssue26925<T> {
+    fn default() -> Self {
+        Self { a: None }
+    }
+}
+
+struct SpecializedImpl<A, B> {
+    a: A,
+    b: B,
+}
+
+impl<T: Default> Default for SpecializedImpl<T, T> {
+    fn default() -> Self {
+        Self {
+            a: T::default(),
+            b: T::default(),
+        }
+    }
+}
+
+struct WithoutSelfCurly {
+    a: bool,
+}
+
+impl Default for WithoutSelfCurly {
+    fn default() -> Self {
+        WithoutSelfCurly { a: false }
+    }
+}
+
+struct WithoutSelfParan(bool);
+
+impl Default for WithoutSelfParan {
+    fn default() -> Self {
+        WithoutSelfParan(false)
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/derivable_impls.stderr b/src/tools/clippy/tests/ui/derivable_impls.stderr
new file mode 100644
index 00000000000..4ed64fade02
--- /dev/null
+++ b/src/tools/clippy/tests/ui/derivable_impls.stderr
@@ -0,0 +1,77 @@
+error: this `impl` can be derived
+  --> $DIR/derivable_impls.rs:18:1
+   |
+LL | / impl std::default::Default for FooDefault<'_> {
+LL | |     fn default() -> Self {
+LL | |         Self {
+LL | |             a: false,
+...  |
+LL | |     }
+LL | | }
+   | |_^
+   |
+   = note: `-D clippy::derivable-impls` implied by `-D warnings`
+   = help: try annotating `FooDefault` with `#[derive(Default)]`
+
+error: this `impl` can be derived
+  --> $DIR/derivable_impls.rs:39:1
+   |
+LL | / impl std::default::Default for TupleDefault {
+LL | |     fn default() -> Self {
+LL | |         Self(false, 0, 0u64)
+LL | |     }
+LL | | }
+   | |_^
+   |
+   = help: try annotating `TupleDefault` with `#[derive(Default)]`
+
+error: this `impl` can be derived
+  --> $DIR/derivable_impls.rs:91:1
+   |
+LL | / impl Default for StrDefault<'_> {
+LL | |     fn default() -> Self {
+LL | |         Self("")
+LL | |     }
+LL | | }
+   | |_^
+   |
+   = help: try annotating `StrDefault` with `#[derive(Default)]`
+
+error: this `impl` can be derived
+  --> $DIR/derivable_impls.rs:117:1
+   |
+LL | / impl Default for Y {
+LL | |     fn default() -> Self {
+LL | |         Self(mac!())
+LL | |     }
+LL | | }
+   | |_^
+   |
+   = help: try annotating `Y` with `#[derive(Default)]`
+
+error: this `impl` can be derived
+  --> $DIR/derivable_impls.rs:156:1
+   |
+LL | / impl Default for WithoutSelfCurly {
+LL | |     fn default() -> Self {
+LL | |         WithoutSelfCurly { a: false }
+LL | |     }
+LL | | }
+   | |_^
+   |
+   = help: try annotating `WithoutSelfCurly` with `#[derive(Default)]`
+
+error: this `impl` can be derived
+  --> $DIR/derivable_impls.rs:164:1
+   |
+LL | / impl Default for WithoutSelfParan {
+LL | |     fn default() -> Self {
+LL | |         WithoutSelfParan(false)
+LL | |     }
+LL | | }
+   | |_^
+   |
+   = help: try annotating `WithoutSelfParan` with `#[derive(Default)]`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/entry.fixed b/src/tools/clippy/tests/ui/entry.fixed
index cfad3090ba3..8a36ec833d7 100644
--- a/src/tools/clippy/tests/ui/entry.fixed
+++ b/src/tools/clippy/tests/ui/entry.fixed
@@ -4,7 +4,7 @@
 #![warn(clippy::map_entry)]
 #![feature(asm)]
 
-use std::collections::{BTreeMap, HashMap};
+use std::collections::HashMap;
 use std::hash::Hash;
 
 macro_rules! m {
@@ -142,14 +142,13 @@ fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, m2: &mut HashMa
     if !m.contains_key(&k) {
         insert!(m, k, v);
     }
-}
 
-fn btree_map<K: Eq + Ord + Copy, V: Copy>(m: &mut BTreeMap<K, V>, k: K, v: V, v2: V) {
-    // insert then do something, use if let
-    if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) {
-        e.insert(v);
-        foo();
-    }
+    // or_insert_with. Partial move of a local declared in the closure is ok.
+    m.entry(k).or_insert_with(|| {
+        let x = (String::new(), String::new());
+        let _ = x.0;
+        v
+    });
 }
 
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/entry.rs b/src/tools/clippy/tests/ui/entry.rs
index fa9280b58de..d972a201ad7 100644
--- a/src/tools/clippy/tests/ui/entry.rs
+++ b/src/tools/clippy/tests/ui/entry.rs
@@ -4,7 +4,7 @@
 #![warn(clippy::map_entry)]
 #![feature(asm)]
 
-use std::collections::{BTreeMap, HashMap};
+use std::collections::HashMap;
 use std::hash::Hash;
 
 macro_rules! m {
@@ -146,13 +146,12 @@ fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, m2: &mut HashMa
     if !m.contains_key(&k) {
         insert!(m, k, v);
     }
-}
 
-fn btree_map<K: Eq + Ord + Copy, V: Copy>(m: &mut BTreeMap<K, V>, k: K, v: V, v2: V) {
-    // insert then do something, use if let
+    // or_insert_with. Partial move of a local declared in the closure is ok.
     if !m.contains_key(&k) {
+        let x = (String::new(), String::new());
+        let _ = x.0;
         m.insert(k, v);
-        foo();
     }
 }
 
diff --git a/src/tools/clippy/tests/ui/entry.stderr b/src/tools/clippy/tests/ui/entry.stderr
index 8f2e383d675..1076500498d 100644
--- a/src/tools/clippy/tests/ui/entry.stderr
+++ b/src/tools/clippy/tests/ui/entry.stderr
@@ -165,21 +165,23 @@ LL | |         m.insert(m!(k), m!(v));
 LL | |     }
    | |_____^ help: try this: `m.entry(m!(k)).or_insert_with(|| m!(v));`
 
-error: usage of `contains_key` followed by `insert` on a `BTreeMap`
-  --> $DIR/entry.rs:153:5
+error: usage of `contains_key` followed by `insert` on a `HashMap`
+  --> $DIR/entry.rs:151:5
    |
 LL | /     if !m.contains_key(&k) {
+LL | |         let x = (String::new(), String::new());
+LL | |         let _ = x.0;
 LL | |         m.insert(k, v);
-LL | |         foo();
 LL | |     }
    | |_____^
    |
 help: try this
    |
-LL ~     if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) {
-LL +         e.insert(v);
-LL +         foo();
-LL +     }
+LL ~     m.entry(k).or_insert_with(|| {
+LL +         let x = (String::new(), String::new());
+LL +         let _ = x.0;
+LL +         v
+LL +     });
    |
 
 error: aborting due to 10 previous errors
diff --git a/src/tools/clippy/tests/ui/entry_btree.fixed b/src/tools/clippy/tests/ui/entry_btree.fixed
new file mode 100644
index 00000000000..94979104556
--- /dev/null
+++ b/src/tools/clippy/tests/ui/entry_btree.fixed
@@ -0,0 +1,18 @@
+// run-rustfix
+
+#![warn(clippy::map_entry)]
+#![allow(dead_code)]
+
+use std::collections::BTreeMap;
+
+fn foo() {}
+
+fn btree_map<K: Eq + Ord + Copy, V: Copy>(m: &mut BTreeMap<K, V>, k: K, v: V) {
+    // insert then do something, use if let
+    if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) {
+        e.insert(v);
+        foo();
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/entry_btree.rs b/src/tools/clippy/tests/ui/entry_btree.rs
new file mode 100644
index 00000000000..080c1d959e8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/entry_btree.rs
@@ -0,0 +1,18 @@
+// run-rustfix
+
+#![warn(clippy::map_entry)]
+#![allow(dead_code)]
+
+use std::collections::BTreeMap;
+
+fn foo() {}
+
+fn btree_map<K: Eq + Ord + Copy, V: Copy>(m: &mut BTreeMap<K, V>, k: K, v: V) {
+    // insert then do something, use if let
+    if !m.contains_key(&k) {
+        m.insert(k, v);
+        foo();
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/entry_btree.stderr b/src/tools/clippy/tests/ui/entry_btree.stderr
new file mode 100644
index 00000000000..5c6fcdf1a28
--- /dev/null
+++ b/src/tools/clippy/tests/ui/entry_btree.stderr
@@ -0,0 +1,20 @@
+error: usage of `contains_key` followed by `insert` on a `BTreeMap`
+  --> $DIR/entry_btree.rs:12:5
+   |
+LL | /     if !m.contains_key(&k) {
+LL | |         m.insert(k, v);
+LL | |         foo();
+LL | |     }
+   | |_____^
+   |
+   = note: `-D clippy::map-entry` implied by `-D warnings`
+help: try this
+   |
+LL ~     if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) {
+LL +         e.insert(v);
+LL +         foo();
+LL +     }
+   |
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/linkedlist.rs b/src/tools/clippy/tests/ui/linkedlist.rs
index 2c3b25cd45e..690ea810a62 100644
--- a/src/tools/clippy/tests/ui/linkedlist.rs
+++ b/src/tools/clippy/tests/ui/linkedlist.rs
@@ -1,6 +1,6 @@
 #![feature(associated_type_defaults)]
 #![warn(clippy::linkedlist)]
-#![allow(dead_code, clippy::needless_pass_by_value)]
+#![allow(unused, dead_code, clippy::needless_pass_by_value)]
 
 extern crate alloc;
 use alloc::collections::linked_list::LinkedList;
@@ -20,24 +20,29 @@ impl Foo for LinkedList<u8> {
     const BAR: Option<LinkedList<u8>> = None;
 }
 
-struct Bar;
+pub struct Bar {
+    priv_linked_list_field: LinkedList<u8>,
+    pub pub_linked_list_field: LinkedList<u8>,
+}
 impl Bar {
     fn foo(_: LinkedList<u8>) {}
 }
 
-pub fn test(my_favourite_linked_list: LinkedList<u8>) {
-    println!("{:?}", my_favourite_linked_list)
-}
-
-pub fn test_ret() -> Option<LinkedList<u8>> {
-    unimplemented!();
+// All of these test should be trigger the lint because they are not
+// part of the public api
+fn test(my_favorite_linked_list: LinkedList<u8>) {}
+fn test_ret() -> Option<LinkedList<u8>> {
+    None
 }
-
-pub fn test_local_not_linted() {
+fn test_local_not_linted() {
     let _: LinkedList<u8>;
 }
 
-fn main() {
-    test(LinkedList::new());
-    test_local_not_linted();
+// All of these test should be allowed because they are part of the
+// public api and `avoid_breaking_exported_api` is `false` by default.
+pub fn pub_test(the_most_awesome_linked_list: LinkedList<u8>) {}
+pub fn pub_test_ret() -> Option<LinkedList<u8>> {
+    None
 }
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/linkedlist.stderr b/src/tools/clippy/tests/ui/linkedlist.stderr
index 38ae71714d6..51327df1321 100644
--- a/src/tools/clippy/tests/ui/linkedlist.stderr
+++ b/src/tools/clippy/tests/ui/linkedlist.stderr
@@ -40,7 +40,15 @@ LL |     const BAR: Option<LinkedList<u8>>;
    = help: a `VecDeque` might work
 
 error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
-  --> $DIR/linkedlist.rs:25:15
+  --> $DIR/linkedlist.rs:24:29
+   |
+LL |     priv_linked_list_field: LinkedList<u8>,
+   |                             ^^^^^^^^^^^^^^
+   |
+   = help: a `VecDeque` might work
+
+error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
+  --> $DIR/linkedlist.rs:28:15
    |
 LL |     fn foo(_: LinkedList<u8>) {}
    |               ^^^^^^^^^^^^^^
@@ -48,20 +56,20 @@ LL |     fn foo(_: LinkedList<u8>) {}
    = help: a `VecDeque` might work
 
 error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
-  --> $DIR/linkedlist.rs:28:39
+  --> $DIR/linkedlist.rs:33:34
    |
-LL | pub fn test(my_favourite_linked_list: LinkedList<u8>) {
-   |                                       ^^^^^^^^^^^^^^
+LL | fn test(my_favorite_linked_list: LinkedList<u8>) {}
+   |                                  ^^^^^^^^^^^^^^
    |
    = help: a `VecDeque` might work
 
 error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
-  --> $DIR/linkedlist.rs:32:29
+  --> $DIR/linkedlist.rs:34:25
    |
-LL | pub fn test_ret() -> Option<LinkedList<u8>> {
-   |                             ^^^^^^^^^^^^^^
+LL | fn test_ret() -> Option<LinkedList<u8>> {
+   |                         ^^^^^^^^^^^^^^
    |
    = help: a `VecDeque` might work
 
-error: aborting due to 8 previous errors
+error: aborting due to 9 previous errors
 
diff --git a/src/tools/clippy/tests/ui/manual_flatten.rs b/src/tools/clippy/tests/ui/manual_flatten.rs
index b5bd35a6878..7db6b730963 100644
--- a/src/tools/clippy/tests/ui/manual_flatten.rs
+++ b/src/tools/clippy/tests/ui/manual_flatten.rs
@@ -91,6 +91,19 @@ fn main() {
         }
     }
 
+    struct Test {
+        a: usize,
+    }
+
+    let mut vec_of_struct = [Some(Test { a: 1 }), None];
+
+    // Usage of `if let` expression should not trigger lint
+    for n in vec_of_struct.iter_mut() {
+        if let Some(z) = n {
+            *n = None;
+        }
+    }
+
     // Using manual flatten should not trigger the lint
     for n in vec![Some(1), Some(2), Some(3)].iter().flatten() {
         println!("{}", n);
diff --git a/src/tools/clippy/tests/ui/manual_map_option_2.fixed b/src/tools/clippy/tests/ui/manual_map_option_2.fixed
new file mode 100644
index 00000000000..8cc12149403
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_map_option_2.fixed
@@ -0,0 +1,50 @@
+// run-rustfix
+
+#![warn(clippy::manual_map)]
+#![allow(clippy::toplevel_ref_arg)]
+
+fn main() {
+    // Lint. `y` is declared within the arm, so it isn't captured by the map closure
+    let _ = Some(0).map(|x| {
+            let y = (String::new(), String::new());
+            (x, y.0)
+        });
+
+    // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured by the map
+    // closure
+    let s = Some(String::new());
+    let _ = match &s {
+        Some(x) => Some((x.clone(), s)),
+        None => None,
+    };
+
+    // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured by the map
+    // closure
+    let s = Some(String::new());
+    let _ = match &s {
+        Some(x) => Some({
+            let clone = x.clone();
+            let s = || s;
+            (clone, s())
+        }),
+        None => None,
+    };
+
+    // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured as a mutable
+    // reference by the map closure
+    let mut s = Some(String::new());
+    let _ = match &s {
+        Some(x) => Some({
+            let clone = x.clone();
+            let ref mut s = s;
+            (clone, s)
+        }),
+        None => None,
+    };
+
+    // Lint. `s` is captured by reference, so no lifetime issues.
+    let s = Some(String::new());
+    let _ = s.as_ref().map(|x| {
+            if let Some(ref s) = s { (x.clone(), s) } else { panic!() }
+        });
+}
diff --git a/src/tools/clippy/tests/ui/manual_map_option_2.rs b/src/tools/clippy/tests/ui/manual_map_option_2.rs
new file mode 100644
index 00000000000..0862b201ead
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_map_option_2.rs
@@ -0,0 +1,56 @@
+// run-rustfix
+
+#![warn(clippy::manual_map)]
+#![allow(clippy::toplevel_ref_arg)]
+
+fn main() {
+    // Lint. `y` is declared within the arm, so it isn't captured by the map closure
+    let _ = match Some(0) {
+        Some(x) => Some({
+            let y = (String::new(), String::new());
+            (x, y.0)
+        }),
+        None => None,
+    };
+
+    // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured by the map
+    // closure
+    let s = Some(String::new());
+    let _ = match &s {
+        Some(x) => Some((x.clone(), s)),
+        None => None,
+    };
+
+    // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured by the map
+    // closure
+    let s = Some(String::new());
+    let _ = match &s {
+        Some(x) => Some({
+            let clone = x.clone();
+            let s = || s;
+            (clone, s())
+        }),
+        None => None,
+    };
+
+    // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured as a mutable
+    // reference by the map closure
+    let mut s = Some(String::new());
+    let _ = match &s {
+        Some(x) => Some({
+            let clone = x.clone();
+            let ref mut s = s;
+            (clone, s)
+        }),
+        None => None,
+    };
+
+    // Lint. `s` is captured by reference, so no lifetime issues.
+    let s = Some(String::new());
+    let _ = match &s {
+        Some(x) => Some({
+            if let Some(ref s) = s { (x.clone(), s) } else { panic!() }
+        }),
+        None => None,
+    };
+}
diff --git a/src/tools/clippy/tests/ui/manual_map_option_2.stderr b/src/tools/clippy/tests/ui/manual_map_option_2.stderr
new file mode 100644
index 00000000000..711ff6c4a4b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_map_option_2.stderr
@@ -0,0 +1,43 @@
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option_2.rs:8:13
+   |
+LL |       let _ = match Some(0) {
+   |  _____________^
+LL | |         Some(x) => Some({
+LL | |             let y = (String::new(), String::new());
+LL | |             (x, y.0)
+LL | |         }),
+LL | |         None => None,
+LL | |     };
+   | |_____^
+   |
+   = note: `-D clippy::manual-map` implied by `-D warnings`
+help: try this
+   |
+LL ~     let _ = Some(0).map(|x| {
+LL +             let y = (String::new(), String::new());
+LL +             (x, y.0)
+LL ~         });
+   |
+
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option_2.rs:50:13
+   |
+LL |       let _ = match &s {
+   |  _____________^
+LL | |         Some(x) => Some({
+LL | |             if let Some(ref s) = s { (x.clone(), s) } else { panic!() }
+LL | |         }),
+LL | |         None => None,
+LL | |     };
+   | |_____^
+   |
+help: try this
+   |
+LL ~     let _ = s.as_ref().map(|x| {
+LL +             if let Some(ref s) = s { (x.clone(), s) } else { panic!() }
+LL ~         });
+   |
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_split_once.fixed b/src/tools/clippy/tests/ui/manual_split_once.fixed
new file mode 100644
index 00000000000..3a0332939d4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_split_once.fixed
@@ -0,0 +1,50 @@
+// run-rustfix
+
+#![feature(custom_inner_attributes)]
+#![warn(clippy::manual_split_once)]
+#![allow(clippy::iter_skip_next, clippy::iter_nth_zero)]
+
+extern crate itertools;
+
+#[allow(unused_imports)]
+use itertools::Itertools;
+
+fn main() {
+    let _ = Some("key=value".split_once('=').map_or("key=value", |x| x.0));
+    let _ = "key=value".splitn(2, '=').nth(2);
+    let _ = "key=value".split_once('=').map_or("key=value", |x| x.0);
+    let _ = "key=value".split_once('=').map_or("key=value", |x| x.0);
+    let _ = "key=value".split_once('=').unwrap().1;
+    let _ = "key=value".split_once('=').unwrap().1;
+    let (_, _) = "key=value".split_once('=').unwrap();
+
+    let s = String::from("key=value");
+    let _ = s.split_once('=').map_or(&*s, |x| x.0);
+
+    let s = Box::<str>::from("key=value");
+    let _ = s.split_once('=').map_or(&*s, |x| x.0);
+
+    let s = &"key=value";
+    let _ = s.split_once('=').map_or(*s, |x| x.0);
+
+    fn _f(s: &str) -> Option<&str> {
+        let _ = s.split_once("key=value").map_or(s, |x| x.0);
+        let _ = s.split_once("key=value")?.1;
+        let _ = s.split_once("key=value")?.1;
+        None
+    }
+
+    // Don't lint, slices don't have `split_once`
+    let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
+}
+
+fn _msrv_1_51() {
+    #![clippy::msrv = "1.51"]
+    // `str::split_once` was stabilized in 1.16. Do not lint this
+    let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+}
+
+fn _msrv_1_52() {
+    #![clippy::msrv = "1.52"]
+    let _ = "key=value".split_once('=').unwrap().1;
+}
diff --git a/src/tools/clippy/tests/ui/manual_split_once.rs b/src/tools/clippy/tests/ui/manual_split_once.rs
new file mode 100644
index 00000000000..e6093b63fe8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_split_once.rs
@@ -0,0 +1,50 @@
+// run-rustfix
+
+#![feature(custom_inner_attributes)]
+#![warn(clippy::manual_split_once)]
+#![allow(clippy::iter_skip_next, clippy::iter_nth_zero)]
+
+extern crate itertools;
+
+#[allow(unused_imports)]
+use itertools::Itertools;
+
+fn main() {
+    let _ = "key=value".splitn(2, '=').next();
+    let _ = "key=value".splitn(2, '=').nth(2);
+    let _ = "key=value".splitn(2, '=').next().unwrap();
+    let _ = "key=value".splitn(2, '=').nth(0).unwrap();
+    let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+    let _ = "key=value".splitn(2, '=').skip(1).next().unwrap();
+    let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap();
+
+    let s = String::from("key=value");
+    let _ = s.splitn(2, '=').next().unwrap();
+
+    let s = Box::<str>::from("key=value");
+    let _ = s.splitn(2, '=').nth(0).unwrap();
+
+    let s = &"key=value";
+    let _ = s.splitn(2, '=').skip(0).next().unwrap();
+
+    fn _f(s: &str) -> Option<&str> {
+        let _ = s.splitn(2, "key=value").next()?;
+        let _ = s.splitn(2, "key=value").nth(1)?;
+        let _ = s.splitn(2, "key=value").skip(1).next()?;
+        None
+    }
+
+    // Don't lint, slices don't have `split_once`
+    let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
+}
+
+fn _msrv_1_51() {
+    #![clippy::msrv = "1.51"]
+    // `str::split_once` was stabilized in 1.16. Do not lint this
+    let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+}
+
+fn _msrv_1_52() {
+    #![clippy::msrv = "1.52"]
+    let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+}
diff --git a/src/tools/clippy/tests/ui/manual_split_once.stderr b/src/tools/clippy/tests/ui/manual_split_once.stderr
new file mode 100644
index 00000000000..4f15196b469
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_split_once.stderr
@@ -0,0 +1,82 @@
+error: manual implementation of `split_once`
+  --> $DIR/manual_split_once.rs:13:13
+   |
+LL |     let _ = "key=value".splitn(2, '=').next();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some("key=value".split_once('=').map_or("key=value", |x| x.0))`
+   |
+   = note: `-D clippy::manual-split-once` implied by `-D warnings`
+
+error: manual implementation of `split_once`
+  --> $DIR/manual_split_once.rs:15:13
+   |
+LL |     let _ = "key=value".splitn(2, '=').next().unwrap();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)`
+
+error: manual implementation of `split_once`
+  --> $DIR/manual_split_once.rs:16:13
+   |
+LL |     let _ = "key=value".splitn(2, '=').nth(0).unwrap();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)`
+
+error: manual implementation of `split_once`
+  --> $DIR/manual_split_once.rs:17:13
+   |
+LL |     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
+
+error: manual implementation of `split_once`
+  --> $DIR/manual_split_once.rs:18:13
+   |
+LL |     let _ = "key=value".splitn(2, '=').skip(1).next().unwrap();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
+
+error: manual implementation of `split_once`
+  --> $DIR/manual_split_once.rs:19:18
+   |
+LL |     let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap();
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=')`
+
+error: manual implementation of `split_once`
+  --> $DIR/manual_split_once.rs:22:13
+   |
+LL |     let _ = s.splitn(2, '=').next().unwrap();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)`
+
+error: manual implementation of `split_once`
+  --> $DIR/manual_split_once.rs:25:13
+   |
+LL |     let _ = s.splitn(2, '=').nth(0).unwrap();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)`
+
+error: manual implementation of `split_once`
+  --> $DIR/manual_split_once.rs:28:13
+   |
+LL |     let _ = s.splitn(2, '=').skip(0).next().unwrap();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(*s, |x| x.0)`
+
+error: manual implementation of `split_once`
+  --> $DIR/manual_split_once.rs:31:17
+   |
+LL |         let _ = s.splitn(2, "key=value").next()?;
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value").map_or(s, |x| x.0)`
+
+error: manual implementation of `split_once`
+  --> $DIR/manual_split_once.rs:32:17
+   |
+LL |         let _ = s.splitn(2, "key=value").nth(1)?;
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1`
+
+error: manual implementation of `split_once`
+  --> $DIR/manual_split_once.rs:33:17
+   |
+LL |         let _ = s.splitn(2, "key=value").skip(1).next()?;
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1`
+
+error: manual implementation of `split_once`
+  --> $DIR/manual_split_once.rs:49:13
+   |
+LL |     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
+
+error: aborting due to 13 previous errors
+
diff --git a/src/tools/clippy/tests/ui/mem_replace.fixed b/src/tools/clippy/tests/ui/mem_replace.fixed
index 3b6224254a0..b609ba65946 100644
--- a/src/tools/clippy/tests/ui/mem_replace.fixed
+++ b/src/tools/clippy/tests/ui/mem_replace.fixed
@@ -51,9 +51,29 @@ fn replace_with_default() {
 
     let mut binary_heap: BinaryHeap<i32> = BinaryHeap::new();
     let _ = std::mem::take(&mut binary_heap);
+
+    let mut tuple = (vec![1, 2], BinaryHeap::<i32>::new());
+    let _ = std::mem::take(&mut tuple);
+
+    let mut refstr = "hello";
+    let _ = std::mem::take(&mut refstr);
+
+    let mut slice: &[i32] = &[1, 2, 3];
+    let _ = std::mem::take(&mut slice);
+}
+
+// lint is disabled for primitives because in this case `take`
+// has no clear benefit over `replace` and sometimes is harder to read
+fn dont_lint_primitive() {
+    let mut pbool = true;
+    let _ = std::mem::replace(&mut pbool, false);
+
+    let mut pint = 5;
+    let _ = std::mem::replace(&mut pint, 0);
 }
 
 fn main() {
     replace_option_with_none();
     replace_with_default();
+    dont_lint_primitive();
 }
diff --git a/src/tools/clippy/tests/ui/mem_replace.rs b/src/tools/clippy/tests/ui/mem_replace.rs
index 0a36db9e921..93f6dcdec83 100644
--- a/src/tools/clippy/tests/ui/mem_replace.rs
+++ b/src/tools/clippy/tests/ui/mem_replace.rs
@@ -51,9 +51,29 @@ fn replace_with_default() {
 
     let mut binary_heap: BinaryHeap<i32> = BinaryHeap::new();
     let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new());
+
+    let mut tuple = (vec![1, 2], BinaryHeap::<i32>::new());
+    let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new()));
+
+    let mut refstr = "hello";
+    let _ = std::mem::replace(&mut refstr, "");
+
+    let mut slice: &[i32] = &[1, 2, 3];
+    let _ = std::mem::replace(&mut slice, &[]);
+}
+
+// lint is disabled for primitives because in this case `take`
+// has no clear benefit over `replace` and sometimes is harder to read
+fn dont_lint_primitive() {
+    let mut pbool = true;
+    let _ = std::mem::replace(&mut pbool, false);
+
+    let mut pint = 5;
+    let _ = std::mem::replace(&mut pint, 0);
 }
 
 fn main() {
     replace_option_with_none();
     replace_with_default();
+    dont_lint_primitive();
 }
diff --git a/src/tools/clippy/tests/ui/mem_replace.stderr b/src/tools/clippy/tests/ui/mem_replace.stderr
index f8aa1538bff..90dc6c95f85 100644
--- a/src/tools/clippy/tests/ui/mem_replace.stderr
+++ b/src/tools/clippy/tests/ui/mem_replace.stderr
@@ -98,5 +98,23 @@ error: replacing a value of type `T` with `T::default()` is better expressed usi
 LL |     let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut binary_heap)`
 
-error: aborting due to 16 previous errors
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+  --> $DIR/mem_replace.rs:56:13
+   |
+LL |     let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new()));
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut tuple)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+  --> $DIR/mem_replace.rs:59:13
+   |
+LL |     let _ = std::mem::replace(&mut refstr, "");
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut refstr)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+  --> $DIR/mem_replace.rs:62:13
+   |
+LL |     let _ = std::mem::replace(&mut slice, &[]);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut slice)`
+
+error: aborting due to 19 previous errors
 
diff --git a/src/tools/clippy/tests/ui/methods.rs b/src/tools/clippy/tests/ui/methods.rs
index 513d930e056..c441b35b992 100644
--- a/src/tools/clippy/tests/ui/methods.rs
+++ b/src/tools/clippy/tests/ui/methods.rs
@@ -32,7 +32,7 @@ use std::ops::Mul;
 use std::rc::{self, Rc};
 use std::sync::{self, Arc};
 
-use option_helpers::IteratorFalsePositives;
+use option_helpers::{IteratorFalsePositives, IteratorMethodFalsePositives};
 
 struct Lt<'a> {
     foo: &'a u32,
@@ -131,6 +131,9 @@ fn filter_next() {
     // Check that we don't lint if the caller is not an `Iterator`.
     let foo = IteratorFalsePositives { foo: 0 };
     let _ = foo.filter().next();
+
+    let foo = IteratorMethodFalsePositives {};
+    let _ = foo.filter(42).next();
 }
 
 fn main() {
diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.rs b/src/tools/clippy/tests/ui/min_rust_version_attr.rs
index 7f9f7ddc535..8d9fc5a864d 100644
--- a/src/tools/clippy/tests/ui/min_rust_version_attr.rs
+++ b/src/tools/clippy/tests/ui/min_rust_version_attr.rs
@@ -4,6 +4,11 @@
 
 use std::ops::{Deref, RangeFrom};
 
+fn approx_const() {
+    let log2_10 = 3.321928094887362;
+    let log10_2 = 0.301029995663981;
+}
+
 fn cloned_instead_of_copied() {
     let _ = [1].iter().cloned();
 }
diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr
index a2e4e86ed6b..360dcfb230c 100644
--- a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr
+++ b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr
@@ -1,12 +1,12 @@
 error: stripping a prefix manually
-  --> $DIR/min_rust_version_attr.rs:160:24
+  --> $DIR/min_rust_version_attr.rs:165:24
    |
 LL |             assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
    |                        ^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::manual-strip` implied by `-D warnings`
 note: the prefix was tested here
-  --> $DIR/min_rust_version_attr.rs:159:9
+  --> $DIR/min_rust_version_attr.rs:164:9
    |
 LL |         if s.starts_with("hello, ") {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -17,13 +17,13 @@ LL ~             assert_eq!(<stripped>.to_uppercase(), "WORLD!");
    |
 
 error: stripping a prefix manually
-  --> $DIR/min_rust_version_attr.rs:172:24
+  --> $DIR/min_rust_version_attr.rs:177:24
    |
 LL |             assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
    |                        ^^^^^^^^^^^^^^^^^^^^
    |
 note: the prefix was tested here
-  --> $DIR/min_rust_version_attr.rs:171:9
+  --> $DIR/min_rust_version_attr.rs:176:9
    |
 LL |         if s.starts_with("hello, ") {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/missing-doc-crate.stderr b/src/tools/clippy/tests/ui/missing-doc-crate.stderr
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/tools/clippy/tests/ui/missing-doc-crate.stderr
+++ /dev/null
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.stderr
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.stderr
+++ /dev/null
diff --git a/src/tools/clippy/tests/ui/mut_range_bound.rs b/src/tools/clippy/tests/ui/mut_range_bound.rs
index 1348dd2a3d8..e1ae1ef9282 100644
--- a/src/tools/clippy/tests/ui/mut_range_bound.rs
+++ b/src/tools/clippy/tests/ui/mut_range_bound.rs
@@ -1,14 +1,6 @@
 #![allow(unused)]
 
-fn main() {
-    mut_range_bound_upper();
-    mut_range_bound_lower();
-    mut_range_bound_both();
-    mut_range_bound_no_mutation();
-    immut_range_bound();
-    mut_borrow_range_bound();
-    immut_borrow_range_bound();
-}
+fn main() {}
 
 fn mut_range_bound_upper() {
     let mut m = 4;
@@ -61,3 +53,32 @@ fn immut_range_bound() {
         continue;
     } // no warning
 }
+
+fn mut_range_bound_break() {
+    let mut m = 4;
+    for i in 0..m {
+        if m == 4 {
+            m = 5; // no warning because of immediate break
+            break;
+        }
+    }
+}
+
+fn mut_range_bound_no_immediate_break() {
+    let mut m = 4;
+    for i in 0..m {
+        m = 2; // warning because it is not immediately followed by break
+        if m == 4 {
+            break;
+        }
+    }
+
+    let mut n = 3;
+    for i in n..10 {
+        if n == 4 {
+            n = 1; // FIXME: warning because is is not immediately followed by break
+            let _ = 2;
+            break;
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/mut_range_bound.stderr b/src/tools/clippy/tests/ui/mut_range_bound.stderr
index 0eeb76e0ec5..4b5a3fc1e41 100644
--- a/src/tools/clippy/tests/ui/mut_range_bound.stderr
+++ b/src/tools/clippy/tests/ui/mut_range_bound.stderr
@@ -1,34 +1,59 @@
-error: attempt to mutate range bound within loop; note that the range of the loop is unchanged
-  --> $DIR/mut_range_bound.rs:16:9
+error: attempt to mutate range bound within loop
+  --> $DIR/mut_range_bound.rs:8:9
    |
 LL |         m = 5;
    |         ^
    |
    = note: `-D clippy::mut-range-bound` implied by `-D warnings`
+   = note: the range of the loop is unchanged
 
-error: attempt to mutate range bound within loop; note that the range of the loop is unchanged
-  --> $DIR/mut_range_bound.rs:23:9
+error: attempt to mutate range bound within loop
+  --> $DIR/mut_range_bound.rs:15:9
    |
 LL |         m *= 2;
    |         ^
+   |
+   = note: the range of the loop is unchanged
 
-error: attempt to mutate range bound within loop; note that the range of the loop is unchanged
-  --> $DIR/mut_range_bound.rs:31:9
+error: attempt to mutate range bound within loop
+  --> $DIR/mut_range_bound.rs:23:9
    |
 LL |         m = 5;
    |         ^
+   |
+   = note: the range of the loop is unchanged
 
-error: attempt to mutate range bound within loop; note that the range of the loop is unchanged
-  --> $DIR/mut_range_bound.rs:32:9
+error: attempt to mutate range bound within loop
+  --> $DIR/mut_range_bound.rs:24:9
    |
 LL |         n = 7;
    |         ^
+   |
+   = note: the range of the loop is unchanged
 
-error: attempt to mutate range bound within loop; note that the range of the loop is unchanged
-  --> $DIR/mut_range_bound.rs:46:22
+error: attempt to mutate range bound within loop
+  --> $DIR/mut_range_bound.rs:38:22
    |
 LL |         let n = &mut m; // warning
    |                      ^
+   |
+   = note: the range of the loop is unchanged
+
+error: attempt to mutate range bound within loop
+  --> $DIR/mut_range_bound.rs:70:9
+   |
+LL |         m = 2; // warning because it is not immediately followed by break
+   |         ^
+   |
+   = note: the range of the loop is unchanged
+
+error: attempt to mutate range bound within loop
+  --> $DIR/mut_range_bound.rs:79:13
+   |
+LL |             n = 1; // FIXME: warning because is is not immediately followed by break
+   |             ^
+   |
+   = note: the range of the loop is unchanged
 
-error: aborting due to 5 previous errors
+error: aborting due to 7 previous errors
 
diff --git a/src/tools/clippy/tests/ui/needless_option_as_deref.fixed b/src/tools/clippy/tests/ui/needless_option_as_deref.fixed
new file mode 100644
index 00000000000..d721452ae88
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_option_as_deref.fixed
@@ -0,0 +1,13 @@
+// run-rustfix
+
+#[warn(clippy::needless_option_as_deref)]
+
+fn main() {
+    // should lint
+    let _: Option<&usize> = Some(&1);
+    let _: Option<&mut usize> = Some(&mut 1);
+
+    // should not lint
+    let _ = Some(Box::new(1)).as_deref();
+    let _ = Some(Box::new(1)).as_deref_mut();
+}
diff --git a/src/tools/clippy/tests/ui/needless_option_as_deref.rs b/src/tools/clippy/tests/ui/needless_option_as_deref.rs
new file mode 100644
index 00000000000..bb15512adf6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_option_as_deref.rs
@@ -0,0 +1,13 @@
+// run-rustfix
+
+#[warn(clippy::needless_option_as_deref)]
+
+fn main() {
+    // should lint
+    let _: Option<&usize> = Some(&1).as_deref();
+    let _: Option<&mut usize> = Some(&mut 1).as_deref_mut();
+
+    // should not lint
+    let _ = Some(Box::new(1)).as_deref();
+    let _ = Some(Box::new(1)).as_deref_mut();
+}
diff --git a/src/tools/clippy/tests/ui/needless_option_as_deref.stderr b/src/tools/clippy/tests/ui/needless_option_as_deref.stderr
new file mode 100644
index 00000000000..5dd507b4a71
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_option_as_deref.stderr
@@ -0,0 +1,16 @@
+error: derefed type is same as origin
+  --> $DIR/needless_option_as_deref.rs:7:29
+   |
+LL |     let _: Option<&usize> = Some(&1).as_deref();
+   |                             ^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&1)`
+   |
+   = note: `-D clippy::needless-option-as-deref` implied by `-D warnings`
+
+error: derefed type is same as origin
+  --> $DIR/needless_option_as_deref.rs:8:33
+   |
+LL |     let _: Option<&mut usize> = Some(&mut 1).as_deref_mut();
+   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&mut 1)`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/option_if_let_else.fixed b/src/tools/clippy/tests/ui/option_if_let_else.fixed
index 769ccc14bc1..d1815d0aec3 100644
--- a/src/tools/clippy/tests/ui/option_if_let_else.fixed
+++ b/src/tools/clippy/tests/ui/option_if_let_else.fixed
@@ -1,3 +1,4 @@
+// edition:2018
 // run-rustfix
 #![warn(clippy::option_if_let_else)]
 #![allow(clippy::redundant_closure)]
@@ -86,4 +87,65 @@ fn main() {
     test_map_or_else(None);
     let _ = negative_tests(None);
     let _ = impure_else(None);
+
+    let _ = Some(0).map_or(0, |x| loop {
+            if x == 0 {
+                break x;
+            }
+        });
+
+    // #7576
+    const fn _f(x: Option<u32>) -> u32 {
+        // Don't lint, `map_or` isn't const
+        if let Some(x) = x { x } else { 10 }
+    }
+
+    // #5822
+    let s = String::new();
+    // Don't lint, `Some` branch consumes `s`, but else branch uses `s`
+    let _ = if let Some(x) = Some(0) {
+        let s = s;
+        s.len() + x
+    } else {
+        s.len()
+    };
+
+    let s = String::new();
+    // Lint, both branches immutably borrow `s`.
+    let _ = Some(0).map_or_else(|| s.len(), |x| s.len() + x);
+
+    let s = String::new();
+    // Lint, `Some` branch consumes `s`, but else branch doesn't use `s`.
+    let _ = Some(0).map_or(1, |x| {
+        let s = s;
+        s.len() + x
+    });
+
+    let s = Some(String::new());
+    // Don't lint, `Some` branch borrows `s`, but else branch consumes `s`
+    let _ = if let Some(x) = &s {
+        x.len()
+    } else {
+        let _s = s;
+        10
+    };
+
+    let mut s = Some(String::new());
+    // Don't lint, `Some` branch mutably borrows `s`, but else branch also borrows  `s`
+    let _ = if let Some(x) = &mut s {
+        x.push_str("test");
+        x.len()
+    } else {
+        let _s = &s;
+        10
+    };
+
+    async fn _f1(x: u32) -> u32 {
+        x
+    }
+
+    async fn _f2() {
+        // Don't lint. `await` can't be moved into a closure.
+        let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 };
+    }
 }
diff --git a/src/tools/clippy/tests/ui/option_if_let_else.rs b/src/tools/clippy/tests/ui/option_if_let_else.rs
index e2f8dec3b93..a15627338cb 100644
--- a/src/tools/clippy/tests/ui/option_if_let_else.rs
+++ b/src/tools/clippy/tests/ui/option_if_let_else.rs
@@ -1,3 +1,4 @@
+// edition:2018
 // run-rustfix
 #![warn(clippy::option_if_let_else)]
 #![allow(clippy::redundant_closure)]
@@ -105,4 +106,71 @@ fn main() {
     test_map_or_else(None);
     let _ = negative_tests(None);
     let _ = impure_else(None);
+
+    let _ = if let Some(x) = Some(0) {
+        loop {
+            if x == 0 {
+                break x;
+            }
+        }
+    } else {
+        0
+    };
+
+    // #7576
+    const fn _f(x: Option<u32>) -> u32 {
+        // Don't lint, `map_or` isn't const
+        if let Some(x) = x { x } else { 10 }
+    }
+
+    // #5822
+    let s = String::new();
+    // Don't lint, `Some` branch consumes `s`, but else branch uses `s`
+    let _ = if let Some(x) = Some(0) {
+        let s = s;
+        s.len() + x
+    } else {
+        s.len()
+    };
+
+    let s = String::new();
+    // Lint, both branches immutably borrow `s`.
+    let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() };
+
+    let s = String::new();
+    // Lint, `Some` branch consumes `s`, but else branch doesn't use `s`.
+    let _ = if let Some(x) = Some(0) {
+        let s = s;
+        s.len() + x
+    } else {
+        1
+    };
+
+    let s = Some(String::new());
+    // Don't lint, `Some` branch borrows `s`, but else branch consumes `s`
+    let _ = if let Some(x) = &s {
+        x.len()
+    } else {
+        let _s = s;
+        10
+    };
+
+    let mut s = Some(String::new());
+    // Don't lint, `Some` branch mutably borrows `s`, but else branch also borrows  `s`
+    let _ = if let Some(x) = &mut s {
+        x.push_str("test");
+        x.len()
+    } else {
+        let _s = &s;
+        10
+    };
+
+    async fn _f1(x: u32) -> u32 {
+        x
+    }
+
+    async fn _f2() {
+        // Don't lint. `await` can't be moved into a closure.
+        let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 };
+    }
 }
diff --git a/src/tools/clippy/tests/ui/option_if_let_else.stderr b/src/tools/clippy/tests/ui/option_if_let_else.stderr
index 099e49ef8e3..ed748ee8b39 100644
--- a/src/tools/clippy/tests/ui/option_if_let_else.stderr
+++ b/src/tools/clippy/tests/ui/option_if_let_else.stderr
@@ -1,5 +1,5 @@
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:7:5
+  --> $DIR/option_if_let_else.rs:8:5
    |
 LL | /     if let Some(x) = string {
 LL | |         (true, x)
@@ -11,19 +11,19 @@ LL | |     }
    = note: `-D clippy::option-if-let-else` implied by `-D warnings`
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:25:13
+  --> $DIR/option_if_let_else.rs:26:13
    |
 LL |     let _ = if let Some(s) = *string { s.len() } else { 0 };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())`
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:26:13
+  --> $DIR/option_if_let_else.rs:27:13
    |
 LL |     let _ = if let Some(s) = &num { s } else { &0 };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:27:13
+  --> $DIR/option_if_let_else.rs:28:13
    |
 LL |       let _ = if let Some(s) = &mut num {
    |  _____________^
@@ -43,13 +43,13 @@ LL ~     });
    |
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:33:13
+  --> $DIR/option_if_let_else.rs:34:13
    |
 LL |     let _ = if let Some(ref s) = num { s } else { &0 };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:34:13
+  --> $DIR/option_if_let_else.rs:35:13
    |
 LL |       let _ = if let Some(mut s) = num {
    |  _____________^
@@ -69,7 +69,7 @@ LL ~     });
    |
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:40:13
+  --> $DIR/option_if_let_else.rs:41:13
    |
 LL |       let _ = if let Some(ref mut s) = num {
    |  _____________^
@@ -89,7 +89,7 @@ LL ~     });
    |
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:49:5
+  --> $DIR/option_if_let_else.rs:50:5
    |
 LL | /     if let Some(x) = arg {
 LL | |         let y = x * x;
@@ -108,7 +108,7 @@ LL +     })
    |
 
 error: use Option::map_or_else instead of an if let/else
-  --> $DIR/option_if_let_else.rs:62:13
+  --> $DIR/option_if_let_else.rs:63:13
    |
 LL |       let _ = if let Some(x) = arg {
    |  _____________^
@@ -120,7 +120,7 @@ LL | |     };
    | |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)`
 
 error: use Option::map_or_else instead of an if let/else
-  --> $DIR/option_if_let_else.rs:71:13
+  --> $DIR/option_if_let_else.rs:72:13
    |
 LL |       let _ = if let Some(x) = arg {
    |  _____________^
@@ -143,10 +143,58 @@ LL ~     }, |x| x * x * x * x);
    |
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:100:13
+  --> $DIR/option_if_let_else.rs:101:13
    |
 LL |     let _ = if let Some(x) = optional { x + 2 } else { 5 };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)`
 
-error: aborting due to 11 previous errors
+error: use Option::map_or instead of an if let/else
+  --> $DIR/option_if_let_else.rs:110:13
+   |
+LL |       let _ = if let Some(x) = Some(0) {
+   |  _____________^
+LL | |         loop {
+LL | |             if x == 0 {
+LL | |                 break x;
+...  |
+LL | |         0
+LL | |     };
+   | |_____^
+   |
+help: try
+   |
+LL ~     let _ = Some(0).map_or(0, |x| loop {
+LL +             if x == 0 {
+LL +                 break x;
+LL +             }
+LL ~         });
+   |
+
+error: use Option::map_or_else instead of an if let/else
+  --> $DIR/option_if_let_else.rs:138:13
+   |
+LL |     let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() };
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or_else(|| s.len(), |x| s.len() + x)`
+
+error: use Option::map_or instead of an if let/else
+  --> $DIR/option_if_let_else.rs:142:13
+   |
+LL |       let _ = if let Some(x) = Some(0) {
+   |  _____________^
+LL | |         let s = s;
+LL | |         s.len() + x
+LL | |     } else {
+LL | |         1
+LL | |     };
+   | |_____^
+   |
+help: try
+   |
+LL ~     let _ = Some(0).map_or(1, |x| {
+LL +         let s = s;
+LL +         s.len() + x
+LL ~     });
+   |
+
+error: aborting due to 14 previous errors
 
diff --git a/src/tools/clippy/tests/ui/proc_macro.stderr b/src/tools/clippy/tests/ui/proc_macro.stderr
index 872cbc66af6..48fd58c9a49 100644
--- a/src/tools/clippy/tests/ui/proc_macro.stderr
+++ b/src/tools/clippy/tests/ui/proc_macro.stderr
@@ -1,10 +1,11 @@
-error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly
+error: approximate value of `f{32, 64}::consts::PI` found
   --> $DIR/proc_macro.rs:10:14
    |
 LL |     let _x = 3.14;
    |              ^^^^
    |
    = note: `#[deny(clippy::approx_constant)]` on by default
+   = help: consider using the constant directly
 
 error: aborting due to previous error
 
diff --git a/src/tools/clippy/tests/ui/rc_buffer_redefined_string.stderr b/src/tools/clippy/tests/ui/rc_buffer_redefined_string.stderr
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/src/tools/clippy/tests/ui/rc_buffer_redefined_string.stderr
+++ /dev/null
diff --git a/src/tools/clippy/tests/ui/rc_mutex.rs b/src/tools/clippy/tests/ui/rc_mutex.rs
index 657a3ecf6a0..18e8a2e01e0 100644
--- a/src/tools/clippy/tests/ui/rc_mutex.rs
+++ b/src/tools/clippy/tests/ui/rc_mutex.rs
@@ -1,13 +1,17 @@
 #![warn(clippy::rc_mutex)]
-#![allow(clippy::blacklisted_name)]
+#![allow(unused, clippy::blacklisted_name)]
 
 use std::rc::Rc;
 use std::sync::Mutex;
 
-pub struct MyStruct {
+pub struct MyStructWithPrivItem {
     foo: Rc<Mutex<i32>>,
 }
 
+pub struct MyStructWithPubItem {
+    pub foo: Rc<Mutex<i32>>,
+}
+
 pub struct SubT<T> {
     foo: T,
 }
@@ -17,18 +21,16 @@ pub enum MyEnum {
     Two,
 }
 
-pub fn test1<T>(foo: Rc<Mutex<T>>) {}
-
-pub fn test2(foo: Rc<Mutex<MyEnum>>) {}
+// All of these test should be trigger the lint because they are not
+// part of the public api
+fn test1<T>(foo: Rc<Mutex<T>>) {}
+fn test2(foo: Rc<Mutex<MyEnum>>) {}
+fn test3(foo: Rc<Mutex<SubT<usize>>>) {}
 
-pub fn test3(foo: Rc<Mutex<SubT<usize>>>) {}
+// All of these test should be allowed because they are part of the
+// public api and `avoid_breaking_exported_api` is `false` by default.
+pub fn pub_test1<T>(foo: Rc<Mutex<T>>) {}
+pub fn pub_test2(foo: Rc<Mutex<MyEnum>>) {}
+pub fn pub_test3(foo: Rc<Mutex<SubT<usize>>>) {}
 
-fn main() {
-    test1(Rc::new(Mutex::new(1)));
-    test2(Rc::new(Mutex::new(MyEnum::One)));
-    test3(Rc::new(Mutex::new(SubT { foo: 1 })));
-
-    let _my_struct = MyStruct {
-        foo: Rc::new(Mutex::new(1)),
-    };
-}
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/rc_mutex.stderr b/src/tools/clippy/tests/ui/rc_mutex.stderr
index 8e58e2bc2d0..fe84361d781 100644
--- a/src/tools/clippy/tests/ui/rc_mutex.stderr
+++ b/src/tools/clippy/tests/ui/rc_mutex.stderr
@@ -1,28 +1,35 @@
-error: found `Rc<Mutex<_>>`. Consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
+error: usage of `Rc<Mutex<_>>`
   --> $DIR/rc_mutex.rs:8:10
    |
 LL |     foo: Rc<Mutex<i32>>,
    |          ^^^^^^^^^^^^^^
    |
    = note: `-D clippy::rc-mutex` implied by `-D warnings`
+   = help: consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
 
-error: found `Rc<Mutex<_>>`. Consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
-  --> $DIR/rc_mutex.rs:20:22
+error: usage of `Rc<Mutex<_>>`
+  --> $DIR/rc_mutex.rs:26:18
    |
-LL | pub fn test1<T>(foo: Rc<Mutex<T>>) {}
-   |                      ^^^^^^^^^^^^
+LL | fn test1<T>(foo: Rc<Mutex<T>>) {}
+   |                  ^^^^^^^^^^^^
+   |
+   = help: consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
 
-error: found `Rc<Mutex<_>>`. Consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
-  --> $DIR/rc_mutex.rs:22:19
+error: usage of `Rc<Mutex<_>>`
+  --> $DIR/rc_mutex.rs:27:15
+   |
+LL | fn test2(foo: Rc<Mutex<MyEnum>>) {}
+   |               ^^^^^^^^^^^^^^^^^
    |
-LL | pub fn test2(foo: Rc<Mutex<MyEnum>>) {}
-   |                   ^^^^^^^^^^^^^^^^^
+   = help: consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
 
-error: found `Rc<Mutex<_>>`. Consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
-  --> $DIR/rc_mutex.rs:24:19
+error: usage of `Rc<Mutex<_>>`
+  --> $DIR/rc_mutex.rs:28:15
+   |
+LL | fn test3(foo: Rc<Mutex<SubT<usize>>>) {}
+   |               ^^^^^^^^^^^^^^^^^^^^^^
    |
-LL | pub fn test3(foo: Rc<Mutex<SubT<usize>>>) {}
-   |                   ^^^^^^^^^^^^^^^^^^^^^^
+   = help: consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead
 
 error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui/redundant_allocation.rs b/src/tools/clippy/tests/ui/redundant_allocation.rs
index 1b4f2a66c70..52fbc91e325 100644
--- a/src/tools/clippy/tests/ui/redundant_allocation.rs
+++ b/src/tools/clippy/tests/ui/redundant_allocation.rs
@@ -77,4 +77,24 @@ mod outer_arc {
     }
 }
 
+// https://github.com/rust-lang/rust-clippy/issues/7487
+mod box_dyn {
+    use std::boxed::Box;
+    use std::rc::Rc;
+    use std::sync::Arc;
+
+    pub trait T {}
+
+    struct S {
+        a: Box<Box<dyn T>>,
+        b: Rc<Box<dyn T>>,
+        c: Arc<Box<dyn T>>,
+    }
+
+    pub fn test_box(_: Box<Box<dyn T>>) {}
+    pub fn test_rc(_: Rc<Box<dyn T>>) {}
+    pub fn test_arc(_: Arc<Box<dyn T>>) {}
+    pub fn test_rc_box(_: Rc<Box<Box<dyn T>>>) {}
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/redundant_allocation.stderr b/src/tools/clippy/tests/ui/redundant_allocation.stderr
index fdab74eb538..c3b10e5f5e6 100644
--- a/src/tools/clippy/tests/ui/redundant_allocation.stderr
+++ b/src/tools/clippy/tests/ui/redundant_allocation.stderr
@@ -134,5 +134,14 @@ LL |     pub fn arc_test9<T>(foo: Arc<Rc<T>>) -> Arc<Rc<SubT<T>>> {
    = note: `Rc<SubT<T>>` is already on the heap, `Arc<Rc<SubT<T>>>` makes an extra allocation
    = help: consider using just `Arc<SubT<T>>` or `Rc<SubT<T>>`
 
-error: aborting due to 15 previous errors
+error: usage of `Rc<Box<Box<dyn T>>>`
+  --> $DIR/redundant_allocation.rs:97:27
+   |
+LL |     pub fn test_rc_box(_: Rc<Box<Box<dyn T>>>) {}
+   |                           ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `Box<Box<dyn T>>` is already on the heap, `Rc<Box<Box<dyn T>>>` makes an extra allocation
+   = help: consider using just `Rc<Box<dyn T>>` or `Box<Box<dyn T>>`
+
+error: aborting due to 16 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.fixed b/src/tools/clippy/tests/ui/unnecessary_operation.fixed
index 2fca96c4cd5..bf0ec8deb34 100644
--- a/src/tools/clippy/tests/ui/unnecessary_operation.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.fixed
@@ -62,10 +62,10 @@ fn main() {
     get_number();
     5;get_number();
     42;get_number();
-    [42, 55];get_usize();
+    assert!([42, 55].len() > get_usize());
     42;get_number();
     get_number();
-    [42; 55];get_usize();
+    assert!([42; 55].len() > get_usize());
     get_number();
     String::from("blah");
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.stderr b/src/tools/clippy/tests/ui/unnecessary_operation.stderr
index f88c9f9908b..f66d08ecb82 100644
--- a/src/tools/clippy/tests/ui/unnecessary_operation.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.stderr
@@ -1,128 +1,128 @@
-error: statement can be reduced
+error: unnecessary operation
   --> $DIR/unnecessary_operation.rs:51:5
    |
 LL |     Tuple(get_number());
-   |     ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
+   |     ^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
    |
    = note: `-D clippy::unnecessary-operation` implied by `-D warnings`
 
-error: statement can be reduced
+error: unnecessary operation
   --> $DIR/unnecessary_operation.rs:52:5
    |
 LL |     Struct { field: get_number() };
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
-error: statement can be reduced
+error: unnecessary operation
   --> $DIR/unnecessary_operation.rs:53:5
    |
 LL |     Struct { ..get_struct() };
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_struct();`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_struct();`
 
-error: statement can be reduced
+error: unnecessary operation
   --> $DIR/unnecessary_operation.rs:54:5
    |
 LL |     Enum::Tuple(get_number());
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
-error: statement can be reduced
+error: unnecessary operation
   --> $DIR/unnecessary_operation.rs:55:5
    |
 LL |     Enum::Struct { field: get_number() };
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
-error: statement can be reduced
+error: unnecessary operation
   --> $DIR/unnecessary_operation.rs:56:5
    |
 LL |     5 + get_number();
-   |     ^^^^^^^^^^^^^^^^^ help: replace it with: `5;get_number();`
+   |     ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();`
 
-error: statement can be reduced
+error: unnecessary operation
   --> $DIR/unnecessary_operation.rs:57:5
    |
 LL |     *&get_number();
-   |     ^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
+   |     ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
-error: statement can be reduced
+error: unnecessary operation
   --> $DIR/unnecessary_operation.rs:58:5
    |
 LL |     &get_number();
-   |     ^^^^^^^^^^^^^^ help: replace it with: `get_number();`
+   |     ^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
-error: statement can be reduced
+error: unnecessary operation
   --> $DIR/unnecessary_operation.rs:59:5
    |
 LL |     (5, 6, get_number());
-   |     ^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `5;6;get_number();`
+   |     ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;6;get_number();`
 
-error: statement can be reduced
+error: unnecessary operation
   --> $DIR/unnecessary_operation.rs:60:5
    |
 LL |     box get_number();
-   |     ^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
+   |     ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
-error: statement can be reduced
+error: unnecessary operation
   --> $DIR/unnecessary_operation.rs:61:5
    |
 LL |     get_number()..;
-   |     ^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
+   |     ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
-error: statement can be reduced
+error: unnecessary operation
   --> $DIR/unnecessary_operation.rs:62:5
    |
 LL |     ..get_number();
-   |     ^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
+   |     ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
-error: statement can be reduced
+error: unnecessary operation
   --> $DIR/unnecessary_operation.rs:63:5
    |
 LL |     5..get_number();
-   |     ^^^^^^^^^^^^^^^^ help: replace it with: `5;get_number();`
+   |     ^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();`
 
-error: statement can be reduced
+error: unnecessary operation
   --> $DIR/unnecessary_operation.rs:64:5
    |
 LL |     [42, get_number()];
-   |     ^^^^^^^^^^^^^^^^^^^ help: replace it with: `42;get_number();`
+   |     ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();`
 
-error: statement can be reduced
+error: unnecessary operation
   --> $DIR/unnecessary_operation.rs:65:5
    |
 LL |     [42, 55][get_usize()];
-   |     ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `[42, 55];get_usize();`
+   |     ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42, 55].len() > get_usize());`
 
-error: statement can be reduced
+error: unnecessary operation
   --> $DIR/unnecessary_operation.rs:66:5
    |
 LL |     (42, get_number()).1;
-   |     ^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `42;get_number();`
+   |     ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();`
 
-error: statement can be reduced
+error: unnecessary operation
   --> $DIR/unnecessary_operation.rs:67:5
    |
 LL |     [get_number(); 55];
-   |     ^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();`
+   |     ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();`
 
-error: statement can be reduced
+error: unnecessary operation
   --> $DIR/unnecessary_operation.rs:68:5
    |
 LL |     [42; 55][get_usize()];
-   |     ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `[42; 55];get_usize();`
+   |     ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42; 55].len() > get_usize());`
 
-error: statement can be reduced
+error: unnecessary operation
   --> $DIR/unnecessary_operation.rs:69:5
    |
 LL | /     {
 LL | |         get_number()
 LL | |     };
-   | |______^ help: replace it with: `get_number();`
+   | |______^ help: statement can be reduced to: `get_number();`
 
-error: statement can be reduced
+error: unnecessary operation
   --> $DIR/unnecessary_operation.rs:72:5
    |
 LL | /     FooString {
 LL | |         s: String::from("blah"),
 LL | |     };
-   | |______^ help: replace it with: `String::from("blah");`
+   | |______^ help: statement can be reduced to: `String::from("blah");`
 
 error: aborting due to 20 previous errors
 
diff --git a/src/tools/clippy/tests/versioncheck.rs b/src/tools/clippy/tests/versioncheck.rs
index 1eaec4a50a6..77102b8cac0 100644
--- a/src/tools/clippy/tests/versioncheck.rs
+++ b/src/tools/clippy/tests/versioncheck.rs
@@ -1,4 +1,7 @@
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+#![warn(rust_2018_idioms, unused_lifetimes)]
 #![allow(clippy::single_match_else)]
+
 use rustc_tools_util::VersionInfo;
 
 #[test]