about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-12-07 01:25:44 +0000
committerbors <bors@rust-lang.org>2021-12-07 01:25:44 +0000
commit953f8c8b1f6e98a4da7acd28aab7e88843348a5f (patch)
tree95f52bda075c0b9051ecf619506a3703b1adcb58 /src
parent0fb1c371d4a14f9ce7a721d8aea683a6e6774f6c (diff)
parente36e5a519b84addb3a05c2ae5068d51540674cf3 (diff)
downloadrust-953f8c8b1f6e98a4da7acd28aab7e88843348a5f.tar.gz
rust-953f8c8b1f6e98a4da7acd28aab7e88843348a5f.zip
Auto merge of #91590 - flip1995:clippyup, r=Manishearth
Update Clippy

Since RLS is now already broken #91543 , we shouldn't be blocked by it anymore. I plan to do the RLS update once new rustc-ap packages are released.

r? `@Manishearth`
Diffstat (limited to 'src')
-rw-r--r--src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.md18
-rw-r--r--src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.yml44
-rw-r--r--src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.md43
-rw-r--r--src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.yml57
-rw-r--r--src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.md35
-rw-r--r--src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.yml50
-rw-r--r--src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.md44
-rw-r--r--src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.yml68
-rw-r--r--src/tools/clippy/.github/ISSUE_TEMPLATE/ice.md52
-rw-r--r--src/tools/clippy/.github/ISSUE_TEMPLATE/ice.yml48
-rw-r--r--src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.md36
-rw-r--r--src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.yml71
-rw-r--r--src/tools/clippy/.github/workflows/clippy_dev.yml12
-rw-r--r--src/tools/clippy/CHANGELOG.md20
-rw-r--r--src/tools/clippy/Cargo.toml3
-rw-r--r--src/tools/clippy/clippy_dev/Cargo.toml1
-rw-r--r--src/tools/clippy/clippy_dev/src/fmt.rs44
-rw-r--r--src/tools/clippy/clippy_dev/src/lib.rs1
-rw-r--r--src/tools/clippy/clippy_dev/src/lint.rs20
-rw-r--r--src/tools/clippy/clippy_dev/src/main.rs15
-rw-r--r--src/tools/clippy/clippy_dev/src/new_lint.rs21
-rw-r--r--src/tools/clippy/clippy_dev/src/update_lints.rs5
-rw-r--r--src/tools/clippy/clippy_lints/Cargo.toml2
-rw-r--r--src/tools/clippy/clippy_lints/src/absurd_extreme_comparisons.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/approx_const.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/arithmetic.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/as_conversions.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/asm_syntax.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/assertions_on_constants.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/assign_ops.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/async_yields_async.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/await_holding_invalid.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/bit_mask.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/blacklisted_name.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/booleans.rs30
-rw-r--r--src/tools/clippy/clippy_lints/src/bytecount.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/cargo_common_metadata.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs41
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/mod.rs29
-rw-r--r--src/tools/clippy/clippy_lints/src/checked_conversions.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/cognitive_complexity.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/collapsible_if.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/collapsible_match.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/comparison_chain.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/copies.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/copy_iterator.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/create_dir.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/dbg_macro.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/default.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/deprecated_lints.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/dereference.rs338
-rw-r--r--src/tools/clippy/clippy_lints/src/derivable_impls.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/derive.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_methods.rs (renamed from src/tools/clippy/clippy_lints/src/disallowed_method.rs)13
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_types.rs (renamed from src/tools/clippy/clippy_lints/src/disallowed_type.rs)15
-rw-r--r--src/tools/clippy/clippy_lints/src/doc.rs30
-rw-r--r--src/tools/clippy/clippy_lints/src/double_comparison.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/double_parens.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/drop_forget_ref.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/duration_subsec.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/else_if_without_else.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/empty_enum.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/entry.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/enum_clike.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/enum_variants.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/eq_op.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/equatable_if_let.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/erasing_op.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/escape.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/eta_reduction.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/eval_order_dependence.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/excessive_bools.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/exhaustive_items.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/exit.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/explicit_write.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/fallible_impl_from.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/feature_name.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/float_literal.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/format.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/format_args.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/formatting.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/from_over_into.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/from_str_radix_10.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/mod.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/future_not_send.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/get_last_with_len.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/identity_op.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/if_let_mutex.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/if_not_else.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_hasher.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_return.rs31
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/index_refutable_slice.rs276
-rw-r--r--src/tools/clippy/clippy_lints/src/indexing_slicing.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/infinite_iter.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/inherent_impl.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/inherent_to_string.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/int_plus_one.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/integer_division.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/items_after_statements.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/large_const_arrays.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/large_enum_variant.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/large_stack_arrays.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/len_zero.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/let_if_seq.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/let_underscore.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_all.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_complexity.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_internal.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_lints.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_nursery.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_style.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs136
-rw-r--r--src/tools/clippy/clippy_lints/src/lifetimes.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/literal_representation.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs58
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/mod.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/needless_collect.rs178
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/utils.rs74
-rw-r--r--src/tools/clippy/clippy_lints/src/macro_use.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/main_recursion.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_assert.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_async_fn.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_map.rs81
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_ok_or.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_strip.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/map_clone.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/map_err_ignore.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/map_unit_fn.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/match_on_vec_items.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/match_result_ok.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/match_str_case_mismatch.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/matches.rs191
-rw-r--r--src/tools/clippy/clippy_lints/src/mem_forget.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/mem_replace.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs107
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs139
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs59
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/search_is_some.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/str_splitn.rs (renamed from src/tools/clippy/clippy_lints/src/methods/manual_split_once.rs)150
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/utils.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/minmax.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/misc.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/misc_early/mod.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_doc.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_inline.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/module_style.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/multiple_crate_versions.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/mut_key.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/mut_mut.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/mut_mutex_lock.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/mut_reference.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/mutex_atomic.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_bitwise_bool.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_bool.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_borrow.rs282
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_continue.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_for_each.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_late_init.rs349
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_option_as_deref.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_question_mark.rs34
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_update.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/neg_multiply.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/new_without_default.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/no_effect.rs23
-rw-r--r--src/tools/clippy/clippy_lints/src/non_copy_const.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/non_expressive_names.rs17
-rw-r--r--src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs29
-rw-r--r--src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs33
-rw-r--r--src/tools/clippy/clippy_lints/src/octal_escapes.rs150
-rw-r--r--src/tools/clippy/clippy_lints/src/open_options.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/option_env_unwrap.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/option_if_let_else.rs17
-rw-r--r--src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/panic_unimplemented.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/precedence.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr_eq.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/question_mark.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/ranges.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_clone.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_closure_call.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_else.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_field_names.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_slicing.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/ref_option_ref.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/reference.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/regex.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/repeat_once.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/returns.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/same_name_method.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/self_assignment.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/self_named_constructors.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/serde_api.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/shadow.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/single_component_path_imports.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/strings.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs67
-rw-r--r--src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/swap.rs50
-rw-r--r--src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/temporary_assignment.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/to_digit_is_some.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/to_string_in_display.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/trailing_empty_array.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/trait_bounds.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/mod.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/transmuting_null.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/try_err.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/types/mod.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/unicode.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/uninit_vec.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_hash.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_types/mod.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/unnamed_address.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_async.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_io_amount.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_self.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_unit.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/unwrap.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/unwrap_in_result.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/use_self.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/useless_conversion.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/author.rs1159
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/conf.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints.rs80
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs29
-rw-r--r--src/tools/clippy/clippy_lints/src/vec.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/vec_init_then_push.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/verbose_file_reads.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/wildcard_dependencies.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/wildcard_imports.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/write.rs29
-rw-r--r--src/tools/clippy/clippy_lints/src/zero_div_zero.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs1
-rw-r--r--src/tools/clippy/clippy_utils/Cargo.toml2
-rw-r--r--src/tools/clippy/clippy_utils/src/ast_utils.rs115
-rw-r--r--src/tools/clippy/clippy_utils/src/attrs.rs15
-rw-r--r--src/tools/clippy/clippy_utils/src/eager_or_lazy.rs307
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs120
-rw-r--r--src/tools/clippy/clippy_utils/src/msrvs.rs5
-rw-r--r--src/tools/clippy/clippy_utils/src/paths.rs5
-rw-r--r--src/tools/clippy/clippy_utils/src/ptr.rs58
-rw-r--r--src/tools/clippy/clippy_utils/src/source.rs16
-rw-r--r--src/tools/clippy/clippy_utils/src/sugg.rs277
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs39
-rw-r--r--src/tools/clippy/clippy_utils/src/usage.rs111
-rw-r--r--src/tools/clippy/clippy_utils/src/visitors.rs289
-rw-r--r--src/tools/clippy/doc/adding_lints.md15
-rw-r--r--src/tools/clippy/doc/changelog_update.md2
-rw-r--r--src/tools/clippy/doc/common_tools_writing_lints.md155
-rw-r--r--src/tools/clippy/lintcheck/src/main.rs4
-rw-r--r--src/tools/clippy/rust-toolchain4
-rw-r--r--src/tools/clippy/src/driver.rs2
-rw-r--r--src/tools/clippy/tests/compile-test.rs3
-rw-r--r--src/tools/clippy/tests/fmt.rs5
-rw-r--r--src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.rs87
-rw-r--r--src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.stderr73
-rw-r--r--src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed1
-rw-r--r--src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs1
-rw-r--r--src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr10
-rw-r--r--src/tools/clippy/tests/ui-internal/custom_ice_message.rs1
-rw-r--r--src/tools/clippy/tests/ui-internal/default_lint.rs1
-rw-r--r--src/tools/clippy/tests/ui-internal/default_lint.stderr2
-rw-r--r--src/tools/clippy/tests/ui-internal/if_chain_style.rs2
-rw-r--r--src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed1
-rw-r--r--src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs1
-rw-r--r--src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr8
-rw-r--r--src/tools/clippy/tests/ui-internal/invalid_paths.rs1
-rw-r--r--src/tools/clippy/tests/ui-internal/invalid_paths.stderr4
-rw-r--r--src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs1
-rw-r--r--src/tools/clippy/tests/ui-internal/lint_without_lint_pass.stderr2
-rw-r--r--src/tools/clippy/tests/ui-internal/match_type_on_diag_item.rs1
-rw-r--r--src/tools/clippy/tests/ui-internal/match_type_on_diag_item.stderr6
-rw-r--r--src/tools/clippy/tests/ui-internal/outer_expn_data.fixed1
-rw-r--r--src/tools/clippy/tests/ui-internal/outer_expn_data.rs1
-rw-r--r--src/tools/clippy/tests/ui-internal/outer_expn_data.stderr2
-rw-r--r--src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed6
-rw-r--r--src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs6
-rw-r--r--src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr10
-rw-r--r--src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs23
-rw-r--r--src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr22
-rw-r--r--src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.rs10
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml (renamed from src/tools/clippy/tests/ui-toml/toml_disallowed_method/clippy.toml)0
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs (renamed from src/tools/clippy/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.rs)2
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr (renamed from src/tools/clippy/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr)8
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_disallowed_types/clippy.toml (renamed from src/tools/clippy/tests/ui-toml/toml_disallowed_type/clippy.toml)0
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs (renamed from src/tools/clippy/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs)2
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr (renamed from src/tools/clippy/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr)44
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr2
-rw-r--r--src/tools/clippy/tests/ui/author.stdout12
-rw-r--r--src/tools/clippy/tests/ui/author/blocks.rs9
-rw-r--r--src/tools/clippy/tests/ui/author/blocks.stdout68
-rw-r--r--src/tools/clippy/tests/ui/author/call.stdout14
-rw-r--r--src/tools/clippy/tests/ui/author/for_loop.rs8
-rw-r--r--src/tools/clippy/tests/ui/author/for_loop.stdout49
-rw-r--r--src/tools/clippy/tests/ui/author/if.rs7
-rw-r--r--src/tools/clippy/tests/ui/author/if.stdout54
-rw-r--r--src/tools/clippy/tests/ui/author/issue_3849.stdout14
-rw-r--r--src/tools/clippy/tests/ui/author/loop.rs36
-rw-r--r--src/tools/clippy/tests/ui/author/loop.stdout113
-rw-r--r--src/tools/clippy/tests/ui/author/matches.stdout49
-rw-r--r--src/tools/clippy/tests/ui/author/repeat.rs5
-rw-r--r--src/tools/clippy/tests/ui/author/repeat.stdout11
-rw-r--r--src/tools/clippy/tests/ui/author/struct.rs40
-rw-r--r--src/tools/clippy/tests/ui/author/struct.stdout64
-rw-r--r--src/tools/clippy/tests/ui/cast_lossless_bool.fixed42
-rw-r--r--src/tools/clippy/tests/ui/cast_lossless_bool.rs42
-rw-r--r--src/tools/clippy/tests/ui/cast_lossless_bool.stderr82
-rw-r--r--src/tools/clippy/tests/ui/crashes/auxiliary/ice-7934-aux.rs4
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-7934.rs7
-rw-r--r--src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.rs14
-rw-r--r--src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.stderr12
-rw-r--r--src/tools/clippy/tests/ui/doc/doc-fixable.stderr209
-rw-r--r--src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr21
-rw-r--r--src/tools/clippy/tests/ui/explicit_counter_loop.rs30
-rw-r--r--src/tools/clippy/tests/ui/explicit_counter_loop.stderr16
-rw-r--r--src/tools/clippy/tests/ui/floating_point_abs.fixed6
-rw-r--r--src/tools/clippy/tests/ui/floating_point_abs.rs6
-rw-r--r--src/tools/clippy/tests/ui/floating_point_abs.stderr16
-rw-r--r--src/tools/clippy/tests/ui/floating_point_mul_add.fixed11
-rw-r--r--src/tools/clippy/tests/ui/floating_point_mul_add.rs11
-rw-r--r--src/tools/clippy/tests/ui/floating_point_mul_add.stderr20
-rw-r--r--src/tools/clippy/tests/ui/floating_point_rad.fixed7
-rw-r--r--src/tools/clippy/tests/ui/floating_point_rad.rs7
-rw-r--r--src/tools/clippy/tests/ui/floating_point_rad.stderr4
-rw-r--r--src/tools/clippy/tests/ui/if_then_some_else_none.rs11
-rw-r--r--src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs166
-rw-r--r--src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.stderr158
-rw-r--r--src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs28
-rw-r--r--src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr22
-rw-r--r--src/tools/clippy/tests/ui/iter_cloned_collect.fixed3
-rw-r--r--src/tools/clippy/tests/ui/iter_cloned_collect.rs3
-rw-r--r--src/tools/clippy/tests/ui/iter_cloned_collect.stderr8
-rw-r--r--src/tools/clippy/tests/ui/let_if_seq.rs3
-rw-r--r--src/tools/clippy/tests/ui/let_if_seq.stderr8
-rw-r--r--src/tools/clippy/tests/ui/let_underscore_lock.rs14
-rw-r--r--src/tools/clippy/tests/ui/let_underscore_lock.stderr46
-rw-r--r--src/tools/clippy/tests/ui/manual_assert.edition2018.fixed2
-rw-r--r--src/tools/clippy/tests/ui/manual_assert.edition2018.stderr14
-rw-r--r--src/tools/clippy/tests/ui/manual_assert.edition2021.fixed2
-rw-r--r--src/tools/clippy/tests/ui/manual_assert.edition2021.stderr14
-rw-r--r--src/tools/clippy/tests/ui/manual_assert.fixed2
-rw-r--r--src/tools/clippy/tests/ui/manual_assert.rs2
-rw-r--r--src/tools/clippy/tests/ui/manual_map_option_2.fixed10
-rw-r--r--src/tools/clippy/tests/ui/manual_map_option_2.rs19
-rw-r--r--src/tools/clippy/tests/ui/manual_map_option_2.stderr32
-rw-r--r--src/tools/clippy/tests/ui/manual_split_once.fixed6
-rw-r--r--src/tools/clippy/tests/ui/manual_split_once.rs2
-rw-r--r--src/tools/clippy/tests/ui/manual_split_once.stderr10
-rw-r--r--src/tools/clippy/tests/ui/match_overlapping_arm.rs18
-rw-r--r--src/tools/clippy/tests/ui/match_overlapping_arm.stderr26
-rw-r--r--src/tools/clippy/tests/ui/min_max.rs7
-rw-r--r--src/tools/clippy/tests/ui/min_max.stderr26
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_attr.rs19
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_attr.stderr8
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow.fixed23
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow.rs23
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow.stderr50
-rw-r--r--src/tools/clippy/tests/ui/needless_collect_indirect.rs31
-rw-r--r--src/tools/clippy/tests/ui/needless_late_init.rs167
-rw-r--r--src/tools/clippy/tests/ui/needless_late_init.stderr150
-rw-r--r--src/tools/clippy/tests/ui/needless_late_init_fixable.fixed38
-rw-r--r--src/tools/clippy/tests/ui/needless_late_init_fixable.rs38
-rw-r--r--src/tools/clippy/tests/ui/needless_late_init_fixable.stderr103
-rw-r--r--src/tools/clippy/tests/ui/needless_question_mark.stderr24
-rw-r--r--src/tools/clippy/tests/ui/needless_return.fixed1
-rw-r--r--src/tools/clippy/tests/ui/needless_return.rs1
-rw-r--r--src/tools/clippy/tests/ui/needless_return.stderr36
-rw-r--r--src/tools/clippy/tests/ui/needless_splitn.fixed27
-rw-r--r--src/tools/clippy/tests/ui/needless_splitn.rs27
-rw-r--r--src/tools/clippy/tests/ui/needless_splitn.stderr40
-rw-r--r--src/tools/clippy/tests/ui/no_effect.rs35
-rw-r--r--src/tools/clippy/tests/ui/no_effect.stderr60
-rw-r--r--src/tools/clippy/tests/ui/non_send_fields_in_send_ty.rs5
-rw-r--r--src/tools/clippy/tests/ui/non_send_fields_in_send_ty.stderr20
-rw-r--r--src/tools/clippy/tests/ui/octal_escapes.rs20
-rw-r--r--src/tools/clippy/tests/ui/octal_escapes.stderr131
-rw-r--r--src/tools/clippy/tests/ui/option_env_unwrap.rs1
-rw-r--r--src/tools/clippy/tests/ui/option_env_unwrap.stderr12
-rw-r--r--src/tools/clippy/tests/ui/option_filter_map.fixed10
-rw-r--r--src/tools/clippy/tests/ui/option_filter_map.rs10
-rw-r--r--src/tools/clippy/tests/ui/option_filter_map.stderr16
-rw-r--r--src/tools/clippy/tests/ui/option_if_let_else.fixed15
-rw-r--r--src/tools/clippy/tests/ui/option_if_let_else.rs17
-rw-r--r--src/tools/clippy/tests/ui/option_if_let_else.stderr24
-rw-r--r--src/tools/clippy/tests/ui/option_map_or_none.fixed18
-rw-r--r--src/tools/clippy/tests/ui/option_map_or_none.rs16
-rw-r--r--src/tools/clippy/tests/ui/option_map_or_none.stderr53
-rw-r--r--src/tools/clippy/tests/ui/or_fun_call.fixed30
-rw-r--r--src/tools/clippy/tests/ui/or_fun_call.rs18
-rw-r--r--src/tools/clippy/tests/ui/or_fun_call.stderr60
-rw-r--r--src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.rs9
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_late.rs1
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_late.stderr6
-rw-r--r--src/tools/clippy/tests/ui/redundant_else.rs2
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed6
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs6
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr14
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed4
-rw-r--r--src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr4
-rw-r--r--src/tools/clippy/tests/ui/rename.fixed5
-rw-r--r--src/tools/clippy/tests/ui/rename.rs5
-rw-r--r--src/tools/clippy/tests/ui/rename.stderr74
-rw-r--r--src/tools/clippy/tests/ui/same_name_method.stderr10
-rw-r--r--src/tools/clippy/tests/ui/search_is_some.rs6
-rw-r--r--src/tools/clippy/tests/ui/search_is_some.stderr20
-rw-r--r--src/tools/clippy/tests/ui/search_is_some_fixable.fixed68
-rw-r--r--src/tools/clippy/tests/ui/search_is_some_fixable.rs68
-rw-r--r--src/tools/clippy/tests/ui/search_is_some_fixable.stderr184
-rw-r--r--src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed216
-rw-r--r--src/tools/clippy/tests/ui/search_is_some_fixable_none.rs222
-rw-r--r--src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr293
-rw-r--r--src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed218
-rw-r--r--src/tools/clippy/tests/ui/search_is_some_fixable_some.rs221
-rw-r--r--src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr276
-rw-r--r--src/tools/clippy/tests/ui/semicolon_if_nothing_returned.rs10
-rw-r--r--src/tools/clippy/tests/ui/semicolon_if_nothing_returned.stderr10
-rw-r--r--src/tools/clippy/tests/ui/shadow.rs6
-rw-r--r--src/tools/clippy/tests/ui/shadow.stderr14
-rw-r--r--src/tools/clippy/tests/ui/single_char_pattern.fixed10
-rw-r--r--src/tools/clippy/tests/ui/single_char_pattern.rs10
-rw-r--r--src/tools/clippy/tests/ui/single_char_pattern.stderr102
-rw-r--r--src/tools/clippy/tests/ui/strlen_on_c_strings.fixed34
-rw-r--r--src/tools/clippy/tests/ui/strlen_on_c_strings.rs22
-rw-r--r--src/tools/clippy/tests/ui/strlen_on_c_strings.stderr47
-rw-r--r--src/tools/clippy/tests/ui/suspicious_splitn.rs1
-rw-r--r--src/tools/clippy/tests/ui/suspicious_splitn.stderr18
-rw-r--r--src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs4
-rw-r--r--src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr14
-rw-r--r--src/tools/clippy/tests/ui/unicode.rs8
-rw-r--r--src/tools/clippy/tests/ui/unicode.stderr14
-rw-r--r--src/tools/clippy/triagebot.toml5
-rw-r--r--src/tools/clippy/util/gh-pages/index.html16
491 files changed, 9876 insertions, 3243 deletions
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.md b/src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.md
deleted file mode 100644
index 866303a1f9f..00000000000
--- a/src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.md
+++ /dev/null
@@ -1,18 +0,0 @@
----
-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/blank_issue.yml b/src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.yml
new file mode 100644
index 00000000000..d610e8c7bc4
--- /dev/null
+++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.yml
@@ -0,0 +1,44 @@
+name: Blank Issue
+description: Create a blank issue.
+body:
+  - type: markdown
+    attributes:
+      value: Thank you for filing an issue!
+  - type: textarea
+    id: problem
+    attributes:
+      label: Description
+      description: >
+        Please provide a discription of the issue, along with any information
+        you feel relevant to replicate it.
+    validations:
+      required: true
+  - type: textarea
+    id: version
+    attributes:
+      label: Version
+      description: "Rust version (`rustc -Vv`)"
+      placeholder: |
+        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
+      render: text
+  - type: textarea
+    id: labels
+    attributes:
+      label: Additional Labels
+      description: >
+        Additional labels can be added to this issue by including the following
+        command
+      placeholder: |
+        @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
deleted file mode 100644
index 119a498fb99..00000000000
--- a/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,43 +0,0 @@
----
-name: Bug Report
-about: Create a bug report for Clippy
-labels: C-bug
----
-<!--
-Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
-along with any information you feel relevant to replicating the bug.
--->
-
-I tried this code:
-
-```rust
-<code>
-```
-
-I expected to see this happen: *explanation*
-
-Instead, this happened: *explanation*
-
-### Meta
-
-**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/bug_report.yml b/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 00000000000..68877efc9e1
--- /dev/null
+++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,57 @@
+name: Bug Report
+description: Create a bug report for Clippy
+labels: ["C-bug"]
+body:
+  - type: markdown
+    attributes:
+      value: Thank you for filing a bug report! 🐛
+  - type: textarea
+    id: problem
+    attributes:
+      label: Summary
+      description: >
+        Please provide a short summary of the bug, along with any information
+        you feel relevant to replicate the bug.
+    validations:
+      required: true
+  - type: textarea
+    id: reproducer
+    attributes:
+      label: Reproducer
+      description: Please provide the code and steps to repoduce the bug
+      value: |
+        I tried this code:
+
+        ```rust
+        <code>
+        ```
+
+        I expected to see this happen:
+
+        Instead, this happened:
+  - type: textarea
+    id: version
+    attributes:
+      label: Version
+      description: "Rust version (`rustc -Vv`)"
+      placeholder: |
+        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
+      render: text
+  - type: textarea
+    id: labels
+    attributes:
+      label: Additional Labels
+      description: >
+        Additional labels can be added to this issue by including the following
+        command
+      placeholder: |
+        @rustbot label +<label>
+
+        Common labels for this issue type are:
+        * `I-suggestion-causes-error`
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.md b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.md
deleted file mode 100644
index d9ea2db34ed..00000000000
--- a/src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.md
+++ /dev/null
@@ -1,35 +0,0 @@
----
-name: Bug Report (False Negative)
-about: Create a bug report about missing warnings from a lint
-labels: C-bug, I-false-negative
----
-<!--
-Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
-along with any information you feel relevant to replicating the bug.
--->
-Lint name:
-
-
-I tried this code:
-
-```rust
-<code>
-```
-
-I expected to see this happen: *explanation*
-
-Instead, this happened: *explanation*
-
-### Meta
-
-**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_negative.yml b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.yml
new file mode 100644
index 00000000000..9357ccc4f4e
--- /dev/null
+++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.yml
@@ -0,0 +1,50 @@
+name: Bug Report (False Negative)
+description: Create a bug report about missing warnings from a lint
+labels: ["C-bug", "I-false-negative"]
+body:
+  - type: markdown
+    attributes:
+      value: Thank you for filing a bug report! 🐛
+  - type: textarea
+    id: problem
+    attributes:
+      label: Summary
+      description: >
+        Please provide a short summary of the bug, along with any information
+        you feel relevant to replicate the bug.
+    validations:
+      required: true
+  - type: input
+    id: lint-name
+    attributes:
+      label: Lint Name
+      description: Please provide the lint name.
+  - type: textarea
+    id: reproducer
+    attributes:
+      label: Reproducer
+      description: Please provide the code and steps to repoduce the bug
+      value: |
+        I tried this code:
+
+        ```rust
+        <code>
+        ```
+
+        I expected to see this happen:
+
+        Instead, this happened:
+  - type: textarea
+    id: version
+    attributes:
+      label: Version
+      description: "Rust version (`rustc -Vv`)"
+      placeholder: |
+        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
+      render: text
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.md b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.md
deleted file mode 100644
index 82158e02f08..00000000000
--- a/src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.md
+++ /dev/null
@@ -1,44 +0,0 @@
----
-name: Bug Report (False Positive)
-about: Create a bug report about a wrongly emitted lint warning
-labels: C-bug, I-false-positive
----
-<!--
-Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
-along with any information you feel relevant to replicating the bug.
--->
-Lint name:
-
-
-I tried this code:
-
-```rust
-<code>
-```
-
-I expected to see this happen: *explanation*
-
-Instead, this happened: *explanation*
-
-### Meta
-
-**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/false_positive.yml b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.yml
new file mode 100644
index 00000000000..b7dd400ee73
--- /dev/null
+++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.yml
@@ -0,0 +1,68 @@
+name: Bug Report (False Positive)
+description: Create a bug report about a wrongly emitted lint warning
+labels: ["C-bug", "I-false-positive"]
+body:
+  - type: markdown
+    attributes:
+      value: Thank you for filing a bug report! 🐛
+  - type: textarea
+    id: problem
+    attributes:
+      label: Summary
+      description: >
+        Please provide a short summary of the bug, along with any information
+        you feel relevant to replicate the bug.
+    validations:
+      required: true
+  - type: input
+    id: lint-name
+    attributes:
+      label: Lint Name
+      description: Please provide the lint name.
+  - type: textarea
+    id: reproducer
+    attributes:
+      label: Reproducer
+      description: >
+        Please provide the code and steps to repoduce the bug together with the
+        output from Clippy.
+      value: |
+        I tried this code:
+
+        ```rust
+        <code>
+        ```
+
+        I saw this happen:
+
+        ```
+        <output>
+        ```
+
+        I expected to see this happen:
+  - type: textarea
+    id: version
+    attributes:
+      label: Version
+      description: "Rust version (`rustc -Vv`)"
+      placeholder: |
+        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
+      render: text
+  - type: textarea
+    id: labels
+    attributes:
+      label: Additional Labels
+      description: >
+        Additional labels can be added to this issue by including the following
+        command
+      placeholder: |
+        @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
deleted file mode 100644
index 6c1bed663c6..00000000000
--- a/src/tools/clippy/.github/ISSUE_TEMPLATE/ice.md
+++ /dev/null
@@ -1,52 +0,0 @@
----
-name: Internal Compiler Error
-about: Create a report for an internal compiler error in Clippy.
-labels: C-bug, I-ICE
----
-<!--
-Thank you for finding an Internal Compiler Error! 🧊  If possible, try to provide
-a minimal verifiable example. You can read "Rust Bug Minimization Patterns" for
-how to create smaller examples.
-
-http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/
-
--->
-
-### Code
-
-```rust
-<code>
-```
-
-### Meta
-
-**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
-
-```
-<output>
-```
-
-<!--
-Include a backtrace in the code block by setting `RUST_BACKTRACE=1` in your
-environment. E.g. `RUST_BACKTRACE=1 cargo clippy`.
--->
-<details><summary>Backtrace</summary>
-  <p>
-  
-  ```
-  <backtrace>
-  ```
-  
-  </p>
-</details>
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/ice.yml b/src/tools/clippy/.github/ISSUE_TEMPLATE/ice.yml
new file mode 100644
index 00000000000..2a5b8b3c891
--- /dev/null
+++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/ice.yml
@@ -0,0 +1,48 @@
+name: Internal Compiler Error
+description: Create a report for an internal compiler error (ICE) in Clippy.
+labels: ["C-bug", "I-ICE"]
+body:
+  - type: markdown
+    attributes:
+      value: Thank you for finding an Internal Compiler Error! 🧊
+  - type: textarea
+    id: problem
+    attributes:
+      label: Summary
+      description: |
+        If possible, try to provide a minimal verifiable example. You can read ["Rust Bug Minimization Patterns"][mve] for how to create smaller examples. Otherwise, provide the crate where the ICE occured.
+
+        [mve]: http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/
+    validations:
+      required: true
+  - type: textarea
+    id: version
+    attributes:
+      label: Version
+      description: "Rust version (`rustc -Vv`)"
+      placeholder: |
+        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
+      render: text
+  - type: textarea
+    id: error
+    attributes:
+      label: Error output
+      description: >
+        Include a backtrace in the code block by setting `RUST_BACKTRACE=1` in
+        your environment. E.g. `RUST_BACKTRACE=1 cargo clippy`.
+      value: |
+        <details><summary>Backtrace</summary>
+          <p>
+
+          ```
+          <backtrace>
+          ```
+
+          </p>
+        </details>
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.md b/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.md
deleted file mode 100644
index 2216bb9f293..00000000000
--- a/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.md
+++ /dev/null
@@ -1,36 +0,0 @@
----
-name: New lint suggestion
-about: Suggest a new Clippy lint.
-labels: A-lint
----
-
-### What it does
-
-*What does this lint do?*
-
-### Categories (optional)
-
-- Kind: *See <https://github.com/rust-lang/rust-clippy/blob/master/README.md#clippy> for list of lint kinds*
-
-*What is the advantage of the recommended code over the original code*
-
-For example:
-- Remove bounds check inserted by ...
-- Remove the need to duplicate/store ...
-- Remove typo ...
-
-### Drawbacks
-
-None.
-
-### Example
-
-```rust
-<code>
-```
-
-Could be written as:
-
-```rust
-<code>
-```
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.yml b/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.yml
new file mode 100644
index 00000000000..0b43d8d70c0
--- /dev/null
+++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.yml
@@ -0,0 +1,71 @@
+name: New lint suggestion
+description: Suggest a new Clippy lint.
+labels: ["A-lint"]
+body:
+  - type: markdown
+    attributes:
+      value: Thank you for your lint idea!
+  - type: textarea
+    id: what
+    attributes:
+      label: What it does
+      description: What does this lint do?
+    validations:
+      required: true
+  - type: input
+    id: lint-name
+    attributes:
+      label: Lint Name
+      description: Please provide the lint name.
+  - type: dropdown
+    id: category
+    attributes:
+      label: Category
+      description: >
+        What category should this lint go into? If you're unsure you can select
+        multiple categories. You can find a category description in the
+        `README`.
+      multiple: true
+      options:
+        - correctness
+        - suspicious
+        - style
+        - complexity
+        - perf
+        - pedantic
+        - restriction
+        - cargo
+  - type: textarea
+    id: advantage
+    attributes:
+      label: Advantage
+      description: >
+        What is the advantage of the recommended code over the original code?
+      placeholder: |
+        - Remove bounds check inserted by ...
+        - Remove the need to duplicate/store ...
+        - Remove typo ...
+  - type: textarea
+    id: drawbacks
+    attributes:
+      label: Drawbacks
+      description: What might be possible drawbacks of such a lint?
+  - type: textarea
+    id: example
+    attributes:
+      label: Example
+      description: >
+        Include a short example showing when the lint should trigger together
+        with the improved code.
+      value: |
+        ```rust
+        <code>
+        ```
+
+        Could be written as:
+
+        ```rust
+        <code>
+        ```
+    validations:
+      required: true
diff --git a/src/tools/clippy/.github/workflows/clippy_dev.yml b/src/tools/clippy/.github/workflows/clippy_dev.yml
index 9a5416153ab..fe8bce00fa8 100644
--- a/src/tools/clippy/.github/workflows/clippy_dev.yml
+++ b/src/tools/clippy/.github/workflows/clippy_dev.yml
@@ -25,18 +25,6 @@ jobs:
     - name: Checkout
       uses: actions/checkout@v2.3.3
 
-    - name: remove toolchain file
-      run: rm rust-toolchain
-
-    - name: rust-toolchain
-      uses: actions-rs/toolchain@v1.0.6
-      with:
-        toolchain: nightly
-        target: x86_64-unknown-linux-gnu
-        profile: minimal
-        components: rustfmt
-        default: true
-
     # Run
     - name: Build
       run: cargo build --features deny-warnings
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index 85a6a6be8b7..157ea0c963a 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -70,7 +70,7 @@ Current beta, release 2021-12-02
   [#7560](https://github.com/rust-lang/rust-clippy/pull/7560)
 * [`unnecessary_unwrap`]: Now also checks for `expect`s
   [#7584](https://github.com/rust-lang/rust-clippy/pull/7584)
-* [`disallowed_method`]: Allow adding a reason that will be displayed with the
+* [`disallowed_methods`]: Allow adding a reason that will be displayed with the
   lint message
   [#7621](https://github.com/rust-lang/rust-clippy/pull/7621)
 * [`approx_constant`]: Now checks the MSRV for `LOG10_2` and `LOG2_10`
@@ -174,7 +174,7 @@ Current stable, released 2021-10-21
 
 * [`needless_continue`]: Now also lints in `loop { continue; }` case
   [#7477](https://github.com/rust-lang/rust-clippy/pull/7477)
-* [`disallowed_type`]: Now also primitive types can be disallowed
+* [`disallowed_types`]: Now also primitive types can be disallowed
   [#7488](https://github.com/rust-lang/rust-clippy/pull/7488)
 * [`manual_swap`]: Now also lints on xor swaps
   [#7506](https://github.com/rust-lang/rust-clippy/pull/7506)
@@ -248,7 +248,7 @@ Released 2021-09-09
   [#7403](https://github.com/rust-lang/rust-clippy/pull/7403)
 * [`disallowed_script_idents`]
   [#7400](https://github.com/rust-lang/rust-clippy/pull/7400)
-* [`disallowed_type`]
+* [`disallowed_types`]
   [#7315](https://github.com/rust-lang/rust-clippy/pull/7315)
 * [`missing_enforced_import_renames`]
   [#7300](https://github.com/rust-lang/rust-clippy/pull/7300)
@@ -294,7 +294,7 @@ Released 2021-09-09
   [#7379](https://github.com/rust-lang/rust-clippy/pull/7379)
 * [`redundant_closure`]: Suggests `&mut` for `FnMut`
   [#7437](https://github.com/rust-lang/rust-clippy/pull/7437)
-* [`disallowed_method`], [`disallowed_type`]: The configuration values `disallowed-method` and `disallowed-type`
+* [`disallowed_methods`], [`disallowed_types`]: The configuration values `disallowed-method` and `disallowed-type`
   no longer require fully qualified paths
   [#7345](https://github.com/rust-lang/rust-clippy/pull/7345)
 * [`zst_offset`]: Fixed lint invocation after it was accidentally suppressed
@@ -703,7 +703,7 @@ Released 2021-05-06
 
 ### Enhancements
 
-* [`disallowed_method`]: Now supports functions in addition to methods
+* [`disallowed_methods`]: Now supports functions in addition to methods
   [#6674](https://github.com/rust-lang/rust-clippy/pull/6674)
 * [`upper_case_acronyms`]: Added a new configuration `upper-case-acronyms-aggressive` to
   trigger the lint if there is more than one uppercase character next to each other
@@ -1044,7 +1044,7 @@ Released 2020-12-31
 
 * [`field_reassign_with_default`] [#5911](https://github.com/rust-lang/rust-clippy/pull/5911)
 * [`await_holding_refcell_ref`] [#6029](https://github.com/rust-lang/rust-clippy/pull/6029)
-* [`disallowed_method`] [#6081](https://github.com/rust-lang/rust-clippy/pull/6081)
+* [`disallowed_methods`] [#6081](https://github.com/rust-lang/rust-clippy/pull/6081)
 * [`inline_asm_x86_att_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
 * [`inline_asm_x86_intel_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
 * [`from_iter_instead_of_collect`] [#6101](https://github.com/rust-lang/rust-clippy/pull/6101)
@@ -2821,9 +2821,9 @@ Released 2018-09-13
 [`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
+[`disallowed_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods
 [`disallowed_script_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents
-[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
+[`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types
 [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
 [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
 [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
@@ -2904,6 +2904,7 @@ Released 2018-09-13
 [`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops
 [`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping
 [`inconsistent_struct_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor
+[`index_refutable_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice
 [`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing
 [`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask
 [`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string
@@ -3032,12 +3033,14 @@ Released 2018-09-13
 [`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue
 [`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_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init
 [`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
 [`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
+[`needless_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_splitn
 [`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
@@ -3054,6 +3057,7 @@ Released 2018-09-13
 [`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
 [`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces
 [`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref
+[`octal_escapes`]: https://rust-lang.github.io/rust-clippy/master/index.html#octal_escapes
 [`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
 [`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
 [`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
index 602877bb9d6..8661a867758 100644
--- a/src/tools/clippy/Cargo.toml
+++ b/src/tools/clippy/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy"
-version = "0.1.58"
+version = "0.1.59"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
@@ -47,6 +47,7 @@ itertools = "0.10"
 quote = "1.0"
 serde = { version = "1.0", features = ["derive"] }
 syn = { version = "1.0", features = ["full"] }
+parking_lot = "0.11.2"
 
 [build-dependencies]
 rustc_tools_util = { version = "0.2", path = "rustc_tools_util" }
diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml
index affb283017c..d350d9a0018 100644
--- a/src/tools/clippy/clippy_dev/Cargo.toml
+++ b/src/tools/clippy/clippy_dev/Cargo.toml
@@ -12,6 +12,7 @@ opener = "0.5"
 regex = "1.5"
 shell-escape = "0.1"
 walkdir = "2.3"
+cargo_metadata = "0.14"
 
 [features]
 deny-warnings = []
diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs
index c81eb40d52f..9ceadee58ea 100644
--- a/src/tools/clippy/clippy_dev/src/fmt.rs
+++ b/src/tools/clippy/clippy_dev/src/fmt.rs
@@ -1,6 +1,7 @@
 use crate::clippy_project_root;
+use itertools::Itertools;
 use shell_escape::escape;
-use std::ffi::OsStr;
+use std::ffi::{OsStr, OsString};
 use std::path::Path;
 use std::process::{self, Command};
 use std::{fs, io};
@@ -56,15 +57,22 @@ pub fn run(check: bool, verbose: bool) {
         success &= cargo_fmt(context, &project_root.join("rustc_tools_util"))?;
         success &= cargo_fmt(context, &project_root.join("lintcheck"))?;
 
-        for entry in WalkDir::new(project_root.join("tests")) {
-            let entry = entry?;
-            let path = entry.path();
-
-            if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" {
-                continue;
-            }
-
-            success &= rustfmt(context, path)?;
+        let chunks = WalkDir::new(project_root.join("tests"))
+            .into_iter()
+            .filter_map(|entry| {
+                let entry = entry.expect("failed to find tests");
+                let path = entry.path();
+
+                if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" {
+                    None
+                } else {
+                    Some(entry.into_path().into_os_string())
+                }
+            })
+            .chunks(250);
+
+        for chunk in &chunks {
+            success &= rustfmt(context, chunk)?;
         }
 
         Ok(success)
@@ -149,7 +157,7 @@ fn exec(
 }
 
 fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
-    let mut args = vec!["+nightly", "fmt", "--all"];
+    let mut args = vec!["fmt", "--all"];
     if context.check {
         args.push("--");
         args.push("--check");
@@ -162,7 +170,7 @@ fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
 fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
     let program = "rustfmt";
     let dir = std::env::current_dir()?;
-    let args = &["+nightly", "--version"];
+    let args = &["--version"];
 
     if context.verbose {
         println!("{}", format_command(&program, &dir, args));
@@ -185,14 +193,14 @@ fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
     }
 }
 
-fn rustfmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
-    let mut args = vec!["+nightly".as_ref(), path.as_os_str()];
+fn rustfmt(context: &FmtContext, paths: impl Iterator<Item = OsString>) -> Result<bool, CliError> {
+    let mut args = Vec::new();
     if context.check {
-        args.push("--check".as_ref());
+        args.push(OsString::from("--check"));
     }
+    args.extend(paths);
+
     let success = exec(context, "rustfmt", std::env::current_dir()?, &args)?;
-    if !success {
-        eprintln!("rustfmt failed on {}", path.display());
-    }
+
     Ok(success)
 }
diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs
index 5538f62c8e7..59fde447547 100644
--- a/src/tools/clippy/clippy_dev/src/lib.rs
+++ b/src/tools/clippy/clippy_dev/src/lib.rs
@@ -7,6 +7,7 @@ use std::path::PathBuf;
 
 pub mod bless;
 pub mod fmt;
+pub mod lint;
 pub mod new_lint;
 pub mod serve;
 pub mod setup;
diff --git a/src/tools/clippy/clippy_dev/src/lint.rs b/src/tools/clippy/clippy_dev/src/lint.rs
new file mode 100644
index 00000000000..dfd16f71054
--- /dev/null
+++ b/src/tools/clippy/clippy_dev/src/lint.rs
@@ -0,0 +1,20 @@
+use std::process::{self, Command};
+
+pub fn run(filename: &str) {
+    let code = Command::new("cargo")
+        .args(["run", "--bin", "clippy-driver", "--"])
+        .args(["-L", "./target/debug"])
+        .args(["-Z", "no-codegen"])
+        .args(["--edition", "2021"])
+        .arg(filename)
+        .env("__CLIPPY_INTERNAL_TESTS", "true")
+        .status()
+        .expect("failed to run cargo")
+        .code();
+
+    if code.is_none() {
+        eprintln!("Killed by signal");
+    }
+
+    process::exit(code.unwrap_or(1));
+}
diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs
index b5c04efce3b..30a241c8ba1 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, update_lints};
+use clippy_dev::{bless, fmt, lint, new_lint, serve, setup, update_lints};
 fn main() {
     let matches = get_clap_config();
 
@@ -55,6 +55,10 @@ fn main() {
             let lint = matches.value_of("lint");
             serve::run(port, lint);
         },
+        ("lint", Some(matches)) => {
+            let filename = matches.value_of("filename").unwrap();
+            lint::run(filename);
+        },
         _ => {},
     }
 }
@@ -219,5 +223,14 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
                 )
                 .arg(Arg::with_name("lint").help("Which lint's page to load initially (optional)")),
         )
+        .subcommand(
+            SubCommand::with_name("lint")
+                .about("Manually run clippy on a file")
+                .arg(
+                    Arg::with_name("filename")
+                        .required(true)
+                        .help("The path to a file to lint"),
+                ),
+        )
         .get_matches()
 }
diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs
index 43a478ee77d..59658b42c79 100644
--- a/src/tools/clippy/clippy_dev/src/new_lint.rs
+++ b/src/tools/clippy/clippy_dev/src/new_lint.rs
@@ -132,6 +132,18 @@ fn to_camel_case(name: &str) -> String {
         .collect()
 }
 
+fn get_stabilisation_version() -> String {
+    let mut command = cargo_metadata::MetadataCommand::new();
+    command.no_deps();
+    if let Ok(metadata) = command.exec() {
+        if let Some(pkg) = metadata.packages.iter().find(|pkg| pkg.name == "clippy") {
+            return format!("{}.{}.0", pkg.version.minor, pkg.version.patch);
+        }
+    }
+
+    String::from("<TODO set version(see doc/adding_lints.md)>")
+}
+
 fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String {
     let mut contents = format!(
         indoc! {"
@@ -178,6 +190,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
         },
     };
 
+    let version = get_stabilisation_version();
     let lint_name = lint.name;
     let category = lint.category;
     let name_camel = to_camel_case(lint.name);
@@ -212,7 +225,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
     });
 
     result.push_str(&format!(
-        indoc! {"
+        indoc! {r#"
             declare_clippy_lint! {{
                 /// ### What it does
                 ///
@@ -226,11 +239,13 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
                 /// ```rust
                 /// // example code which does not raise clippy warning
                 /// ```
+                #[clippy::version = "{version}"]
                 pub {name_upper},
                 {category},
-                \"default lint description\"
+                "default lint description"
             }}
-        "},
+        "#},
+        version = version,
         name_upper = name_upper,
         category = category,
     ));
diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs
index 23f58bc4915..8dd073ef405 100644
--- a/src/tools/clippy/clippy_dev/src/update_lints.rs
+++ b/src/tools/clippy/clippy_dev/src/update_lints.rs
@@ -18,6 +18,7 @@ static DEC_CLIPPY_LINT_RE: SyncLazy<Regex> = SyncLazy::new(|| {
         r#"(?x)
     declare_clippy_lint!\s*[\{(]
     (?:\s+///.*)*
+    (?:\s*\#\[clippy::version\s*=\s*"[^"]*"\])?
     \s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
     (?P<cat>[a-z_]+)\s*,\s*
     "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
@@ -31,6 +32,7 @@ static DEC_DEPRECATED_LINT_RE: SyncLazy<Regex> = SyncLazy::new(|| {
         r#"(?x)
     declare_deprecated_lint!\s*[{(]\s*
     (?:\s+///.*)*
+    (?:\s*\#\[clippy::version\s*=\s*"[^"]*"\])?
     \s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
     "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
 "#,
@@ -495,6 +497,7 @@ fn test_parse_contents() {
     let result: Vec<Lint> = parse_contents(
         r#"
 declare_clippy_lint! {
+    #[clippy::version = "Hello Clippy!"]
     pub PTR_ARG,
     style,
     "really long \
@@ -502,6 +505,7 @@ declare_clippy_lint! {
 }
 
 declare_clippy_lint!{
+    #[clippy::version = "Test version"]
     pub DOC_MARKDOWN,
     pedantic,
     "single line"
@@ -509,6 +513,7 @@ declare_clippy_lint!{
 
 /// some doc comment
 declare_deprecated_lint! {
+    #[clippy::version = "I'm a version"]
     pub SHOULD_ASSERT_EQ,
     "`assert!()` will be more flexible with RFC 2011"
 }
diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml
index 281480b8d94..0661c280386 100644
--- a/src/tools/clippy/clippy_lints/Cargo.toml
+++ b/src/tools/clippy/clippy_lints/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_lints"
-version = "0.1.58"
+version = "0.1.59"
 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/absurd_extreme_comparisons.rs b/src/tools/clippy/clippy_lints/src/absurd_extreme_comparisons.rs
index 1483f3f9185..7665aa8380b 100644
--- a/src/tools/clippy/clippy_lints/src/absurd_extreme_comparisons.rs
+++ b/src/tools/clippy/clippy_lints/src/absurd_extreme_comparisons.rs
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     /// if vec.len() <= 0 {}
     /// if 100 > i32::MAX {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ABSURD_EXTREME_COMPARISONS,
     correctness,
     "a comparison with a maximum or minimum value that is always true or false"
diff --git a/src/tools/clippy/clippy_lints/src/approx_const.rs b/src/tools/clippy/clippy_lints/src/approx_const.rs
index fb54ac1ec51..12435eefbc4 100644
--- a/src/tools/clippy/clippy_lints/src/approx_const.rs
+++ b/src/tools/clippy/clippy_lints/src/approx_const.rs
@@ -33,6 +33,7 @@ declare_clippy_lint! {
     /// let x = std::f32::consts::PI;
     /// let y = std::f64::consts::FRAC_1_PI;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub APPROX_CONSTANT,
     correctness,
     "the approximate of a known float constant (in `std::fXX::consts`)"
diff --git a/src/tools/clippy/clippy_lints/src/arithmetic.rs b/src/tools/clippy/clippy_lints/src/arithmetic.rs
index 36fe7b7a867..e0c1d6ab6e1 100644
--- a/src/tools/clippy/clippy_lints/src/arithmetic.rs
+++ b/src/tools/clippy/clippy_lints/src/arithmetic.rs
@@ -25,6 +25,7 @@ declare_clippy_lint! {
     /// # let a = 0;
     /// a + 1;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INTEGER_ARITHMETIC,
     restriction,
     "any integer arithmetic expression which could overflow or panic"
@@ -43,6 +44,7 @@ declare_clippy_lint! {
     /// # let a = 0.0;
     /// a + 1.0;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FLOAT_ARITHMETIC,
     restriction,
     "any floating-point arithmetic statement"
diff --git a/src/tools/clippy/clippy_lints/src/as_conversions.rs b/src/tools/clippy/clippy_lints/src/as_conversions.rs
index 0be460d67a7..53704da1046 100644
--- a/src/tools/clippy/clippy_lints/src/as_conversions.rs
+++ b/src/tools/clippy/clippy_lints/src/as_conversions.rs
@@ -38,6 +38,7 @@ declare_clippy_lint! {
     /// f(a.try_into().expect("Unexpected u16 overflow in f"));
     /// ```
     ///
+    #[clippy::version = "1.41.0"]
     pub AS_CONVERSIONS,
     restriction,
     "using a potentially dangerous silent `as` conversion"
diff --git a/src/tools/clippy/clippy_lints/src/asm_syntax.rs b/src/tools/clippy/clippy_lints/src/asm_syntax.rs
index 825832eb79d..0322698f029 100644
--- a/src/tools/clippy/clippy_lints/src/asm_syntax.rs
+++ b/src/tools/clippy/clippy_lints/src/asm_syntax.rs
@@ -75,6 +75,7 @@ declare_clippy_lint! {
     /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax));
     /// # }
     /// ```
+    #[clippy::version = "1.49.0"]
     pub INLINE_ASM_X86_INTEL_SYNTAX,
     restriction,
     "prefer AT&T x86 assembly syntax"
@@ -111,6 +112,7 @@ declare_clippy_lint! {
     /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr);
     /// # }
     /// ```
+    #[clippy::version = "1.49.0"]
     pub INLINE_ASM_X86_ATT_SYNTAX,
     restriction,
     "prefer Intel x86 assembly syntax"
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 d834a1d317a..521fc84ee9c 100644
--- a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
+++ b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
@@ -26,6 +26,7 @@ declare_clippy_lint! {
     /// const B: bool = false;
     /// assert!(B)
     /// ```
+    #[clippy::version = "1.34.0"]
     pub ASSERTIONS_ON_CONSTANTS,
     style,
     "`assert!(true)` / `assert!(false)` will be optimized out by the compiler, and should probably be replaced by a `panic!()` or `unreachable!()`"
diff --git a/src/tools/clippy/clippy_lints/src/assign_ops.rs b/src/tools/clippy/clippy_lints/src/assign_ops.rs
index 2097a1feff9..e16f4369da9 100644
--- a/src/tools/clippy/clippy_lints/src/assign_ops.rs
+++ b/src/tools/clippy/clippy_lints/src/assign_ops.rs
@@ -34,6 +34,7 @@ declare_clippy_lint! {
     /// // Good
     /// a += b;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ASSIGN_OP_PATTERN,
     style,
     "assigning the result of an operation on a variable to that same variable"
@@ -60,6 +61,7 @@ declare_clippy_lint! {
     /// // ...
     /// a += a + b;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MISREFACTORED_ASSIGN_OP,
     suspicious,
     "having a variable on both sides of an assign op"
diff --git a/src/tools/clippy/clippy_lints/src/async_yields_async.rs b/src/tools/clippy/clippy_lints/src/async_yields_async.rs
index 182736a5a20..0619490e73c 100644
--- a/src/tools/clippy/clippy_lints/src/async_yields_async.rs
+++ b/src/tools/clippy/clippy_lints/src/async_yields_async.rs
@@ -34,6 +34,7 @@ declare_clippy_lint! {
     ///   };
     /// }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub ASYNC_YIELDS_ASYNC,
     correctness,
     "async blocks that return a type that can be awaited"
diff --git a/src/tools/clippy/clippy_lints/src/attrs.rs b/src/tools/clippy/clippy_lints/src/attrs.rs
index 6f8b645dd70..1edb7c950e7 100644
--- a/src/tools/clippy/clippy_lints/src/attrs.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs.rs
@@ -1,8 +1,9 @@
 //! checks for attributes
 
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::match_panic_def_id;
+use clippy_utils::msrvs;
 use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
+use clippy_utils::{extract_msrv_attr, match_panic_def_id, meets_msrv};
 use if_chain::if_chain;
 use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
 use rustc_errors::Applicability;
@@ -12,7 +13,8 @@ use rustc_hir::{
 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::Span;
 use rustc_span::sym;
 use rustc_span::symbol::{Symbol, SymbolStr};
@@ -64,6 +66,7 @@ declare_clippy_lint! {
     /// #[inline(always)]
     /// fn not_quite_hot_code(..) { ... }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INLINE_ALWAYS,
     pedantic,
     "use of `#[inline(always)]`"
@@ -98,6 +101,7 @@ declare_clippy_lint! {
     /// #[macro_use]
     /// extern crate baz;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USELESS_ATTRIBUTE,
     correctness,
     "use of lint attributes on `extern crate` items"
@@ -117,6 +121,7 @@ declare_clippy_lint! {
     /// #[deprecated(since = "forever")]
     /// fn something_else() { /* ... */ }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DEPRECATED_SEMVER,
     correctness,
     "use of `#[deprecated(since = \"x\")]` where x is not semver"
@@ -154,6 +159,7 @@ declare_clippy_lint! {
     /// #[allow(dead_code)]
     /// fn this_is_fine_too() { }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EMPTY_LINE_AFTER_OUTER_ATTR,
     nursery,
     "empty line after outer attribute"
@@ -177,6 +183,7 @@ declare_clippy_lint! {
     /// ```rust
     /// #![deny(clippy::as_conversions)]
     /// ```
+    #[clippy::version = "1.47.0"]
     pub BLANKET_CLIPPY_RESTRICTION_LINTS,
     suspicious,
     "enabling the complete restriction group"
@@ -208,6 +215,7 @@ declare_clippy_lint! {
     /// #[rustfmt::skip]
     /// fn main() { }
     /// ```
+    #[clippy::version = "1.32.0"]
     pub DEPRECATED_CFG_ATTR,
     complexity,
     "usage of `cfg_attr(rustfmt)` instead of tool attributes"
@@ -240,6 +248,7 @@ declare_clippy_lint! {
     /// fn conditional() { }
     /// ```
     /// Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details.
+    #[clippy::version = "1.45.0"]
     pub MISMATCHED_TARGET_OS,
     correctness,
     "usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`"
@@ -497,7 +506,11 @@ fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool {
     }
 }
 
-declare_lint_pass!(EarlyAttributes => [
+pub struct EarlyAttributes {
+    pub msrv: Option<RustcVersion>,
+}
+
+impl_lint_pass!(EarlyAttributes => [
     DEPRECATED_CFG_ATTR,
     MISMATCHED_TARGET_OS,
     EMPTY_LINE_AFTER_OUTER_ATTR,
@@ -509,9 +522,11 @@ impl EarlyLintPass for EarlyAttributes {
     }
 
     fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
-        check_deprecated_cfg_attr(cx, attr);
+        check_deprecated_cfg_attr(cx, attr, self.msrv);
         check_mismatched_target_os(cx, attr);
     }
+
+    extract_msrv_attr!(EarlyContext);
 }
 
 fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
@@ -548,8 +563,9 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It
     }
 }
 
-fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) {
+fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Option<RustcVersion>) {
     if_chain! {
+        if meets_msrv(msrv.as_ref(), &msrvs::TOOL_ATTRIBUTES);
         // check cfg_attr
         if attr.has_name(sym::cfg_attr);
         if let Some(items) = attr.meta_item_list();
diff --git a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
index 28615b9217c..1cc3418d474 100644
--- a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
+++ b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
@@ -47,6 +47,7 @@ declare_clippy_lint! {
     ///   bar.await;
     /// }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub AWAIT_HOLDING_LOCK,
     pedantic,
     "Inside an async function, holding a MutexGuard while calling await"
@@ -88,6 +89,7 @@ declare_clippy_lint! {
     ///   bar.await;
     /// }
     /// ```
+    #[clippy::version = "1.49.0"]
     pub AWAIT_HOLDING_REFCELL_REF,
     pedantic,
     "Inside an async function, holding a RefCell ref while calling await"
diff --git a/src/tools/clippy/clippy_lints/src/bit_mask.rs b/src/tools/clippy/clippy_lints/src/bit_mask.rs
index 11346e7c96a..0977cf22b2c 100644
--- a/src/tools/clippy/clippy_lints/src/bit_mask.rs
+++ b/src/tools/clippy/clippy_lints/src/bit_mask.rs
@@ -41,6 +41,7 @@ declare_clippy_lint! {
     /// # let x = 1;
     /// if (x & 1 == 2) { }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub BAD_BIT_MASK,
     correctness,
     "expressions of the form `_ & mask == select` that will only ever return `true` or `false`"
@@ -73,6 +74,7 @@ declare_clippy_lint! {
     /// # let x = 1;
     /// if (x | 1 > 3) {  }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INEFFECTIVE_BIT_MASK,
     correctness,
     "expressions where a bit mask will be rendered useless by a comparison, e.g., `(x | 1) > 2`"
@@ -95,6 +97,7 @@ declare_clippy_lint! {
     /// # let x = 1;
     /// if x & 0b1111 == 0 { }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub VERBOSE_BIT_MASK,
     pedantic,
     "expressions where a bit mask is less readable than the corresponding method call"
diff --git a/src/tools/clippy/clippy_lints/src/blacklisted_name.rs b/src/tools/clippy/clippy_lints/src/blacklisted_name.rs
index 916c78c982a..1600fb25d89 100644
--- a/src/tools/clippy/clippy_lints/src/blacklisted_name.rs
+++ b/src/tools/clippy/clippy_lints/src/blacklisted_name.rs
@@ -17,6 +17,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let foo = 3.14;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub BLACKLISTED_NAME,
     style,
     "usage of a blacklisted/placeholder name"
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 47e5b0d583d..b59f49357df 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
@@ -41,6 +41,7 @@ declare_clippy_lint! {
     /// let res = { let x = somefunc(); x };
     /// if res { /* ... */ }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub BLOCKS_IN_IF_CONDITIONS,
     style,
     "useless or complex blocks that can be eliminated in conditions"
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 cdc192a47e4..d0b8c52a36a 100644
--- a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
+++ b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
@@ -23,6 +23,7 @@ declare_clippy_lint! {
     /// // Good
     /// assert!(!"a".is_empty());
     /// ```
+    #[clippy::version = "1.53.0"]
     pub BOOL_ASSERT_COMPARISON,
     style,
     "Using a boolean as comparison value in an assert_* macro when there is no need"
@@ -72,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison {
             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;
+                        let nb_bool_args = usize::from(is_bool_lit(a)) + usize::from(is_bool_lit(b));
 
                         if nb_bool_args != 1 {
                             // If there are two boolean arguments, we definitely don't understand
diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs
index 8282800c819..51835ee7488 100644
--- a/src/tools/clippy/clippy_lints/src/booleans.rs
+++ b/src/tools/clippy/clippy_lints/src/booleans.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
-use clippy_utils::{eq_expr_value, get_trait_def_id, in_macro, paths};
+use clippy_utils::{eq_expr_value, get_trait_def_id, paths};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -31,6 +31,7 @@ declare_clippy_lint! {
     /// if a && true  // should be: if a
     /// if !(a == b)  // should be: if a != b
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NONMINIMAL_BOOL,
     complexity,
     "boolean expressions that can be written more concisely"
@@ -52,6 +53,7 @@ declare_clippy_lint! {
     /// if a && b || a { ... }
     /// ```
     /// The `b` is unnecessary, the expression is equivalent to `if a`.
+    #[clippy::version = "pre 1.29.0"]
     pub LOGIC_BUG,
     correctness,
     "boolean expressions that contain terminals which can be eliminated"
@@ -453,22 +455,20 @@ impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> {
     type Map = Map<'tcx>;
 
     fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
-        if in_macro(e.span) {
-            return;
-        }
-        match &e.kind {
-            ExprKind::Binary(binop, _, _) if binop.node == BinOpKind::Or || binop.node == BinOpKind::And => {
-                self.bool_expr(e);
-            },
-            ExprKind::Unary(UnOp::Not, inner) => {
-                if self.cx.typeck_results().node_types()[inner.hir_id].is_bool() {
+        if !e.span.from_expansion() {
+            match &e.kind {
+                ExprKind::Binary(binop, _, _) if binop.node == BinOpKind::Or || binop.node == BinOpKind::And => {
                     self.bool_expr(e);
-                } else {
-                    walk_expr(self, e);
-                }
-            },
-            _ => walk_expr(self, e),
+                },
+                ExprKind::Unary(UnOp::Not, inner) => {
+                    if self.cx.typeck_results().node_types()[inner.hir_id].is_bool() {
+                        self.bool_expr(e);
+                    }
+                },
+                _ => {},
+            }
         }
+        walk_expr(self, e);
     }
     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
         NestedVisitorMap::None
diff --git a/src/tools/clippy/clippy_lints/src/bytecount.rs b/src/tools/clippy/clippy_lints/src/bytecount.rs
index a07cd5e5f4e..afb317421d0 100644
--- a/src/tools/clippy/clippy_lints/src/bytecount.rs
+++ b/src/tools/clippy/clippy_lints/src/bytecount.rs
@@ -30,6 +30,7 @@ declare_clippy_lint! {
     /// # let vec = vec![1_u8];
     /// &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NAIVE_BYTECOUNT,
     pedantic,
     "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
@@ -73,10 +74,10 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount {
                     if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
                         &args[0]
                     } else {
-                        &filter_recv
+                        filter_recv
                     }
                 } else {
-                    &filter_recv
+                    filter_recv
                 };
                 let mut applicability = Applicability::MaybeIncorrect;
                 span_lint_and_sugg(
diff --git a/src/tools/clippy/clippy_lints/src/cargo_common_metadata.rs b/src/tools/clippy/clippy_lints/src/cargo_common_metadata.rs
index ff619c59b6e..23f79fdc682 100644
--- a/src/tools/clippy/clippy_lints/src/cargo_common_metadata.rs
+++ b/src/tools/clippy/clippy_lints/src/cargo_common_metadata.rs
@@ -42,6 +42,7 @@ declare_clippy_lint! {
     /// keywords = ["clippy", "lint", "plugin"]
     /// categories = ["development-tools", "development-tools::cargo-plugins"]
     /// ```
+    #[clippy::version = "1.32.0"]
     pub CARGO_COMMON_METADATA,
     cargo,
     "common metadata is defined in `Cargo.toml`"
diff --git a/src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs
index c876553c165..3f286dd9e2f 100644
--- a/src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs
+++ b/src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs
@@ -27,6 +27,7 @@ declare_clippy_lint! {
     ///     filename.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case("rs")) == Some(true)
     /// }
     /// ```
+    #[clippy::version = "1.51.0"]
     pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
     pedantic,
     "Checks for calls to ends_with with case-sensitive file extensions"
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
index 869deecfbd5..4a95bed1148 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
@@ -1,16 +1,24 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_constant;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::is_isize_or_usize;
+use clippy_utils::{in_constant, meets_msrv, msrvs};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, FloatTy, Ty};
+use rustc_semver::RustcVersion;
 
 use super::{utils, CAST_LOSSLESS};
 
-pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
-    if !should_lint(cx, expr, cast_from, cast_to) {
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
+    cast_op: &Expr<'_>,
+    cast_from: Ty<'_>,
+    cast_to: Ty<'_>,
+    msrv: &Option<RustcVersion>,
+) {
+    if !should_lint(cx, expr, cast_from, cast_to, msrv) {
         return;
     }
 
@@ -32,21 +40,36 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, c
         },
     );
 
+    let message = if cast_from.is_bool() {
+        format!(
+            "casting `{0:}` to `{1:}` is more cleanly stated with `{1:}::from(_)`",
+            cast_from, cast_to
+        )
+    } else {
+        format!(
+            "casting `{}` to `{}` may become silently lossy if you later change the type",
+            cast_from, cast_to
+        )
+    };
+
     span_lint_and_sugg(
         cx,
         CAST_LOSSLESS,
         expr.span,
-        &format!(
-            "casting `{}` to `{}` may become silently lossy if you later change the type",
-            cast_from, cast_to
-        ),
+        &message,
         "try",
         format!("{}::from({})", cast_to, sugg),
         applicability,
     );
 }
 
-fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) -> bool {
+fn should_lint(
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
+    cast_from: Ty<'_>,
+    cast_to: Ty<'_>,
+    msrv: &Option<RustcVersion>,
+) -> bool {
     // Do not suggest using From in consts/statics until it is valid to do so (see #2267).
     if in_constant(cx, expr.hir_id) {
         return false;
@@ -72,7 +95,7 @@ fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to
             };
             from_nbits < to_nbits
         },
-
+        (false, true) if matches!(cast_from.kind(), ty::Bool) && meets_msrv(msrv.as_ref(), &msrvs::FROM_BOOL) => true,
         (_, _) => {
             matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64))
         },
diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs
index 233abd17894..aee1e50b94a 100644
--- a/src/tools/clippy/clippy_lints/src/casts/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs
@@ -40,6 +40,7 @@ declare_clippy_lint! {
     /// let x = u64::MAX;
     /// x as f64;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CAST_PRECISION_LOSS,
     pedantic,
     "casts that cause loss of precision, e.g., `x as f32` where `x: u64`"
@@ -61,6 +62,7 @@ declare_clippy_lint! {
     /// let y: i8 = -1;
     /// y as u128; // will return 18446744073709551615
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CAST_SIGN_LOSS,
     pedantic,
     "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`"
@@ -83,6 +85,7 @@ declare_clippy_lint! {
     ///     x as u8
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CAST_POSSIBLE_TRUNCATION,
     pedantic,
     "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`"
@@ -106,6 +109,7 @@ declare_clippy_lint! {
     /// ```rust
     /// u32::MAX as i32; // will yield a value of `-1`
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CAST_POSSIBLE_WRAP,
     pedantic,
     "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`"
@@ -138,6 +142,7 @@ declare_clippy_lint! {
     ///     u64::from(x)
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CAST_LOSSLESS,
     pedantic,
     "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`"
@@ -163,6 +168,7 @@ declare_clippy_lint! {
     /// let _ = 2_i32;
     /// let _ = 0.5_f32;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNNECESSARY_CAST,
     complexity,
     "cast to the same type, e.g., `x as i32` where `x: i32`"
@@ -190,6 +196,7 @@ declare_clippy_lint! {
     /// (&1u8 as *const u8).cast::<u16>();
     /// (&mut 1u8 as *mut u8).cast::<u16>();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CAST_PTR_ALIGNMENT,
     pedantic,
     "cast from a pointer to a more-strictly-aligned pointer"
@@ -217,6 +224,7 @@ declare_clippy_lint! {
     /// fn fun2() -> i32 { 1 }
     /// let a = fun2 as usize;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FN_TO_NUMERIC_CAST,
     style,
     "casting a function pointer to a numeric type other than usize"
@@ -247,6 +255,7 @@ declare_clippy_lint! {
     /// let fn_ptr = fn2 as usize;
     /// let fn_ptr_truncated = fn_ptr as i32;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
     style,
     "casting a function pointer to a numeric type not wide enough to store the address"
@@ -283,6 +292,7 @@ declare_clippy_lint! {
     /// }
     /// let _ = fn3 as fn() -> u16;
     /// ```
+    #[clippy::version = "1.58.0"]
     pub FN_TO_NUMERIC_CAST_ANY,
     restriction,
     "casting a function pointer to any integer type"
@@ -317,6 +327,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.33.0"]
     pub CAST_REF_TO_MUT,
     correctness,
     "a cast of reference to a mutable pointer"
@@ -344,6 +355,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// b'x'
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CHAR_LIT_AS_U8,
     complexity,
     "casting a character literal to `u8` truncates"
@@ -372,6 +384,7 @@ declare_clippy_lint! {
     /// let _ = ptr.cast::<i32>();
     /// let _ = mut_ptr.cast::<i32>();
     /// ```
+    #[clippy::version = "1.51.0"]
     pub PTR_AS_PTR,
     pedantic,
     "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`"
@@ -426,12 +439,16 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
             fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
             fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
             fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
-            if cast_from.is_numeric() && cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
-                cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
-                cast_possible_wrap::check(cx, expr, cast_from, cast_to);
-                cast_precision_loss::check(cx, expr, cast_from, cast_to);
-                cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to);
-                cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
+
+            if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
+                if cast_from.is_numeric() {
+                    cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
+                    cast_possible_wrap::check(cx, expr, cast_from, cast_to);
+                    cast_precision_loss::check(cx, expr, cast_from, cast_to);
+                    cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
+                }
+
+                cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
             }
         }
 
diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs
index 842bbf006cc..ffe6340bd77 100644
--- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs
+++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     /// i32::try_from(foo).is_ok()
     /// # ;
     /// ```
+    #[clippy::version = "1.37.0"]
     pub CHECKED_CONVERSIONS,
     pedantic,
     "`try_from` could replace manual bounds checking when casting"
diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
index 1ccb8c5d880..84a2373efe1 100644
--- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
+++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
@@ -27,6 +27,7 @@ declare_clippy_lint! {
     ///
     /// ### Example
     /// No. You'll see it when you get the warning.
+    #[clippy::version = "1.35.0"]
     pub COGNITIVE_COMPLEXITY,
     nursery,
     "functions that should be split up into multiple functions"
diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs
index 4aa87980715..f03f34e5a4b 100644
--- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs
+++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs
@@ -47,6 +47,7 @@ declare_clippy_lint! {
     ///     …
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub COLLAPSIBLE_IF,
     style,
     "nested `if`s that can be collapsed (e.g., `if x { if y { ... } }`"
@@ -82,6 +83,7 @@ declare_clippy_lint! {
     ///     …
     /// }
     /// ```
+    #[clippy::version = "1.51.0"]
     pub COLLAPSIBLE_ELSE_IF,
     style,
     "nested `else`-`if` expressions that can be collapsed (e.g., `else { if x { ... } }`)"
diff --git a/src/tools/clippy/clippy_lints/src/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/collapsible_match.rs
index a4693fa213b..626f9971f01 100644
--- a/src/tools/clippy/clippy_lints/src/collapsible_match.rs
+++ b/src/tools/clippy/clippy_lints/src/collapsible_match.rs
@@ -41,6 +41,7 @@ declare_clippy_lint! {
     ///     };
     /// }
     /// ```
+    #[clippy::version = "1.50.0"]
     pub COLLAPSIBLE_MATCH,
     style,
     "Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together."
diff --git a/src/tools/clippy/clippy_lints/src/comparison_chain.rs b/src/tools/clippy/clippy_lints/src/comparison_chain.rs
index 597a3c67024..399d11472b0 100644
--- a/src/tools/clippy/clippy_lints/src/comparison_chain.rs
+++ b/src/tools/clippy/clippy_lints/src/comparison_chain.rs
@@ -49,6 +49,7 @@ declare_clippy_lint! {
     ///      }
     /// }
     /// ```
+    #[clippy::version = "1.40.0"]
     pub COMPARISON_CHAIN,
     style,
     "`if`s that can be rewritten with `match` and `cmp`"
diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs
index 8abf10c0d1c..d07bc23235b 100644
--- a/src/tools/clippy/clippy_lints/src/copies.rs
+++ b/src/tools/clippy/clippy_lints/src/copies.rs
@@ -1,8 +1,8 @@
 use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
 use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
 use clippy_utils::{
-    both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, in_macro, is_else_clause,
-    is_lint_allowed, search_same, ContainsName, SpanlessEq, SpanlessHash,
+    both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, is_else_clause, is_lint_allowed,
+    search_same, ContainsName, SpanlessEq, SpanlessHash,
 };
 use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashSet;
@@ -41,6 +41,7 @@ declare_clippy_lint! {
     ///     …
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub IFS_SAME_COND,
     correctness,
     "consecutive `if`s with the same condition"
@@ -88,6 +89,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.41.0"]
     pub SAME_FUNCTIONS_IN_IF_CONDITION,
     pedantic,
     "consecutive `if`s with the same function call"
@@ -109,6 +111,7 @@ declare_clippy_lint! {
     ///     42
     /// };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub IF_SAME_THEN_ELSE,
     correctness,
     "`if` with the same `then` and `else` blocks"
@@ -147,6 +150,7 @@ declare_clippy_lint! {
     ///     42
     /// };
     /// ```
+    #[clippy::version = "1.53.0"]
     pub BRANCHES_SHARING_CODE,
     nursery,
     "`if` statement with shared code in all blocks"
@@ -623,7 +627,7 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
 
     let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool {
         // Do not lint if any expr originates from a macro
-        if in_macro(lhs.span) || in_macro(rhs.span) {
+        if lhs.span.from_expansion() || rhs.span.from_expansion() {
             return false;
         }
         // Do not spawn warning if `IFS_SAME_COND` already produced it.
diff --git a/src/tools/clippy/clippy_lints/src/copy_iterator.rs b/src/tools/clippy/clippy_lints/src/copy_iterator.rs
index c2e9e8b3ab7..026683f6006 100644
--- a/src/tools/clippy/clippy_lints/src/copy_iterator.rs
+++ b/src/tools/clippy/clippy_lints/src/copy_iterator.rs
@@ -28,6 +28,7 @@ declare_clippy_lint! {
     /// let a: Vec<_> = my_iterator.take(1).collect();
     /// let b: Vec<_> = my_iterator.collect();
     /// ```
+    #[clippy::version = "1.30.0"]
     pub COPY_ITERATOR,
     pedantic,
     "implementing `Iterator` on a `Copy` type"
diff --git a/src/tools/clippy/clippy_lints/src/create_dir.rs b/src/tools/clippy/clippy_lints/src/create_dir.rs
index e4ee2772483..6bc4054a5ab 100644
--- a/src/tools/clippy/clippy_lints/src/create_dir.rs
+++ b/src/tools/clippy/clippy_lints/src/create_dir.rs
@@ -23,6 +23,7 @@ declare_clippy_lint! {
     /// ```rust
     /// std::fs::create_dir_all("foo");
     /// ```
+    #[clippy::version = "1.48.0"]
     pub CREATE_DIR,
     restriction,
     "calling `std::fs::create_dir` instead of `std::fs::create_dir_all`"
diff --git a/src/tools/clippy/clippy_lints/src/dbg_macro.rs b/src/tools/clippy/clippy_lints/src/dbg_macro.rs
index bab4a696f83..5a0b60fdfbc 100644
--- a/src/tools/clippy/clippy_lints/src/dbg_macro.rs
+++ b/src/tools/clippy/clippy_lints/src/dbg_macro.rs
@@ -15,6 +15,14 @@ declare_clippy_lint! {
     /// `dbg!` macro is intended as a debugging tool. It
     /// should not be in version control.
     ///
+    /// ### Known problems
+    /// * The lint level is unaffected by crate attributes. The level can still
+    ///   be set for functions, modules and other items. To change the level for
+    ///   the entire crate, please use command line flags. More information and a
+    ///   configuration example can be found in [clippy#6610].
+    ///
+    /// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
+    ///
     /// ### Example
     /// ```rust,ignore
     /// // Bad
@@ -23,6 +31,7 @@ declare_clippy_lint! {
     /// // Good
     /// true
     /// ```
+    #[clippy::version = "1.34.0"]
     pub DBG_MACRO,
     restriction,
     "`dbg!` macro is intended as a debugging tool"
diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs
index cde27d3ad2a..a0b137efe22 100644
--- a/src/tools/clippy/clippy_lints/src/default.rs
+++ b/src/tools/clippy/clippy_lints/src/default.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
 use clippy_utils::source::snippet_with_macro_callsite;
 use clippy_utils::ty::{has_drop, is_copy};
-use clippy_utils::{any_parent_is_automatically_derived, contains_name, in_macro, match_def_path, paths};
+use clippy_utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths};
 use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -29,6 +29,7 @@ declare_clippy_lint! {
     /// // Good
     /// let s = String::default();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DEFAULT_TRAIT_ACCESS,
     pedantic,
     "checks for literal calls to `Default::default()`"
@@ -62,6 +63,7 @@ declare_clippy_lint! {
     ///     .. Default::default()
     /// };
     /// ```
+    #[clippy::version = "1.49.0"]
     pub FIELD_REASSIGN_WITH_DEFAULT,
     style,
     "binding initialized with Default should have its fields set in the initializer"
@@ -78,7 +80,7 @@ impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]);
 impl LateLintPass<'_> for Default {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if_chain! {
-            if !in_macro(expr.span);
+            if !expr.span.from_expansion();
             // Avoid cases already linted by `field_reassign_with_default`
             if !self.reassigned_linted.contains(&expr.span);
             if let ExprKind::Call(path, ..) = expr.kind;
@@ -125,7 +127,7 @@ impl LateLintPass<'_> for Default {
                 if let StmtKind::Local(local) = stmt.kind;
                 if let Some(expr) = local.init;
                 if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
-                if !in_macro(expr.span);
+                if !expr.span.from_expansion();
                 // only take bindings to identifiers
                 if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind;
                 // only when assigning `... = Default::default()`
diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
index 3f1b7ea6214..3573ea5f026 100644
--- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
+++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
@@ -46,6 +46,7 @@ declare_clippy_lint! {
     /// let i = 10i32;
     /// let f = 1.23f64;
     /// ```
+    #[clippy::version = "1.52.0"]
     pub DEFAULT_NUMERIC_FALLBACK,
     restriction,
     "usage of unconstrained numeric literals which may cause default numeric fallback."
diff --git a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs
index 9d8524ec91c..bba27576c89 100644
--- a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs
@@ -21,6 +21,7 @@ declare_deprecated_lint! {
     /// ### Deprecation reason
     /// This used to check for `assert!(a == b)` and recommend
     /// replacement with `assert_eq!(a, b)`, but this is no longer needed after RFC 2011.
+    #[clippy::version = "pre 1.29.0"]
     pub SHOULD_ASSERT_EQ,
     "`assert!()` will be more flexible with RFC 2011"
 }
@@ -32,6 +33,7 @@ declare_deprecated_lint! {
     /// ### Deprecation reason
     /// This used to check for `Vec::extend`, which was slower than
     /// `Vec::extend_from_slice`. Thanks to specialization, this is no longer true.
+    #[clippy::version = "pre 1.29.0"]
     pub EXTEND_FROM_SLICE,
     "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice"
 }
@@ -45,6 +47,7 @@ declare_deprecated_lint! {
     /// an infinite iterator, which is better expressed by `iter::repeat`,
     /// but the method has been removed for `Iterator::step_by` which panics
     /// if given a zero
+    #[clippy::version = "pre 1.29.0"]
     pub RANGE_STEP_BY_ZERO,
     "`iterator.step_by(0)` panics nowadays"
 }
@@ -56,6 +59,7 @@ declare_deprecated_lint! {
     /// ### Deprecation reason
     /// This used to check for `Vec::as_slice`, which was unstable with good
     /// stable alternatives. `Vec::as_slice` has now been stabilized.
+    #[clippy::version = "pre 1.29.0"]
     pub UNSTABLE_AS_SLICE,
     "`Vec::as_slice` has been stabilized in 1.7"
 }
@@ -67,6 +71,7 @@ declare_deprecated_lint! {
     /// ### Deprecation reason
     /// This used to check for `Vec::as_mut_slice`, which was unstable with good
     /// stable alternatives. `Vec::as_mut_slice` has now been stabilized.
+    #[clippy::version = "pre 1.29.0"]
     pub UNSTABLE_AS_MUT_SLICE,
     "`Vec::as_mut_slice` has been stabilized in 1.7"
 }
@@ -80,6 +85,7 @@ declare_deprecated_lint! {
     /// between non-pointer types of differing alignment is well-defined behavior (it's semantically
     /// equivalent to a memcpy). This lint has thus been refactored into two separate lints:
     /// cast_ptr_alignment and transmute_ptr_to_ptr.
+    #[clippy::version = "pre 1.29.0"]
     pub MISALIGNED_TRANSMUTE,
     "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr"
 }
@@ -92,6 +98,7 @@ declare_deprecated_lint! {
     /// This lint is too subjective, not having a good reason for being in clippy.
     /// Additionally, compound assignment operators may be overloaded separately from their non-assigning
     /// counterparts, so this lint may suggest a change in behavior or the code may not compile.
+    #[clippy::version = "1.30.0"]
     pub ASSIGN_OPS,
     "using compound assignment operators (e.g., `+=`) is harmless"
 }
@@ -104,6 +111,7 @@ declare_deprecated_lint! {
     /// The original rule will only lint for `if let`. After
     /// making it support to lint `match`, naming as `if let` is not suitable for it.
     /// So, this lint is deprecated.
+    #[clippy::version = "pre 1.29.0"]
     pub IF_LET_REDUNDANT_PATTERN_MATCHING,
     "this lint has been changed to redundant_pattern_matching"
 }
@@ -117,6 +125,7 @@ declare_deprecated_lint! {
     /// Vec::with_capacity(n); vec.set_len(n);` with `let vec = vec![0; n];`. The
     /// replacement has very different performance characteristics so the lint is
     /// deprecated.
+    #[clippy::version = "pre 1.29.0"]
     pub UNSAFE_VECTOR_INITIALIZATION,
     "the replacement suggested by this lint had substantially different behavior"
 }
@@ -127,6 +136,7 @@ declare_deprecated_lint! {
     ///
     /// ### Deprecation reason
     /// This lint has been superseded by #[must_use] in rustc.
+    #[clippy::version = "1.39.0"]
     pub UNUSED_COLLECT,
     "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint"
 }
@@ -137,6 +147,7 @@ declare_deprecated_lint! {
     ///
     /// ### Deprecation reason
     /// Associated-constants are now preferred.
+    #[clippy::version = "1.44.0"]
     pub REPLACE_CONSTS,
     "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants"
 }
@@ -147,6 +158,7 @@ declare_deprecated_lint! {
     ///
     /// ### Deprecation reason
     /// The regex! macro does not exist anymore.
+    #[clippy::version = "1.47.0"]
     pub REGEX_MACRO,
     "the regex! macro has been removed from the regex crate in 2018"
 }
@@ -158,6 +170,7 @@ declare_deprecated_lint! {
     /// ### Deprecation reason
     /// This lint has been replaced by `manual_find_map`, a
     /// more specific lint.
+    #[clippy::version = "1.51.0"]
     pub FIND_MAP,
     "this lint has been replaced by `manual_find_map`, a more specific lint"
 }
@@ -169,6 +182,7 @@ declare_deprecated_lint! {
     /// ### Deprecation reason
     /// This lint has been replaced by `manual_filter_map`, a
     /// more specific lint.
+    #[clippy::version = "1.53.0"]
     pub FILTER_MAP,
     "this lint has been replaced by `manual_filter_map`, a more specific lint"
 }
@@ -181,6 +195,7 @@ declare_deprecated_lint! {
     /// The `avoid_breaking_exported_api` config option was added, which
     /// enables the `enum_variant_names` lint for public items.
     /// ```
+    #[clippy::version = "1.54.0"]
     pub PUB_ENUM_VARIANT_NAMES,
     "set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items"
 }
@@ -192,6 +207,7 @@ declare_deprecated_lint! {
     /// ### Deprecation reason
     /// The `avoid_breaking_exported_api` config option was added, which
     /// enables the `wrong_self_conversion` lint for public items.
+    #[clippy::version = "1.54.0"]
     pub WRONG_PUB_SELF_CONVENTION,
     "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items"
 }
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index ce59311c4aa..fa2b348591b 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -1,14 +1,20 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_context;
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use clippy_utils::ty::peel_mid_ty_refs;
-use clippy_utils::{get_parent_node, in_macro, is_lint_allowed};
-use rustc_ast::util::parser::PREC_PREFIX;
+use clippy_utils::{get_parent_expr, get_parent_node, is_lint_allowed, path_to_local};
+use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
+use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::Applicability;
-use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, UnOp};
+use rustc_hir::{
+    BindingAnnotation, Body, BodyId, BorrowKind, Destination, Expr, ExprKind, HirId, MatchSource, Mutability, Node,
+    Pat, PatKind, UnOp,
+};
 use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
 use rustc_middle::ty::{self, Ty, TyCtxt, TyS, TypeckResults};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{symbol::sym, Span};
+use std::iter;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -34,13 +40,70 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// let _ = d.unwrap().deref();
     /// ```
+    #[clippy::version = "1.44.0"]
     pub EXPLICIT_DEREF_METHODS,
     pedantic,
     "Explicit use of deref or deref_mut method while not in a method chain."
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for address of operations (`&`) that are going to
+    /// be dereferenced immediately by the compiler.
+    ///
+    /// ### Why is this bad?
+    /// Suggests that the receiver of the expression borrows
+    /// the expression.
+    ///
+    /// ### Example
+    /// ```rust
+    /// fn fun(_a: &i32) {}
+    ///
+    /// // Bad
+    /// let x: &i32 = &&&&&&5;
+    /// fun(&x);
+    ///
+    /// // Good
+    /// let x: &i32 = &5;
+    /// fun(x);
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub NEEDLESS_BORROW,
+    style,
+    "taking a reference that is going to be automatically dereferenced"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `ref` bindings which create a reference to a reference.
+    ///
+    /// ### Why is this bad?
+    /// The address-of operator at the use site is clearer about the need for a reference.
+    ///
+    /// ### Example
+    /// ```rust
+    /// // Bad
+    /// let x = Some("");
+    /// if let Some(ref x) = x {
+    ///     // use `x` here
+    /// }
+    ///
+    /// // Good
+    /// let x = Some("");
+    /// if let Some(x) = x {
+    ///     // use `&x` here
+    /// }
+    /// ```
+    #[clippy::version = "1.54.0"]
+    pub REF_BINDING_TO_REFERENCE,
+    pedantic,
+    "`ref` binding to a reference"
+}
+
 impl_lint_pass!(Dereferencing => [
     EXPLICIT_DEREF_METHODS,
+    NEEDLESS_BORROW,
+    REF_BINDING_TO_REFERENCE,
 ]);
 
 #[derive(Default)]
@@ -51,6 +114,18 @@ pub struct Dereferencing {
     // expression. This is to store the id of that expression so it can be skipped when
     // `check_expr` is called for it.
     skip_expr: Option<HirId>,
+
+    /// The body the first local was found in. Used to emit lints when the traversal of the body has
+    /// been finished. Note we can't lint at the end of every body as they can be nested within each
+    /// other.
+    current_body: Option<BodyId>,
+    /// The list of locals currently being checked by the lint.
+    /// If the value is `None`, then the binding has been seen as a ref pattern, but is not linted.
+    /// This is needed for or patterns where one of the branches can be linted, but another can not
+    /// be.
+    ///
+    /// e.g. `m!(x) | Foo::Bar(ref x)`
+    ref_locals: FxIndexMap<HirId, Option<RefPat>>,
 }
 
 struct StateData {
@@ -67,6 +142,9 @@ enum State {
         ty_changed_count: usize,
         is_final_ufcs: bool,
     },
+    DerefedBorrow {
+        count: u32,
+    },
 }
 
 // A reference operation considered by this lint pass
@@ -76,15 +154,31 @@ enum RefOp {
     AddrOf,
 }
 
+struct RefPat {
+    /// Whether every usage of the binding is dereferenced.
+    always_deref: bool,
+    /// The spans of all the ref bindings for this local.
+    spans: Vec<Span>,
+    /// The applicability of this suggestion.
+    app: Applicability,
+    /// All the replacements which need to be made.
+    replacements: Vec<(Span, String)>,
+}
+
 impl<'tcx> LateLintPass<'tcx> for Dereferencing {
+    #[allow(clippy::too_many_lines)]
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         // Skip path expressions from deref calls. e.g. `Deref::deref(e)`
         if Some(expr.hir_id) == self.skip_expr.take() {
             return;
         }
 
+        if let Some(local) = path_to_local(expr) {
+            self.check_local_usage(cx, expr, local);
+        }
+
         // Stop processing sub expressions when a macro call is seen
-        if in_macro(expr.span) {
+        if expr.span.from_expansion() {
             if let Some((state, data)) = self.state.take() {
                 report(cx, expr, state, data);
             }
@@ -127,6 +221,48 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
                             },
                         ));
                     },
+                    RefOp::AddrOf => {
+                        // Find the number of times the borrow is auto-derefed.
+                        let mut iter = find_adjustments(cx.tcx, typeck, expr).iter();
+                        if let Some((i, adjust)) = iter.by_ref().enumerate().find_map(|(i, adjust)| {
+                            if !matches!(adjust.kind, Adjust::Deref(_)) {
+                                Some((i, adjust))
+                            } else if !adjust.target.is_ref() {
+                                // Add one to the number of references found.
+                                Some((i + 1, adjust))
+                            } else {
+                                None
+                            }
+                        }) {
+                            // Found two consecutive derefs. At least one can be removed.
+                            if i > 1 {
+                                let target_mut = iter::once(adjust)
+                                    .chain(iter)
+                                    .find_map(|adjust| match adjust.kind {
+                                        Adjust::Borrow(AutoBorrow::Ref(_, m)) => Some(m.into()),
+                                        _ => None,
+                                    })
+                                    // This default should never happen. Auto-deref always reborrows.
+                                    .unwrap_or(Mutability::Not);
+                                self.state = Some((
+                                    // Subtract one for the current borrow expression, and one to cover the last
+                                    // reference which can't be removed (it's either reborrowed, or needed for
+                                    // auto-deref to happen).
+                                    State::DerefedBorrow {
+                                        count:
+                                            // Truncation here would require more than a `u32::MAX` level reference. The compiler
+                                            // does not support this.
+                                            #[allow(clippy::cast_possible_truncation)]
+                                            { i as u32 - 2 }
+                                    },
+                                    StateData {
+                                        span: expr.span,
+                                        target_mut,
+                                    },
+                                ));
+                            }
+                        }
+                    },
                     _ => (),
                 }
             },
@@ -143,10 +279,80 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
                     data,
                 ));
             },
+            (Some((State::DerefedBorrow { count }, data)), RefOp::AddrOf) if count != 0 => {
+                self.state = Some((State::DerefedBorrow { count: count - 1 }, data));
+            },
 
             (Some((state, data)), _) => report(cx, expr, state, data),
         }
     }
+
+    fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
+        if let PatKind::Binding(BindingAnnotation::Ref, id, name, _) = pat.kind {
+            if let Some(opt_prev_pat) = self.ref_locals.get_mut(&id) {
+                // This binding id has been seen before. Add this pattern to the list of changes.
+                if let Some(prev_pat) = opt_prev_pat {
+                    if pat.span.from_expansion() {
+                        // Doesn't match the context of the previous pattern. Can't lint here.
+                        *opt_prev_pat = None;
+                    } else {
+                        prev_pat.spans.push(pat.span);
+                        prev_pat.replacements.push((
+                            pat.span,
+                            snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut prev_pat.app)
+                                .0
+                                .into(),
+                        ));
+                    }
+                }
+                return;
+            }
+
+            if_chain! {
+                if !pat.span.from_expansion();
+                if let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind();
+                // only lint immutable refs, because borrowed `&mut T` cannot be moved out
+                if let ty::Ref(_, _, Mutability::Not) = *tam.kind();
+                then {
+                    let mut app = Applicability::MachineApplicable;
+                    let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0;
+                    self.current_body = self.current_body.or(cx.enclosing_body);
+                    self.ref_locals.insert(
+                        id,
+                        Some(RefPat {
+                            always_deref: true,
+                            spans: vec![pat.span],
+                            app,
+                            replacements: vec![(pat.span, snip.into())],
+                        }),
+                    );
+                }
+            }
+        }
+    }
+
+    fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
+        if Some(body.id()) == self.current_body {
+            for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) {
+                let replacements = pat.replacements;
+                let app = pat.app;
+                span_lint_and_then(
+                    cx,
+                    if pat.always_deref {
+                        NEEDLESS_BORROW
+                    } else {
+                        REF_BINDING_TO_REFERENCE
+                    },
+                    pat.spans,
+                    "this pattern creates a reference to a reference",
+                    |diag| {
+                        diag.multipart_suggestion("try this", replacements, app);
+                    },
+                );
+            }
+            self.current_body = None;
+        }
+    }
 }
 
 fn try_parse_ref_op(
@@ -250,6 +456,48 @@ fn is_linted_explicit_deref_position(parent: Option<Node<'_>>, child_id: HirId,
     }
 }
 
+/// Adjustments are sometimes made in the parent block rather than the expression itself.
+fn find_adjustments(
+    tcx: TyCtxt<'tcx>,
+    typeck: &'tcx TypeckResults<'_>,
+    expr: &'tcx Expr<'_>,
+) -> &'tcx [Adjustment<'tcx>] {
+    let map = tcx.hir();
+    let mut iter = map.parent_iter(expr.hir_id);
+    let mut prev = expr;
+
+    loop {
+        match typeck.expr_adjustments(prev) {
+            [] => (),
+            a => break a,
+        };
+
+        match iter.next().map(|(_, x)| x) {
+            Some(Node::Block(_)) => {
+                if let Some((_, Node::Expr(e))) = iter.next() {
+                    prev = e;
+                } else {
+                    // This shouldn't happen. Blocks are always contained in an expression.
+                    break &[];
+                }
+            },
+            Some(Node::Expr(&Expr {
+                kind: ExprKind::Break(Destination { target_id: Ok(id), .. }, _),
+                ..
+            })) => {
+                if let Some(Node::Expr(e)) = map.find(id) {
+                    prev = e;
+                    iter = map.parent_iter(id);
+                } else {
+                    // This shouldn't happen. The destination should exist.
+                    break &[];
+                }
+            },
+            _ => break &[],
+        }
+    }
+}
+
 #[allow(clippy::needless_pass_by_value)]
 fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) {
     match state {
@@ -300,5 +548,83 @@ fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: Stat
                 app,
             );
         },
+        State::DerefedBorrow { .. } => {
+            let mut app = Applicability::MachineApplicable;
+            let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
+            span_lint_and_sugg(
+                cx,
+                NEEDLESS_BORROW,
+                data.span,
+                &format!(
+                    "this expression borrows a reference (`{}`) that is immediately dereferenced by the compiler",
+                    cx.typeck_results().expr_ty(expr),
+                ),
+                "change this to",
+                snip.into(),
+                app,
+            );
+        },
+    }
+}
+
+impl Dereferencing {
+    fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, local: HirId) {
+        if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
+            if let Some(pat) = outer_pat {
+                // Check for auto-deref
+                if !matches!(
+                    cx.typeck_results().expr_adjustments(e),
+                    [
+                        Adjustment {
+                            kind: Adjust::Deref(_),
+                            ..
+                        },
+                        Adjustment {
+                            kind: Adjust::Deref(_),
+                            ..
+                        },
+                        ..
+                    ]
+                ) {
+                    match get_parent_expr(cx, e) {
+                        // Field accesses are the same no matter the number of references.
+                        Some(Expr {
+                            kind: ExprKind::Field(..),
+                            ..
+                        }) => (),
+                        Some(&Expr {
+                            span,
+                            kind: ExprKind::Unary(UnOp::Deref, _),
+                            ..
+                        }) if !span.from_expansion() => {
+                            // Remove explicit deref.
+                            let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0;
+                            pat.replacements.push((span, snip.into()));
+                        },
+                        Some(parent) if !parent.span.from_expansion() => {
+                            // Double reference might be needed at this point.
+                            if parent.precedence().order() == PREC_POSTFIX {
+                                // Parentheses would be needed here, don't lint.
+                                *outer_pat = None;
+                            } else {
+                                pat.always_deref = false;
+                                let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
+                                pat.replacements.push((e.span, format!("&{}", snip)));
+                            }
+                        },
+                        _ if !e.span.from_expansion() => {
+                            // Double reference might be needed at this point.
+                            pat.always_deref = false;
+                            let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
+                            pat.replacements.push((e.span, format!("&{}", snip)));
+                        },
+                        // Edge case for macros. The span of the identifier will usually match the context of the
+                        // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
+                        // macros
+                        _ => *outer_pat = None,
+                    }
+                }
+            }
+        }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
index fdef0abe970..d0fab2b48fb 100644
--- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::{in_macro, is_automatically_derived, is_default_equivalent, remove_blocks};
+use clippy_utils::{is_automatically_derived, is_default_equivalent, remove_blocks};
 use rustc_hir::{
     def::{DefKind, Res},
     Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind,
@@ -46,6 +46,7 @@ declare_clippy_lint! {
     /// has exactly equal bounds, and therefore this lint is disabled for types with
     /// generic parameters.
     ///
+    #[clippy::version = "1.57.0"]
     pub DERIVABLE_IMPLS,
     complexity,
     "manual implementation of the `Default` trait which is equal to a derive"
@@ -72,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
             }) = item.kind;
             if let attrs = cx.tcx.hir().attrs(item.hir_id());
             if !is_automatically_derived(attrs);
-            if !in_macro(item.span);
+            if !item.span.from_expansion();
             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();
diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs
index 94b35ad88af..097cb65f56e 100644
--- a/src/tools/clippy/clippy_lints/src/derive.rs
+++ b/src/tools/clippy/clippy_lints/src/derive.rs
@@ -37,6 +37,7 @@ declare_clippy_lint! {
     ///     ...
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DERIVE_HASH_XOR_EQ,
     correctness,
     "deriving `Hash` but implementing `PartialEq` explicitly"
@@ -87,6 +88,7 @@ declare_clippy_lint! {
     /// #[derive(Ord, PartialOrd, PartialEq, Eq)]
     /// struct Foo;
     /// ```
+    #[clippy::version = "1.47.0"]
     pub DERIVE_ORD_XOR_PARTIAL_ORD,
     correctness,
     "deriving `Ord` but implementing `PartialOrd` explicitly"
@@ -113,6 +115,7 @@ declare_clippy_lint! {
     ///     // ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXPL_IMPL_CLONE_ON_COPY,
     pedantic,
     "implementing `Clone` explicitly on `Copy` types"
@@ -146,6 +149,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub UNSAFE_DERIVE_DESERIALIZE,
     pedantic,
     "deriving `serde::Deserialize` on a type that has methods using `unsafe`"
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_method.rs b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs
index 22d726cdcb7..6d4065907fb 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_method.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs
@@ -47,18 +47,19 @@ declare_clippy_lint! {
     /// let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config.
     /// xs.push(123); // Vec::push is _not_ disallowed in the config.
     /// ```
-    pub DISALLOWED_METHOD,
+    #[clippy::version = "1.49.0"]
+    pub DISALLOWED_METHODS,
     nursery,
     "use of a disallowed method call"
 }
 
 #[derive(Clone, Debug)]
-pub struct DisallowedMethod {
+pub struct DisallowedMethods {
     conf_disallowed: Vec<conf::DisallowedMethod>,
     disallowed: DefIdMap<Option<String>>,
 }
 
-impl DisallowedMethod {
+impl DisallowedMethods {
     pub fn new(conf_disallowed: Vec<conf::DisallowedMethod>) -> Self {
         Self {
             conf_disallowed,
@@ -67,9 +68,9 @@ impl DisallowedMethod {
     }
 }
 
-impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]);
+impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]);
 
-impl<'tcx> LateLintPass<'tcx> for DisallowedMethod {
+impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
     fn check_crate(&mut self, cx: &LateContext<'_>) {
         for conf in &self.conf_disallowed {
             let (path, reason) = match conf {
@@ -97,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethod {
         };
         let func_path = cx.tcx.def_path_str(def_id);
         let msg = format!("use of a disallowed method `{}`", func_path);
-        span_lint_and_then(cx, DISALLOWED_METHOD, expr.span, &msg, |diag| {
+        span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| {
             if let Some(reason) = reason {
                 diag.note(reason);
             }
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
index 6d38d30cd0b..3c3f3631849 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
@@ -38,6 +38,7 @@ declare_clippy_lint! {
     /// let zähler = 10; // OK, it's still latin.
     /// let カウンタ = 10; // Will spawn the lint.
     /// ```
+    #[clippy::version = "1.55.0"]
     pub DISALLOWED_SCRIPT_IDENTS,
     restriction,
     "usage of non-allowed Unicode scripts"
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_type.rs b/src/tools/clippy/clippy_lints/src/disallowed_types.rs
index 48f781516f4..eaed4032713 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_type.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_types.rs
@@ -42,18 +42,19 @@ declare_clippy_lint! {
     /// // A similar type that is allowed by the config
     /// use std::collections::HashMap;
     /// ```
-    pub DISALLOWED_TYPE,
+    #[clippy::version = "1.55.0"]
+    pub DISALLOWED_TYPES,
     nursery,
-    "use of a disallowed type"
+    "use of disallowed types"
 }
 #[derive(Clone, Debug)]
-pub struct DisallowedType {
+pub struct DisallowedTypes {
     conf_disallowed: Vec<conf::DisallowedType>,
     def_ids: FxHashMap<DefId, Option<String>>,
     prim_tys: FxHashMap<PrimTy, Option<String>>,
 }
 
-impl DisallowedType {
+impl DisallowedTypes {
     pub fn new(conf_disallowed: Vec<conf::DisallowedType>) -> Self {
         Self {
             conf_disallowed,
@@ -79,9 +80,9 @@ impl DisallowedType {
     }
 }
 
-impl_lint_pass!(DisallowedType => [DISALLOWED_TYPE]);
+impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]);
 
-impl<'tcx> LateLintPass<'tcx> for DisallowedType {
+impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
     fn check_crate(&mut self, cx: &LateContext<'_>) {
         for conf in &self.conf_disallowed {
             let (path, reason) = match conf {
@@ -124,7 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedType {
 fn emit(cx: &LateContext<'_>, name: &str, span: Span, reason: Option<&str>) {
     span_lint_and_then(
         cx,
-        DISALLOWED_TYPE,
+        DISALLOWED_TYPES,
         span,
         &format!("`{}` is not allowed according to config", name),
         |diag| {
diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs
index d4ba072807f..2cdd59c5691 100644
--- a/src/tools/clippy/clippy_lints/src/doc.rs
+++ b/src/tools/clippy/clippy_lints/src/doc.rs
@@ -1,5 +1,5 @@
 use clippy_utils::attrs::is_doc_hidden;
-use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_then};
 use clippy_utils::source::{first_line_of_span, snippet_with_applicability};
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 use clippy_utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty};
@@ -10,7 +10,7 @@ use rustc_ast::token::CommentKind;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::emitter::EmitterWriter;
-use rustc_errors::{Applicability, Handler};
+use rustc_errors::{Applicability, Handler, SuggestionStyle};
 use rustc_hir as hir;
 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
 use rustc_hir::{AnonConst, Expr, ExprKind, QPath};
@@ -67,6 +67,7 @@ declare_clippy_lint! {
     /// /// [SmallVec]: SmallVec
     /// fn main() {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DOC_MARKDOWN,
     pedantic,
     "presence of `_`, `::` or camel-case outside backticks in documentation"
@@ -101,6 +102,7 @@ declare_clippy_lint! {
     ///     unimplemented!();
     /// }
     /// ```
+    #[clippy::version = "1.39.0"]
     pub MISSING_SAFETY_DOC,
     style,
     "`pub unsafe fn` without `# Safety` docs"
@@ -129,6 +131,7 @@ declare_clippy_lint! {
     ///     unimplemented!();
     /// }
     /// ```
+    #[clippy::version = "1.41.0"]
     pub MISSING_ERRORS_DOC,
     pedantic,
     "`pub fn` returns `Result` without `# Errors` in doc comment"
@@ -159,6 +162,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.52.0"]
     pub MISSING_PANICS_DOC,
     pedantic,
     "`pub fn` may panic without `# Panics` in doc comment"
@@ -187,6 +191,7 @@ declare_clippy_lint! {
     ///     unimplemented!();
     /// }
     /// ``````
+    #[clippy::version = "1.40.0"]
     pub NEEDLESS_DOCTEST_MAIN,
     style,
     "presence of `fn main() {` in code examples"
@@ -639,7 +644,9 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
                             | ItemKind::ExternCrate(..)
                             | ItemKind::ForeignMod(..) => return false,
                             // We found a main function ...
-                            ItemKind::Fn(box Fn { sig, body: Some(block), .. }) if item.ident.name == sym::main => {
+                            ItemKind::Fn(box Fn {
+                                sig, body: Some(block), ..
+                            }) if item.ident.name == sym::main => {
                                 let is_async = matches!(sig.header.asyncness, Async::Yes { .. });
                                 let returns_nothing = match &sig.decl.output {
                                     FnRetTy::Default(..) => true,
@@ -763,14 +770,23 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) {
     if has_underscore(word) || word.contains("::") || is_camel_case(word) {
         let mut applicability = Applicability::MachineApplicable;
 
-        span_lint_and_sugg(
+        span_lint_and_then(
             cx,
             DOC_MARKDOWN,
             span,
             "item in documentation is missing backticks",
-            "try",
-            format!("`{}`", snippet_with_applicability(cx, span, "..", &mut applicability)),
-            applicability,
+            |diag| {
+                let snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
+                diag.span_suggestion_with_style(
+                    span,
+                    "try",
+                    format!("`{}`", snippet),
+                    applicability,
+                    // always show the suggestion in a separate line, since the
+                    // inline presentation adds another pair of backticks
+                    SuggestionStyle::ShowAlways,
+                );
+            },
         );
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/double_comparison.rs b/src/tools/clippy/clippy_lints/src/double_comparison.rs
index 6520bb91faf..176092e5b28 100644
--- a/src/tools/clippy/clippy_lints/src/double_comparison.rs
+++ b/src/tools/clippy/clippy_lints/src/double_comparison.rs
@@ -31,6 +31,7 @@ declare_clippy_lint! {
     /// # let y = 2;
     /// if x <= y {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DOUBLE_COMPARISONS,
     complexity,
     "unnecessary double comparisons that can be simplified"
diff --git a/src/tools/clippy/clippy_lints/src/double_parens.rs b/src/tools/clippy/clippy_lints/src/double_parens.rs
index d0d87b6df9a..e10f740d24a 100644
--- a/src/tools/clippy/clippy_lints/src/double_parens.rs
+++ b/src/tools/clippy/clippy_lints/src/double_parens.rs
@@ -32,6 +32,7 @@ declare_clippy_lint! {
     /// // Good
     /// foo(0);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DOUBLE_PARENS,
     complexity,
     "Warn on unnecessary double parentheses"
diff --git a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
index 0f3dc866afb..a8f8e3d8a42 100644
--- a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
@@ -25,6 +25,7 @@ declare_clippy_lint! {
     /// // still locked
     /// operation_that_requires_mutex_to_be_unlocked();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DROP_REF,
     correctness,
     "calls to `std::mem::drop` with a reference instead of an owned value"
@@ -46,6 +47,7 @@ declare_clippy_lint! {
     /// let x = Box::new(1);
     /// std::mem::forget(&x) // Should have been forget(x), x will still be dropped
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FORGET_REF,
     correctness,
     "calls to `std::mem::forget` with a reference instead of an owned value"
@@ -67,6 +69,7 @@ declare_clippy_lint! {
     /// std::mem::drop(x) // A copy of x is passed to the function, leaving the
     ///                   // original unaffected
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DROP_COPY,
     correctness,
     "calls to `std::mem::drop` with a value that implements Copy"
@@ -94,6 +97,7 @@ declare_clippy_lint! {
     /// std::mem::forget(x) // A copy of x is passed to the function, leaving the
     ///                     // original unaffected
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FORGET_COPY,
     correctness,
     "calls to `std::mem::forget` with a value that implements Copy"
diff --git a/src/tools/clippy/clippy_lints/src/duration_subsec.rs b/src/tools/clippy/clippy_lints/src/duration_subsec.rs
index 3774de62521..3070d105014 100644
--- a/src/tools/clippy/clippy_lints/src/duration_subsec.rs
+++ b/src/tools/clippy/clippy_lints/src/duration_subsec.rs
@@ -33,6 +33,7 @@ declare_clippy_lint! {
     /// let _micros = dur.subsec_micros();
     /// let _millis = dur.subsec_millis();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DURATION_SUBSEC,
     complexity,
     "checks for calculation of subsecond microseconds or milliseconds"
diff --git a/src/tools/clippy/clippy_lints/src/else_if_without_else.rs b/src/tools/clippy/clippy_lints/src/else_if_without_else.rs
index b64246515f3..92c56c762aa 100644
--- a/src/tools/clippy/clippy_lints/src/else_if_without_else.rs
+++ b/src/tools/clippy/clippy_lints/src/else_if_without_else.rs
@@ -40,6 +40,7 @@ declare_clippy_lint! {
     ///     // We don't care about zero.
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ELSE_IF_WITHOUT_ELSE,
     restriction,
     "`if` expression with an `else if`, but without a final `else` branch"
diff --git a/src/tools/clippy/clippy_lints/src/empty_enum.rs b/src/tools/clippy/clippy_lints/src/empty_enum.rs
index 3453c2da278..af9e65e6361 100644
--- a/src/tools/clippy/clippy_lints/src/empty_enum.rs
+++ b/src/tools/clippy/clippy_lints/src/empty_enum.rs
@@ -34,6 +34,7 @@ declare_clippy_lint! {
     ///
     /// struct Test(!);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EMPTY_ENUM,
     pedantic,
     "enum with no variants"
diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs
index 57fd24bd4f0..3d92eb16870 100644
--- a/src/tools/clippy/clippy_lints/src/entry.rs
+++ b/src/tools/clippy/clippy_lints/src/entry.rs
@@ -54,6 +54,7 @@ declare_clippy_lint! {
     /// # let v = 1;
     /// map.entry(k).or_insert(v);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MAP_ENTRY,
     perf,
     "use of `contains_key` followed by `insert` on a `HashMap` or `BTreeMap`"
diff --git a/src/tools/clippy/clippy_lints/src/enum_clike.rs b/src/tools/clippy/clippy_lints/src/enum_clike.rs
index a2c3c7a7b49..3b6661c817b 100644
--- a/src/tools/clippy/clippy_lints/src/enum_clike.rs
+++ b/src/tools/clippy/clippy_lints/src/enum_clike.rs
@@ -28,6 +28,7 @@ declare_clippy_lint! {
     ///     Y = 0,
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ENUM_CLIKE_UNPORTABLE_VARIANT,
     correctness,
     "C-like enums that are `repr(isize/usize)` and have values that don't fit into an `i32`"
diff --git a/src/tools/clippy/clippy_lints/src/enum_variants.rs b/src/tools/clippy/clippy_lints/src/enum_variants.rs
index 404b67c8f29..fc3a35efaf8 100644
--- a/src/tools/clippy/clippy_lints/src/enum_variants.rs
+++ b/src/tools/clippy/clippy_lints/src/enum_variants.rs
@@ -34,6 +34,7 @@ declare_clippy_lint! {
     ///     Battenberg,
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ENUM_VARIANT_NAMES,
     style,
     "enums where all variants share a prefix/postfix"
@@ -59,6 +60,7 @@ declare_clippy_lint! {
     ///     struct BlackForest;
     /// }
     /// ```
+    #[clippy::version = "1.33.0"]
     pub MODULE_NAME_REPETITIONS,
     pedantic,
     "type names prefixed/postfixed with their containing module's name"
@@ -89,6 +91,7 @@ declare_clippy_lint! {
     ///     ...
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MODULE_INCEPTION,
     style,
     "modules that have the same name as their parent module"
diff --git a/src/tools/clippy/clippy_lints/src/eq_op.rs b/src/tools/clippy/clippy_lints/src/eq_op.rs
index 655560afd42..10123460527 100644
--- a/src/tools/clippy/clippy_lints/src/eq_op.rs
+++ b/src/tools/clippy/clippy_lints/src/eq_op.rs
@@ -1,9 +1,7 @@
 use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then};
 use clippy_utils::source::snippet;
 use clippy_utils::ty::{implements_trait, is_copy};
-use clippy_utils::{
-    ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, in_macro, is_expn_of, is_in_test_function,
-};
+use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, is_expn_of, is_in_test_function};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, StmtKind};
@@ -36,6 +34,7 @@ declare_clippy_lint! {
     /// # let b = 4;
     /// assert_eq!(a, a);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EQ_OP,
     correctness,
     "equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)"
@@ -61,6 +60,7 @@ declare_clippy_lint! {
     /// // Good
     /// x == *y
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OP_REF,
     style,
     "taking a reference to satisfy the type constraints on `==`"
@@ -102,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp {
             }
             let macro_with_not_op = |expr_kind: &ExprKind<'_>| {
                 if let ExprKind::Unary(_, expr) = *expr_kind {
-                    in_macro(expr.span)
+                    expr.span.from_expansion()
                 } else {
                     false
                 }
diff --git a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs
index e8b1d6f6eda..8905cc0de45 100644
--- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs
+++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs
@@ -32,6 +32,7 @@ declare_clippy_lint! {
     ///     do_thing();
     /// }
     /// ```
+    #[clippy::version = "1.57.0"]
     pub EQUATABLE_IF_LET,
     nursery,
     "using pattern matching instead of equality"
diff --git a/src/tools/clippy/clippy_lints/src/erasing_op.rs b/src/tools/clippy/clippy_lints/src/erasing_op.rs
index d0944c37cf5..d49cec26be5 100644
--- a/src/tools/clippy/clippy_lints/src/erasing_op.rs
+++ b/src/tools/clippy/clippy_lints/src/erasing_op.rs
@@ -21,6 +21,7 @@ declare_clippy_lint! {
     /// 0 * x;
     /// x & 0;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ERASING_OP,
     correctness,
     "using erasing operations, e.g., `x * 0` or `y & 0`"
diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs
index 75b1c882c23..bc5d2f6278d 100644
--- a/src/tools/clippy/clippy_lints/src/escape.rs
+++ b/src/tools/clippy/clippy_lints/src/escape.rs
@@ -41,6 +41,7 @@ declare_clippy_lint! {
     /// foo(x);
     /// println!("{}", x);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub BOXED_LOCAL,
     perf,
     "using `Box<T>` where unnecessary"
diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
index 9247343b52a..5a4b4247104 100644
--- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs
+++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
@@ -1,8 +1,8 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::higher::VecArgs;
 use clippy_utils::source::snippet_opt;
-use clippy_utils::usage::UsedAfterExprVisitor;
-use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local_id};
+use clippy_utils::usage::local_used_after_expr;
+use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local, path_to_local_id};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::def_id::DefId;
@@ -39,6 +39,7 @@ declare_clippy_lint! {
     /// ```
     /// where `foo(_)` is a plain function that takes the exact argument type of
     /// `x`.
+    #[clippy::version = "pre 1.29.0"]
     pub REDUNDANT_CLOSURE,
     style,
     "redundant closures, i.e., `|a| foo(a)` (which can be written as just `foo`)"
@@ -60,6 +61,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// Some('a').map(char::to_uppercase);
     /// ```
+    #[clippy::version = "1.35.0"]
     pub REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
     pedantic,
     "redundant closures for method calls"
@@ -118,7 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
                             if let ty::Closure(_, substs) = callee_ty.peel_refs().kind();
                             if substs.as_closure().kind() == ClosureKind::FnMut;
                             if get_enclosing_loop_or_closure(cx.tcx, expr).is_some()
-                                || UsedAfterExprVisitor::is_found(cx, callee);
+                                || path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, callee));
 
                             then {
                                 // Mutable closure is used after current expr; we cannot consume it.
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 1b56dd4b081..cdac9f3e6e1 100644
--- a/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs
+++ b/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs
@@ -40,6 +40,7 @@ declare_clippy_lint! {
     /// };
     /// let a = tmp + x;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EVAL_ORDER_DEPENDENCE,
     suspicious,
     "whether a variable read occurs before a write depends on sub-expression evaluation order"
@@ -67,6 +68,7 @@ declare_clippy_lint! {
     /// let x = (a, b, c, panic!());
     /// // can simply be replaced by `panic!()`
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DIVERGING_SUB_EXPRESSION,
     complexity,
     "whether an expression contains a diverging sub expression"
diff --git a/src/tools/clippy/clippy_lints/src/excessive_bools.rs b/src/tools/clippy/clippy_lints/src/excessive_bools.rs
index 09b6e200838..245a4fc12fd 100644
--- a/src/tools/clippy/clippy_lints/src/excessive_bools.rs
+++ b/src/tools/clippy/clippy_lints/src/excessive_bools.rs
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::in_macro;
 use rustc_ast::ast::{AssocItemKind, Extern, Fn, FnSig, Impl, Item, ItemKind, Trait, Ty, TyKind};
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -38,6 +37,7 @@ declare_clippy_lint! {
     ///     Finished,
     /// }
     /// ```
+    #[clippy::version = "1.43.0"]
     pub STRUCT_EXCESSIVE_BOOLS,
     pedantic,
     "using too many bools in a struct"
@@ -76,6 +76,7 @@ declare_clippy_lint! {
     ///
     /// fn f(shape: Shape, temperature: Temperature) { ... }
     /// ```
+    #[clippy::version = "1.43.0"]
     pub FN_PARAMS_EXCESSIVE_BOOLS,
     pedantic,
     "using too many bools in function parameters"
@@ -135,7 +136,7 @@ fn is_bool_ty(ty: &Ty) -> bool {
 
 impl EarlyLintPass for ExcessiveBools {
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
-        if in_macro(item.span) {
+        if item.span.from_expansion() {
             return;
         }
         match &item.kind {
diff --git a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs
index bb4684ce38b..b0f50b5c144 100644
--- a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs
+++ b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs
@@ -31,6 +31,7 @@ declare_clippy_lint! {
     ///     Baz
     /// }
     /// ```
+    #[clippy::version = "1.51.0"]
     pub EXHAUSTIVE_ENUMS,
     restriction,
     "detects exported enums that have not been marked #[non_exhaustive]"
@@ -60,6 +61,7 @@ declare_clippy_lint! {
     ///     baz: String,
     /// }
     /// ```
+    #[clippy::version = "1.51.0"]
     pub EXHAUSTIVE_STRUCTS,
     restriction,
     "detects exported structs that have not been marked #[non_exhaustive]"
diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs
index 9cd5b2d9f44..d64cc61916c 100644
--- a/src/tools/clippy/clippy_lints/src/exit.rs
+++ b/src/tools/clippy/clippy_lints/src/exit.rs
@@ -18,6 +18,7 @@ declare_clippy_lint! {
     /// ```ignore
     /// std::process::exit(0)
     /// ```
+    #[clippy::version = "1.41.0"]
     pub EXIT,
     restriction,
     "`std::process::exit` is called, terminating the program"
diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs
index 4f46ef906f4..6b327b9ce17 100644
--- a/src/tools/clippy/clippy_lints/src/explicit_write.rs
+++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs
@@ -23,6 +23,7 @@ declare_clippy_lint! {
     /// // this would be clearer as `eprintln!("foo: {:?}", bar);`
     /// writeln!(&mut std::io::stderr(), "foo: {:?}", bar).unwrap();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXPLICIT_WRITE,
     complexity,
     "using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work"
diff --git a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
index 70337f5bbeb..05d300058cf 100644
--- a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
+++ b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
@@ -44,6 +44,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FALLIBLE_IMPL_FROM,
     nursery,
     "Warn on impls of `From<..>` that contain `panic!()` or `unwrap()`"
diff --git a/src/tools/clippy/clippy_lints/src/feature_name.rs b/src/tools/clippy/clippy_lints/src/feature_name.rs
index f534327f7a0..dc6bef52ddd 100644
--- a/src/tools/clippy/clippy_lints/src/feature_name.rs
+++ b/src/tools/clippy/clippy_lints/src/feature_name.rs
@@ -31,6 +31,7 @@ declare_clippy_lint! {
     /// ghi = []
     /// ```
     ///
+    #[clippy::version = "1.57.0"]
     pub REDUNDANT_FEATURE_NAMES,
     cargo,
     "usage of a redundant feature name"
@@ -60,6 +61,7 @@ declare_clippy_lint! {
     /// def = []
     ///
     /// ```
+    #[clippy::version = "1.57.0"]
     pub NEGATIVE_FEATURE_NAMES,
     cargo,
     "usage of a negative feature name"
diff --git a/src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs b/src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs
index c33d80b8e8e..ca8886228de 100644
--- a/src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs
+++ b/src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs
@@ -37,6 +37,7 @@ declare_clippy_lint! {
     ///     (a - b).abs() < f32::EPSILON
     /// }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub FLOAT_EQUALITY_WITHOUT_ABS,
     suspicious,
     "float equality check without `.abs()`"
diff --git a/src/tools/clippy/clippy_lints/src/float_literal.rs b/src/tools/clippy/clippy_lints/src/float_literal.rs
index 1e8a5bd7d34..d30dede833c 100644
--- a/src/tools/clippy/clippy_lints/src/float_literal.rs
+++ b/src/tools/clippy/clippy_lints/src/float_literal.rs
@@ -27,6 +27,7 @@ declare_clippy_lint! {
     /// let v: f64 = 0.123_456_789_9;
     /// println!("{}", v); //  0.123_456_789_9
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXCESSIVE_PRECISION,
     style,
     "excessive precision for float literal"
@@ -50,6 +51,7 @@ declare_clippy_lint! {
     /// let _: f32 = 16_777_216.0;
     /// let _: f64 = 16_777_217.0;
     /// ```
+    #[clippy::version = "1.43.0"]
     pub LOSSY_FLOAT_LITERAL,
     restriction,
     "lossy whole number float literals"
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 eda611117ba..3df511ea8e7 100644
--- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
+++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
@@ -4,7 +4,7 @@ use clippy_utils::consts::{
 };
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::higher;
-use clippy_utils::{eq_expr_value, get_parent_expr, numeric_literal, sugg};
+use clippy_utils::{eq_expr_value, get_parent_expr, in_constant, numeric_literal, sugg};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
@@ -43,6 +43,7 @@ declare_clippy_lint! {
     /// let _ = a.ln_1p();
     /// let _ = a.exp_m1();
     /// ```
+    #[clippy::version = "1.43.0"]
     pub IMPRECISE_FLOPS,
     nursery,
     "usage of imprecise floating point operations"
@@ -99,6 +100,7 @@ declare_clippy_lint! {
     /// let _ = a.abs();
     /// let _ = -a.abs();
     /// ```
+    #[clippy::version = "1.43.0"]
     pub SUBOPTIMAL_FLOPS,
     nursery,
     "usage of sub-optimal floating point operations"
@@ -685,6 +687,11 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
 
 impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        // All of these operations are currently not const.
+        if in_constant(cx, expr.hir_id) {
+            return;
+        }
+
         if let ExprKind::MethodCall(path, _, args, _) = &expr.kind {
             let recv_ty = cx.typeck_results().expr_ty(&args[0]);
 
diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs
index 7169ac9ad6c..3f043e5f2f1 100644
--- a/src/tools/clippy/clippy_lints/src/format.rs
+++ b/src/tools/clippy/clippy_lints/src/format.rs
@@ -33,6 +33,7 @@ declare_clippy_lint! {
     /// // Good
     /// foo.to_owned();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USELESS_FORMAT,
     complexity,
     "useless use of `format!`"
diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs
index 8b27442aa94..f0e1a67dcdd 100644
--- a/src/tools/clippy/clippy_lints/src/format_args.rs
+++ b/src/tools/clippy/clippy_lints/src/format_args.rs
@@ -10,7 +10,7 @@ use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment};
 use rustc_middle::ty::Ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::{sym, BytePos, ExpnData, ExpnKind, Span, Symbol};
+use rustc_span::{sym, ExpnData, ExpnKind, Span, Symbol};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -31,6 +31,7 @@ declare_clippy_lint! {
     /// # use std::panic::Location;
     /// println!("error: something failed at {}", Location::caller());
     /// ```
+    #[clippy::version = "1.58.0"]
     pub FORMAT_IN_FORMAT_ARGS,
     perf,
     "`format!` used in a macro that does formatting"
@@ -56,6 +57,7 @@ declare_clippy_lint! {
     /// # use std::panic::Location;
     /// println!("error: something failed at {}", Location::caller());
     /// ```
+    #[clippy::version = "1.58.0"]
     pub TO_STRING_IN_FORMAT_ARGS,
     perf,
     "`to_string` applied to a type that implements `Display` in format args"
@@ -128,7 +130,7 @@ fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symb
             span_lint_and_then(
                 cx,
                 FORMAT_IN_FORMAT_ARGS,
-                trim_semicolon(cx, call_site),
+                call_site,
                 &format!("`format!` in `{}!` args", name),
                 |diag| {
                     diag.help(&format!(
@@ -192,13 +194,6 @@ fn is_aliased(args: &[FormatArgsArg<'_>], i: usize) -> bool {
         .any(|(j, arg)| i != j && std::ptr::eq(value, arg.value))
 }
 
-fn trim_semicolon(cx: &LateContext<'_>, span: Span) -> Span {
-    snippet_opt(cx, span).map_or(span, |snippet| {
-        let snippet = snippet.trim_end_matches(';');
-        span.with_hi(span.lo() + BytePos(u32::try_from(snippet.len()).unwrap()))
-    })
-}
-
 fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
 where
     I: Iterator<Item = &'tcx Adjustment<'tcx>>,
diff --git a/src/tools/clippy/clippy_lints/src/formatting.rs b/src/tools/clippy/clippy_lints/src/formatting.rs
index b4f186525c5..3e85c8a9c80 100644
--- a/src/tools/clippy/clippy_lints/src/formatting.rs
+++ b/src/tools/clippy/clippy_lints/src/formatting.rs
@@ -21,6 +21,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// a =- 42; // confusing, should it be `a -= 42` or `a = -42`?
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SUSPICIOUS_ASSIGNMENT_FORMATTING,
     suspicious,
     "suspicious formatting of `*=`, `-=` or `!=`"
@@ -43,6 +44,7 @@ declare_clippy_lint! {
     /// if foo &&! bar { // this should be `foo && !bar` but looks like a different operator
     /// }
     /// ```
+    #[clippy::version = "1.40.0"]
     pub SUSPICIOUS_UNARY_OP_FORMATTING,
     suspicious,
     "suspicious formatting of unary `-` or `!` on the RHS of a BinOp"
@@ -79,6 +81,7 @@ declare_clippy_lint! {
     /// if bar { // this is the `else` block of the previous `if`, but should it be?
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SUSPICIOUS_ELSE_FORMATTING,
     suspicious,
     "suspicious formatting of `else`"
@@ -99,6 +102,7 @@ declare_clippy_lint! {
     ///     -4, -5, -6
     /// ];
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub POSSIBLE_MISSING_COMMA,
     correctness,
     "possible missing comma in array"
diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs
index 347c6eb12cb..866ff216f84 100644
--- a/src/tools/clippy/clippy_lints/src/from_over_into.rs
+++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs
@@ -34,6 +34,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.51.0"]
     pub FROM_OVER_INTO,
     style,
     "Warns on implementations of `Into<..>` to use `From<..>`"
diff --git a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs
index 98ce3db025c..73e800073b0 100644
--- a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs
+++ b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs
@@ -35,6 +35,7 @@ declare_clippy_lint! {
     /// let input: &str = get_input();
     /// let num: u16 = input.parse()?;
     /// ```
+    #[clippy::version = "1.52.0"]
     pub FROM_STR_RADIX_10,
     style,
     "from_str_radix with radix 10"
diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs
index d7c5ec9ba35..ad031cbc09d 100644
--- a/src/tools/clippy/clippy_lints/src/functions/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs
@@ -26,6 +26,7 @@ declare_clippy_lint! {
     ///     // ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TOO_MANY_ARGUMENTS,
     complexity,
     "functions with too many arguments"
@@ -49,6 +50,7 @@ declare_clippy_lint! {
     ///     println!("");
     /// }
     /// ```
+    #[clippy::version = "1.34.0"]
     pub TOO_MANY_LINES,
     pedantic,
     "functions with too many lines"
@@ -84,6 +86,7 @@ declare_clippy_lint! {
     ///     println!("{}", unsafe { *x });
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NOT_UNSAFE_PTR_ARG_DEREF,
     correctness,
     "public functions dereferencing raw pointer arguments but not marked `unsafe`"
@@ -103,6 +106,7 @@ declare_clippy_lint! {
     /// #[must_use]
     /// fn useless() { }
     /// ```
+    #[clippy::version = "1.40.0"]
     pub MUST_USE_UNIT,
     style,
     "`#[must_use]` attribute on a unit-returning function / method"
@@ -126,6 +130,7 @@ declare_clippy_lint! {
     ///     unimplemented!();
     /// }
     /// ```
+    #[clippy::version = "1.40.0"]
     pub DOUBLE_MUST_USE,
     style,
     "`#[must_use]` attribute on a `#[must_use]`-returning function / method"
@@ -155,6 +160,7 @@ declare_clippy_lint! {
     /// // this could be annotated with `#[must_use]`.
     /// fn id<T>(t: T) -> T { t }
     /// ```
+    #[clippy::version = "1.40.0"]
     pub MUST_USE_CANDIDATE,
     pedantic,
     "function or method that could take a `#[must_use]` attribute"
@@ -204,6 +210,7 @@ declare_clippy_lint! {
     ///
     /// Note that there are crates that simplify creating the error type, e.g.
     /// [`thiserror`](https://docs.rs/thiserror).
+    #[clippy::version = "1.49.0"]
     pub RESULT_UNIT_ERR,
     style,
     "public function returning `Result` with an `Err` type of `()`"
diff --git a/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs b/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs
index 008ef661b55..65efbbab41a 100644
--- a/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs
@@ -56,8 +56,8 @@ pub(super) fn check_fn(
                     continue;
                 }
             } else {
-                let multi_idx = line.find("/*").unwrap_or_else(|| line.len());
-                let single_idx = line.find("//").unwrap_or_else(|| line.len());
+                let multi_idx = line.find("/*").unwrap_or(line.len());
+                let single_idx = line.find("//").unwrap_or(line.len());
                 code_in_line |= multi_idx > 0 && single_idx > 0;
                 // Implies multi_idx is below line.len()
                 if multi_idx < single_idx {
diff --git a/src/tools/clippy/clippy_lints/src/future_not_send.rs b/src/tools/clippy/clippy_lints/src/future_not_send.rs
index 6b2ac985555..fefdcfed42f 100644
--- a/src/tools/clippy/clippy_lints/src/future_not_send.rs
+++ b/src/tools/clippy/clippy_lints/src/future_not_send.rs
@@ -41,6 +41,7 @@ declare_clippy_lint! {
     /// ```rust
     /// async fn is_send(bytes: std::sync::Arc<[u8]>) {}
     /// ```
+    #[clippy::version = "1.44.0"]
     pub FUTURE_NOT_SEND,
     nursery,
     "public Futures must be Send"
diff --git a/src/tools/clippy/clippy_lints/src/get_last_with_len.rs b/src/tools/clippy/clippy_lints/src/get_last_with_len.rs
index f3929b0f1e6..edca701869e 100644
--- a/src/tools/clippy/clippy_lints/src/get_last_with_len.rs
+++ b/src/tools/clippy/clippy_lints/src/get_last_with_len.rs
@@ -39,6 +39,7 @@ declare_clippy_lint! {
     /// let x = vec![2, 3, 5];
     /// let last_element = x.last();
     /// ```
+    #[clippy::version = "1.37.0"]
     pub GET_LAST_WITH_LEN,
     complexity,
     "Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler"
diff --git a/src/tools/clippy/clippy_lints/src/identity_op.rs b/src/tools/clippy/clippy_lints/src/identity_op.rs
index 414f465c494..b4e7bbc7671 100644
--- a/src/tools/clippy/clippy_lints/src/identity_op.rs
+++ b/src/tools/clippy/clippy_lints/src/identity_op.rs
@@ -22,6 +22,7 @@ declare_clippy_lint! {
     /// # let x = 1;
     /// x / 1 + 0 * 1 - 0 | 0;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub IDENTITY_OP,
     complexity,
     "using identity operations, e.g., `x + 0` or `y / 1`"
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 a4118bf54b6..e20741d2407 100644
--- a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
+++ b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     ///     use_locked(locked);
     /// }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub IF_LET_MUTEX,
     correctness,
     "locking a `Mutex` in an `if let` block can cause deadlocks"
diff --git a/src/tools/clippy/clippy_lints/src/if_not_else.rs b/src/tools/clippy/clippy_lints/src/if_not_else.rs
index ac938156237..3d59b783337 100644
--- a/src/tools/clippy/clippy_lints/src/if_not_else.rs
+++ b/src/tools/clippy/clippy_lints/src/if_not_else.rs
@@ -39,6 +39,7 @@ declare_clippy_lint! {
     ///     a()
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub IF_NOT_ELSE,
     pedantic,
     "`if` branches that could be swapped so no negation operation is necessary on the condition"
diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
index a2dac57454f..30d222bd7d2 100644
--- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
+++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
@@ -1,9 +1,9 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::source::snippet_with_macro_callsite;
-use clippy_utils::{higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs};
+use clippy_utils::{contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs};
 use if_chain::if_chain;
 use rustc_hir::LangItem::{OptionNone, OptionSome};
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_semver::RustcVersion;
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     ///     42
     /// });
     /// ```
+    #[clippy::version = "1.53.0"]
     pub IF_THEN_SOME_ELSE_NONE,
     restriction,
     "Finds if-else that could be written using `bool::then`"
@@ -81,6 +82,7 @@ impl LateLintPass<'_> for IfThenSomeElseNone {
             if let Some(els_expr) = els_block.expr;
             if let ExprKind::Path(ref qpath) = els_expr.kind;
             if is_lang_ctor(cx, qpath, OptionNone);
+            if !stmts_contains_early_return(then_block.stmts);
             then {
                 let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
                 let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
@@ -113,3 +115,11 @@ impl LateLintPass<'_> for IfThenSomeElseNone {
 
     extract_msrv_attr!(LateContext);
 }
+
+fn stmts_contains_early_return(stmts: &[Stmt<'_>]) -> bool {
+    stmts.iter().any(|stmt| {
+        let Stmt { kind: StmtKind::Semi(e), .. } = stmt else { return false };
+
+        contains_return(e)
+    })
+}
diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
index 81eb51e6f7c..6358228dd47 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
@@ -54,6 +54,7 @@ declare_clippy_lint! {
     ///
     /// pub fn foo<S: BuildHasher>(map: &mut HashMap<i32, i32, S>) { }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub IMPLICIT_HASHER,
     pedantic,
     "missing generalization over different hashers"
diff --git a/src/tools/clippy/clippy_lints/src/implicit_return.rs b/src/tools/clippy/clippy_lints/src/implicit_return.rs
index fa7b5302cb1..07caeada80d 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_return.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_return.rs
@@ -2,10 +2,10 @@ use clippy_utils::{
     diagnostics::span_lint_and_sugg,
     get_async_fn_body, is_async_fn,
     source::{snippet_with_applicability, snippet_with_context, walk_span_to_context},
-    visitors::visit_break_exprs,
+    visitors::expr_visitor_no_bodies,
 };
 use rustc_errors::Applicability;
-use rustc_hir::intravisit::FnKind;
+use rustc_hir::intravisit::{FnKind, Visitor};
 use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
@@ -35,6 +35,7 @@ declare_clippy_lint! {
     ///     return x;
     /// }
     /// ```
+    #[clippy::version = "1.33.0"]
     pub IMPLICIT_RETURN,
     restriction,
     "use a return statement like `return expr` instead of an expression"
@@ -144,20 +145,24 @@ fn lint_implicit_returns(
 
         ExprKind::Loop(block, ..) => {
             let mut add_return = false;
-            visit_break_exprs(block, |break_expr, dest, sub_expr| {
-                if dest.target_id.ok() == Some(expr.hir_id) {
-                    if call_site_span.is_none() && break_expr.span.ctxt() == ctxt {
-                        // At this point sub_expr can be `None` in async functions which either diverge, or return the
-                        // unit type.
-                        if let Some(sub_expr) = sub_expr {
-                            lint_break(cx, break_expr.span, sub_expr.span);
+            expr_visitor_no_bodies(|e| {
+                if let ExprKind::Break(dest, sub_expr) = e.kind {
+                    if dest.target_id.ok() == Some(expr.hir_id) {
+                        if call_site_span.is_none() && e.span.ctxt() == ctxt {
+                            // At this point sub_expr can be `None` in async functions which either diverge, or return
+                            // the unit type.
+                            if let Some(sub_expr) = sub_expr {
+                                lint_break(cx, e.span, sub_expr.span);
+                            }
+                        } else {
+                            // the break expression is from a macro call, add a return to the loop
+                            add_return = true;
                         }
-                    } else {
-                        // the break expression is from a macro call, add a return to the loop
-                        add_return = true;
                     }
                 }
-            });
+                true
+            })
+            .visit_block(block);
             if add_return {
                 #[allow(clippy::option_if_let_else)]
                 if let Some(span) = call_site_span {
diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
index a4f60ded3a6..4088c54623d 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::higher;
-use clippy_utils::{in_macro, SpanlessEq};
+use clippy_utils::SpanlessEq;
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -30,6 +30,7 @@ declare_clippy_lint! {
     /// // Good
     /// i = i.saturating_sub(1);
     /// ```
+    #[clippy::version = "1.44.0"]
     pub IMPLICIT_SATURATING_SUB,
     pedantic,
     "Perform saturating subtraction instead of implicitly checking lower bound of data type"
@@ -39,7 +40,7 @@ declare_lint_pass!(ImplicitSaturatingSub => [IMPLICIT_SATURATING_SUB]);
 
 impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-        if in_macro(expr.span) {
+        if expr.span.from_expansion() {
             return;
         }
         if_chain! {
diff --git a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
index 1f8240a1f63..1debdef9d86 100644
--- a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
+++ b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
 use clippy_utils::source::snippet;
 use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashMap;
@@ -56,6 +55,7 @@ declare_clippy_lint! {
     /// # let y = 2;
     /// Foo { x, y };
     /// ```
+    #[clippy::version = "1.52.0"]
     pub INCONSISTENT_STRUCT_CONSTRUCTOR,
     pedantic,
     "the order of the field init shorthand is inconsistent with the order in the struct definition"
@@ -66,7 +66,7 @@ declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRU
 impl LateLintPass<'_> for InconsistentStructConstructor {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
         if_chain! {
-            if !in_macro(expr.span);
+            if !expr.span.from_expansion();
             if let ExprKind::Struct(qpath, fields, base) = expr.kind;
             let ty = cx.typeck_results().expr_ty(expr);
             if let Some(adt_def) = ty.ty_adt_def();
diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
new file mode 100644
index 00000000000..69f1c90beec
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
@@ -0,0 +1,276 @@
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::higher::IfLet;
+use clippy_utils::ty::is_copy;
+use clippy_utils::{is_expn_of, is_lint_allowed, meets_msrv, msrvs, path_to_local};
+use if_chain::if_chain;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::hir::map::Map;
+use rustc_middle::ty;
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::{symbol::Ident, Span};
+use std::convert::TryInto;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// The lint checks for slice bindings in patterns that are only used to
+    /// access individual slice values.
+    ///
+    /// ### Why is this bad?
+    /// Accessing slice values using indices can lead to panics. Using refutable
+    /// patterns can avoid these. Binding to individual values also improves the
+    /// readability as they can be named.
+    ///
+    /// ### Limitations
+    /// This lint currently only checks for immutable access inside `if let`
+    /// patterns.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+    ///
+    /// if let Some(slice) = slice {
+    ///     println!("{}", slice[0]);
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+    ///
+    /// if let Some(&[first, ..]) = slice {
+    ///     println!("{}", first);
+    /// }
+    /// ```
+    #[clippy::version = "1.58.0"]
+    pub INDEX_REFUTABLE_SLICE,
+    nursery,
+    "avoid indexing on slices which could be destructed"
+}
+
+#[derive(Copy, Clone)]
+pub struct IndexRefutableSlice {
+    max_suggested_slice: u64,
+    msrv: Option<RustcVersion>,
+}
+
+impl IndexRefutableSlice {
+    pub fn new(max_suggested_slice_pattern_length: u64, msrv: Option<RustcVersion>) -> Self {
+        Self {
+            max_suggested_slice: max_suggested_slice_pattern_length,
+            msrv,
+        }
+    }
+}
+
+impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]);
+
+impl LateLintPass<'_> for IndexRefutableSlice {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
+        if_chain! {
+            if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some();
+            if let Some(IfLet {let_pat, if_then, ..}) = IfLet::hir(cx, expr);
+            if !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id);
+            if meets_msrv(self.msrv.as_ref(), &msrvs::SLICE_PATTERNS);
+
+            let found_slices = find_slice_values(cx, let_pat);
+            if !found_slices.is_empty();
+            let filtered_slices = filter_lintable_slices(cx, found_slices, self.max_suggested_slice, if_then);
+            if !filtered_slices.is_empty();
+            then {
+                for slice in filtered_slices.values() {
+                    lint_slice(cx, slice);
+                }
+            }
+        }
+    }
+
+    extract_msrv_attr!(LateContext);
+}
+
+fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxHashMap<hir::HirId, SliceLintInformation> {
+    let mut removed_pat: FxHashSet<hir::HirId> = FxHashSet::default();
+    let mut slices: FxHashMap<hir::HirId, SliceLintInformation> = FxHashMap::default();
+    pat.walk_always(|pat| {
+        if let hir::PatKind::Binding(binding, value_hir_id, ident, sub_pat) = pat.kind {
+            // We'll just ignore mut and ref mut for simplicity sake right now
+            if let hir::BindingAnnotation::Mutable | hir::BindingAnnotation::RefMut = binding {
+                return;
+            }
+
+            // This block catches bindings with sub patterns. It would be hard to build a correct suggestion
+            // for them and it's likely that the user knows what they are doing in such a case.
+            if removed_pat.contains(&value_hir_id) {
+                return;
+            }
+            if sub_pat.is_some() {
+                removed_pat.insert(value_hir_id);
+                slices.remove(&value_hir_id);
+                return;
+            }
+
+            let bound_ty = cx.typeck_results().node_type(pat.hir_id);
+            if let ty::Slice(inner_ty) | ty::Array(inner_ty, _) = bound_ty.peel_refs().kind() {
+                // The values need to use the `ref` keyword if they can't be copied.
+                // This will need to be adjusted if the lint want to support multable access in the future
+                let src_is_ref = bound_ty.is_ref() && binding != hir::BindingAnnotation::Ref;
+                let needs_ref = !(src_is_ref || is_copy(cx, inner_ty));
+
+                let slice_info = slices
+                    .entry(value_hir_id)
+                    .or_insert_with(|| SliceLintInformation::new(ident, needs_ref));
+                slice_info.pattern_spans.push(pat.span);
+            }
+        }
+    });
+
+    slices
+}
+
+fn lint_slice(cx: &LateContext<'_>, slice: &SliceLintInformation) {
+    let used_indices = slice
+        .index_use
+        .iter()
+        .map(|(index, _)| *index)
+        .collect::<FxHashSet<_>>();
+
+    let value_name = |index| format!("{}_{}", slice.ident.name, index);
+
+    if let Some(max_index) = used_indices.iter().max() {
+        let opt_ref = if slice.needs_ref { "ref " } else { "" };
+        let pat_sugg_idents = (0..=*max_index)
+            .map(|index| {
+                if used_indices.contains(&index) {
+                    format!("{}{}", opt_ref, value_name(index))
+                } else {
+                    "_".to_string()
+                }
+            })
+            .collect::<Vec<_>>();
+        let pat_sugg = format!("[{}, ..]", pat_sugg_idents.join(", "));
+
+        span_lint_and_then(
+            cx,
+            INDEX_REFUTABLE_SLICE,
+            slice.ident.span,
+            "this binding can be a slice pattern to avoid indexing",
+            |diag| {
+                diag.multipart_suggestion(
+                    "try using a slice pattern here",
+                    slice
+                        .pattern_spans
+                        .iter()
+                        .map(|span| (*span, pat_sugg.clone()))
+                        .collect(),
+                    Applicability::MaybeIncorrect,
+                );
+
+                diag.multipart_suggestion(
+                    "and replace the index expressions here",
+                    slice
+                        .index_use
+                        .iter()
+                        .map(|(index, span)| (*span, value_name(*index)))
+                        .collect(),
+                    Applicability::MaybeIncorrect,
+                );
+
+                // The lint message doesn't contain a warning about the removed index expression,
+                // since `filter_lintable_slices` will only return slices where all access indices
+                // are known at compile time. Therefore, they can be removed without side effects.
+            },
+        );
+    }
+}
+
+#[derive(Debug)]
+struct SliceLintInformation {
+    ident: Ident,
+    needs_ref: bool,
+    pattern_spans: Vec<Span>,
+    index_use: Vec<(u64, Span)>,
+}
+
+impl SliceLintInformation {
+    fn new(ident: Ident, needs_ref: bool) -> Self {
+        Self {
+            ident,
+            needs_ref,
+            pattern_spans: Vec::new(),
+            index_use: Vec::new(),
+        }
+    }
+}
+
+fn filter_lintable_slices<'a, 'tcx>(
+    cx: &'a LateContext<'tcx>,
+    slice_lint_info: FxHashMap<hir::HirId, SliceLintInformation>,
+    max_suggested_slice: u64,
+    scope: &'tcx hir::Expr<'tcx>,
+) -> FxHashMap<hir::HirId, SliceLintInformation> {
+    let mut visitor = SliceIndexLintingVisitor {
+        cx,
+        slice_lint_info,
+        max_suggested_slice,
+    };
+
+    intravisit::walk_expr(&mut visitor, scope);
+
+    visitor.slice_lint_info
+}
+
+struct SliceIndexLintingVisitor<'a, 'tcx> {
+    cx: &'a LateContext<'tcx>,
+    slice_lint_info: FxHashMap<hir::HirId, SliceLintInformation>,
+    max_suggested_slice: u64,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> {
+    type Map = Map<'tcx>;
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
+    }
+
+    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
+        if let Some(local_id) = path_to_local(expr) {
+            let Self {
+                cx,
+                ref mut slice_lint_info,
+                max_suggested_slice,
+            } = *self;
+
+            if_chain! {
+                // Check if this is even a local we're interested in
+                if let Some(use_info) = slice_lint_info.get_mut(&local_id);
+
+                let map = cx.tcx.hir();
+
+                // Checking for slice indexing
+                let parent_id = map.get_parent_node(expr.hir_id);
+                if let Some(hir::Node::Expr(parent_expr)) = map.find(parent_id);
+                if let hir::ExprKind::Index(_, index_expr) = parent_expr.kind;
+                if let Some((Constant::Int(index_value), _)) = constant(cx, cx.typeck_results(), index_expr);
+                if let Ok(index_value) = index_value.try_into();
+                if index_value < max_suggested_slice;
+
+                // Make sure that this slice index is read only
+                let maybe_addrof_id = map.get_parent_node(parent_id);
+                if let Some(hir::Node::Expr(maybe_addrof_expr)) = map.find(maybe_addrof_id);
+                if let hir::ExprKind::AddrOf(_kind, hir::Mutability::Not, _inner_expr) = maybe_addrof_expr.kind;
+                then {
+                    use_info.index_use.push((index_value, map.span(parent_expr.hir_id)));
+                    return;
+                }
+            }
+
+            // The slice was used for something other than indexing
+            self.slice_lint_info.remove(&local_id);
+        }
+        intravisit::walk_expr(self, expr);
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
index f52f090d387..9ead4bb27a5 100644
--- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
+++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
@@ -33,6 +33,7 @@ declare_clippy_lint! {
     /// x[0];
     /// x[3];
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OUT_OF_BOUNDS_INDEXING,
     correctness,
     "out of bounds constant indexing"
@@ -85,6 +86,7 @@ declare_clippy_lint! {
     /// y.get(10..);
     /// y.get(..100);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INDEXING_SLICING,
     restriction,
     "indexing/slicing usage"
diff --git a/src/tools/clippy/clippy_lints/src/infinite_iter.rs b/src/tools/clippy/clippy_lints/src/infinite_iter.rs
index 68c1fa35fcc..c7db47a552b 100644
--- a/src/tools/clippy/clippy_lints/src/infinite_iter.rs
+++ b/src/tools/clippy/clippy_lints/src/infinite_iter.rs
@@ -20,6 +20,7 @@ declare_clippy_lint! {
     ///
     /// iter::repeat(1_u8).collect::<Vec<_>>();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INFINITE_ITER,
     correctness,
     "infinite iteration"
@@ -42,6 +43,7 @@ declare_clippy_lint! {
     /// let infinite_iter = 0..;
     /// [0..].iter().zip(infinite_iter.take_while(|x| *x > 5));
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MAYBE_INFINITE_ITER,
     pedantic,
     "possible infinite iteration"
diff --git a/src/tools/clippy/clippy_lints/src/inherent_impl.rs b/src/tools/clippy/clippy_lints/src/inherent_impl.rs
index 0d23bec27a3..254d3f8a4d0 100644
--- a/src/tools/clippy/clippy_lints/src/inherent_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/inherent_impl.rs
@@ -1,7 +1,7 @@
 //! lint on inherent implementations
 
 use clippy_utils::diagnostics::span_lint_and_note;
-use clippy_utils::{in_macro, is_lint_allowed};
+use clippy_utils::is_lint_allowed;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::{def_id::LocalDefId, Item, ItemKind, Node};
 use rustc_lint::{LateContext, LateLintPass};
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     ///     fn other() {}
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MULTIPLE_INHERENT_IMPL,
     restriction,
     "Multiple inherent impl that could be grouped"
@@ -123,8 +124,10 @@ fn get_impl_span(cx: &LateContext<'_>, id: LocalDefId) -> Option<Span> {
         ..
     }) = cx.tcx.hir().get(id)
     {
-        (!in_macro(span) && impl_item.generics.params.is_empty() && !is_lint_allowed(cx, MULTIPLE_INHERENT_IMPL, id))
-            .then(|| span)
+        (!span.from_expansion()
+            && impl_item.generics.params.is_empty()
+            && !is_lint_allowed(cx, MULTIPLE_INHERENT_IMPL, id))
+        .then(|| span)
     } else {
         None
     }
diff --git a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs
index 61dd0eb4af7..60d234cd6f0 100644
--- a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs
+++ b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs
@@ -41,6 +41,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.38.0"]
     pub INHERENT_TO_STRING,
     style,
     "type implements inherent method `to_string()`, but should instead implement the `Display` trait"
@@ -88,6 +89,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.38.0"]
     pub INHERENT_TO_STRING_SHADOW_DISPLAY,
     correctness,
     "type implements inherent method `to_string()`, which gets shadowed by the implementation of the `Display` trait"
diff --git a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs
index 3e3df903f17..df69d3dcc51 100644
--- a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs
+++ b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs
@@ -24,6 +24,7 @@ declare_clippy_lint! {
     ///     fn name(&self) -> &'static str;
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INLINE_FN_WITHOUT_BODY,
     correctness,
     "use of `#[inline]` on trait methods without bodies"
diff --git a/src/tools/clippy/clippy_lints/src/int_plus_one.rs b/src/tools/clippy/clippy_lints/src/int_plus_one.rs
index 6850e0c3476..3716d36ad88 100644
--- a/src/tools/clippy/clippy_lints/src/int_plus_one.rs
+++ b/src/tools/clippy/clippy_lints/src/int_plus_one.rs
@@ -28,6 +28,7 @@ declare_clippy_lint! {
     /// # let y = 1;
     /// if x > y {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INT_PLUS_ONE,
     complexity,
     "instead of using `x >= y + 1`, use `x > y`"
diff --git a/src/tools/clippy/clippy_lints/src/integer_division.rs b/src/tools/clippy/clippy_lints/src/integer_division.rs
index c962e814fa5..fa786205678 100644
--- a/src/tools/clippy/clippy_lints/src/integer_division.rs
+++ b/src/tools/clippy/clippy_lints/src/integer_division.rs
@@ -23,6 +23,7 @@ declare_clippy_lint! {
     /// let x = 3f32 / 2f32;
     /// println!("{}", x);
     /// ```
+    #[clippy::version = "1.37.0"]
     pub INTEGER_DIVISION,
     restriction,
     "integer division may cause loss of precision"
diff --git a/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs b/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs
index 82438d85c7a..36e03e50a8e 100644
--- a/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs
+++ b/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs
@@ -30,6 +30,7 @@ declare_clippy_lint! {
     /// let x: u8 = 1;
     /// (x as u32) > 300;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INVALID_UPCAST_COMPARISONS,
     pedantic,
     "a comparison involving an upcast which is always true or false"
diff --git a/src/tools/clippy/clippy_lints/src/items_after_statements.rs b/src/tools/clippy/clippy_lints/src/items_after_statements.rs
index 3736d237642..b118d3c8b87 100644
--- a/src/tools/clippy/clippy_lints/src/items_after_statements.rs
+++ b/src/tools/clippy/clippy_lints/src/items_after_statements.rs
@@ -45,6 +45,7 @@ declare_clippy_lint! {
     ///     foo(); // prints "foo"
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ITEMS_AFTER_STATEMENTS,
     pedantic,
     "blocks where an item comes after a statement"
diff --git a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs
index 6c779348ca2..968bbc524b2 100644
--- a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs
+++ b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs
@@ -32,6 +32,7 @@ declare_clippy_lint! {
     ///    }
     /// }
     /// ```
+    #[clippy::version = "1.57.0"]
     pub ITER_NOT_RETURNING_ITERATOR,
     pedantic,
     "methods named `iter` or `iter_mut` that do not return an `Iterator`"
diff --git a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs
index fe6814e35d0..80260e4cd83 100644
--- a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs
+++ b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs
@@ -27,6 +27,7 @@ declare_clippy_lint! {
     /// // Good
     /// pub static a = [0u32; 1_000_000];
     /// ```
+    #[clippy::version = "1.44.0"]
     pub LARGE_CONST_ARRAYS,
     perf,
     "large non-scalar const array may cause performance overhead"
diff --git a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs
index 392166237be..0191713f60d 100644
--- a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs
+++ b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs
@@ -40,6 +40,7 @@ declare_clippy_lint! {
     ///     B(Box<[i32; 8000]>),
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub LARGE_ENUM_VARIANT,
     perf,
     "large size difference between variants on an enum"
diff --git a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs
index bbb6c1f902c..1cc2c28c04a 100644
--- a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs
+++ b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs
@@ -19,6 +19,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// let a = [0u32; 1_000_000];
     /// ```
+    #[clippy::version = "1.41.0"]
     pub LARGE_STACK_ARRAYS,
     pedantic,
     "allocating large arrays on stack may cause stack overflow"
diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs
index f336fb9d42f..09cd0d22d8b 100644
--- a/src/tools/clippy/clippy_lints/src/len_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/len_zero.rs
@@ -46,6 +46,7 @@ declare_clippy_lint! {
     ///     ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub LEN_ZERO,
     style,
     "checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead"
@@ -71,6 +72,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub LEN_WITHOUT_IS_EMPTY,
     style,
     "traits or impls with a public `len` method but no corresponding `is_empty` method"
@@ -108,6 +110,7 @@ declare_clippy_lint! {
     ///     ..
     /// }
     /// ```
+    #[clippy::version = "1.49.0"]
     pub COMPARISON_TO_EMPTY,
     style,
     "checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead"
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 7f2c7b707f0..db09d00d730 100644
--- a/src/tools/clippy/clippy_lints/src/let_if_seq.rs
+++ b/src/tools/clippy/clippy_lints/src/let_if_seq.rs
@@ -48,6 +48,7 @@ declare_clippy_lint! {
     ///     None
     /// };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USELESS_LET_IF_SEQ,
     nursery,
     "unidiomatic `let mut` declaration followed by initialization in `if`"
diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs
index 9efd7aba7e8..d03276f7f98 100644
--- a/src/tools/clippy/clippy_lints/src/let_underscore.rs
+++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs
@@ -26,6 +26,7 @@ declare_clippy_lint! {
     /// // is_ok() is marked #[must_use]
     /// let _ = f().is_ok();
     /// ```
+    #[clippy::version = "1.42.0"]
     pub LET_UNDERSCORE_MUST_USE,
     restriction,
     "non-binding let on a `#[must_use]` expression"
@@ -33,7 +34,8 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for `let _ = sync_lock`
+    /// Checks for `let _ = sync_lock`.
+    /// This supports `mutex` and `rwlock` in `std::sync` and `parking_lot`.
     ///
     /// ### Why is this bad?
     /// This statement immediately drops the lock instead of
@@ -53,6 +55,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// let _lock = mutex.lock();
     /// ```
+    #[clippy::version = "1.43.0"]
     pub LET_UNDERSCORE_LOCK,
     correctness,
     "non-binding let on a synchronization lock"
@@ -94,6 +97,7 @@ declare_clippy_lint! {
     ///     // dropped at end of scope
     /// }
     /// ```
+    #[clippy::version = "1.50.0"]
     pub LET_UNDERSCORE_DROP,
     pedantic,
     "non-binding let on a type that implements `Drop`"
@@ -101,10 +105,12 @@ declare_clippy_lint! {
 
 declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]);
 
-const SYNC_GUARD_PATHS: [&[&str]; 3] = [
+const SYNC_GUARD_PATHS: [&[&str]; 5] = [
     &paths::MUTEX_GUARD,
     &paths::RWLOCK_READ_GUARD,
     &paths::RWLOCK_WRITE_GUARD,
+    &paths::PARKING_LOT_RAWMUTEX,
+    &paths::PARKING_LOT_RAWRWLOCK,
 ];
 
 impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_all.rs b/src/tools/clippy/clippy_lints/src/lib.register_all.rs
index 15edb79d36c..b7b5f059de6 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_all.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_all.rs
@@ -33,6 +33,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(copies::IFS_SAME_COND),
     LintId::of(copies::IF_SAME_THEN_ELSE),
     LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
+    LintId::of(dereference::NEEDLESS_BORROW),
     LintId::of(derivable_impls::DERIVABLE_IMPLS),
     LintId::of(derive::DERIVE_HASH_XOR_EQ),
     LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
@@ -158,7 +159,9 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(methods::MANUAL_SPLIT_ONCE),
     LintId::of(methods::MANUAL_STR_REPEAT),
     LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
+    LintId::of(methods::MAP_FLATTEN),
     LintId::of(methods::MAP_IDENTITY),
+    LintId::of(methods::NEEDLESS_SPLITN),
     LintId::of(methods::NEW_RET_NO_SELF),
     LintId::of(methods::OK_EXPECT),
     LintId::of(methods::OPTION_AS_REF_DEREF),
@@ -203,8 +206,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
     LintId::of(needless_bool::BOOL_COMPARISON),
     LintId::of(needless_bool::NEEDLESS_BOOL),
-    LintId::of(needless_borrow::NEEDLESS_BORROW),
     LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
+    LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
     LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF),
     LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
     LintId::of(needless_update::NEEDLESS_UPDATE),
@@ -218,6 +221,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
     LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
     LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
+    LintId::of(octal_escapes::OCTAL_ESCAPES),
     LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
     LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
     LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs b/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs
index c51341bdf0c..a21ddf73a11 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs
@@ -41,7 +41,9 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
     LintId::of(methods::MANUAL_FILTER_MAP),
     LintId::of(methods::MANUAL_FIND_MAP),
     LintId::of(methods::MANUAL_SPLIT_ONCE),
+    LintId::of(methods::MAP_FLATTEN),
     LintId::of(methods::MAP_IDENTITY),
+    LintId::of(methods::NEEDLESS_SPLITN),
     LintId::of(methods::OPTION_AS_REF_DEREF),
     LintId::of(methods::OPTION_FILTER_MAP),
     LintId::of(methods::SEARCH_IS_SOME),
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_internal.rs b/src/tools/clippy/clippy_lints/src/lib.register_internal.rs
index c8c1e0262ab..7d4c7d2adb5 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_internal.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_internal.rs
@@ -9,9 +9,11 @@ store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![
     LintId::of(utils::internal_lints::DEFAULT_LINT),
     LintId::of(utils::internal_lints::IF_CHAIN_STYLE),
     LintId::of(utils::internal_lints::INTERNING_DEFINED_SYMBOL),
+    LintId::of(utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE),
     LintId::of(utils::internal_lints::INVALID_PATHS),
     LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS),
     LintId::of(utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM),
+    LintId::of(utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE),
     LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA),
     LintId::of(utils::internal_lints::PRODUCE_ICE),
     LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR),
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_lints.rs b/src/tools/clippy/clippy_lints/src/lib.register_lints.rs
index 2cb86418e3c..bb159e50373 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_lints.rs
@@ -16,12 +16,16 @@ store.register_lints(&[
     #[cfg(feature = "internal-lints")]
     utils::internal_lints::INTERNING_DEFINED_SYMBOL,
     #[cfg(feature = "internal-lints")]
+    utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE,
+    #[cfg(feature = "internal-lints")]
     utils::internal_lints::INVALID_PATHS,
     #[cfg(feature = "internal-lints")]
     utils::internal_lints::LINT_WITHOUT_LINT_PASS,
     #[cfg(feature = "internal-lints")]
     utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
     #[cfg(feature = "internal-lints")]
+    utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE,
+    #[cfg(feature = "internal-lints")]
     utils::internal_lints::OUTER_EXPN_EXPN_DATA,
     #[cfg(feature = "internal-lints")]
     utils::internal_lints::PRODUCE_ICE,
@@ -88,14 +92,16 @@ store.register_lints(&[
     default::FIELD_REASSIGN_WITH_DEFAULT,
     default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK,
     dereference::EXPLICIT_DEREF_METHODS,
+    dereference::NEEDLESS_BORROW,
+    dereference::REF_BINDING_TO_REFERENCE,
     derivable_impls::DERIVABLE_IMPLS,
     derive::DERIVE_HASH_XOR_EQ,
     derive::DERIVE_ORD_XOR_PARTIAL_ORD,
     derive::EXPL_IMPL_CLONE_ON_COPY,
     derive::UNSAFE_DERIVE_DESERIALIZE,
-    disallowed_method::DISALLOWED_METHOD,
+    disallowed_methods::DISALLOWED_METHODS,
     disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS,
-    disallowed_type::DISALLOWED_TYPE,
+    disallowed_types::DISALLOWED_TYPES,
     doc::DOC_MARKDOWN,
     doc::MISSING_ERRORS_DOC,
     doc::MISSING_PANICS_DOC,
@@ -164,6 +170,7 @@ store.register_lints(&[
     implicit_return::IMPLICIT_RETURN,
     implicit_saturating_sub::IMPLICIT_SATURATING_SUB,
     inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR,
+    index_refutable_slice::INDEX_REFUTABLE_SLICE,
     indexing_slicing::INDEXING_SLICING,
     indexing_slicing::OUT_OF_BOUNDS_INDEXING,
     infinite_iter::INFINITE_ITER,
@@ -288,6 +295,7 @@ store.register_lints(&[
     methods::MAP_FLATTEN,
     methods::MAP_IDENTITY,
     methods::MAP_UNWRAP_OR,
+    methods::NEEDLESS_SPLITN,
     methods::NEW_RET_NO_SELF,
     methods::OK_EXPECT,
     methods::OPTION_AS_REF_DEREF,
@@ -351,11 +359,10 @@ store.register_lints(&[
     needless_bitwise_bool::NEEDLESS_BITWISE_BOOL,
     needless_bool::BOOL_COMPARISON,
     needless_bool::NEEDLESS_BOOL,
-    needless_borrow::NEEDLESS_BORROW,
-    needless_borrow::REF_BINDING_TO_REFERENCE,
     needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
     needless_continue::NEEDLESS_CONTINUE,
     needless_for_each::NEEDLESS_FOR_EACH,
+    needless_late_init::NEEDLESS_LATE_INIT,
     needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF,
     needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
     needless_question_mark::NEEDLESS_QUESTION_MARK,
@@ -374,6 +381,7 @@ store.register_lints(&[
     non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS,
     non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY,
     nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
+    octal_escapes::OCTAL_ESCAPES,
     open_options::NONSENSICAL_OPEN_OPTIONS,
     option_env_unwrap::OPTION_ENV_UNWRAP,
     option_if_let_else::OPTION_IF_LET_ELSE,
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs b/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs
index 44c75a11eec..59182fd8175 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs
@@ -6,13 +6,14 @@ 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(disallowed_methods::DISALLOWED_METHODS),
+    LintId::of(disallowed_types::DISALLOWED_TYPES),
     LintId::of(equatable_if_let::EQUATABLE_IF_LET),
     LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM),
     LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS),
     LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS),
     LintId::of(future_not_send::FUTURE_NOT_SEND),
+    LintId::of(index_refutable_slice::INDEX_REFUTABLE_SLICE),
     LintId::of(let_if_seq::USELESS_LET_IF_SEQ),
     LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
     LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs b/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs
index 404ca20b5ab..70a4a624378 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs
@@ -21,6 +21,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
     LintId::of(copy_iterator::COPY_ITERATOR),
     LintId::of(default::DEFAULT_TRAIT_ACCESS),
     LintId::of(dereference::EXPLICIT_DEREF_METHODS),
+    LintId::of(dereference::REF_BINDING_TO_REFERENCE),
     LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY),
     LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE),
     LintId::of(doc::DOC_MARKDOWN),
@@ -62,13 +63,11 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
     LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
     LintId::of(methods::IMPLICIT_CLONE),
     LintId::of(methods::INEFFICIENT_TO_STRING),
-    LintId::of(methods::MAP_FLATTEN),
     LintId::of(methods::MAP_UNWRAP_OR),
     LintId::of(misc::FLOAT_CMP),
     LintId::of(misc::USED_UNDERSCORE_BINDING),
     LintId::of(mut_mut::MUT_MUT),
     LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL),
-    LintId::of(needless_borrow::REF_BINDING_TO_REFERENCE),
     LintId::of(needless_continue::NEEDLESS_CONTINUE),
     LintId::of(needless_for_each::NEEDLESS_FOR_EACH),
     LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_style.rs b/src/tools/clippy/clippy_lints/src/lib.register_style.rs
index 744880bda3e..ea87e7e7a73 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_style.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_style.rs
@@ -15,6 +15,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
     LintId::of(collapsible_match::COLLAPSIBLE_MATCH),
     LintId::of(comparison_chain::COMPARISON_CHAIN),
     LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
+    LintId::of(dereference::NEEDLESS_BORROW),
     LintId::of(doc::MISSING_SAFETY_DOC),
     LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
     LintId::of(enum_variants::ENUM_VARIANT_NAMES),
@@ -81,7 +82,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
     LintId::of(misc_early::REDUNDANT_PATTERN),
     LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
     LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
-    LintId::of(needless_borrow::NEEDLESS_BORROW),
+    LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
     LintId::of(neg_multiply::NEG_MULTIPLY),
     LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
     LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs b/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs
index a3f964d1580..414bfc42fdf 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs
@@ -16,6 +16,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
     LintId::of(methods::SUSPICIOUS_MAP),
     LintId::of(mut_key::MUTABLE_KEY_TYPE),
     LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
+    LintId::of(octal_escapes::OCTAL_ESCAPES),
     LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
     LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
 ])
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 7174d0a082e..bd9710ec407 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -8,6 +8,7 @@
 #![feature(rustc_private)]
 #![feature(stmt_expr_attributes)]
 #![feature(control_flow_enum)]
+#![feature(let_else)]
 #![recursion_limit = "512"]
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
 #![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)]
@@ -153,6 +154,10 @@ macro_rules! declare_clippy_lint {
 
 #[cfg(feature = "metadata-collector-lint")]
 mod deprecated_lints;
+#[cfg_attr(
+    any(feature = "internal-lints", feature = "metadata-collector-lint"),
+    allow(clippy::missing_clippy_version_attribute)
+)]
 mod utils;
 
 // begin lints modules, do not remove this comment, it’s used in `update_lints`
@@ -189,9 +194,9 @@ mod default_numeric_fallback;
 mod dereference;
 mod derivable_impls;
 mod derive;
-mod disallowed_method;
+mod disallowed_methods;
 mod disallowed_script_idents;
-mod disallowed_type;
+mod disallowed_types;
 mod doc;
 mod double_comparison;
 mod double_parens;
@@ -233,6 +238,7 @@ mod implicit_hasher;
 mod implicit_return;
 mod implicit_saturating_sub;
 mod inconsistent_struct_constructor;
+mod index_refutable_slice;
 mod indexing_slicing;
 mod infinite_iter;
 mod inherent_impl;
@@ -290,10 +296,10 @@ mod mutex_atomic;
 mod needless_arbitrary_self_type;
 mod needless_bitwise_bool;
 mod needless_bool;
-mod needless_borrow;
 mod needless_borrowed_ref;
 mod needless_continue;
 mod needless_for_each;
+mod needless_late_init;
 mod needless_option_as_deref;
 mod needless_pass_by_value;
 mod needless_question_mark;
@@ -307,6 +313,7 @@ mod non_expressive_names;
 mod non_octal_unix_permissions;
 mod non_send_fields_in_send_ty;
 mod nonstandard_macro_braces;
+mod octal_escapes;
 mod open_options;
 mod option_env_unwrap;
 mod option_if_let_else;
@@ -404,10 +411,21 @@ use crate::utils::conf::TryConf;
 /// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass.
 ///
 /// Used in `./src/driver.rs`.
-pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) {
+pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
     // NOTE: Do not add any more pre-expansion passes. These should be removed eventually.
+
+    let msrv = conf.msrv.as_ref().and_then(|s| {
+        parse_msrv(s, None, None).or_else(|| {
+            sess.err(&format!(
+                "error reading Clippy's configuration file. `{}` is not a valid Rust version",
+                s
+            ));
+            None
+        })
+    });
+
     store.register_pre_expansion_pass(|| Box::new(write::Write::default()));
-    store.register_pre_expansion_pass(|| Box::new(attrs::EarlyAttributes));
+    store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv }));
     store.register_pre_expansion_pass(|| Box::new(dbg_macro::DbgMacro));
 }
 
@@ -441,7 +459,6 @@ pub fn read_conf(sess: &Session) -> Conf {
 ///
 /// Used in `./src/driver.rs`.
 #[allow(clippy::too_many_lines)]
-#[rustfmt::skip]
 pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
     register_removed_non_tool_lints(store);
 
@@ -493,11 +510,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     let vec_box_size_threshold = conf.vec_box_size_threshold;
     let type_complexity_threshold = conf.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(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));
@@ -535,7 +554,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
 
     let msrv = conf.msrv.as_ref().and_then(|s| {
         parse_msrv(s, None, None).or_else(|| {
-            sess.err(&format!("error reading Clippy's configuration file. `{}` is not a valid Rust version", s));
+            sess.err(&format!(
+                "error reading Clippy's configuration file. `{}` is not a valid Rust version",
+                s
+            ));
             None
         })
     });
@@ -560,6 +582,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
 
     store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount));
     store.register_late_pass(|| Box::new(same_name_method::SameNameMethod));
+    let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length;
+    store.register_late_pass(move || {
+        Box::new(index_refutable_slice::IndexRefutableSlice::new(
+            max_suggested_slice_pattern_length,
+            msrv,
+        ))
+    });
     store.register_late_pass(|| Box::new(map_clone::MapClone));
     store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore));
     store.register_late_pass(|| Box::new(shadow::Shadow::default()));
@@ -573,16 +602,19 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(zero_div_zero::ZeroDiv));
     store.register_late_pass(|| Box::new(mutex_atomic::Mutex));
     store.register_late_pass(|| Box::new(needless_update::NeedlessUpdate));
-    store.register_late_pass(|| Box::new(needless_borrow::NeedlessBorrow::default()));
     store.register_late_pass(|| Box::new(needless_borrowed_ref::NeedlessBorrowedRef));
     store.register_late_pass(|| Box::new(no_effect::NoEffect));
     store.register_late_pass(|| Box::new(temporary_assignment::TemporaryAssignment));
     store.register_late_pass(|| Box::new(transmute::Transmute));
     let cognitive_complexity_threshold = conf.cognitive_complexity_threshold;
-    store.register_late_pass(move || Box::new(cognitive_complexity::CognitiveComplexity::new(cognitive_complexity_threshold)));
+    store.register_late_pass(move || {
+        Box::new(cognitive_complexity::CognitiveComplexity::new(
+            cognitive_complexity_threshold,
+        ))
+    });
     let too_large_for_stack = conf.too_large_for_stack;
-    store.register_late_pass(move || Box::new(escape::BoxedLocal{too_large_for_stack}));
-    store.register_late_pass(move || Box::new(vec::UselessVec{too_large_for_stack}));
+    store.register_late_pass(move || Box::new(escape::BoxedLocal { too_large_for_stack }));
+    store.register_late_pass(move || Box::new(vec::UselessVec { too_large_for_stack }));
     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));
@@ -603,7 +635,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move || Box::new(blacklisted_name::BlacklistedName::new(blacklisted_names.clone())));
     let too_many_arguments_threshold = conf.too_many_arguments_threshold;
     let too_many_lines_threshold = conf.too_many_lines_threshold;
-    store.register_late_pass(move || Box::new(functions::Functions::new(too_many_arguments_threshold, too_many_lines_threshold)));
+    store.register_late_pass(move || {
+        Box::new(functions::Functions::new(
+            too_many_arguments_threshold,
+            too_many_lines_threshold,
+        ))
+    });
     let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>();
     store.register_late_pass(move || Box::new(doc::DocMarkdown::new(doc_valid_idents.clone())));
     store.register_late_pass(|| Box::new(neg_multiply::NegMultiply));
@@ -688,14 +725,32 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(multiple_crate_versions::MultipleCrateVersions));
     store.register_late_pass(|| Box::new(wildcard_dependencies::WildcardDependencies));
     let literal_representation_lint_fraction_readability = conf.unreadable_literal_lint_fractions;
-    store.register_early_pass(move || Box::new(literal_representation::LiteralDigitGrouping::new(literal_representation_lint_fraction_readability)));
+    store.register_early_pass(move || {
+        Box::new(literal_representation::LiteralDigitGrouping::new(
+            literal_representation_lint_fraction_readability,
+        ))
+    });
     let literal_representation_threshold = conf.literal_representation_threshold;
-    store.register_early_pass(move || Box::new(literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold)));
+    store.register_early_pass(move || {
+        Box::new(literal_representation::DecimalLiteralRepresentation::new(
+            literal_representation_threshold,
+        ))
+    });
     let enum_variant_name_threshold = conf.enum_variant_name_threshold;
-    store.register_late_pass(move || Box::new(enum_variants::EnumVariantNames::new(enum_variant_name_threshold, avoid_breaking_exported_api)));
+    store.register_late_pass(move || {
+        Box::new(enum_variants::EnumVariantNames::new(
+            enum_variant_name_threshold,
+            avoid_breaking_exported_api,
+        ))
+    });
     store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments));
     let upper_case_acronyms_aggressive = conf.upper_case_acronyms_aggressive;
-    store.register_late_pass(move || Box::new(upper_case_acronyms::UpperCaseAcronyms::new(avoid_breaking_exported_api, upper_case_acronyms_aggressive)));
+    store.register_late_pass(move || {
+        Box::new(upper_case_acronyms::UpperCaseAcronyms::new(
+            avoid_breaking_exported_api,
+            upper_case_acronyms_aggressive,
+        ))
+    });
     store.register_late_pass(|| Box::new(default::Default::default()));
     store.register_late_pass(|| Box::new(unused_self::UnusedSelf));
     store.register_late_pass(|| Box::new(mutable_debug_assertion::DebugAssertWithMutCall));
@@ -710,7 +765,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_early_pass(|| Box::new(single_component_path_imports::SingleComponentPathImports));
     let max_fn_params_bools = conf.max_fn_params_bools;
     let max_struct_bools = conf.max_struct_bools;
-    store.register_early_pass(move || Box::new(excessive_bools::ExcessiveBools::new(max_struct_bools, max_fn_params_bools)));
+    store.register_early_pass(move || {
+        Box::new(excessive_bools::ExcessiveBools::new(
+            max_struct_bools,
+            max_fn_params_bools,
+        ))
+    });
     store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap));
     let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
     store.register_late_pass(move || Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
@@ -729,9 +789,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(vec_resize_to_zero::VecResizeToZero));
     store.register_late_pass(|| Box::new(panic_in_result_fn::PanicInResultFn));
     let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
-    store.register_early_pass(move || Box::new(non_expressive_names::NonExpressiveNames {
-        single_char_binding_names_threshold,
-    }));
+    store.register_early_pass(move || {
+        Box::new(non_expressive_names::NonExpressiveNames {
+            single_char_binding_names_threshold,
+        })
+    });
     let macro_matcher = conf.standard_macro_braces.iter().cloned().collect::<FxHashSet<_>>();
     store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(&macro_matcher)));
     store.register_late_pass(|| Box::new(macro_use::MacroUseImports::default()));
@@ -746,7 +808,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
     store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync));
     let disallowed_methods = conf.disallowed_methods.clone();
-    store.register_late_pass(move || Box::new(disallowed_method::DisallowedMethod::new(disallowed_methods.clone())));
+    store.register_late_pass(move || Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone())));
     store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax));
     store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax));
     store.register_late_pass(|| Box::new(undropped_manually_drops::UndroppedManuallyDrops));
@@ -754,7 +816,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(strings::StringToString));
     store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues));
     store.register_late_pass(|| Box::new(vec_init_then_push::VecInitThenPush::default()));
-    store.register_late_pass(|| Box::new(case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons));
+    store.register_late_pass(|| {
+        Box::new(case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons)
+    });
     store.register_late_pass(|| Box::new(redundant_slicing::RedundantSlicing));
     store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10));
     store.register_late_pass(|| Box::new(manual_map::ManualMap));
@@ -763,9 +827,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     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.clone();
-    store.register_late_pass(move || Box::new(disallowed_type::DisallowedType::new(disallowed_types.clone())));
+    store.register_late_pass(move || Box::new(disallowed_types::DisallowedTypes::new(disallowed_types.clone())));
     let import_renames = conf.enforced_import_renames.clone();
-    store.register_late_pass(move || Box::new(missing_enforced_import_rename::ImportRename::new(import_renames.clone())));
+    store.register_late_pass(move || {
+        Box::new(missing_enforced_import_rename::ImportRename::new(
+            import_renames.clone(),
+        ))
+    });
     let scripts = conf.allowed_scripts.clone();
     store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(&scripts)));
     store.register_late_pass(|| Box::new(strlen_on_c_strings::StrlenOnCStrings));
@@ -774,11 +842,17 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move || Box::new(iter_not_returning_iterator::IterNotReturningIterator));
     store.register_late_pass(move || Box::new(manual_assert::ManualAssert));
     let enable_raw_pointer_heuristic_for_send = conf.enable_raw_pointer_heuristic_for_send;
-    store.register_late_pass(move || Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(enable_raw_pointer_heuristic_for_send)));
+    store.register_late_pass(move || {
+        Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(
+            enable_raw_pointer_heuristic_for_send,
+        ))
+    });
     store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::default()));
     store.register_late_pass(|| Box::new(match_str_case_mismatch::MatchStrCaseMismatch));
     store.register_late_pass(move || Box::new(format_args::FormatArgs));
     store.register_late_pass(|| Box::new(trailing_empty_array::TrailingEmptyArray));
+    store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
+    store.register_late_pass(|| Box::new(needless_late_init::NeedlessLateInit));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
@@ -852,6 +926,8 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
     ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters");
     ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str");
     ls.register_renamed("clippy::if_let_some_result", "clippy::match_result_ok");
+    ls.register_renamed("clippy::disallowed_type", "clippy::disallowed_types");
+    ls.register_renamed("clippy::disallowed_method", "clippy::disallowed_methods");
 
     // uplifted lints
     ls.register_renamed("clippy::invalid_ref", "invalid_value");
diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs
index cb0b96e0652..fad3343d128 100644
--- a/src/tools/clippy/clippy_lints/src/lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{in_macro, trait_ref_of_method};
+use clippy_utils::trait_ref_of_method;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir::intravisit::{
     walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty,
@@ -45,6 +45,7 @@ declare_clippy_lint! {
     ///     x
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_LIFETIMES,
     complexity,
     "using explicit lifetimes for references in function arguments when elision rules \
@@ -73,6 +74,7 @@ declare_clippy_lint! {
     ///     // ...
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXTRA_UNUSED_LIFETIMES,
     complexity,
     "unused lifetimes in function definitions"
@@ -128,7 +130,7 @@ fn check_fn_inner<'tcx>(
     span: Span,
     report_extra_lifetimes: bool,
 ) {
-    if in_macro(span) || has_where_lifetimes(cx, &generics.where_clause) {
+    if span.from_expansion() || has_where_lifetimes(cx, &generics.where_clause) {
         return;
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs
index 0e5121ca3d7..130543bbbee 100644
--- a/src/tools/clippy/clippy_lints/src/literal_representation.rs
+++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs
@@ -2,11 +2,8 @@
 //! floating-point literal expressions.
 
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::numeric_literal::{NumericLiteral, Radix};
 use clippy_utils::source::snippet_opt;
-use clippy_utils::{
-    in_macro,
-    numeric_literal::{NumericLiteral, Radix},
-};
 use if_chain::if_chain;
 use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind};
 use rustc_errors::Applicability;
@@ -31,6 +28,7 @@ declare_clippy_lint! {
     /// // Good
     /// let x: u64 = 61_864_918_973_511;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNREADABLE_LITERAL,
     pedantic,
     "long literal without underscores"
@@ -56,6 +54,7 @@ declare_clippy_lint! {
     /// // Good
     /// 2_i32;
     /// ```
+    #[clippy::version = "1.30.0"]
     pub MISTYPED_LITERAL_SUFFIXES,
     correctness,
     "mistyped literal suffix"
@@ -78,6 +77,7 @@ declare_clippy_lint! {
     /// // Good
     /// let x: u64 = 61_864_918_973_511;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INCONSISTENT_DIGIT_GROUPING,
     style,
     "integer literals with digits grouped inconsistently"
@@ -96,6 +96,7 @@ declare_clippy_lint! {
     /// let x: u32 = 0xFFF_FFF;
     /// let y: u8 = 0b01_011_101;
     /// ```
+    #[clippy::version = "1.49.0"]
     pub UNUSUAL_BYTE_GROUPINGS,
     style,
     "binary or hex literals that aren't grouped by four"
@@ -114,6 +115,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let x: u64 = 6186491_8973511;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub LARGE_DIGIT_GROUPS,
     pedantic,
     "grouping digits into groups that are too large"
@@ -131,6 +133,7 @@ declare_clippy_lint! {
     /// `255` => `0xFF`
     /// `65_535` => `0xFFFF`
     /// `4_042_322_160` => `0xF0F0_F0F0`
+    #[clippy::version = "pre 1.29.0"]
     pub DECIMAL_LITERAL_REPRESENTATION,
     restriction,
     "using decimal representation when hexadecimal would be better"
@@ -283,7 +286,7 @@ impl LiteralDigitGrouping {
                         | WarningType::InconsistentDigitGrouping
                         | WarningType::UnusualByteGroupings
                         | WarningType::LargeDigitGroups => {
-                            !in_macro(lit.span)
+                            !lit.span.from_expansion()
                         }
                         WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => {
                             true
diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs
index 6f213d7a699..e0150990cfe 100644
--- a/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs
@@ -1,5 +1,5 @@
 use super::{make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT_COUNTER_LOOP};
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::{get_enclosing_block, is_integer_const};
 use if_chain::if_chain;
@@ -7,6 +7,7 @@ use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_block, walk_expr};
 use rustc_hir::{Expr, Pat};
 use rustc_lint::LateContext;
+use rustc_middle::ty::{self, UintTy};
 
 // To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be
 // incremented exactly once in the loop body, and initialized to zero
@@ -30,24 +31,59 @@ pub(super) fn check<'tcx>(
             walk_block(&mut initialize_visitor, block);
 
             if_chain! {
-                if let Some((name, initializer)) = initialize_visitor.get_result();
+                if let Some((name, ty, initializer)) = initialize_visitor.get_result();
                 if is_integer_const(cx, initializer, 0);
                 then {
                     let mut applicability = Applicability::MachineApplicable;
 
-                    span_lint_and_sugg(
+                    let int_name = match ty.map(ty::TyS::kind) {
+                        // usize or inferred
+                        Some(ty::Uint(UintTy::Usize)) | None => {
+                            span_lint_and_sugg(
+                                cx,
+                                EXPLICIT_COUNTER_LOOP,
+                                expr.span.with_hi(arg.span.hi()),
+                                &format!("the variable `{}` is used as a loop counter", name),
+                                "consider using",
+                                format!(
+                                    "for ({}, {}) in {}.enumerate()",
+                                    name,
+                                    snippet_with_applicability(cx, pat.span, "item", &mut applicability),
+                                    make_iterator_snippet(cx, arg, &mut applicability),
+                                ),
+                                applicability,
+                            );
+                            return;
+                        }
+                        Some(ty::Int(int_ty)) => int_ty.name_str(),
+                        Some(ty::Uint(uint_ty)) => uint_ty.name_str(),
+                        _ => return,
+                    };
+
+                    span_lint_and_then(
                         cx,
                         EXPLICIT_COUNTER_LOOP,
                         expr.span.with_hi(arg.span.hi()),
                         &format!("the variable `{}` is used as a loop counter", name),
-                        "consider using",
-                        format!(
-                            "for ({}, {}) in {}.enumerate()",
-                            name,
-                            snippet_with_applicability(cx, pat.span, "item", &mut applicability),
-                            make_iterator_snippet(cx, arg, &mut applicability),
-                        ),
-                        applicability,
+                        |diag| {
+                            diag.span_suggestion(
+                                expr.span.with_hi(arg.span.hi()),
+                                "consider using",
+                                format!(
+                                    "for ({}, {}) in (0_{}..).zip({})",
+                                    name,
+                                    snippet_with_applicability(cx, pat.span, "item", &mut applicability),
+                                    int_name,
+                                    make_iterator_snippet(cx, arg, &mut applicability),
+                                ),
+                                applicability,
+                            );
+
+                            diag.note(&format!(
+                                "`{}` is of type `{}`, making it ineligible for `Iterator::enumerate`",
+                                name, int_name
+                            ));
+                        },
                     );
                 }
             }
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs
index 2362b4b2067..6cda9268534 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs
@@ -445,7 +445,7 @@ fn get_loop_counters<'a, 'tcx>(
                 let mut initialize_visitor = InitializeVisitor::new(cx, expr, var_id);
                 walk_block(&mut initialize_visitor, block);
 
-                initialize_visitor.get_result().map(|(_, initializer)| Start {
+                initialize_visitor.get_result().map(|(_, _, initializer)| Start {
                     id: var_id,
                     kind: StartKind::Counter { initializer },
                 })
diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs
index fd4881b2947..e2f9aee063d 100644
--- a/src/tools/clippy/clippy_lints/src/loops/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs
@@ -47,6 +47,7 @@ declare_clippy_lint! {
     /// # let mut dst = vec![0; 65];
     /// dst[64..(src.len() + 64)].clone_from_slice(&src[..]);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MANUAL_MEMCPY,
     perf,
     "manually copying items between slices"
@@ -75,6 +76,7 @@ declare_clippy_lint! {
     ///     println!("{}", i);
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_RANGE_LOOP,
     style,
     "for-looping over a range of indices where an iterator over items would do"
@@ -107,6 +109,7 @@ declare_clippy_lint! {
     ///     // ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXPLICIT_ITER_LOOP,
     pedantic,
     "for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do"
@@ -135,6 +138,7 @@ declare_clippy_lint! {
     ///     // ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXPLICIT_INTO_ITER_LOOP,
     pedantic,
     "for-looping over `_.into_iter()` when `_` would do"
@@ -158,6 +162,7 @@ declare_clippy_lint! {
     ///     ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ITER_NEXT_LOOP,
     correctness,
     "for-looping over `_.next()` which is probably not intended"
@@ -201,6 +206,7 @@ declare_clippy_lint! {
     ///     // ..
     /// }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub FOR_LOOPS_OVER_FALLIBLES,
     suspicious,
     "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`"
@@ -233,6 +239,7 @@ declare_clippy_lint! {
     ///     // .. do something with x
     /// };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WHILE_LET_LOOP,
     complexity,
     "`loop { if let { ... } else break }`, which can be written as a `while let` loop"
@@ -254,6 +261,7 @@ declare_clippy_lint! {
     /// // should be
     /// let len = iterator.count();
     /// ```
+    #[clippy::version = "1.30.0"]
     pub NEEDLESS_COLLECT,
     perf,
     "collecting an iterator when collect is not needed"
@@ -284,6 +292,7 @@ declare_clippy_lint! {
     /// # fn bar(bar: usize, baz: usize) {}
     /// for (i, item) in v.iter().enumerate() { bar(i, *item); }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXPLICIT_COUNTER_LOOP,
     complexity,
     "for-looping with an explicit counter when `_.enumerate()` would do"
@@ -317,6 +326,7 @@ declare_clippy_lint! {
     /// ```no_run
     /// loop {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EMPTY_LOOP,
     suspicious,
     "empty `loop {}`, which should block or sleep"
@@ -336,6 +346,7 @@ declare_clippy_lint! {
     ///     ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WHILE_LET_ON_ITERATOR,
     style,
     "using a `while let` loop instead of a for loop on an iterator"
@@ -364,6 +375,7 @@ declare_clippy_lint! {
     ///     ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FOR_KV_MAP,
     style,
     "looping on a map using `iter` when `keys` or `values` would do"
@@ -385,6 +397,7 @@ declare_clippy_lint! {
     ///     break;
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEVER_LOOP,
     correctness,
     "any loop that will always `break` or `return`"
@@ -420,6 +433,7 @@ declare_clippy_lint! {
     ///     println!("{}", i); // prints numbers from 0 to 42, not 0 to 21
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MUT_RANGE_BOUND,
     suspicious,
     "for loop over a range where one of the bounds is a mutable variable"
@@ -446,6 +460,7 @@ declare_clippy_lint! {
     ///     println!("let me loop forever!");
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WHILE_IMMUTABLE_CONDITION,
     correctness,
     "variables used within while expression are not mutated in the body"
@@ -480,6 +495,7 @@ declare_clippy_lint! {
     /// let mut vec: Vec<u8> = vec![item1; 20];
     /// vec.resize(20 + 30, item2);
     /// ```
+    #[clippy::version = "1.47.0"]
     pub SAME_ITEM_PUSH,
     style,
     "the same item is pushed inside of a for loop"
@@ -506,6 +522,7 @@ declare_clippy_lint! {
     /// let item = &item1;
     /// println!("{}", item);
     /// ```
+    #[clippy::version = "1.49.0"]
     pub SINGLE_ELEMENT_LOOP,
     complexity,
     "there is no reason to have a single element loop"
@@ -537,6 +554,7 @@ declare_clippy_lint! {
     ///     println!("{}", n);
     /// }
     /// ```
+    #[clippy::version = "1.52.0"]
     pub MANUAL_FLATTEN,
     complexity,
     "for loops over `Option`s or `Result`s with a single expression can be simplified"
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 e87f4b66912..6f3acb45ba4 100644
--- a/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs
@@ -3,13 +3,16 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_trait_method, path_to_local_id};
+use clippy_utils::{can_move_expr_to_closure, is_trait_method, path_to_local, path_to_local_id, CaptureKind};
 use if_chain::if_chain;
+use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor};
-use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, StmtKind};
+use rustc_hir::{Block, Expr, ExprKind, HirId, HirIdSet, Local, Mutability, Node, PatKind, Stmt, StmtKind};
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
+use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty::{self, TyS};
 use rustc_span::sym;
 use rustc_span::{MultiSpan, Span};
 
@@ -83,7 +86,8 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo
                     is_type_diagnostic_item(cx, ty, sym::VecDeque) ||
                     is_type_diagnostic_item(cx, ty, sym::BinaryHeap) ||
                     is_type_diagnostic_item(cx, ty, sym::LinkedList);
-                if let Some(iter_calls) = detect_iter_and_into_iters(block, id);
+                let iter_ty = cx.typeck_results().expr_ty(iter_source);
+                if let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty));
                 if let [iter_call] = &*iter_calls;
                 then {
                     let mut used_count_visitor = UsedCountVisitor {
@@ -167,37 +171,89 @@ enum IterFunctionKind {
     Contains(Span),
 }
 
-struct IterFunctionVisitor {
-    uses: Vec<IterFunction>,
+struct IterFunctionVisitor<'a, 'tcx> {
+    illegal_mutable_capture_ids: HirIdSet,
+    current_mutably_captured_ids: HirIdSet,
+    cx: &'a LateContext<'tcx>,
+    uses: Vec<Option<IterFunction>>,
+    hir_id_uses_map: FxHashMap<HirId, usize>,
+    current_statement_hir_id: Option<HirId>,
     seen_other: bool,
     target: HirId,
 }
-impl<'tcx> Visitor<'tcx> for IterFunctionVisitor {
+impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
+    fn visit_block(&mut self, block: &'tcx Block<'tcx>) {
+        for (expr, hir_id) in block.stmts.iter().filter_map(get_expr_and_hir_id_from_stmt) {
+            self.visit_block_expr(expr, hir_id);
+        }
+        if let Some(expr) = block.expr {
+            self.visit_block_expr(expr, None);
+        }
+    }
+
     fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
         // Check function calls on our collection
         if let ExprKind::MethodCall(method_name, _, [recv, args @ ..], _) = &expr.kind {
+            if method_name.ident.name == sym!(collect) && is_trait_method(self.cx, expr, sym::Iterator) {
+                self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(recv));
+                self.visit_expr(recv);
+                return;
+            }
+
             if path_to_local_id(recv, self.target) {
-                match &*method_name.ident.name.as_str() {
-                    "into_iter" => self.uses.push(IterFunction {
-                        func: IterFunctionKind::IntoIter,
-                        span: expr.span,
-                    }),
-                    "len" => self.uses.push(IterFunction {
-                        func: IterFunctionKind::Len,
-                        span: expr.span,
-                    }),
-                    "is_empty" => self.uses.push(IterFunction {
-                        func: IterFunctionKind::IsEmpty,
-                        span: expr.span,
-                    }),
-                    "contains" => self.uses.push(IterFunction {
-                        func: IterFunctionKind::Contains(args[0].span),
-                        span: expr.span,
-                    }),
-                    _ => self.seen_other = true,
+                if self
+                    .illegal_mutable_capture_ids
+                    .intersection(&self.current_mutably_captured_ids)
+                    .next()
+                    .is_none()
+                {
+                    if let Some(hir_id) = self.current_statement_hir_id {
+                        self.hir_id_uses_map.insert(hir_id, self.uses.len());
+                    }
+                    match &*method_name.ident.name.as_str() {
+                        "into_iter" => self.uses.push(Some(IterFunction {
+                            func: IterFunctionKind::IntoIter,
+                            span: expr.span,
+                        })),
+                        "len" => self.uses.push(Some(IterFunction {
+                            func: IterFunctionKind::Len,
+                            span: expr.span,
+                        })),
+                        "is_empty" => self.uses.push(Some(IterFunction {
+                            func: IterFunctionKind::IsEmpty,
+                            span: expr.span,
+                        })),
+                        "contains" => self.uses.push(Some(IterFunction {
+                            func: IterFunctionKind::Contains(args[0].span),
+                            span: expr.span,
+                        })),
+                        _ => {
+                            self.seen_other = true;
+                            if let Some(hir_id) = self.current_statement_hir_id {
+                                self.hir_id_uses_map.remove(&hir_id);
+                            }
+                        },
+                    }
                 }
                 return;
             }
+
+            if let Some(hir_id) = path_to_local(recv) {
+                if let Some(index) = self.hir_id_uses_map.remove(&hir_id) {
+                    if self
+                        .illegal_mutable_capture_ids
+                        .intersection(&self.current_mutably_captured_ids)
+                        .next()
+                        .is_none()
+                    {
+                        if let Some(hir_id) = self.current_statement_hir_id {
+                            self.hir_id_uses_map.insert(hir_id, index);
+                        }
+                    } else {
+                        self.uses[index] = None;
+                    }
+                }
+            }
         }
         // Check if the collection is used for anything else
         if path_to_local_id(expr, self.target) {
@@ -213,6 +269,28 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor {
     }
 }
 
+impl<'tcx> IterFunctionVisitor<'_, 'tcx> {
+    fn visit_block_expr(&mut self, expr: &'tcx Expr<'tcx>, hir_id: Option<HirId>) {
+        self.current_statement_hir_id = hir_id;
+        self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(expr));
+        self.visit_expr(expr);
+    }
+}
+
+fn get_expr_and_hir_id_from_stmt<'v>(stmt: &'v Stmt<'v>) -> Option<(&'v Expr<'v>, Option<HirId>)> {
+    match stmt.kind {
+        StmtKind::Expr(expr) | StmtKind::Semi(expr) => Some((expr, None)),
+        StmtKind::Item(..) => None,
+        StmtKind::Local(Local { init, pat, .. }) => {
+            if let PatKind::Binding(_, hir_id, ..) = pat.kind {
+                init.map(|init_expr| (init_expr, Some(hir_id)))
+            } else {
+                init.map(|init_expr| (init_expr, None))
+            }
+        },
+    }
+}
+
 struct UsedCountVisitor<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
     id: HirId,
@@ -237,12 +315,60 @@ impl<'a, 'tcx> Visitor<'tcx> for UsedCountVisitor<'a, 'tcx> {
 
 /// Detect the occurrences of calls to `iter` or `into_iter` for the
 /// given identifier
-fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, id: HirId) -> Option<Vec<IterFunction>> {
+fn detect_iter_and_into_iters<'tcx: 'a, 'a>(
+    block: &'tcx Block<'tcx>,
+    id: HirId,
+    cx: &'a LateContext<'tcx>,
+    captured_ids: HirIdSet,
+) -> Option<Vec<IterFunction>> {
     let mut visitor = IterFunctionVisitor {
         uses: Vec::new(),
         target: id,
         seen_other: false,
+        cx,
+        current_mutably_captured_ids: HirIdSet::default(),
+        illegal_mutable_capture_ids: captured_ids,
+        hir_id_uses_map: FxHashMap::default(),
+        current_statement_hir_id: None,
     };
     visitor.visit_block(block);
-    if visitor.seen_other { None } else { Some(visitor.uses) }
+    if visitor.seen_other {
+        None
+    } else {
+        Some(visitor.uses.into_iter().flatten().collect())
+    }
+}
+
+fn get_captured_ids(cx: &LateContext<'tcx>, ty: &'_ TyS<'_>) -> HirIdSet {
+    fn get_captured_ids_recursive(cx: &LateContext<'tcx>, ty: &'_ TyS<'_>, set: &mut HirIdSet) {
+        match ty.kind() {
+            ty::Adt(_, generics) => {
+                for generic in *generics {
+                    if let GenericArgKind::Type(ty) = generic.unpack() {
+                        get_captured_ids_recursive(cx, ty, set);
+                    }
+                }
+            },
+            ty::Closure(def_id, _) => {
+                let closure_hir_node = cx.tcx.hir().get_if_local(*def_id).unwrap();
+                if let Node::Expr(closure_expr) = closure_hir_node {
+                    can_move_expr_to_closure(cx, closure_expr)
+                        .unwrap()
+                        .into_iter()
+                        .for_each(|(hir_id, capture_kind)| {
+                            if matches!(capture_kind, CaptureKind::Ref(Mutability::Mut)) {
+                                set.insert(hir_id);
+                            }
+                        });
+                }
+            },
+            _ => (),
+        }
+    }
+
+    let mut set = HirIdSet::default();
+
+    get_captured_ids_recursive(cx, ty, &mut set);
+
+    set
 }
diff --git a/src/tools/clippy/clippy_lints/src/loops/utils.rs b/src/tools/clippy/clippy_lints/src/loops/utils.rs
index c3939a66c6a..f6b7e1bc353 100644
--- a/src/tools/clippy/clippy_lints/src/loops/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/utils.rs
@@ -1,13 +1,16 @@
 use clippy_utils::ty::{has_iter_method, implements_trait};
 use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_local_id, sugg};
 use if_chain::if_chain;
+use rustc_ast::ast::{LitIntType, LitKind};
 use rustc_errors::Applicability;
-use rustc_hir::intravisit::{walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor};
-use rustc_hir::HirIdMap;
-use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Stmt, StmtKind};
+use rustc_hir::intravisit::{walk_expr, walk_local, walk_pat, walk_stmt, NestedVisitorMap, Visitor};
+use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, Local, Mutability, Pat, PatKind, Stmt};
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
+use rustc_middle::ty::Ty;
+use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{sym, Symbol};
+use rustc_typeck::hir_ty_to_ty;
 use std::iter::Iterator;
 
 #[derive(Debug, PartialEq)]
@@ -105,10 +108,11 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> {
 }
 
 enum InitializeVisitorState<'hir> {
-    Initial,          // Not examined yet
-    Declared(Symbol), // Declared but not (yet) initialized
+    Initial,                            // Not examined yet
+    Declared(Symbol, Option<Ty<'hir>>), // Declared but not (yet) initialized
     Initialized {
         name: Symbol,
+        ty: Option<Ty<'hir>>,
         initializer: &'hir Expr<'hir>,
     },
     DontWarn,
@@ -137,9 +141,9 @@ impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> {
         }
     }
 
-    pub(super) fn get_result(&self) -> Option<(Symbol, &'tcx Expr<'tcx>)> {
-        if let InitializeVisitorState::Initialized { name, initializer } = self.state {
-            Some((name, initializer))
+    pub(super) fn get_result(&self) -> Option<(Symbol, Option<Ty<'tcx>>, &'tcx Expr<'tcx>)> {
+        if let InitializeVisitorState::Initialized { name, ty, initializer } = self.state {
+            Some((name, ty, initializer))
         } else {
             None
         }
@@ -149,22 +153,25 @@ impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> {
 impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
     type Map = Map<'tcx>;
 
-    fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
+    fn visit_local(&mut self, l: &'tcx Local<'_>) {
         // Look for declarations of the variable
         if_chain! {
-            if let StmtKind::Local(local) = stmt.kind;
-            if local.pat.hir_id == self.var_id;
-            if let PatKind::Binding(.., ident, _) = local.pat.kind;
+            if l.pat.hir_id == self.var_id;
+            if let PatKind::Binding(.., ident, _) = l.pat.kind;
             then {
-                self.state = local.init.map_or(InitializeVisitorState::Declared(ident.name), |init| {
+                let ty = l.ty.map(|ty| hir_ty_to_ty(self.cx.tcx, ty));
+
+                self.state = l.init.map_or(InitializeVisitorState::Declared(ident.name, ty), |init| {
                     InitializeVisitorState::Initialized {
                         initializer: init,
+                        ty,
                         name: ident.name,
                     }
                 })
             }
         }
-        walk_stmt(self, stmt);
+
+        walk_local(self, l);
     }
 
     fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
@@ -194,15 +201,38 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
                         self.state = InitializeVisitorState::DontWarn;
                     },
                     ExprKind::Assign(lhs, rhs, _) if lhs.hir_id == expr.hir_id => {
-                        self.state = if_chain! {
-                            if self.depth == 0;
-                            if let InitializeVisitorState::Declared(name)
-                                | InitializeVisitorState::Initialized { name, ..} = self.state;
-                            then {
-                                InitializeVisitorState::Initialized { initializer: rhs, name }
-                            } else {
-                                InitializeVisitorState::DontWarn
+                        self.state = if self.depth == 0 {
+                            match self.state {
+                                InitializeVisitorState::Declared(name, mut ty) => {
+                                    if ty.is_none() {
+                                        if let ExprKind::Lit(Spanned {
+                                            node: LitKind::Int(_, LitIntType::Unsuffixed),
+                                            ..
+                                        }) = rhs.kind
+                                        {
+                                            ty = None;
+                                        } else {
+                                            ty = self.cx.typeck_results().expr_ty_opt(rhs);
+                                        }
+                                    }
+
+                                    InitializeVisitorState::Initialized {
+                                        initializer: rhs,
+                                        ty,
+                                        name,
+                                    }
+                                },
+                                InitializeVisitorState::Initialized { ty, name, .. } => {
+                                    InitializeVisitorState::Initialized {
+                                        initializer: rhs,
+                                        ty,
+                                        name,
+                                    }
+                                },
+                                _ => InitializeVisitorState::DontWarn,
                             }
+                        } else {
+                            InitializeVisitorState::DontWarn
                         }
                     },
                     ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => {
diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs
index c38162743a3..5b22b64a370 100644
--- a/src/tools/clippy/clippy_lints/src/macro_use.rs
+++ b/src/tools/clippy/clippy_lints/src/macro_use.rs
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
 use clippy_utils::source::snippet;
 use hir::def::{DefKind, Res};
 use if_chain::if_chain;
@@ -9,6 +8,7 @@ use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::hygiene::ExpnKind;
 use rustc_span::{edition::Edition, sym, Span};
 
 declare_clippy_lint! {
@@ -24,6 +24,7 @@ declare_clippy_lint! {
     /// #[macro_use]
     /// use some_macro;
     /// ```
+    #[clippy::version = "1.44.0"]
     pub MACRO_USE_IMPORTS,
     pedantic,
     "#[macro_use] is no longer needed"
@@ -212,3 +213,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
         }
     }
 }
+
+fn in_macro(span: Span) -> bool {
+    span.from_expansion() && !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..))
+}
diff --git a/src/tools/clippy/clippy_lints/src/main_recursion.rs b/src/tools/clippy/clippy_lints/src/main_recursion.rs
index 23b3ba2296e..fad8fa467d4 100644
--- a/src/tools/clippy/clippy_lints/src/main_recursion.rs
+++ b/src/tools/clippy/clippy_lints/src/main_recursion.rs
@@ -20,6 +20,7 @@ declare_clippy_lint! {
     ///     main();
     /// }
     /// ```
+    #[clippy::version = "1.38.0"]
     pub MAIN_RECURSION,
     style,
     "recursion using the entrypoint"
diff --git a/src/tools/clippy/clippy_lints/src/manual_assert.rs b/src/tools/clippy/clippy_lints/src/manual_assert.rs
index e55aa3f1850..ed3166086f7 100644
--- a/src/tools/clippy/clippy_lints/src/manual_assert.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_assert.rs
@@ -26,6 +26,7 @@ declare_clippy_lint! {
     /// let sad_people: Vec<&str> = vec![];
     /// assert!(sad_people.is_empty(), "there are sad people: {:?}", sad_people);
     /// ```
+    #[clippy::version = "1.57.0"]
     pub MANUAL_ASSERT,
     pedantic,
     "`panic!` and only a `panic!` in `if`-then statement"
diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
index b632af455f8..86819752f90 100644
--- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
@@ -30,6 +30,7 @@ declare_clippy_lint! {
     /// ```rust
     /// async fn foo() -> i32 { 42 }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub MANUAL_ASYNC_FN,
     style,
     "manual implementations of `async` functions can be simplified using the dedicated syntax"
diff --git a/src/tools/clippy/clippy_lints/src/manual_map.rs b/src/tools/clippy/clippy_lints/src/manual_map.rs
index 96df3d0a490..4d8ad566e6b 100644
--- a/src/tools/clippy/clippy_lints/src/manual_map.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_map.rs
@@ -2,7 +2,7 @@ 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::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::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function};
 use clippy_utils::{
     can_move_expr_to_closure, in_constant, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id,
     peel_hir_expr_refs, peel_hir_expr_while, CaptureKind,
@@ -11,7 +11,8 @@ use rustc_ast::util::parser::PREC_POSTFIX;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_hir::{
-    def::Res, Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath,
+    def::Res, Arm, BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path,
+    QPath, UnsafeSource,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
@@ -36,6 +37,7 @@ declare_clippy_lint! {
     /// ```rust
     /// Some(0).map(|x| x + 1);
     /// ```
+    #[clippy::version = "1.52.0"]
     pub MANUAL_MAP,
     style,
     "reimplementation of `map`"
@@ -92,20 +94,20 @@ impl LateLintPass<'_> for ManualMap {
             return;
         }
 
-        let some_expr = match get_some_expr(cx, some_expr, expr_ctxt) {
+        let some_expr = match get_some_expr(cx, some_expr, false, expr_ctxt) {
             Some(expr) => expr,
             None => return,
         };
 
         // These two lints will go back and forth with each other.
-        if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit
+        if cx.typeck_results().expr_ty(some_expr.expr) == cx.tcx.types.unit
             && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
         {
             return;
         }
 
         // `map` won't perform any adjustments.
-        if !cx.typeck_results().expr_adjustments(some_expr).is_empty() {
+        if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() {
             return;
         }
 
@@ -119,7 +121,7 @@ impl LateLintPass<'_> for ManualMap {
             None => "",
         };
 
-        match can_move_expr_to_closure(cx, some_expr) {
+        match can_move_expr_to_closure(cx, some_expr.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
@@ -157,12 +159,14 @@ impl LateLintPass<'_> for ManualMap {
             };
 
         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() => {
+            if_chain! {
+                if !some_expr.needs_unsafe_block;
+                if let Some(func) = can_pass_as_func(cx, id, some_expr.expr);
+                if func.span.ctxt() == some_expr.expr.span.ctxt();
+                then {
                     snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
-                },
-                _ => {
-                    if path_to_local_id(some_expr, id)
+                } else {
+                    if path_to_local_id(some_expr.expr, id)
                         && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id)
                         && binding_ref.is_some()
                     {
@@ -175,21 +179,23 @@ impl LateLintPass<'_> for ManualMap {
                     } else {
                         ""
                     };
-                    format!(
-                        "|{}{}| {}",
-                        annotation,
-                        some_binding,
-                        snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0
-                    )
-                },
+                    let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0;
+                    if some_expr.needs_unsafe_block {
+                        format!("|{}{}| unsafe {{ {} }}", annotation, some_binding, expr_snip)
+                    } else {
+                        format!("|{}{}| {}", annotation, some_binding, expr_snip)
+                    }
+                }
             }
         } 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
-            )
+            let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0;
+            let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0;
+            if some_expr.needs_unsafe_block {
+                format!("|{}| unsafe {{ {} }}", pat_snip, expr_snip)
+            } else {
+                format!("|{}| {}", pat_snip, expr_snip)
+            }
         } else {
             // Refutable bindings and mixed reference annotations can't be handled by `map`.
             return;
@@ -216,7 +222,9 @@ impl LateLintPass<'_> for ManualMap {
 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()
+                && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) =>
         {
             Some(func)
         },
@@ -236,6 +244,11 @@ enum OptionPat<'a> {
     },
 }
 
+struct SomeExpr<'tcx> {
+    expr: &'tcx Expr<'tcx>,
+    needs_unsafe_block: bool,
+}
+
 // 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: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
@@ -256,7 +269,12 @@ fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxCon
 }
 
 // Checks for an expression wrapped by the `Some` constructor. Returns the contained expression.
-fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ctxt: SyntaxContext) -> Option<&'tcx Expr<'tcx>> {
+fn get_some_expr(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    needs_unsafe_block: bool,
+    ctxt: SyntaxContext,
+) -> Option<SomeExpr<'tcx>> {
     // TODO: Allow more complex expressions.
     match expr.kind {
         ExprKind::Call(
@@ -265,15 +283,24 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ctxt: SyntaxConte
                 ..
             },
             [arg],
-        ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(arg),
+        ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(SomeExpr {
+            expr: arg,
+            needs_unsafe_block,
+        }),
         ExprKind::Block(
             Block {
                 stmts: [],
                 expr: Some(expr),
+                rules,
                 ..
             },
             _,
-        ) => get_some_expr(cx, expr, ctxt),
+        ) => get_some_expr(
+            cx,
+            expr,
+            needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
+            ctxt,
+        ),
         _ => None,
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
index 335ea001ee4..63a72d4fdde 100644
--- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
@@ -52,6 +52,7 @@ declare_clippy_lint! {
     /// #[non_exhaustive]
     /// struct T(pub i32, pub i32);
     /// ```
+    #[clippy::version = "1.45.0"]
     pub MANUAL_NON_EXHAUSTIVE,
     style,
     "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]"
diff --git a/src/tools/clippy/clippy_lints/src/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/manual_ok_or.rs
index cf641d0ce86..b60e2dc366b 100644
--- a/src/tools/clippy/clippy_lints/src/manual_ok_or.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_ok_or.rs
@@ -32,6 +32,7 @@ declare_clippy_lint! {
     /// let foo: Option<i32> = None;
     /// foo.ok_or("error");
     /// ```
+    #[clippy::version = "1.49.0"]
     pub MANUAL_OK_OR,
     pedantic,
     "finds patterns that can be encoded more concisely with `Option::ok_or`"
diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs
index 4e040508b6b..f8e28f1671f 100644
--- a/src/tools/clippy/clippy_lints/src/manual_strip.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs
@@ -42,6 +42,7 @@ declare_clippy_lint! {
     ///     assert_eq!(end.to_uppercase(), "WORLD!");
     /// }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub MANUAL_STRIP,
     complexity,
     "suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing"
diff --git a/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs
index 42478e3416e..aac3c6e0de2 100644
--- a/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs
@@ -35,6 +35,7 @@ declare_clippy_lint! {
     /// let foo: Option<i32> = None;
     /// foo.unwrap_or(1);
     /// ```
+    #[clippy::version = "1.49.0"]
     pub MANUAL_UNWRAP_OR,
     complexity,
     "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`"
diff --git a/src/tools/clippy/clippy_lints/src/map_clone.rs b/src/tools/clippy/clippy_lints/src/map_clone.rs
index 7db5c7e52ea..c2b78e21861 100644
--- a/src/tools/clippy/clippy_lints/src/map_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/map_clone.rs
@@ -37,6 +37,7 @@ declare_clippy_lint! {
     /// let y = x.iter();
     /// let z = y.cloned();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MAP_CLONE,
     style,
     "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
diff --git a/src/tools/clippy/clippy_lints/src/map_err_ignore.rs b/src/tools/clippy/clippy_lints/src/map_err_ignore.rs
index 82d3732326e..61f21d532c5 100644
--- a/src/tools/clippy/clippy_lints/src/map_err_ignore.rs
+++ b/src/tools/clippy/clippy_lints/src/map_err_ignore.rs
@@ -97,6 +97,7 @@ declare_clippy_lint! {
     ///         })
     /// }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub MAP_ERR_IGNORE,
     restriction,
     "`map_err` should not ignore the original error"
diff --git a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs
index 40de9ffcd4e..58c686d95b3 100644
--- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs
@@ -47,6 +47,7 @@ declare_clippy_lint! {
     ///     log_err_msg(format_msg(msg));
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OPTION_MAP_UNIT_FN,
     complexity,
     "using `option.map(f)`, where `f` is a function or closure that returns `()`"
@@ -87,6 +88,7 @@ declare_clippy_lint! {
     ///     log_err_msg(format_msg(msg));
     /// };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub RESULT_MAP_UNIT_FN,
     complexity,
     "using `result.map(f)`, where `f` is a function or closure that returns `()`"
@@ -188,7 +190,7 @@ fn unit_closure<'tcx>(
 /// Anything else will return `a`.
 fn let_binding_name(cx: &LateContext<'_>, var_arg: &hir::Expr<'_>) -> String {
     match &var_arg.kind {
-        hir::ExprKind::Field(_, _) => snippet(cx, var_arg.span, "_").replace(".", "_"),
+        hir::ExprKind::Field(_, _) => snippet(cx, var_arg.span, "_").replace('.', "_"),
         hir::ExprKind::Path(_) => format!("_{}", snippet(cx, var_arg.span, "")),
         _ => "a".to_string(),
     }
diff --git a/src/tools/clippy/clippy_lints/src/match_on_vec_items.rs b/src/tools/clippy/clippy_lints/src/match_on_vec_items.rs
index 552c9a58897..583b577ffe2 100644
--- a/src/tools/clippy/clippy_lints/src/match_on_vec_items.rs
+++ b/src/tools/clippy/clippy_lints/src/match_on_vec_items.rs
@@ -40,6 +40,7 @@ declare_clippy_lint! {
     ///     _ => {},
     /// }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub MATCH_ON_VEC_ITEMS,
     pedantic,
     "matching on vector elements can panic"
diff --git a/src/tools/clippy/clippy_lints/src/match_result_ok.rs b/src/tools/clippy/clippy_lints/src/match_result_ok.rs
index ecf6ad316a4..b1839f00aae 100644
--- a/src/tools/clippy/clippy_lints/src/match_result_ok.rs
+++ b/src/tools/clippy/clippy_lints/src/match_result_ok.rs
@@ -38,6 +38,7 @@ declare_clippy_lint! {
     ///        vec.push(value)
     /// }
     /// ```
+    #[clippy::version = "1.57.0"]
     pub MATCH_RESULT_OK,
     style,
     "usage of `ok()` in `let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead"
diff --git a/src/tools/clippy/clippy_lints/src/match_str_case_mismatch.rs b/src/tools/clippy/clippy_lints/src/match_str_case_mismatch.rs
index f501593c518..3316ebf4051 100644
--- a/src/tools/clippy/clippy_lints/src/match_str_case_mismatch.rs
+++ b/src/tools/clippy/clippy_lints/src/match_str_case_mismatch.rs
@@ -39,6 +39,7 @@ declare_clippy_lint! {
     ///     _ => {},
     /// }
     /// ```
+    #[clippy::version = "1.58.0"]
     pub MATCH_STR_CASE_MISMATCH,
     correctness,
     "creation of a case altering match expression with non-compliant arms"
diff --git a/src/tools/clippy/clippy_lints/src/matches.rs b/src/tools/clippy/clippy_lints/src/matches.rs
index 48e459e0165..eacbfa54cf7 100644
--- a/src/tools/clippy/clippy_lints/src/matches.rs
+++ b/src/tools/clippy/clippy_lints/src/matches.rs
@@ -8,9 +8,9 @@ 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::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,
-    remove_blocks, strip_pat_refs,
+    get_parent_expr, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs,
+    path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks,
+    strip_pat_refs,
 };
 use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
 use core::iter::{once, ExactSizeIterator};
@@ -25,7 +25,6 @@ use rustc_hir::{
 };
 use rustc_hir::{HirIdMap, HirIdSet};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, Ty, TyS, VariantDef};
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -33,8 +32,6 @@ use rustc_span::source_map::{Span, Spanned};
 use rustc_span::sym;
 use std::cmp::Ordering;
 use std::collections::hash_map::Entry;
-use std::iter;
-use std::ops::Bound;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -59,6 +56,7 @@ declare_clippy_lint! {
     ///     bar(foo);
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SINGLE_MATCH,
     style,
     "a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`"
@@ -100,6 +98,7 @@ declare_clippy_lint! {
     ///     bar(&other_ref);
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SINGLE_MATCH_ELSE,
     pedantic,
     "a `match` statement with two arms where the second arm's pattern is a placeholder instead of a specific match pattern"
@@ -131,6 +130,7 @@ declare_clippy_lint! {
     ///     _ => frob(x),
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MATCH_REF_PATS,
     style,
     "a `match` or `if let` with all arms prefixed with `&` instead of deref-ing the match expression"
@@ -165,6 +165,7 @@ declare_clippy_lint! {
     ///     bar();
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MATCH_BOOL,
     pedantic,
     "a `match` on a boolean expression instead of an `if..else` block"
@@ -187,6 +188,7 @@ declare_clippy_lint! {
     ///     _ => (),
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MATCH_OVERLAPPING_ARM,
     style,
     "a `match` with overlapping arms"
@@ -209,6 +211,7 @@ declare_clippy_lint! {
     ///     Err(_) => panic!("err"),
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MATCH_WILD_ERR_ARM,
     pedantic,
     "a `match` with `Err(_)` arm and take drastic actions"
@@ -235,6 +238,7 @@ declare_clippy_lint! {
     /// // Good
     /// let r: Option<&()> = x.as_ref();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MATCH_AS_REF,
     complexity,
     "a `match` on an Option value instead of using `as_ref()` or `as_mut`"
@@ -267,6 +271,7 @@ declare_clippy_lint! {
     ///     Foo::B(_) => {},
     /// }
     /// ```
+    #[clippy::version = "1.34.0"]
     pub WILDCARD_ENUM_MATCH_ARM,
     restriction,
     "a wildcard enum match arm using `_`"
@@ -301,6 +306,7 @@ declare_clippy_lint! {
     ///     Foo::C => {},
     /// }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
     pedantic,
     "a wildcard enum match for a single variant"
@@ -328,6 +334,7 @@ declare_clippy_lint! {
     ///     _ => {},
     /// }
     /// ```
+    #[clippy::version = "1.42.0"]
     pub WILDCARD_IN_OR_PATTERNS,
     complexity,
     "a wildcard pattern used with others patterns in same match arm"
@@ -363,6 +370,7 @@ declare_clippy_lint! {
     /// let wrapper = Wrapper::Data(42);
     /// let Wrapper::Data(data) = wrapper;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INFALLIBLE_DESTRUCTURING_MATCH,
     style,
     "a `match` statement with a single infallible arm instead of a `let`"
@@ -394,6 +402,7 @@ declare_clippy_lint! {
     /// // Good
     /// let (c, d) = (a, b);
     /// ```
+    #[clippy::version = "1.43.0"]
     pub MATCH_SINGLE_BINDING,
     complexity,
     "a match with a single binding instead of using `let` statement"
@@ -424,6 +433,7 @@ declare_clippy_lint! {
     ///     _ => {},
     /// }
     /// ```
+    #[clippy::version = "1.43.0"]
     pub REST_PAT_IN_FULLY_BOUND_STRUCTS,
     restriction,
     "a match on a struct that binds all fields but still uses the wildcard pattern"
@@ -479,6 +489,7 @@ declare_clippy_lint! {
     /// if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
     /// Ok::<i32, i32>(42).is_ok();
     /// ```
+    #[clippy::version = "1.31.0"]
     pub REDUNDANT_PATTERN_MATCHING,
     style,
     "use the proper utility function avoiding an `if let`"
@@ -515,6 +526,7 @@ declare_clippy_lint! {
     /// // Good
     /// let a = matches!(x, Some(0));
     /// ```
+    #[clippy::version = "1.47.0"]
     pub MATCH_LIKE_MATCHES_MACRO,
     style,
     "a match that could be written with the matches! macro"
@@ -559,6 +571,7 @@ declare_clippy_lint! {
     ///     Quz => quz(),
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MATCH_SAME_ARMS,
     pedantic,
     "`match` with identical arm bodies"
@@ -601,7 +614,7 @@ impl_lint_pass!(Matches => [
 
 impl<'tcx> LateLintPass<'tcx> for Matches {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) {
+        if expr.span.from_expansion() {
             return;
         }
 
@@ -633,15 +646,11 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
         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) {
-            check_match_ref_pats(cx, let_expr, once(let_pat), expr);
-        }
     }
 
     fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
         if_chain! {
-            if !in_external_macro(cx.sess(), local.span);
-            if !in_macro(local.span);
+            if !local.span.from_expansion();
             if let Some(expr) = local.init;
             if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind;
             if arms.len() == 1 && arms[0].guard.is_none();
@@ -676,8 +685,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
 
     fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
         if_chain! {
-            if !in_external_macro(cx.sess(), pat.span);
-            if !in_macro(pat.span);
+            if !pat.span.from_expansion();
             if let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind;
             if let Some(def_id) = path.res.opt_def_id();
             let ty = cx.tcx.type_of(def_id);
@@ -704,7 +712,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
 #[rustfmt::skip]
 fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
     if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
-        if in_macro(expr.span) {
+        if expr.span.from_expansion() {
             // Don't lint match expressions present in
             // macro_rules! block
             return;
@@ -1450,7 +1458,7 @@ fn find_bool_lit(ex: &ExprKind<'_>, is_if_let: bool) -> Option<bool> {
 
 #[allow(clippy::too_many_lines)]
 fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) {
-    if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
+    if expr.span.from_expansion() || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
         return;
     }
 
@@ -1605,27 +1613,27 @@ fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'
     None
 }
 
-/// Gets all arms that are unbounded `PatRange`s.
+/// Gets the ranges for each range pattern arm. Applies `ty` bounds for open ranges.
 fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) -> Vec<SpannedRange<FullInt>> {
     arms.iter()
         .filter_map(|arm| {
             if let Arm { pat, guard: None, .. } = *arm {
                 if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
-                    let lhs = match lhs {
+                    let lhs_const = match lhs {
                         Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0,
                         None => miri_to_const(ty.numeric_min_val(cx.tcx)?)?,
                     };
-                    let rhs = match rhs {
+                    let rhs_const = match rhs {
                         Some(rhs) => constant(cx, cx.typeck_results(), rhs)?.0,
                         None => miri_to_const(ty.numeric_max_val(cx.tcx)?)?,
                     };
 
-                    let lhs_val = lhs.int_value(cx, ty)?;
-                    let rhs_val = rhs.int_value(cx, ty)?;
+                    let lhs_val = lhs_const.int_value(cx, ty)?;
+                    let rhs_val = rhs_const.int_value(cx, ty)?;
 
                     let rhs_bound = match range_end {
-                        RangeEnd::Included => Bound::Included(rhs_val),
-                        RangeEnd::Excluded => Bound::Excluded(rhs_val),
+                        RangeEnd::Included => EndBound::Included(rhs_val),
+                        RangeEnd::Excluded => EndBound::Excluded(rhs_val),
                     };
                     return Some(SpannedRange {
                         span: pat.span,
@@ -1637,7 +1645,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
                     let value = constant_full_int(cx, cx.typeck_results(), value)?;
                     return Some(SpannedRange {
                         span: pat.span,
-                        node: (value, Bound::Included(value)),
+                        node: (value, EndBound::Included(value)),
                     });
                 }
             }
@@ -1646,10 +1654,16 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
         .collect()
 }
 
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum EndBound<T> {
+    Included(T),
+    Excluded(T),
+}
+
 #[derive(Debug, Eq, PartialEq)]
-pub struct SpannedRange<T> {
+struct SpannedRange<T> {
     pub span: Span,
-    pub node: (T, Bound<T>),
+    pub node: (T, EndBound<T>),
 }
 
 // Checks if arm has the form `None => None`
@@ -1698,82 +1712,63 @@ where
     ref_count > 1
 }
 
-pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &SpannedRange<T>)>
+fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &SpannedRange<T>)>
 where
     T: Copy + Ord,
 {
-    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
-    enum Kind<'a, T> {
-        Start(T, &'a SpannedRange<T>),
-        End(Bound<T>, &'a SpannedRange<T>),
+    #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
+    enum BoundKind {
+        EndExcluded,
+        Start,
+        EndIncluded,
     }
 
-    impl<'a, T: Copy> Kind<'a, T> {
-        fn range(&self) -> &'a SpannedRange<T> {
-            match *self {
-                Kind::Start(_, r) | Kind::End(_, r) => r,
-            }
-        }
-
-        fn value(self) -> Bound<T> {
-            match self {
-                Kind::Start(t, _) => Bound::Included(t),
-                Kind::End(t, _) => t,
-            }
-        }
-    }
+    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
+    struct RangeBound<'a, T>(T, BoundKind, &'a SpannedRange<T>);
 
-    impl<'a, T: Copy + Ord> PartialOrd for Kind<'a, T> {
+    impl<'a, T: Copy + Ord> PartialOrd for RangeBound<'a, T> {
         fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
             Some(self.cmp(other))
         }
     }
 
-    impl<'a, T: Copy + Ord> Ord for Kind<'a, T> {
-        fn cmp(&self, other: &Self) -> Ordering {
-            match (self.value(), other.value()) {
-                (Bound::Included(a), Bound::Included(b)) | (Bound::Excluded(a), Bound::Excluded(b)) => a.cmp(&b),
-                // Range patterns cannot be unbounded (yet)
-                (Bound::Unbounded, _) | (_, Bound::Unbounded) => unimplemented!(),
-                (Bound::Included(a), Bound::Excluded(b)) => match a.cmp(&b) {
-                    Ordering::Equal => Ordering::Greater,
-                    other => other,
-                },
-                (Bound::Excluded(a), Bound::Included(b)) => match a.cmp(&b) {
-                    Ordering::Equal => Ordering::Less,
-                    other => other,
-                },
-            }
+    impl<'a, T: Copy + Ord> Ord for RangeBound<'a, T> {
+        fn cmp(&self, RangeBound(other_value, other_kind, _): &Self) -> Ordering {
+            let RangeBound(self_value, self_kind, _) = *self;
+            (self_value, self_kind).cmp(&(*other_value, *other_kind))
         }
     }
 
     let mut values = Vec::with_capacity(2 * ranges.len());
 
-    for r in ranges {
-        values.push(Kind::Start(r.node.0, r));
-        values.push(Kind::End(r.node.1, r));
+    for r @ SpannedRange { node: (start, end), .. } in ranges {
+        values.push(RangeBound(*start, BoundKind::Start, r));
+        values.push(match end {
+            EndBound::Excluded(val) => RangeBound(*val, BoundKind::EndExcluded, r),
+            EndBound::Included(val) => RangeBound(*val, BoundKind::EndIncluded, r),
+        });
     }
 
     values.sort();
 
-    for (a, b) in iter::zip(&values, values.iter().skip(1)) {
-        match (a, b) {
-            (&Kind::Start(_, ra), &Kind::End(_, rb)) => {
-                if ra.node != rb.node {
-                    return Some((ra, rb));
-                }
-            },
-            (&Kind::End(a, _), &Kind::Start(b, _)) if a != Bound::Included(b) => (),
-            _ => {
-                // skip if the range `a` is completely included into the range `b`
-                if let Ordering::Equal | Ordering::Less = a.cmp(b) {
-                    let kind_a = Kind::End(a.range().node.1, a.range());
-                    let kind_b = Kind::End(b.range().node.1, b.range());
-                    if let Ordering::Equal | Ordering::Greater = kind_a.cmp(&kind_b) {
-                        return None;
+    let mut started = vec![];
+
+    for RangeBound(_, kind, range) in values {
+        match kind {
+            BoundKind::Start => started.push(range),
+            BoundKind::EndExcluded | BoundKind::EndIncluded => {
+                let mut overlap = None;
+
+                while let Some(last_started) = started.pop() {
+                    if last_started == range {
+                        break;
                     }
+                    overlap = Some(last_started);
+                }
+
+                if let Some(first_overlapping) = overlap {
+                    return Some((range, first_overlapping));
                 }
-                return Some((a.range(), b.range()));
             },
         }
     }
@@ -1785,7 +1780,8 @@ mod redundant_pattern_match {
     use super::REDUNDANT_PATTERN_MATCHING;
     use clippy_utils::diagnostics::span_lint_and_then;
     use clippy_utils::higher;
-    use clippy_utils::source::{snippet, snippet_with_applicability};
+    use clippy_utils::source::snippet;
+    use clippy_utils::sugg::Sugg;
     use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type};
     use clippy_utils::{is_lang_ctor, is_qpath_def_path, is_trait_method, paths};
     use if_chain::if_chain;
@@ -1795,7 +1791,7 @@ mod redundant_pattern_match {
     use rustc_hir::LangItem::{OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
     use rustc_hir::{
         intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
-        Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, Pat, PatKind, QPath,
+        Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, Pat, PatKind, QPath, UnOp,
     };
     use rustc_lint::LateContext;
     use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
@@ -2049,8 +2045,10 @@ mod redundant_pattern_match {
 
         let result_expr = match &let_expr.kind {
             ExprKind::AddrOf(_, _, borrowed) => borrowed,
+            ExprKind::Unary(UnOp::Deref, deref) => deref,
             _ => let_expr,
         };
+
         span_lint_and_then(
             cx,
             REDUNDANT_PATTERN_MATCHING,
@@ -2069,12 +2067,15 @@ mod redundant_pattern_match {
                 // ^^^^^^^^^^^^^^^^^^^
                 let span = expr_span.until(op_span.shrink_to_hi());
 
-                let mut app = if needs_drop {
+                let app = if needs_drop {
                     Applicability::MaybeIncorrect
                 } else {
                     Applicability::MachineApplicable
                 };
-                let sugg = snippet_with_applicability(cx, op_span, "_", &mut app);
+
+                let sugg = Sugg::hir_with_macro_callsite(cx, result_expr, "_")
+                    .maybe_par()
+                    .to_string();
 
                 diag.span_suggestion(span, "try this", format!("{} {}.{}", keyword, sugg, good_method), app);
 
@@ -2224,29 +2225,29 @@ fn test_overlapping() {
     };
 
     assert_eq!(None, overlapping::<u8>(&[]));
-    assert_eq!(None, overlapping(&[sp(1, Bound::Included(4))]));
+    assert_eq!(None, overlapping(&[sp(1, EndBound::Included(4))]));
     assert_eq!(
         None,
-        overlapping(&[sp(1, Bound::Included(4)), sp(5, Bound::Included(6))])
+        overlapping(&[sp(1, EndBound::Included(4)), sp(5, EndBound::Included(6))])
     );
     assert_eq!(
         None,
         overlapping(&[
-            sp(1, Bound::Included(4)),
-            sp(5, Bound::Included(6)),
-            sp(10, Bound::Included(11))
+            sp(1, EndBound::Included(4)),
+            sp(5, EndBound::Included(6)),
+            sp(10, EndBound::Included(11))
         ],)
     );
     assert_eq!(
-        Some((&sp(1, Bound::Included(4)), &sp(3, Bound::Included(6)))),
-        overlapping(&[sp(1, Bound::Included(4)), sp(3, Bound::Included(6))])
+        Some((&sp(1, EndBound::Included(4)), &sp(3, EndBound::Included(6)))),
+        overlapping(&[sp(1, EndBound::Included(4)), sp(3, EndBound::Included(6))])
     );
     assert_eq!(
-        Some((&sp(5, Bound::Included(6)), &sp(6, Bound::Included(11)))),
+        Some((&sp(5, EndBound::Included(6)), &sp(6, EndBound::Included(11)))),
         overlapping(&[
-            sp(1, Bound::Included(4)),
-            sp(5, Bound::Included(6)),
-            sp(6, Bound::Included(11))
+            sp(1, EndBound::Included(4)),
+            sp(5, EndBound::Included(6)),
+            sp(6, EndBound::Included(11))
         ],)
     );
 }
diff --git a/src/tools/clippy/clippy_lints/src/mem_forget.rs b/src/tools/clippy/clippy_lints/src/mem_forget.rs
index eb437dc47af..5ffcfd4d264 100644
--- a/src/tools/clippy/clippy_lints/src/mem_forget.rs
+++ b/src/tools/clippy/clippy_lints/src/mem_forget.rs
@@ -19,6 +19,7 @@ declare_clippy_lint! {
     /// # use std::rc::Rc;
     /// mem::forget(Rc::new(55))
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MEM_FORGET,
     restriction,
     "`mem::forget` usage on `Drop` types, likely to cause memory leaks"
diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs
index 1e6057a8fe9..7fc39f17232 100644
--- a/src/tools/clippy/clippy_lints/src/mem_replace.rs
+++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs
@@ -1,7 +1,7 @@
 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::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 clippy_utils::{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::LangItem::OptionNone;
@@ -35,6 +35,7 @@ declare_clippy_lint! {
     /// let mut an_option = Some(0);
     /// let taken = an_option.take();
     /// ```
+    #[clippy::version = "1.31.0"]
     pub MEM_REPLACE_OPTION_WITH_NONE,
     style,
     "replacing an `Option` with `None` instead of `take()`"
@@ -66,6 +67,7 @@ declare_clippy_lint! {
     /// The [take_mut](https://docs.rs/take_mut) crate offers a sound solution,
     /// at the cost of either lazily creating a replacement value or aborting
     /// on panic, to ensure that the uninitialized value cannot be observed.
+    #[clippy::version = "1.39.0"]
     pub MEM_REPLACE_WITH_UNINIT,
     correctness,
     "`mem::replace(&mut _, mem::uninitialized())` or `mem::replace(&mut _, mem::zeroed())`"
@@ -90,6 +92,7 @@ declare_clippy_lint! {
     /// let mut text = String::from("foo");
     /// let taken = std::mem::take(&mut text);
     /// ```
+    #[clippy::version = "1.42.0"]
     pub MEM_REPLACE_WITH_DEFAULT,
     style,
     "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`"
@@ -213,7 +216,7 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<
             expr_span,
             "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`",
             |diag| {
-                if !in_macro(expr_span) {
+                if !expr_span.from_expansion() {
                     let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, ""));
 
                     diag.span_suggestion(
diff --git a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs
index da428a7b487..b7690cf9222 100644
--- a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs
@@ -1,7 +1,7 @@
 use super::{contains_return, BIND_INSTEAD_OF_MAP};
 use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::{snippet, snippet_with_macro_callsite};
-use clippy_utils::{in_macro, remove_blocks, visitors::find_all_ret_expressions};
+use clippy_utils::{remove_blocks, visitors::find_all_ret_expressions};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -106,7 +106,7 @@ pub(crate) trait BindInsteadOfMap {
         let mut suggs = Vec::new();
         let can_sugg: bool = find_all_ret_expressions(cx, closure_expr, |ret_expr| {
             if_chain! {
-                if !in_macro(ret_expr.span);
+                if !ret_expr.span.from_expansion();
                 if let hir::ExprKind::Call(func_path, [arg]) = ret_expr.kind;
                 if let hir::ExprKind::Path(QPath::Resolved(_, path)) = func_path.kind;
                 if Self::is_variant(cx, path.res);
diff --git a/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs
index 99c03844f49..8ea9312c0f7 100644
--- a/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs
@@ -69,7 +69,7 @@ fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) -
                         // i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>`
                         let ty_str = ty.to_string();
                         let start = ty_str.find('<').unwrap_or(0);
-                        let end = ty_str.find('>').unwrap_or_else(|| ty_str.len());
+                        let end = ty_str.find('>').unwrap_or(ty_str.len());
                         let nb_wildcard = ty_str[start..end].split(',').count();
                         let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1));
                         format!("{}<{}>", elements.join("::"), wildcards)
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs b/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs
index dd4ef6e4b58..30d56113c6c 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_cloned_collect.rs
@@ -9,7 +9,7 @@ use rustc_span::sym;
 
 use super::ITER_CLONED_COLLECT;
 
-pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, recv: &'tcx hir::Expr<'_>) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, method_name: &str, expr: &hir::Expr<'_>, recv: &'tcx hir::Expr<'_>) {
     if_chain! {
         if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec);
         if let Some(slice) = derefs_to_slice(cx, recv, cx.typeck_results().expr_ty(recv));
@@ -20,8 +20,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, recv: &'
                 cx,
                 ITER_CLONED_COLLECT,
                 to_replace,
-                "called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \
-                more readable",
+                &format!("called `iter().{}().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \
+                more readable", method_name),
                 "try",
                 ".to_vec()".to_string(),
                 Applicability::MachineApplicable,
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index e46739fea34..58ec2213535 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -33,7 +33,6 @@ 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;
@@ -50,6 +49,7 @@ mod single_char_insert_string;
 mod single_char_pattern;
 mod single_char_push_string;
 mod skip_while_next;
+mod str_splitn;
 mod string_extend_chars;
 mod suspicious_map;
 mod suspicious_splitn;
@@ -68,7 +68,7 @@ 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};
+use clippy_utils::{contains_return, get_trait_def_id, iter_input_pats, meets_msrv, msrvs, paths, return_ty};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_hir::def::Res;
@@ -99,6 +99,7 @@ declare_clippy_lint! {
     /// ```rust
     /// [1, 2, 3].iter().copied();
     /// ```
+    #[clippy::version = "1.53.0"]
     pub CLONED_INSTEAD_OF_COPIED,
     pedantic,
     "used `cloned` where `copied` could be used instead"
@@ -121,6 +122,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect();
     /// ```
+    #[clippy::version = "1.53.0"]
     pub FLAT_MAP_OPTION,
     pedantic,
     "used `flat_map` where `filter_map` could be used instead"
@@ -166,6 +168,7 @@ declare_clippy_lint! {
     /// // Good
     /// res.expect("more helpful message");
     /// ```
+    #[clippy::version = "1.45.0"]
     pub UNWRAP_USED,
     restriction,
     "using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`"
@@ -208,6 +211,7 @@ declare_clippy_lint! {
     /// res?;
     /// # Ok::<(), ()>(())
     /// ```
+    #[clippy::version = "1.45.0"]
     pub EXPECT_USED,
     restriction,
     "using `.expect()` on `Result` or `Option`, which might be better handled"
@@ -237,6 +241,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SHOULD_IMPLEMENT_TRAIT,
     style,
     "defining a method that should be implementing a std trait"
@@ -284,6 +289,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WRONG_SELF_CONVENTION,
     style,
     "defining a method named with an established prefix (like \"into_\") that takes `self` with the wrong convention"
@@ -310,6 +316,7 @@ declare_clippy_lint! {
     /// // Good
     /// x.expect("why did I do this again?");
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OK_EXPECT,
     style,
     "using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result"
@@ -335,6 +342,7 @@ declare_clippy_lint! {
     /// // Good
     /// x.unwrap_or_default();
     /// ```
+    #[clippy::version = "1.56.0"]
     pub UNWRAP_OR_ELSE_DEFAULT,
     style,
     "using `.unwrap_or_else(Default::default)`, which is more succinctly expressed as `.unwrap_or_default()`"
@@ -375,6 +383,7 @@ declare_clippy_lint! {
     /// // Good
     /// x.map_or_else(some_function, |a| a + 1);
     /// ```
+    #[clippy::version = "1.45.0"]
     pub MAP_UNWRAP_OR,
     pedantic,
     "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`"
@@ -401,6 +410,7 @@ declare_clippy_lint! {
     /// // Good
     /// opt.and_then(|a| Some(a + 1));
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OPTION_MAP_OR_NONE,
     style,
     "using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`"
@@ -426,6 +436,7 @@ declare_clippy_lint! {
     /// # let r: Result<u32, &str> = Ok(1);
     /// assert_eq!(Some(1), r.ok());
     /// ```
+    #[clippy::version = "1.44.0"]
     pub RESULT_MAP_OR_INTO_OPTION,
     style,
     "using `Result.map_or(None, Some)`, which is more succinctly expressed as `ok()`"
@@ -458,6 +469,7 @@ declare_clippy_lint! {
     /// let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 });
     /// let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 });
     /// ```
+    #[clippy::version = "1.45.0"]
     pub BIND_INSTEAD_OF_MAP,
     complexity,
     "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`"
@@ -481,6 +493,7 @@ declare_clippy_lint! {
     /// # let vec = vec![1];
     /// vec.iter().find(|x| **x == 0);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FILTER_NEXT,
     complexity,
     "using `filter(p).next()`, which is more succinctly expressed as `.find(p)`"
@@ -504,6 +517,7 @@ declare_clippy_lint! {
     /// # let vec = vec![1];
     /// vec.iter().find(|x| **x != 0);
     /// ```
+    #[clippy::version = "1.42.0"]
     pub SKIP_WHILE_NEXT,
     complexity,
     "using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`"
@@ -527,8 +541,9 @@ declare_clippy_lint! {
     /// // Good
     /// vec.iter().flat_map(|x| x.iter());
     /// ```
+    #[clippy::version = "1.31.0"]
     pub MAP_FLATTEN,
-    pedantic,
+    complexity,
     "using combinations of `flatten` and `map` which can usually be written as a single method call"
 }
 
@@ -553,6 +568,7 @@ declare_clippy_lint! {
     /// ```rust
     /// (0_i32..10).filter_map(|n| n.checked_add(1));
     /// ```
+    #[clippy::version = "1.51.0"]
     pub MANUAL_FILTER_MAP,
     complexity,
     "using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`"
@@ -579,6 +595,7 @@ declare_clippy_lint! {
     /// ```rust
     /// (0_i32..10).find_map(|n| n.checked_add(1));
     /// ```
+    #[clippy::version = "1.51.0"]
     pub MANUAL_FIND_MAP,
     complexity,
     "using `_.find(_).map(_)` in a way that can be written more simply as `find_map(_)`"
@@ -601,6 +618,7 @@ declare_clippy_lint! {
     /// ```rust
     ///  (0..3).find_map(|x| if x == 2 { Some(x) } else { None });
     /// ```
+    #[clippy::version = "1.36.0"]
     pub FILTER_MAP_NEXT,
     pedantic,
     "using combination of `filter_map` and `next` which can usually be written as a single method call"
@@ -623,6 +641,7 @@ declare_clippy_lint! {
     /// # let iter = vec![vec![0]].into_iter();
     /// iter.flatten();
     /// ```
+    #[clippy::version = "1.39.0"]
     pub FLAT_MAP_IDENTITY,
     complexity,
     "call to `flat_map` where `flatten` is sufficient"
@@ -652,6 +671,7 @@ declare_clippy_lint! {
     ///
     /// let _ = !"hello world".contains("world");
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SEARCH_IS_SOME,
     complexity,
     "using an iterator or string search followed by `is_some()` or `is_none()`, which is more succinctly expressed as a call to `any()` or `contains()` (with negation in case of `is_none()`)"
@@ -676,6 +696,7 @@ declare_clippy_lint! {
     /// let name = "foo";
     /// if name.starts_with('_') {};
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CHARS_NEXT_CMP,
     style,
     "using `.chars().next()` to check if a string starts with a char"
@@ -710,6 +731,7 @@ declare_clippy_lint! {
     /// # let foo = Some(String::new());
     /// foo.unwrap_or_default();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OR_FUN_CALL,
     perf,
     "using any `*or` method with a function call, which suggests `*or_else`"
@@ -748,6 +770,7 @@ declare_clippy_lint! {
     /// # let err_msg = "I'm a teapot";
     /// foo.unwrap_or_else(|| panic!("Err {}: {}", err_code, err_msg));
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub EXPECT_FUN_CALL,
     perf,
     "using any `expect` method with a function call"
@@ -765,6 +788,7 @@ declare_clippy_lint! {
     /// ```rust
     /// 42u64.clone();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CLONE_ON_COPY,
     complexity,
     "using `clone` on a `Copy` type"
@@ -792,6 +816,7 @@ declare_clippy_lint! {
     /// // Good
     /// Rc::clone(&x);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CLONE_ON_REF_PTR,
     restriction,
     "using 'clone' on a ref-counted pointer"
@@ -814,6 +839,7 @@ declare_clippy_lint! {
     ///     println!("{:p} {:p}", *y, z); // prints out the same pointer
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CLONE_DOUBLE_REF,
     correctness,
     "using `clone` on `&&T`"
@@ -837,6 +863,7 @@ declare_clippy_lint! {
     /// // OK, the specialized impl is used
     /// ["foo", "bar"].iter().map(|&s| s.to_string());
     /// ```
+    #[clippy::version = "1.40.0"]
     pub INEFFICIENT_TO_STRING,
     pedantic,
     "using `to_string` on `&&T` where `T: ToString`"
@@ -898,6 +925,7 @@ declare_clippy_lint! {
     ///     fn new() -> Self;
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEW_RET_NO_SELF,
     style,
     "not returning type containing `Self` in a `new` method"
@@ -922,6 +950,7 @@ declare_clippy_lint! {
     ///
     /// // Good
     /// _.split('x');
+    #[clippy::version = "pre 1.29.0"]
     pub SINGLE_CHAR_PATTERN,
     perf,
     "using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
@@ -941,6 +970,7 @@ declare_clippy_lint! {
     ///     //..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ITERATOR_STEP_BY_ZERO,
     correctness,
     "using `Iterator::step_by(0)`, which will panic at runtime"
@@ -962,6 +992,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let _ = std::iter::empty::<Option<i32>>().flatten();
     /// ```
+    #[clippy::version = "1.53.0"]
     pub OPTION_FILTER_MAP,
     complexity,
     "filtering `Option` for `Some` then force-unwrapping, which can be one type-safe operation"
@@ -989,6 +1020,7 @@ declare_clippy_lint! {
     /// # s.insert(1);
     /// let x = s.iter().next();
     /// ```
+    #[clippy::version = "1.42.0"]
     pub ITER_NTH_ZERO,
     style,
     "replace `iter.nth(0)` with `iter.next()`"
@@ -1015,6 +1047,7 @@ declare_clippy_lint! {
     /// let bad_vec = some_vec.get(3);
     /// let bad_slice = &some_vec[..].get(3);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ITER_NTH,
     perf,
     "using `.iter().nth()` on a standard library type with O(1) element access"
@@ -1039,6 +1072,7 @@ declare_clippy_lint! {
     /// let bad_vec = some_vec.iter().nth(3);
     /// let bad_slice = &some_vec[..].iter().nth(3);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ITER_SKIP_NEXT,
     style,
     "using `.skip(x).next()` on an iterator"
@@ -1075,6 +1109,7 @@ declare_clippy_lint! {
     /// let last = some_vec[3];
     /// some_vec[0] = 1;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub GET_UNWRAP,
     restriction,
     "using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead"
@@ -1098,6 +1133,7 @@ declare_clippy_lint! {
     /// // Good
     /// a.append(&mut b);
     /// ```
+    #[clippy::version = "1.55.0"]
     pub EXTEND_WITH_DRAIN,
     perf,
     "using vec.append(&mut vec) to move the full range of a vecor to another"
@@ -1127,6 +1163,7 @@ declare_clippy_lint! {
     /// s.push_str(abc);
     /// s.push_str(&def);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub STRING_EXTEND_CHARS,
     style,
     "using `x.extend(s.chars())` where s is a `&str` or `String`"
@@ -1150,6 +1187,7 @@ declare_clippy_lint! {
     /// let s = [1, 2, 3, 4, 5];
     /// let s2: Vec<isize> = s.to_vec();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ITER_CLONED_COLLECT,
     style,
     "using `.cloned().collect()` on slice to create a `Vec`"
@@ -1174,6 +1212,7 @@ declare_clippy_lint! {
     /// // Good
     /// name.ends_with('_') || name.ends_with('-');
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CHARS_LAST_CMP,
     style,
     "using `.chars().last()` or `.chars().next_back()` to check if a string ends with a char"
@@ -1199,6 +1238,7 @@ declare_clippy_lint! {
     /// let x: &[i32] = &[1, 2, 3, 4, 5];
     /// do_stuff(x);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USELESS_ASREF,
     complexity,
     "using `as_ref` where the types before and after the call are the same"
@@ -1221,6 +1261,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let _ = (0..3).any(|x| x > 2);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNNECESSARY_FOLD,
     style,
     "using `fold` when a more succinct alternative exists"
@@ -1250,6 +1291,7 @@ declare_clippy_lint! {
     /// // As there is no conditional check on the argument this could be written as:
     /// let _ = (0..4).map(|x| x + 1);
     /// ```
+    #[clippy::version = "1.31.0"]
     pub UNNECESSARY_FILTER_MAP,
     complexity,
     "using `filter_map` when a more succinct alternative exists"
@@ -1273,6 +1315,7 @@ declare_clippy_lint! {
     /// // Good
     /// let _ = (&vec![3, 4, 5]).iter();
     /// ```
+    #[clippy::version = "1.32.0"]
     pub INTO_ITER_ON_REF,
     style,
     "using `.into_iter()` on a reference"
@@ -1292,6 +1335,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let _ = (0..3).map(|x| x + 2).count();
     /// ```
+    #[clippy::version = "1.39.0"]
     pub SUSPICIOUS_MAP,
     suspicious,
     "suspicious usage of map"
@@ -1326,6 +1370,7 @@ declare_clippy_lint! {
     ///     MaybeUninit::uninit().assume_init()
     /// };
     /// ```
+    #[clippy::version = "1.39.0"]
     pub UNINIT_ASSUMED_INIT,
     correctness,
     "`MaybeUninit::uninit().assume_init()`"
@@ -1354,6 +1399,7 @@ declare_clippy_lint! {
     /// let add = x.saturating_add(y);
     /// let sub = x.saturating_sub(y);
     /// ```
+    #[clippy::version = "1.39.0"]
     pub MANUAL_SATURATING_ARITHMETIC,
     style,
     "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`"
@@ -1371,6 +1417,7 @@ declare_clippy_lint! {
     /// ```rust
     /// unsafe { (&() as *const ()).offset(1) };
     /// ```
+    #[clippy::version = "1.41.0"]
     pub ZST_OFFSET,
     correctness,
     "Check for offset calculations on raw pointers to zero-sized types"
@@ -1412,6 +1459,7 @@ declare_clippy_lint! {
     /// # Ok::<_, std::io::Error>(())
     /// # };
     /// ```
+    #[clippy::version = "1.42.0"]
     pub FILETYPE_IS_FILE,
     restriction,
     "`FileType::is_file` is not recommended to test for readable file type"
@@ -1437,6 +1485,7 @@ declare_clippy_lint! {
     /// opt.as_deref()
     /// # ;
     /// ```
+    #[clippy::version = "1.42.0"]
     pub OPTION_AS_REF_DEREF,
     complexity,
     "using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`"
@@ -1463,6 +1512,7 @@ declare_clippy_lint! {
     /// a.get(2);
     /// b.get(0);
     /// ```
+    #[clippy::version = "1.46.0"]
     pub ITER_NEXT_SLICE,
     style,
     "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`"
@@ -1488,6 +1538,7 @@ declare_clippy_lint! {
     /// string.insert(0, 'R');
     /// string.push('R');
     /// ```
+    #[clippy::version = "1.49.0"]
     pub SINGLE_CHAR_ADD_STR,
     style,
     "`push_str()` or `insert_str()` used with a single-character string literal as parameter"
@@ -1526,6 +1577,7 @@ declare_clippy_lint! {
     ///
     /// opt.unwrap_or(42);
     /// ```
+    #[clippy::version = "1.48.0"]
     pub UNNECESSARY_LAZY_EVALUATIONS,
     style,
     "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
@@ -1546,6 +1598,7 @@ declare_clippy_lint! {
     /// ```rust
     /// (0..3).try_for_each(|t| Err(t));
     /// ```
+    #[clippy::version = "1.49.0"]
     pub MAP_COLLECT_RESULT_UNIT,
     style,
     "using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`"
@@ -1578,6 +1631,7 @@ declare_clippy_lint! {
     ///
     /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
     /// ```
+    #[clippy::version = "1.49.0"]
     pub FROM_ITER_INSTEAD_OF_COLLECT,
     pedantic,
     "use `.collect()` instead of `::from_iter()`"
@@ -1607,6 +1661,7 @@ declare_clippy_lint! {
     ///     assert!(x >= 0);
     /// });
     /// ```
+    #[clippy::version = "1.51.0"]
     pub INSPECT_FOR_EACH,
     complexity,
     "using `.inspect().for_each()`, which can be replaced with `.for_each()`"
@@ -1629,6 +1684,7 @@ declare_clippy_lint! {
     /// # let iter = vec![Some(1)].into_iter();
     /// iter.flatten();
     /// ```
+    #[clippy::version = "1.52.0"]
     pub FILTER_MAP_IDENTITY,
     complexity,
     "call to `filter_map` where `flatten` is sufficient"
@@ -1651,6 +1707,7 @@ declare_clippy_lint! {
     /// let x = [1, 2, 3];
     /// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
     /// ```
+    #[clippy::version = "1.52.0"]
     pub MAP_IDENTITY,
     complexity,
     "using iterator.map(|x| x)"
@@ -1672,6 +1729,7 @@ declare_clippy_lint! {
     /// // Good
     /// let _ = "Hello".as_bytes().get(3);
     /// ```
+    #[clippy::version = "1.52.0"]
     pub BYTES_NTH,
     style,
     "replace `.bytes().nth()` with `.as_bytes().get()`"
@@ -1697,6 +1755,7 @@ declare_clippy_lint! {
     /// let b = a.clone();
     /// let c = a.clone();
     /// ```
+    #[clippy::version = "1.52.0"]
     pub IMPLICIT_CLONE,
     pedantic,
     "implicitly cloning a value by invoking a function on its dereferenced type"
@@ -1722,6 +1781,7 @@ declare_clippy_lint! {
     /// let _ = some_vec.len();
     /// let _ = &some_vec[..].len();
     /// ```
+    #[clippy::version = "1.52.0"]
     pub ITER_COUNT,
     complexity,
     "replace `.iter().count()` with `.len()`"
@@ -1751,6 +1811,7 @@ declare_clippy_lint! {
     ///     // use x
     /// }
     /// ```
+    #[clippy::version = "1.54.0"]
     pub SUSPICIOUS_SPLITN,
     correctness,
     "checks for `.splitn(0, ..)` and `.splitn(1, ..)`"
@@ -1771,6 +1832,7 @@ declare_clippy_lint! {
     /// // Good
     /// let x: String = "x".repeat(10);
     /// ```
+    #[clippy::version = "1.54.0"]
     pub MANUAL_STR_REPEAT,
     perf,
     "manual implementation of `str::repeat`"
@@ -1793,11 +1855,36 @@ declare_clippy_lint! {
     /// let (key, value) = _.split_once('=')?;
     /// let value = _.split_once('=')?.1;
     /// ```
+    #[clippy::version = "1.57.0"]
     pub MANUAL_SPLIT_ONCE,
     complexity,
     "replace `.splitn(2, pat)` with `.split_once(pat)`"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for usages of `str::splitn` (or `str::rsplitn`) where using `str::split` would be the same.
+    /// ### Why is this bad?
+    /// The function `split` is simpler and there is no performance difference in these cases, considering
+    /// that both functions return a lazy iterator.
+    /// ### Example
+    /// ```rust
+    /// // Bad
+    /// let str = "key=value=add";
+    /// let _ = str.splitn(3, '=').next().unwrap();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// // Good
+    /// let str = "key=value=add";
+    /// let _ = str.split('=').next().unwrap();
+    /// ```
+    #[clippy::version = "1.58.0"]
+    pub NEEDLESS_SPLITN,
+    complexity,
+    "usages of `str::splitn` that can be replaced with `str::split`"
+}
+
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Option<RustcVersion>,
@@ -1876,7 +1963,8 @@ impl_lint_pass!(Methods => [
     SUSPICIOUS_SPLITN,
     MANUAL_STR_REPEAT,
     EXTEND_WITH_DRAIN,
-    MANUAL_SPLIT_ONCE
+    MANUAL_SPLIT_ONCE,
+    NEEDLESS_SPLITN
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
@@ -1900,7 +1988,7 @@ macro_rules! method_call {
 
 impl<'tcx> LateLintPass<'tcx> for Methods {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        if in_macro(expr.span) {
+        if expr.span.from_expansion() {
             return;
         }
 
@@ -2116,7 +2204,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
             ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
             ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv),
             ("collect", []) => match method_call!(recv) {
-                Some(("cloned", [recv2], _)) => iter_cloned_collect::check(cx, expr, recv2),
+                Some((name @ ("cloned" | "copied"), [recv2], _)) => {
+                    iter_cloned_collect::check(cx, name, expr, recv2);
+                },
                 Some(("map", [m_recv, m_arg], _)) => {
                     map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv);
                 },
@@ -2208,7 +2298,10 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                 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);
+                        str_splitn::check_manual_split_once(cx, name, expr, recv, pat_arg);
+                    }
+                    if count >= 2 {
+                        str_splitn::check_needless_splitn(cx, name, expr, recv, pat_arg, count);
                     }
                 }
             },
diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs
index e99b6b07d15..5e5c1038e82 100644
--- a/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_lang_ctor;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{is_lang_ctor, single_segment_path};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::LangItem::{OptionNone, OptionSome};
@@ -11,6 +11,28 @@ use rustc_span::symbol::sym;
 use super::OPTION_MAP_OR_NONE;
 use super::RESULT_MAP_OR_INTO_OPTION;
 
+// The expression inside a closure may or may not have surrounding braces
+// which causes problems when generating a suggestion.
+fn reduce_unit_expression<'a>(
+    cx: &LateContext<'_>,
+    expr: &'a hir::Expr<'_>,
+) -> Option<(&'a hir::Expr<'a>, &'a [hir::Expr<'a>])> {
+    match expr.kind {
+        hir::ExprKind::Call(func, arg_char) => Some((func, arg_char)),
+        hir::ExprKind::Block(block, _) => {
+            match (block.stmts, block.expr) {
+                (&[], Some(inner_expr)) => {
+                    // If block only contains an expression,
+                    // reduce `|x| { x + 1 }` to `|x| x + 1`
+                    reduce_unit_expression(cx, inner_expr)
+                },
+                _ => None,
+            }
+        },
+        _ => None,
+    }
+}
+
 /// lint use of `_.map_or(None, _)` for `Option`s and `Result`s
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
@@ -31,58 +53,75 @@ pub(super) fn check<'tcx>(
         return;
     }
 
-    let (lint_name, msg, instead, hint) = {
-        let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind {
-            is_lang_ctor(cx, qpath, OptionNone)
-        } else {
-            return;
-        };
+    let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind {
+        is_lang_ctor(cx, qpath, OptionNone)
+    } else {
+        return;
+    };
 
-        if !default_arg_is_none {
-            // nothing to lint!
-            return;
-        }
+    if !default_arg_is_none {
+        // nothing to lint!
+        return;
+    }
 
-        let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind {
-            is_lang_ctor(cx, qpath, OptionSome)
-        } else {
-            false
-        };
+    let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind {
+        is_lang_ctor(cx, qpath, OptionSome)
+    } else {
+        false
+    };
+
+    if is_option {
+        let self_snippet = snippet(cx, recv.span, "..");
+        if_chain! {
+        if let hir::ExprKind::Closure(_, _, id, span, _) = map_arg.kind;
+            let arg_snippet = snippet(cx, span, "..");
+            let body = cx.tcx.hir().body(id);
+                if let Some((func, arg_char)) = reduce_unit_expression(cx, &body.value);
+                if arg_char.len() == 1;
+                if let hir::ExprKind::Path(ref qpath) = func.kind;
+                if let Some(segment) = single_segment_path(qpath);
+                if segment.ident.name == sym::Some;
+                then {
+                    let func_snippet = snippet(cx, arg_char[0].span, "..");
+                    let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
+                       `map(..)` instead";
+                    return span_lint_and_sugg(
+                        cx,
+                        OPTION_MAP_OR_NONE,
+                        expr.span,
+                        msg,
+                        "try using `map` instead",
+                        format!("{0}.map({1} {2})", self_snippet, arg_snippet,func_snippet),
+                        Applicability::MachineApplicable,
+                    );
+                }
 
-        if is_option {
-            let self_snippet = snippet(cx, recv.span, "..");
-            let func_snippet = snippet(cx, map_arg.span, "..");
-            let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
-                       `and_then(..)` instead";
-            (
-                OPTION_MAP_OR_NONE,
-                msg,
-                "try using `and_then` instead",
-                format!("{0}.and_then({1})", self_snippet, func_snippet),
-            )
-        } else if f_arg_is_some {
-            let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \
-                       `ok()` instead";
-            let self_snippet = snippet(cx, recv.span, "..");
-            (
-                RESULT_MAP_OR_INTO_OPTION,
-                msg,
-                "try using `ok` instead",
-                format!("{0}.ok()", self_snippet),
-            )
-        } else {
-            // nothing to lint!
-            return;
         }
-    };
 
-    span_lint_and_sugg(
-        cx,
-        lint_name,
-        expr.span,
-        msg,
-        instead,
-        hint,
-        Applicability::MachineApplicable,
-    );
+        let func_snippet = snippet(cx, map_arg.span, "..");
+        let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
+                       `and_then(..)` instead";
+        return span_lint_and_sugg(
+            cx,
+            OPTION_MAP_OR_NONE,
+            expr.span,
+            msg,
+            "try using `and_then` instead",
+            format!("{0}.and_then({1})", self_snippet, func_snippet),
+            Applicability::MachineApplicable,
+        );
+    } else if f_arg_is_some {
+        let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \
+                       `ok()` instead";
+        let self_snippet = snippet(cx, recv.span, "..");
+        return span_lint_and_sugg(
+            cx,
+            RESULT_MAP_OR_INTO_OPTION,
+            expr.span,
+            msg,
+            "try using `ok` instead",
+            format!("{0}.ok()", self_snippet),
+            Applicability::MachineApplicable,
+        );
+    }
 }
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 fe9ffde0d33..4e4653dadca 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
@@ -1,16 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::eager_or_lazy::is_lazyness_candidate;
-use clippy_utils::is_trait_item;
+use clippy_utils::eager_or_lazy::switch_to_lazy_eval;
 use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_macro_callsite};
-use clippy_utils::ty::implements_trait;
-use clippy_utils::ty::{is_type_diagnostic_item, match_type};
-use clippy_utils::{contains_return, last_path_segment, paths};
+use clippy_utils::ty::{implements_trait, match_type};
+use clippy_utils::{contains_return, is_trait_item, last_path_segment, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
-use rustc_hir::{BlockCheckMode, UnsafeSource};
 use rustc_lint::LateContext;
-use rustc_middle::ty;
 use rustc_span::source_map::Span;
 use rustc_span::symbol::{kw, sym};
 use std::borrow::Cow;
@@ -96,25 +92,10 @@ pub(super) fn check<'tcx>(
             (&paths::RESULT, true, &["or", "unwrap_or"], "else"),
         ];
 
-        if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &arg.kind {
-            if path.ident.name == sym::len {
-                let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
-
-                match ty.kind() {
-                    ty::Slice(_) | ty::Array(_, _) | ty::Str => return,
-                    _ => (),
-                }
-
-                if is_type_diagnostic_item(cx, ty, sym::Vec) {
-                    return;
-                }
-            }
-        }
-
         if_chain! {
             if KNOW_TYPES.iter().any(|k| k.2.contains(&name));
 
-            if is_lazyness_candidate(cx, arg);
+            if switch_to_lazy_eval(cx, arg);
             if !contains_return(arg);
 
             let self_ty = cx.typeck_results().expr_ty(self_expr);
@@ -166,26 +147,30 @@ pub(super) fn check<'tcx>(
         }
     }
 
-    if args.len() == 2 {
-        match args[1].kind {
+    if let [self_arg, arg] = args {
+        let inner_arg = if let hir::ExprKind::Block(
+            hir::Block {
+                stmts: [],
+                expr: Some(expr),
+                ..
+            },
+            _,
+        ) = arg.kind
+        {
+            expr
+        } else {
+            arg
+        };
+        match inner_arg.kind {
             hir::ExprKind::Call(fun, or_args) => {
                 let or_has_args = !or_args.is_empty();
-                if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) {
+                if !check_unwrap_or_default(cx, name, fun, self_arg, arg, or_has_args, expr.span) {
                     let fun_span = if or_has_args { None } else { Some(fun.span) };
-                    check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, fun_span);
+                    check_general_case(cx, name, method_span, self_arg, arg, expr.span, fun_span);
                 }
             },
             hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => {
-                check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None);
-            },
-            hir::ExprKind::Block(block, _)
-                if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) =>
-            {
-                if let Some(block_expr) = block.expr {
-                    if let hir::ExprKind::MethodCall(..) = block_expr.kind {
-                        check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None);
-                    }
-                }
+                check_general_case(cx, name, method_span, self_arg, arg, expr.span, None);
             },
             _ => (),
         }
diff --git a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs
index 0f2e58d8983..5ed4ba94884 100644
--- a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs
@@ -1,5 +1,6 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
 use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::sugg::deref_closure_args;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{is_trait_method, strip_pat_refs};
 use if_chain::if_chain;
@@ -37,6 +38,7 @@ pub(super) fn check<'tcx>(
         if search_snippet.lines().count() <= 1 {
             // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()`
             // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()`
+            let mut applicability = Applicability::MachineApplicable;
             let any_search_snippet = if_chain! {
                 if search_method == "find";
                 if let hir::ExprKind::Closure(_, _, body_id, ..) = search_arg.kind;
@@ -45,9 +47,15 @@ pub(super) fn check<'tcx>(
                 then {
                     if let hir::PatKind::Ref(..) = closure_arg.pat.kind {
                         Some(search_snippet.replacen('&', "", 1))
-                    } else if let PatKind::Binding(_, _, ident, _) = strip_pat_refs(closure_arg.pat).kind {
-                        let name = &*ident.name.as_str();
-                        Some(search_snippet.replace(&format!("*{}", name), name))
+                    } else if let PatKind::Binding(..) = strip_pat_refs(closure_arg.pat).kind {
+                        // `find()` provides a reference to the item, but `any` does not,
+                        // so we should fix item usages for suggestion
+                        if let Some(closure_sugg) = deref_closure_args(cx, search_arg) {
+                            applicability = closure_sugg.applicability;
+                            Some(closure_sugg.suggestion)
+                        } else {
+                            Some(search_snippet.to_string())
+                        }
                     } else {
                         None
                     }
@@ -67,7 +75,7 @@ pub(super) fn check<'tcx>(
                         "any({})",
                         any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
                     ),
-                    Applicability::MachineApplicable,
+                    applicability,
                 );
             } else {
                 let iter = snippet(cx, search_recv.span, "..");
@@ -82,7 +90,7 @@ pub(super) fn check<'tcx>(
                         iter,
                         any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
                     ),
-                    Applicability::MachineApplicable,
+                    applicability,
                 );
             }
         } else {
diff --git a/src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs b/src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs
index d313a3db479..bf9006c6906 100644
--- a/src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/single_char_pattern.rs
@@ -9,18 +9,21 @@ use rustc_span::symbol::Symbol;
 
 use super::SINGLE_CHAR_PATTERN;
 
-const PATTERN_METHODS: [(&str, usize); 19] = [
+const PATTERN_METHODS: [(&str, usize); 24] = [
     ("contains", 1),
     ("starts_with", 1),
     ("ends_with", 1),
     ("find", 1),
     ("rfind", 1),
     ("split", 1),
+    ("split_inclusive", 1),
     ("rsplit", 1),
     ("split_terminator", 1),
     ("rsplit_terminator", 1),
     ("splitn", 2),
     ("rsplitn", 2),
+    ("split_once", 1),
+    ("rsplit_once", 1),
     ("matches", 1),
     ("rmatches", 1),
     ("match_indices", 1),
@@ -29,6 +32,8 @@ const PATTERN_METHODS: [(&str, usize); 19] = [
     ("strip_suffix", 1),
     ("trim_start_matches", 1),
     ("trim_end_matches", 1),
+    ("replace", 1),
+    ("replacen", 1),
 ];
 
 /// lint for length-1 `str`s for methods in `PATTERN_METHODS`
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_split_once.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
index 13eb72251bb..2595f734f11 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_split_once.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
@@ -11,7 +11,13 @@ 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<'_>) {
+pub(super) fn check_manual_split_once(
+    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;
     }
@@ -36,7 +42,7 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se
             format!("{}.{}({})", self_snip, method_name, pat_snip)
         },
         IterUsageKind::RNextTuple => format!("{}.{}({}).map(|(x, y)| (y, x))", self_snip, method_name, pat_snip),
-        IterUsageKind::Next => {
+        IterUsageKind::Next | IterUsageKind::Second => {
             let self_deref = {
                 let adjust = cx.typeck_results().expr_adjustments(self_arg);
                 if adjust.is_empty() {
@@ -51,26 +57,49 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se
                     "*".repeat(adjust.len() - 2)
                 }
             };
-            if usage.unwrap_kind.is_some() {
-                format!(
-                    "{}.{}({}).map_or({}{}, |x| x.0)",
-                    &self_snip, method_name, pat_snip, self_deref, &self_snip
-                )
+            if matches!(usage.kind, IterUsageKind::Next) {
+                match usage.unwrap_kind {
+                    Some(UnwrapKind::Unwrap) => {
+                        if reverse {
+                            format!("{}.{}({}).unwrap().0", self_snip, method_name, pat_snip)
+                        } else {
+                            format!(
+                                "{}.{}({}).map_or({}{}, |x| x.0)",
+                                self_snip, method_name, pat_snip, self_deref, &self_snip
+                            )
+                        }
+                    },
+                    Some(UnwrapKind::QuestionMark) => {
+                        format!(
+                            "{}.{}({}).map_or({}{}, |x| x.0)",
+                            self_snip, method_name, pat_snip, self_deref, &self_snip
+                        )
+                    },
+                    None => {
+                        format!(
+                            "Some({}.{}({}).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
-                )
+                match usage.unwrap_kind {
+                    Some(UnwrapKind::Unwrap) => {
+                        if reverse {
+                            // In this case, no better suggestion is offered.
+                            return;
+                        }
+                        format!("{}.{}({}).unwrap().1", self_snip, method_name, pat_snip)
+                    },
+                    Some(UnwrapKind::QuestionMark) => {
+                        format!("{}.{}({})?.1", self_snip, method_name, pat_snip)
+                    },
+                    None => {
+                        format!("{}.{}({}).map(|x| x.1)", self_snip, method_name, pat_snip)
+                    },
+                }
             }
         },
-        IterUsageKind::Second => {
-            let access_str = match usage.unwrap_kind {
-                Some(UnwrapKind::Unwrap) => ".unwrap().1",
-                Some(UnwrapKind::QuestionMark) => "?.1",
-                None => ".map(|x| x.1)",
-            };
-            format!("{}.{}({}){}", self_snip, method_name, pat_snip, access_str)
-        },
     };
 
     span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
@@ -209,3 +238,86 @@ fn parse_iter_usage(
         span,
     })
 }
+
+use super::NEEDLESS_SPLITN;
+
+pub(super) fn check_needless_splitn(
+    cx: &LateContext<'_>,
+    method_name: &str,
+    expr: &Expr<'_>,
+    self_arg: &Expr<'_>,
+    pat_arg: &Expr<'_>,
+    count: u128,
+) {
+    if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
+        return;
+    }
+    let ctxt = expr.span.ctxt();
+    let mut app = Applicability::MachineApplicable;
+    let (reverse, message) = if method_name == "splitn" {
+        (false, "unnecessary use of `splitn`")
+    } else {
+        (true, "unnecessary use of `rsplitn`")
+    };
+    if_chain! {
+        if count >= 2;
+        if check_iter(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), count);
+        then {
+            span_lint_and_sugg(
+                cx,
+                NEEDLESS_SPLITN,
+                expr.span,
+                message,
+                "try this",
+                format!(
+                    "{}.{}({})",
+                    snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0,
+                    if reverse {"rsplit"} else {"split"},
+                    snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0
+                ),
+                app,
+            );
+        }
+    }
+}
+
+fn check_iter(
+    cx: &LateContext<'tcx>,
+    ctxt: SyntaxContext,
+    mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
+    count: u128,
+) -> bool {
+    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 false;
+            };
+            if_chain! {
+                if let Some(did) = cx.typeck_results().type_dependent_def_id(e.hir_id);
+                if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
+                then {
+                    match (&*name.ident.as_str(), args) {
+                        ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
+                            return true;
+                        },
+                        ("next_tuple", []) if count > 2 => {
+                            return true;
+                        },
+                        ("nth", [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) {
+                                if count > idx + 1 {
+                                    return true;
+                                }
+                            }
+                        },
+                        _ =>  return false,
+                    }
+                }
+            }
+        },
+        _ => return false,
+    };
+    false
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
index 740af750b48..1e2765263c8 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
@@ -30,7 +30,7 @@ pub(super) fn check<'tcx>(
                 return;
             }
 
-            if eager_or_lazy::is_eagerness_candidate(cx, body_expr) {
+            if eager_or_lazy::switch_to_eager_eval(cx, body_expr) {
                 let msg = if is_option {
                     "unnecessary closure used to substitute value for `Option::None`"
                 } else {
diff --git a/src/tools/clippy/clippy_lints/src/methods/utils.rs b/src/tools/clippy/clippy_lints/src/methods/utils.rs
index ba2ce73a116..11ad881ee7b 100644
--- a/src/tools/clippy/clippy_lints/src/methods/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/utils.rs
@@ -66,7 +66,13 @@ pub(super) fn get_hint_if_single_char_arg(
                 // for regular string: "a"
                 &snip[1..(snip.len() - 1)]
             };
-            let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch });
+
+            let hint = format!("'{}'", match ch {
+                "'" => "\\'" ,
+                r"\" => "\\\\",
+                _ => ch,
+            });
+
             Some(hint)
         } else {
             None
diff --git a/src/tools/clippy/clippy_lints/src/minmax.rs b/src/tools/clippy/clippy_lints/src/minmax.rs
index dc2dd45e4ed..a6450aec4f7 100644
--- a/src/tools/clippy/clippy_lints/src/minmax.rs
+++ b/src/tools/clippy/clippy_lints/src/minmax.rs
@@ -26,6 +26,7 @@ declare_clippy_lint! {
     /// ```
     /// It will always be equal to `0`. Probably the author meant to clamp the value
     /// between 0 and 100, but has erroneously swapped `min` and `max`.
+    #[clippy::version = "pre 1.29.0"]
     pub MIN_MAX,
     correctness,
     "`min(_, max(_, _))` (or vice versa) with bounds clamping the result to a constant"
diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs
index 78183add9cc..2299a099910 100644
--- a/src/tools/clippy/clippy_lints/src/misc.rs
+++ b/src/tools/clippy/clippy_lints/src/misc.rs
@@ -56,6 +56,7 @@ declare_clippy_lint! {
     ///     true
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TOPLEVEL_REF_ARG,
     style,
     "an entire binding declared as `ref`, in a function argument or a `let` statement"
@@ -79,6 +80,7 @@ declare_clippy_lint! {
     /// // Good
     /// if x.is_nan() { }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CMP_NAN,
     correctness,
     "comparisons to `NAN`, which will always return false, probably not intended"
@@ -112,6 +114,7 @@ declare_clippy_lint! {
     /// if (y - 1.23f64).abs() < error_margin { }
     /// if (y - x).abs() > error_margin { }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FLOAT_CMP,
     pedantic,
     "using `==` or `!=` on float values instead of comparing difference with an epsilon"
@@ -139,6 +142,7 @@ declare_clippy_lint! {
     /// # let y = String::from("foo");
     /// if x == y {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CMP_OWNED,
     perf,
     "creating owned instances for comparing with others, e.g., `x == \"foo\".to_string()`"
@@ -162,6 +166,7 @@ declare_clippy_lint! {
     /// let a = x % 1;
     /// let a = x % -1;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MODULO_ONE,
     correctness,
     "taking a number modulo +/-1, which can either panic/overflow or always returns 0"
@@ -187,6 +192,7 @@ declare_clippy_lint! {
     /// let y = _x + 1; // Here we are using `_x`, even though it has a leading
     ///                 // underscore. We should rename `_x` to `x`
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USED_UNDERSCORE_BINDING,
     pedantic,
     "using a binding which is prefixed with an underscore"
@@ -207,6 +213,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// f() && g(); // We should write `if f() { g(); }`.
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SHORT_CIRCUIT_STATEMENT,
     complexity,
     "using a short circuit boolean condition as a statement"
@@ -228,6 +235,7 @@ declare_clippy_lint! {
     /// // Good
     /// let a = std::ptr::null::<u32>();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ZERO_PTR,
     style,
     "using `0 as *{const, mut} T`"
@@ -259,6 +267,7 @@ declare_clippy_lint! {
     /// // let error_margin = std::f64::EPSILON;
     /// if (x - ONE).abs() < error_margin { }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub FLOAT_CMP_CONST,
     restriction,
     "using `==` or `!=` on float constants instead of comparing difference with an epsilon"
diff --git a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs
index 7c3f5f22ade..6e09e25109f 100644
--- a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs
@@ -46,6 +46,7 @@ declare_clippy_lint! {
     ///     Foo { .. } => {},
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNNEEDED_FIELD_PATTERN,
     restriction,
     "struct fields bound to a wildcard instead of using `..`"
@@ -67,6 +68,7 @@ declare_clippy_lint! {
     /// // Good
     /// fn bar(a: i32, _b: i32) {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DUPLICATE_UNDERSCORE_ARGUMENT,
     style,
     "function arguments having names which only differ by an underscore"
@@ -85,6 +87,7 @@ declare_clippy_lint! {
     /// let mut x = 3;
     /// --x;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DOUBLE_NEG,
     style,
     "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++"
@@ -106,6 +109,7 @@ declare_clippy_lint! {
     /// // Good
     /// let y = 0x1A9BACD;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MIXED_CASE_HEX_LITERALS,
     style,
     "hex literals whose letter digits are not consistently upper- or lowercased"
@@ -129,6 +133,7 @@ declare_clippy_lint! {
     /// // Good
     /// let y = 123832_i32;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNSEPARATED_LITERAL_SUFFIX,
     restriction,
     "literals whose suffix is not separated by an underscore"
@@ -151,6 +156,7 @@ declare_clippy_lint! {
     /// // Good
     /// let y = 123832i32;
     /// ```
+    #[clippy::version = "1.58.0"]
     pub SEPARATED_LITERAL_SUFFIX,
     restriction,
     "literals whose suffix is separated by an underscore"
@@ -189,6 +195,7 @@ declare_clippy_lint! {
     /// ```
     ///
     /// prints `83` (as `83 == 0o123` while `123 == 0o173`).
+    #[clippy::version = "pre 1.29.0"]
     pub ZERO_PREFIXED_LITERAL,
     complexity,
     "integer literals starting with `0`"
@@ -210,6 +217,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub BUILTIN_TYPE_SHADOW,
     style,
     "shadowing a builtin type"
@@ -239,6 +247,7 @@ declare_clippy_lint! {
     ///     y => (),
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub REDUNDANT_PATTERN,
     style,
     "using `name @ _` in a pattern"
@@ -273,6 +282,7 @@ declare_clippy_lint! {
     ///     _ => (),
     /// }
     /// ```
+    #[clippy::version = "1.40.0"]
     pub UNNEEDED_WILDCARD_PATTERN,
     complexity,
     "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)"
diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
index 5b2584d43a1..a8d41050856 100644
--- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
@@ -63,6 +63,7 @@ declare_clippy_lint! {
     /// }
     /// # }
     /// ```
+    #[clippy::version = "1.34.0"]
     pub MISSING_CONST_FOR_FN,
     nursery,
     "Lint functions definitions that could be made `const fn`"
diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs
index 564f021268c..fc0483a929a 100644
--- a/src/tools/clippy/clippy_lints/src/missing_doc.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs
@@ -26,6 +26,7 @@ declare_clippy_lint! {
     /// allowed-by-default lint for
     /// public members, but has no way to enforce documentation of private items.
     /// This lint fixes that.
+    #[clippy::version = "pre 1.29.0"]
     pub MISSING_DOCS_IN_PRIVATE_ITEMS,
     restriction,
     "detects missing documentation for public and private members"
diff --git a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs
index 448bfc2fdd6..68d49e0f150 100644
--- a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs
@@ -33,6 +33,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// use serde_json::Value as JsonValue;
     /// ```
+    #[clippy::version = "1.55.0"]
     pub MISSING_ENFORCED_IMPORT_RENAMES,
     restriction,
     "enforce import renames"
diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs
index b593c747498..ac2f16b49e3 100644
--- a/src/tools/clippy/clippy_lints/src/missing_inline.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs
@@ -52,6 +52,7 @@ declare_clippy_lint! {
     ///    fn def_bar() {} // missing #[inline]
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MISSING_INLINE_IN_PUBLIC_ITEMS,
     restriction,
     "detects missing `#[inline]` attribute for public callables (functions, trait methods, methods...)"
diff --git a/src/tools/clippy/clippy_lints/src/module_style.rs b/src/tools/clippy/clippy_lints/src/module_style.rs
index d41b5474564..3b65f80cba2 100644
--- a/src/tools/clippy/clippy_lints/src/module_style.rs
+++ b/src/tools/clippy/clippy_lints/src/module_style.rs
@@ -32,6 +32,7 @@ declare_clippy_lint! {
     ///   stuff.rs
     ///   lib.rs
     /// ```
+    #[clippy::version = "1.57.0"]
     pub MOD_MODULE_FILES,
     restriction,
     "checks that module layout is consistent"
@@ -61,6 +62,7 @@ declare_clippy_lint! {
     ///   lib.rs
     /// ```
 
+    #[clippy::version = "1.57.0"]
     pub SELF_NAMED_MODULE_FILES,
     restriction,
     "checks that module layout is consistent"
diff --git a/src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs b/src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs
index f45e68233a1..d182a7d5249 100644
--- a/src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs
+++ b/src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs
@@ -24,6 +24,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let x = -17 % 3;
     /// ```
+    #[clippy::version = "1.42.0"]
     pub MODULO_ARITHMETIC,
     restriction,
     "any modulo arithmetic statement"
diff --git a/src/tools/clippy/clippy_lints/src/multiple_crate_versions.rs b/src/tools/clippy/clippy_lints/src/multiple_crate_versions.rs
index 816b2f275fb..e45cc86d417 100644
--- a/src/tools/clippy/clippy_lints/src/multiple_crate_versions.rs
+++ b/src/tools/clippy/clippy_lints/src/multiple_crate_versions.rs
@@ -33,6 +33,7 @@ declare_clippy_lint! {
     /// ctrlc = "=3.1.0"
     /// ansi_term = "=0.11.0"
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MULTIPLE_CRATE_VERSIONS,
     cargo,
     "multiple versions of the same crate being used"
diff --git a/src/tools/clippy/clippy_lints/src/mut_key.rs b/src/tools/clippy/clippy_lints/src/mut_key.rs
index 8476257f086..5fe887a4573 100644
--- a/src/tools/clippy/clippy_lints/src/mut_key.rs
+++ b/src/tools/clippy/clippy_lints/src/mut_key.rs
@@ -72,6 +72,7 @@ declare_clippy_lint! {
     ///     let _: HashSet<Bad> = HashSet::new();
     /// }
     /// ```
+    #[clippy::version = "1.42.0"]
     pub MUTABLE_KEY_TYPE,
     suspicious,
     "Check for mutable `Map`/`Set` key type"
diff --git a/src/tools/clippy/clippy_lints/src/mut_mut.rs b/src/tools/clippy/clippy_lints/src/mut_mut.rs
index 7c4cac29ba8..bcbea8f1e66 100644
--- a/src/tools/clippy/clippy_lints/src/mut_mut.rs
+++ b/src/tools/clippy/clippy_lints/src/mut_mut.rs
@@ -22,6 +22,7 @@ declare_clippy_lint! {
     /// # let mut y = 1;
     /// let x = &mut &mut y;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MUT_MUT,
     pedantic,
     "usage of double-mut refs, e.g., `&mut &mut ...`"
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 b96fa4774cb..b1e6308d2e1 100644
--- a/src/tools/clippy/clippy_lints/src/mut_mutex_lock.rs
+++ b/src/tools/clippy/clippy_lints/src/mut_mutex_lock.rs
@@ -38,6 +38,7 @@ declare_clippy_lint! {
     /// let value = value_mutex.get_mut().unwrap();
     /// *value += 1;
     /// ```
+    #[clippy::version = "1.49.0"]
     pub MUT_MUTEX_LOCK,
     style,
     "`&mut Mutex::lock` does unnecessary locking"
diff --git a/src/tools/clippy/clippy_lints/src/mut_reference.rs b/src/tools/clippy/clippy_lints/src/mut_reference.rs
index 8d5d7951fc5..63a1cf7b7d5 100644
--- a/src/tools/clippy/clippy_lints/src/mut_reference.rs
+++ b/src/tools/clippy/clippy_lints/src/mut_reference.rs
@@ -23,6 +23,7 @@ declare_clippy_lint! {
     /// // Good
     /// my_vec.push(&value)
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNNECESSARY_MUT_PASSED,
     style,
     "an argument passed as a mutable reference although the callee only demands an immutable reference"
diff --git a/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs b/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs
index 9b44cf9d43a..12e219cd5c8 100644
--- a/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs
+++ b/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs
@@ -26,6 +26,7 @@ declare_clippy_lint! {
     /// fn take_a_mut_parameter(_: &mut u32) -> bool { unimplemented!() }
     /// debug_assert!(take_a_mut_parameter(&mut 5));
     /// ```
+    #[clippy::version = "1.40.0"]
     pub DEBUG_ASSERT_WITH_MUT_CALL,
     nursery,
     "mutable arguments in `debug_assert{,_ne,_eq}!`"
diff --git a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs
index 5feddcbfc61..816377fe65e 100644
--- a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs
+++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     /// # use std::sync::atomic::AtomicBool;
     /// let x = AtomicBool::new(y);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MUTEX_ATOMIC,
     perf,
     "using a mutex where an atomic value could be used instead"
@@ -64,6 +65,7 @@ declare_clippy_lint! {
     /// # use std::sync::atomic::AtomicUsize;
     /// let x = AtomicUsize::new(0usize);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MUTEX_INTEGER,
     nursery,
     "using a mutex for an integer type"
diff --git a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs
index 9a3d9383cd9..9838d3cad9f 100644
--- a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
 use if_chain::if_chain;
 use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind};
 use rustc_errors::Applicability;
@@ -53,6 +52,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.47.0"]
     pub NEEDLESS_ARBITRARY_SELF_TYPE,
     complexity,
     "type of `self` parameter is already by default `Self`"
@@ -78,7 +78,7 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod
             let self_param = match (binding_mode, mutbl) {
                 (Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(),
                 (Mode::Ref(Some(lifetime)), Mutability::Mut) => {
-                    if in_macro(lifetime.ident.span) {
+                    if lifetime.ident.span.from_expansion() {
                         applicability = Applicability::HasPlaceholders;
                         "&'_ mut self".to_string()
                     } else {
@@ -87,7 +87,7 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod
                 },
                 (Mode::Ref(None), Mutability::Not) => "&self".to_string(),
                 (Mode::Ref(Some(lifetime)), Mutability::Not) => {
-                    if in_macro(lifetime.ident.span) {
+                    if lifetime.ident.span.from_expansion() {
                         applicability = Applicability::HasPlaceholders;
                         "&'_ self".to_string()
                     } else {
@@ -114,7 +114,7 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod
 impl EarlyLintPass for NeedlessArbitrarySelfType {
     fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) {
         // Bail out if the parameter it's not a receiver or was not written by the user
-        if !p.is_self() || in_macro(p.span) {
+        if !p.is_self() || p.span.from_expansion() {
             return;
         }
 
diff --git a/src/tools/clippy/clippy_lints/src/needless_bitwise_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bitwise_bool.rs
index 203da29cb91..a8a8d174a82 100644
--- a/src/tools/clippy/clippy_lints/src/needless_bitwise_bool.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_bitwise_bool.rs
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::in_macro;
 use clippy_utils::source::snippet_opt;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -31,6 +30,7 @@ declare_clippy_lint! {
     /// let (x,y) = (true, false);
     /// if x && !y {}
     /// ```
+    #[clippy::version = "1.54.0"]
     pub NEEDLESS_BITWISE_BOOL,
     pedantic,
     "Boolean expressions that use bitwise rather than lazy operators"
@@ -41,7 +41,7 @@ declare_lint_pass!(NeedlessBitwiseBool => [NEEDLESS_BITWISE_BOOL]);
 fn is_bitwise_operation(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     let ty = cx.typeck_results().expr_ty(expr);
     if_chain! {
-        if !in_macro(expr.span);
+        if !expr.span.from_expansion();
         if let (&ExprKind::Binary(ref op, _, right), &ty::Bool) = (&expr.kind, &ty.kind());
         if op.node == BinOpKind::BitAnd || op.node == BinOpKind::BitOr;
         if let ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Binary(..) | ExprKind::Unary(..) = right.kind;
diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs
index 91944653500..3709f3948c2 100644
--- a/src/tools/clippy/clippy_lints/src/needless_bool.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs
@@ -41,6 +41,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// !x
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_BOOL,
     complexity,
     "if-statements with plain booleans in the then- and else-clause, e.g., `if p { true } else { false }`"
@@ -65,6 +66,7 @@ declare_clippy_lint! {
     /// if x {}
     /// if !y {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub BOOL_COMPARISON,
     complexity,
     "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`"
diff --git a/src/tools/clippy/clippy_lints/src/needless_borrow.rs b/src/tools/clippy/clippy_lints/src/needless_borrow.rs
deleted file mode 100644
index f1be90c44f9..00000000000
--- a/src/tools/clippy/clippy_lints/src/needless_borrow.rs
+++ /dev/null
@@ -1,282 +0,0 @@
-//! Checks for needless address of operations (`&`)
-//!
-//! This lint is **warn** by default
-
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::{snippet_opt, snippet_with_applicability, snippet_with_context};
-use clippy_utils::{get_parent_expr, in_macro, path_to_local};
-use if_chain::if_chain;
-use rustc_ast::util::parser::PREC_POSTFIX;
-use rustc_data_structures::fx::FxIndexMap;
-use rustc_errors::Applicability;
-use rustc_hir::{BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, PatKind, UnOp};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
-use rustc_middle::ty::adjustment::{Adjust, Adjustment};
-use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::Span;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for address of operations (`&`) that are going to
-    /// be dereferenced immediately by the compiler.
-    ///
-    /// ### Why is this bad?
-    /// Suggests that the receiver of the expression borrows
-    /// the expression.
-    ///
-    /// ### Example
-    /// ```rust
-    /// fn fun(_a: &i32) {}
-    ///
-    /// // Bad
-    /// let x: &i32 = &&&&&&5;
-    /// fun(&x);
-    ///
-    /// // Good
-    /// let x: &i32 = &5;
-    /// fun(x);
-    /// ```
-    pub NEEDLESS_BORROW,
-    style,
-    "taking a reference that is going to be automatically dereferenced"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for `ref` bindings which create a reference to a reference.
-    ///
-    /// ### Why is this bad?
-    /// The address-of operator at the use site is clearer about the need for a reference.
-    ///
-    /// ### Example
-    /// ```rust
-    /// // Bad
-    /// let x = Some("");
-    /// if let Some(ref x) = x {
-    ///     // use `x` here
-    /// }
-    ///
-    /// // Good
-    /// let x = Some("");
-    /// if let Some(x) = x {
-    ///     // use `&x` here
-    /// }
-    /// ```
-    pub REF_BINDING_TO_REFERENCE,
-    pedantic,
-    "`ref` binding to a reference"
-}
-
-impl_lint_pass!(NeedlessBorrow => [NEEDLESS_BORROW, REF_BINDING_TO_REFERENCE]);
-#[derive(Default)]
-pub struct NeedlessBorrow {
-    /// The body the first local was found in. Used to emit lints when the traversal of the body has
-    /// been finished. Note we can't lint at the end of every body as they can be nested within each
-    /// other.
-    current_body: Option<BodyId>,
-    /// The list of locals currently being checked by the lint.
-    /// If the value is `None`, then the binding has been seen as a ref pattern, but is not linted.
-    /// This is needed for or patterns where one of the branches can be linted, but another can not
-    /// be.
-    ///
-    /// e.g. `m!(x) | Foo::Bar(ref x)`
-    ref_locals: FxIndexMap<HirId, Option<RefPat>>,
-}
-
-struct RefPat {
-    /// Whether every usage of the binding is dereferenced.
-    always_deref: bool,
-    /// The spans of all the ref bindings for this local.
-    spans: Vec<Span>,
-    /// The applicability of this suggestion.
-    app: Applicability,
-    /// All the replacements which need to be made.
-    replacements: Vec<(Span, String)>,
-}
-
-impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-        if let Some(local) = path_to_local(e) {
-            self.check_local_usage(cx, e, local);
-        }
-
-        if e.span.from_expansion() {
-            return;
-        }
-        if let ExprKind::AddrOf(BorrowKind::Ref, mutability, inner) = e.kind {
-            if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(inner).kind() {
-                for adj3 in cx.typeck_results().expr_adjustments(e).windows(3) {
-                    if let [
-                        Adjustment {
-                            kind: Adjust::Deref(_), ..
-                        },
-                        Adjustment {
-                            kind: Adjust::Deref(_), ..
-                        },
-                        Adjustment {
-                            kind: Adjust::Borrow(_),
-                            ..
-                        },
-                    ] = *adj3
-                    {
-                        let help_msg_ty = if matches!(mutability, Mutability::Not) {
-                            format!("&{}", ty)
-                        } else {
-                            format!("&mut {}", ty)
-                        };
-
-                        span_lint_and_then(
-                            cx,
-                            NEEDLESS_BORROW,
-                            e.span,
-                            &format!(
-                                "this expression borrows a reference (`{}`) that is immediately dereferenced \
-                             by the compiler",
-                                help_msg_ty
-                            ),
-                            |diag| {
-                                if let Some(snippet) = snippet_opt(cx, inner.span) {
-                                    diag.span_suggestion(
-                                        e.span,
-                                        "change this to",
-                                        snippet,
-                                        Applicability::MachineApplicable,
-                                    );
-                                }
-                            },
-                        );
-                    }
-                }
-            }
-        }
-    }
-
-    fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
-        if let PatKind::Binding(BindingAnnotation::Ref, id, name, _) = pat.kind {
-            if let Some(opt_prev_pat) = self.ref_locals.get_mut(&id) {
-                // This binding id has been seen before. Add this pattern to the list of changes.
-                if let Some(prev_pat) = opt_prev_pat {
-                    if in_macro(pat.span) {
-                        // Doesn't match the context of the previous pattern. Can't lint here.
-                        *opt_prev_pat = None;
-                    } else {
-                        prev_pat.spans.push(pat.span);
-                        prev_pat.replacements.push((
-                            pat.span,
-                            snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut prev_pat.app)
-                                .0
-                                .into(),
-                        ));
-                    }
-                }
-                return;
-            }
-
-            if_chain! {
-                if !in_macro(pat.span);
-                if let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind();
-                // only lint immutable refs, because borrowed `&mut T` cannot be moved out
-                if let ty::Ref(_, _, Mutability::Not) = *tam.kind();
-                then {
-                    let mut app = Applicability::MachineApplicable;
-                    let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0;
-                    self.current_body = self.current_body.or(cx.enclosing_body);
-                    self.ref_locals.insert(
-                        id,
-                        Some(RefPat {
-                            always_deref: true,
-                            spans: vec![pat.span],
-                            app,
-                            replacements: vec![(pat.span, snip.into())],
-                        }),
-                    );
-                }
-            }
-        }
-    }
-
-    fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
-        if Some(body.id()) == self.current_body {
-            for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) {
-                let replacements = pat.replacements;
-                let app = pat.app;
-                span_lint_and_then(
-                    cx,
-                    if pat.always_deref {
-                        NEEDLESS_BORROW
-                    } else {
-                        REF_BINDING_TO_REFERENCE
-                    },
-                    pat.spans,
-                    "this pattern creates a reference to a reference",
-                    |diag| {
-                        diag.multipart_suggestion("try this", replacements, app);
-                    },
-                );
-            }
-            self.current_body = None;
-        }
-    }
-}
-impl NeedlessBorrow {
-    fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, local: HirId) {
-        if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
-            if let Some(pat) = outer_pat {
-                // Check for auto-deref
-                if !matches!(
-                    cx.typeck_results().expr_adjustments(e),
-                    [
-                        Adjustment {
-                            kind: Adjust::Deref(_),
-                            ..
-                        },
-                        Adjustment {
-                            kind: Adjust::Deref(_),
-                            ..
-                        },
-                        ..
-                    ]
-                ) {
-                    match get_parent_expr(cx, e) {
-                        // Field accesses are the same no matter the number of references.
-                        Some(Expr {
-                            kind: ExprKind::Field(..),
-                            ..
-                        }) => (),
-                        Some(&Expr {
-                            span,
-                            kind: ExprKind::Unary(UnOp::Deref, _),
-                            ..
-                        }) if !in_macro(span) => {
-                            // Remove explicit deref.
-                            let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0;
-                            pat.replacements.push((span, snip.into()));
-                        },
-                        Some(parent) if !in_macro(parent.span) => {
-                            // Double reference might be needed at this point.
-                            if parent.precedence().order() == PREC_POSTFIX {
-                                // Parentheses would be needed here, don't lint.
-                                *outer_pat = None;
-                            } else {
-                                pat.always_deref = false;
-                                let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
-                                pat.replacements.push((e.span, format!("&{}", snip)));
-                            }
-                        },
-                        _ if !in_macro(e.span) => {
-                            // Double reference might be needed at this point.
-                            pat.always_deref = false;
-                            let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
-                            pat.replacements.push((e.span, format!("&{}", snip)));
-                        },
-                        // Edge case for macros. The span of the identifier will usually match the context of the
-                        // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
-                        // macros
-                        _ => *outer_pat = None,
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs b/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs
index 36879eda7c0..0fcc419e722 100644
--- a/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs
@@ -38,6 +38,7 @@ declare_clippy_lint! {
     /// let mut v = Vec::<String>::new();
     /// let _ = v.iter_mut().filter(|a| a.is_empty());
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_BORROWED_REFERENCE,
     complexity,
     "destructuring a reference and borrowing the inner value"
diff --git a/src/tools/clippy/clippy_lints/src/needless_continue.rs b/src/tools/clippy/clippy_lints/src/needless_continue.rs
index 7aa93ed7839..98a3bce1ff3 100644
--- a/src/tools/clippy/clippy_lints/src/needless_continue.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_continue.rs
@@ -110,6 +110,7 @@ declare_clippy_lint! {
     ///     # break;
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_CONTINUE,
     pedantic,
     "`continue` statements that can be replaced by a rearrangement of code"
diff --git a/src/tools/clippy/clippy_lints/src/needless_for_each.rs b/src/tools/clippy/clippy_lints/src/needless_for_each.rs
index 9a6ddc72ce5..0c1da035173 100644
--- a/src/tools/clippy/clippy_lints/src/needless_for_each.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_for_each.rs
@@ -40,6 +40,7 @@ declare_clippy_lint! {
     ///     println!("{}", elem);
     /// }
     /// ```
+    #[clippy::version = "1.53.0"]
     pub NEEDLESS_FOR_EACH,
     pedantic,
     "using `for_each` where a `for` loop would be simpler"
diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs
new file mode 100644
index 00000000000..e0522f3fe0b
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs
@@ -0,0 +1,349 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::path_to_local;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::visitors::{expr_visitor, is_local_used};
+use rustc_errors::Applicability;
+use rustc_hir::intravisit::Visitor;
+use rustc_hir::{Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for late initializations that can be replaced by a `let` statement
+    /// with an initializer.
+    ///
+    /// ### Why is this bad?
+    /// Assigning in the `let` statement is less repetitive.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let a;
+    /// a = 1;
+    ///
+    /// let b;
+    /// match 3 {
+    ///     0 => b = "zero",
+    ///     1 => b = "one",
+    ///     _ => b = "many",
+    /// }
+    ///
+    /// let c;
+    /// if true {
+    ///     c = 1;
+    /// } else {
+    ///     c = -1;
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let a = 1;
+    ///
+    /// let b = match 3 {
+    ///     0 => "zero",
+    ///     1 => "one",
+    ///     _ => "many",
+    /// };
+    ///
+    /// let c = if true {
+    ///     1
+    /// } else {
+    ///     -1
+    /// };
+    /// ```
+    #[clippy::version = "1.58.0"]
+    pub NEEDLESS_LATE_INIT,
+    style,
+    "late initializations that can be replaced by a `let` statement with an initializer"
+}
+declare_lint_pass!(NeedlessLateInit => [NEEDLESS_LATE_INIT]);
+
+fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> bool {
+    let mut seen = false;
+    expr_visitor(cx, |expr| {
+        if let ExprKind::Assign(..) = expr.kind {
+            seen = true;
+        }
+
+        !seen
+    })
+    .visit_stmt(stmt);
+
+    seen
+}
+
+#[derive(Debug)]
+struct LocalAssign {
+    lhs_id: HirId,
+    lhs_span: Span,
+    rhs_span: Span,
+    span: Span,
+}
+
+impl LocalAssign {
+    fn from_expr(expr: &Expr<'_>, span: Span) -> Option<Self> {
+        if let ExprKind::Assign(lhs, rhs, _) = expr.kind {
+            if lhs.span.from_expansion() {
+                return None;
+            }
+
+            Some(Self {
+                lhs_id: path_to_local(lhs)?,
+                lhs_span: lhs.span,
+                rhs_span: rhs.span.source_callsite(),
+                span,
+            })
+        } else {
+            None
+        }
+    }
+
+    fn new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, binding_id: HirId) -> Option<LocalAssign> {
+        let assign = match expr.kind {
+            ExprKind::Block(Block { expr: Some(expr), .. }, _) => Self::from_expr(expr, expr.span),
+            ExprKind::Block(block, _) => {
+                if_chain! {
+                    if let Some((last, other_stmts)) = block.stmts.split_last();
+                    if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = last.kind;
+
+                    let assign = Self::from_expr(expr, last.span)?;
+
+                    // avoid visiting if not needed
+                    if assign.lhs_id == binding_id;
+                    if other_stmts.iter().all(|stmt| !contains_assign_expr(cx, stmt));
+
+                    then {
+                        Some(assign)
+                    } else {
+                        None
+                    }
+                }
+            },
+            ExprKind::Assign(..) => Self::from_expr(expr, expr.span),
+            _ => None,
+        }?;
+
+        if assign.lhs_id == binding_id {
+            Some(assign)
+        } else {
+            None
+        }
+    }
+}
+
+fn assignment_suggestions<'tcx>(
+    cx: &LateContext<'tcx>,
+    binding_id: HirId,
+    exprs: impl IntoIterator<Item = &'tcx Expr<'tcx>>,
+) -> Option<(Applicability, Vec<(Span, String)>)> {
+    let mut assignments = Vec::new();
+
+    for expr in exprs {
+        let ty = cx.typeck_results().expr_ty(expr);
+
+        if ty.is_never() {
+            continue;
+        }
+        if !ty.is_unit() {
+            return None;
+        }
+
+        let assign = LocalAssign::new(cx, expr, binding_id)?;
+
+        assignments.push(assign);
+    }
+
+    let suggestions = assignments
+        .into_iter()
+        .map(|assignment| Some((assignment.span, snippet_opt(cx, assignment.rhs_span)?)))
+        .collect::<Option<Vec<(Span, String)>>>()?;
+
+    let applicability = if suggestions.len() > 1 {
+        // multiple suggestions don't work with rustfix in multipart_suggest
+        // https://github.com/rust-lang/rustfix/issues/141
+        Applicability::Unspecified
+    } else {
+        Applicability::MachineApplicable
+    };
+    Some((applicability, suggestions))
+}
+
+struct Usage<'tcx> {
+    stmt: &'tcx Stmt<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    needs_semi: bool,
+}
+
+fn first_usage<'tcx>(
+    cx: &LateContext<'tcx>,
+    binding_id: HirId,
+    local_stmt_id: HirId,
+    block: &'tcx Block<'tcx>,
+) -> Option<Usage<'tcx>> {
+    block
+        .stmts
+        .iter()
+        .skip_while(|stmt| stmt.hir_id != local_stmt_id)
+        .skip(1)
+        .find(|&stmt| is_local_used(cx, stmt, binding_id))
+        .and_then(|stmt| match stmt.kind {
+            StmtKind::Expr(expr) => Some(Usage {
+                stmt,
+                expr,
+                needs_semi: true,
+            }),
+            StmtKind::Semi(expr) => Some(Usage {
+                stmt,
+                expr,
+                needs_semi: false,
+            }),
+            _ => None,
+        })
+}
+
+fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &Local<'_>) -> Option<String> {
+    let span = local.span.with_hi(match local.ty {
+        // let <pat>: <ty>;
+        // ~~~~~~~~~~~~~~~
+        Some(ty) => ty.span.hi(),
+        // let <pat>;
+        // ~~~~~~~~~
+        None => local.pat.span.hi(),
+    });
+
+    snippet_opt(cx, span)
+}
+
+fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    local: &'tcx Local<'tcx>,
+    local_stmt: &'tcx Stmt<'tcx>,
+    block: &'tcx Block<'tcx>,
+    binding_id: HirId,
+) -> Option<()> {
+    let usage = first_usage(cx, binding_id, local_stmt.hir_id, block)?;
+    let binding_name = cx.tcx.hir().opt_name(binding_id)?;
+    let let_snippet = local_snippet_without_semicolon(cx, local)?;
+
+    match usage.expr.kind {
+        ExprKind::Assign(..) => {
+            let assign = LocalAssign::new(cx, usage.expr, binding_id)?;
+
+            span_lint_and_then(
+                cx,
+                NEEDLESS_LATE_INIT,
+                local_stmt.span,
+                "unneeded late initalization",
+                |diag| {
+                    diag.tool_only_span_suggestion(
+                        local_stmt.span,
+                        "remove the local",
+                        String::new(),
+                        Applicability::MachineApplicable,
+                    );
+
+                    diag.span_suggestion(
+                        assign.lhs_span,
+                        &format!("declare `{}` here", binding_name),
+                        let_snippet,
+                        Applicability::MachineApplicable,
+                    );
+                },
+            );
+        },
+        ExprKind::If(_, then_expr, Some(else_expr)) => {
+            let (applicability, suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?;
+
+            span_lint_and_then(
+                cx,
+                NEEDLESS_LATE_INIT,
+                local_stmt.span,
+                "unneeded late initalization",
+                |diag| {
+                    diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
+
+                    diag.span_suggestion_verbose(
+                        usage.stmt.span.shrink_to_lo(),
+                        &format!("declare `{}` here", binding_name),
+                        format!("{} = ", let_snippet),
+                        applicability,
+                    );
+
+                    diag.multipart_suggestion("remove the assignments from the branches", suggestions, applicability);
+
+                    if usage.needs_semi {
+                        diag.span_suggestion(
+                            usage.stmt.span.shrink_to_hi(),
+                            "add a semicolon after the `if` expression",
+                            ";".to_string(),
+                            applicability,
+                        );
+                    }
+                },
+            );
+        },
+        ExprKind::Match(_, arms, MatchSource::Normal) => {
+            let (applicability, suggestions) = assignment_suggestions(cx, binding_id, arms.iter().map(|arm| arm.body))?;
+
+            span_lint_and_then(
+                cx,
+                NEEDLESS_LATE_INIT,
+                local_stmt.span,
+                "unneeded late initalization",
+                |diag| {
+                    diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
+
+                    diag.span_suggestion_verbose(
+                        usage.stmt.span.shrink_to_lo(),
+                        &format!("declare `{}` here", binding_name),
+                        format!("{} = ", let_snippet),
+                        applicability,
+                    );
+
+                    diag.multipart_suggestion(
+                        "remove the assignments from the `match` arms",
+                        suggestions,
+                        applicability,
+                    );
+
+                    if usage.needs_semi {
+                        diag.span_suggestion(
+                            usage.stmt.span.shrink_to_hi(),
+                            "add a semicolon after the `match` expression",
+                            ";".to_string(),
+                            applicability,
+                        );
+                    }
+                },
+            );
+        },
+        _ => {},
+    };
+
+    Some(())
+}
+
+impl LateLintPass<'tcx> for NeedlessLateInit {
+    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
+        let mut parents = cx.tcx.hir().parent_iter(local.hir_id);
+
+        if_chain! {
+            if let Local {
+                init: None,
+                pat: &Pat {
+                    kind: PatKind::Binding(_, binding_id, _, None),
+                    ..
+                },
+                source: LocalSource::Normal,
+                ..
+            } = local;
+            if let Some((_, Node::Stmt(local_stmt))) = parents.next();
+            if let Some((_, Node::Block(block))) = parents.next();
+
+            then {
+                check(cx, local, local_stmt, block, binding_id);
+            }
+        }
+    }
+}
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
index fbdaaf51f74..a28b08c33ec 100644
--- a/src/tools/clippy/clippy_lints/src/needless_option_as_deref.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_option_as_deref.rs
@@ -1,5 +1,4 @@
 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;
@@ -27,6 +26,7 @@ declare_clippy_lint! {
     /// let a = Some(&1);
     /// let b = a;
     /// ```
+    #[clippy::version = "1.57.0"]
     pub NEEDLESS_OPTION_AS_DEREF,
     complexity,
     "no-op use of `deref` or `deref_mut` method to `Option`."
@@ -38,7 +38,7 @@ declare_lint_pass!(OptionNeedlessDeref=> [
 
 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) {
+        if expr.span.from_expansion() {
             return;
         }
         let typeck = cx.typeck_results();
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
index 352dc6f8bec..35877d51c0c 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
@@ -51,6 +51,7 @@ declare_clippy_lint! {
     ///     assert_eq!(v.len(), 42);
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_PASS_BY_VALUE,
     pedantic,
     "functions taking arguments by value, but not consuming them in its body"
diff --git a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs
index 42e48336e15..1ffed6a0524 100644
--- a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs
@@ -53,6 +53,7 @@ declare_clippy_lint! {
     ///     tr.and_then(|t| t.magic)
     /// }
     /// ```
+    #[clippy::version = "1.51.0"]
     pub NEEDLESS_QUESTION_MARK,
     complexity,
     "Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result<T, E>`."
@@ -92,10 +93,16 @@ impl LateLintPass<'_> for NeedlessQuestionMark {
 }
 
 fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
-    let inner_expr = if_chain! {
+    if_chain! {
         if let ExprKind::Call(path, [arg]) = &expr.kind;
         if let ExprKind::Path(ref qpath) = &path.kind;
-        if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk);
+        let sugg_remove = if is_lang_ctor(cx, qpath, OptionSome) {
+            "Some()"
+        } else if is_lang_ctor(cx, qpath, ResultOk) {
+            "Ok()"
+        } else {
+            return;
+        };
         if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &arg.kind;
         if let ExprKind::Call(called, [inner_expr]) = &inner_expr_with_q.kind;
         if let ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, _)) = &called.kind;
@@ -103,15 +110,16 @@ fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
         let expr_ty = cx.typeck_results().expr_ty(expr);
         let inner_ty = cx.typeck_results().expr_ty(inner_expr);
         if TyS::same_type(expr_ty, inner_ty);
-        then { inner_expr } else { return; }
-    };
-    span_lint_and_sugg(
-        cx,
-        NEEDLESS_QUESTION_MARK,
-        expr.span,
-        "question mark operator is useless here",
-        "try",
-        format!("{}", snippet(cx, inner_expr.span, r#""...""#)),
-        Applicability::MachineApplicable,
-    );
+        then {
+            span_lint_and_sugg(
+                cx,
+                NEEDLESS_QUESTION_MARK,
+                expr.span,
+                "question mark operator is useless here",
+                &format!("try removing question mark and `{}`", sugg_remove),
+                format!("{}", snippet(cx, inner_expr.span, r#""...""#)),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
 }
diff --git a/src/tools/clippy/clippy_lints/src/needless_update.rs b/src/tools/clippy/clippy_lints/src/needless_update.rs
index 2a33b7392ca..ed315efaa2f 100644
--- a/src/tools/clippy/clippy_lints/src/needless_update.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_update.rs
@@ -40,6 +40,7 @@ declare_clippy_lint! {
     ///     ..zero_point
     /// };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_UPDATE,
     complexity,
     "using `Foo { ..base }` when there are no missing fields"
diff --git a/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs b/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs
index 6ad49b70605..efe31a15441 100644
--- a/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs
+++ b/src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     ///     _ => false,
     /// };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEG_CMP_OP_ON_PARTIAL_ORD,
     complexity,
     "The use of negated comparison operators on partially ordered types may produce confusing code."
diff --git a/src/tools/clippy/clippy_lints/src/neg_multiply.rs b/src/tools/clippy/clippy_lints/src/neg_multiply.rs
index 1b15d29439f..cb67fab1740 100644
--- a/src/tools/clippy/clippy_lints/src/neg_multiply.rs
+++ b/src/tools/clippy/clippy_lints/src/neg_multiply.rs
@@ -20,6 +20,7 @@ declare_clippy_lint! {
     /// ```ignore
     /// x * -1
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEG_MULTIPLY,
     style,
     "multiplying integers with `-1`"
diff --git a/src/tools/clippy/clippy_lints/src/new_without_default.rs b/src/tools/clippy/clippy_lints/src/new_without_default.rs
index 0ac27f1cba2..f0c0c89ca8f 100644
--- a/src/tools/clippy/clippy_lints/src/new_without_default.rs
+++ b/src/tools/clippy/clippy_lints/src/new_without_default.rs
@@ -45,6 +45,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEW_WITHOUT_DEFAULT,
     style,
     "`fn new() -> Self` method without `Default` implementation"
diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs
index 6dae8f32043..6fcc9ca29b9 100644
--- a/src/tools/clippy/clippy_lints/src/no_effect.rs
+++ b/src/tools/clippy/clippy_lints/src/no_effect.rs
@@ -22,6 +22,7 @@ declare_clippy_lint! {
     /// ```rust
     /// 0;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NO_EFFECT,
     complexity,
     "statements with no effect"
@@ -44,6 +45,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// let _i_serve_no_purpose = 1;
     /// ```
+    #[clippy::version = "1.58.0"]
     pub NO_EFFECT_UNDERSCORE_BINDING,
     pedantic,
     "binding to `_` prefixed variable with no side-effect"
@@ -62,6 +64,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// compute_array()[0];
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNNECESSARY_OPERATION,
     complexity,
     "outer expressions with no effect"
@@ -130,9 +133,12 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
         },
         ExprKind::Call(callee, args) => {
             if let ExprKind::Path(ref qpath) = callee.kind {
-                let res = cx.qpath_res(qpath, callee.hir_id);
+                if cx.typeck_results().type_dependent_def(expr.hir_id).is_some() {
+                    // type-dependent function call like `impl FnOnce for X`
+                    return false;
+                }
                 let def_matched = matches!(
-                    res,
+                    cx.qpath_res(qpath, callee.hir_id),
                     Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..)
                 );
                 if def_matched || is_range_literal(expr) {
@@ -158,12 +164,13 @@ fn check_unnecessary_operation(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
         if !&reduced.iter().any(|e| e.span.from_expansion());
         then {
             if let ExprKind::Index(..) = &expr.kind {
-                let snippet;
-                if let (Some(arr), Some(func)) = (snippet_opt(cx, reduced[0].span), snippet_opt(cx, reduced[1].span)) {
-                    snippet = format!("assert!({}.len() > {});", &arr, &func);
+                let snippet = if let (Some(arr), Some(func)) =
+                    (snippet_opt(cx, reduced[0].span), snippet_opt(cx, reduced[1].span))
+                {
+                    format!("assert!({}.len() > {});", &arr, &func)
                 } else {
                     return;
-                }
+                };
                 span_lint_hir_and_then(
                     cx,
                     UNNECESSARY_OPERATION,
@@ -235,6 +242,10 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec
         },
         ExprKind::Call(callee, args) => {
             if let ExprKind::Path(ref qpath) = callee.kind {
+                if cx.typeck_results().type_dependent_def(expr.hir_id).is_some() {
+                    // type-dependent function call like `impl FnOnce for X`
+                    return None;
+                }
                 let res = cx.qpath_res(qpath, callee.hir_id);
                 match res {
                     Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..)
diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
index 2ea97eb88f7..074ba9e92ba 100644
--- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs
+++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
@@ -69,6 +69,7 @@ declare_clippy_lint! {
     /// STATIC_ATOM.store(9, SeqCst);
     /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DECLARE_INTERIOR_MUTABLE_CONST,
     style,
     "declaring `const` with interior mutability"
@@ -113,6 +114,7 @@ declare_clippy_lint! {
     /// STATIC_ATOM.store(9, SeqCst);
     /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub BORROW_INTERIOR_MUTABLE_CONST,
     style,
     "referencing `const` with interior mutability"
diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
index e28cc49bf2a..5559fac0a8a 100644
--- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
+++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
@@ -24,6 +24,7 @@ declare_clippy_lint! {
     /// let checked_exp = something;
     /// let checked_expr = something_else;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SIMILAR_NAMES,
     pedantic,
     "similarly named items and bindings"
@@ -42,6 +43,7 @@ declare_clippy_lint! {
     /// ```ignore
     /// let (a, b, c, d, e, f, g) = (...);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MANY_SINGLE_CHAR_NAMES,
     pedantic,
     "too many single character bindings"
@@ -62,6 +64,7 @@ declare_clippy_lint! {
     /// let ___1 = 1;
     /// let __1___2 = 11;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub JUST_UNDERSCORES_AND_DIGITS,
     style,
     "unclear name"
@@ -357,7 +360,12 @@ impl EarlyLintPass for NonExpressiveNames {
             return;
         }
 
-        if let ItemKind::Fn(box ast::Fn { ref sig, body: Some(ref blk), .. }) = item.kind {
+        if let ItemKind::Fn(box ast::Fn {
+            ref sig,
+            body: Some(ref blk),
+            ..
+        }) = item.kind
+        {
             do_check(self, cx, &item.attrs, &sig.decl, blk);
         }
     }
@@ -367,7 +375,12 @@ impl EarlyLintPass for NonExpressiveNames {
             return;
         }
 
-        if let AssocItemKind::Fn(box ast::Fn { ref sig, body: Some(ref blk), .. }) = item.kind {
+        if let AssocItemKind::Fn(box ast::Fn {
+            ref sig,
+            body: Some(ref blk),
+            ..
+        }) = item.kind
+        {
             do_check(self, cx, &item.attrs, &sig.decl, blk);
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
index 3b74f69d375..4b57dbc4c41 100644
--- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
+++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
@@ -32,6 +32,7 @@ declare_clippy_lint! {
     /// let mut options = OpenOptions::new();
     /// options.mode(0o644);
     /// ```
+    #[clippy::version = "1.53.0"]
     pub NON_OCTAL_UNIX_PERMISSIONS,
     correctness,
     "use of non-octal value to set unix file permissions, which will be translated into octal"
diff --git a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs
index 7ebf84d400f..bba542ce8ca 100644
--- a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs
+++ b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs
@@ -1,11 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::is_lint_allowed;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::{implements_trait, is_copy};
+use clippy_utils::{is_lint_allowed, match_def_path, paths};
 use rustc_ast::ImplPolarity;
 use rustc_hir::def_id::DefId;
 use rustc_hir::{FieldDef, Item, ItemKind, Node};
 use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::sym;
@@ -43,6 +44,7 @@ declare_clippy_lint! {
     /// ```
     /// Use thread-safe types like [`std::sync::Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html)
     /// or specify correct bounds on generic type parameters (`T: Send`).
+    #[clippy::version = "1.57.0"]
     pub NON_SEND_FIELDS_IN_SEND_TY,
     suspicious,
     "there is field that does not implement `Send` in a `Send` struct"
@@ -76,6 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy {
         // single `AdtDef` may have multiple `Send` impls due to generic
         // parameters, and the lint is much easier to implement in this way.
         if_chain! {
+            if !in_external_macro(cx.tcx.sess, item.span);
             if let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send);
             if let ItemKind::Impl(hir_impl) = &item.kind;
             if let Some(trait_ref) = &hir_impl.of_trait;
@@ -180,7 +183,7 @@ fn ty_allowed_without_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty
         return true;
     }
 
-    if is_copy(cx, ty) && !contains_raw_pointer(cx, ty) {
+    if is_copy(cx, ty) && !contains_pointer_like(cx, ty) {
         return true;
     }
 
@@ -200,7 +203,7 @@ fn ty_allowed_with_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'t
             .all(|ty| ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait)),
         ty::Array(ty, _) | ty::Slice(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait),
         ty::Adt(_, substs) => {
-            if contains_raw_pointer(cx, ty) {
+            if contains_pointer_like(cx, ty) {
                 // descends only if ADT contains any raw pointers
                 substs.iter().all(|generic_arg| match generic_arg.unpack() {
                     GenericArgKind::Type(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait),
@@ -217,14 +220,20 @@ fn ty_allowed_with_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'t
     }
 }
 
-/// Checks if the type contains any raw pointers in substs (including nested ones).
-fn contains_raw_pointer<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> bool {
+/// Checks if the type contains any pointer-like types in substs (including nested ones)
+fn contains_pointer_like<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> bool {
     for ty_node in target_ty.walk(cx.tcx) {
-        if_chain! {
-            if let GenericArgKind::Type(inner_ty) = ty_node.unpack();
-            if let ty::RawPtr(_) = inner_ty.kind();
-            then {
-                return true;
+        if let GenericArgKind::Type(inner_ty) = ty_node.unpack() {
+            match inner_ty.kind() {
+                ty::RawPtr(_) => {
+                    return true;
+                },
+                ty::Adt(adt_def, _) => {
+                    if match_def_path(cx, adt_def.did, &paths::PTR_NON_NULL) {
+                        return true;
+                    }
+                },
+                _ => (),
             }
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
index ca660a9250d..a04d589f880 100644
--- a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
+++ b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
@@ -3,14 +3,16 @@ use std::{
     hash::{Hash, Hasher},
 };
 
-use clippy_utils::{diagnostics::span_lint_and_help, in_macro, is_direct_expn_of, source::snippet_opt};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::snippet_opt;
 use if_chain::if_chain;
 use rustc_ast::ast;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir::def_id::DefId;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::Span;
+use rustc_span::hygiene::{ExpnKind, MacroKind};
+use rustc_span::{Span, Symbol};
 use serde::{de, Deserialize};
 
 declare_clippy_lint! {
@@ -29,6 +31,7 @@ declare_clippy_lint! {
     /// ```rust
     /// vec![1, 2, 3];
     /// ```
+    #[clippy::version = "1.55.0"]
     pub NONSTANDARD_MACRO_BRACES,
     nursery,
     "check consistent use of braces in macro"
@@ -37,7 +40,7 @@ declare_clippy_lint! {
 const BRACES: &[(&str, &str)] = &[("(", ")"), ("{", "}"), ("[", "]")];
 
 /// The (name, (open brace, close brace), source snippet)
-type MacroInfo<'a> = (&'a str, &'a (String, String), String);
+type MacroInfo<'a> = (Symbol, &'a (String, String), String);
 
 #[derive(Clone, Debug, Default)]
 pub struct MacroBraces {
@@ -93,35 +96,34 @@ impl EarlyLintPass for MacroBraces {
 
 fn is_offending_macro<'a>(cx: &EarlyContext<'_>, span: Span, mac_braces: &'a MacroBraces) -> Option<MacroInfo<'a>> {
     let unnested_or_local = || {
-        let nested = in_macro(span.ctxt().outer_expn_data().call_site);
-        !nested
+        !span.ctxt().outer_expn_data().call_site.from_expansion()
             || span
                 .macro_backtrace()
                 .last()
                 .map_or(false, |e| e.macro_def_id.map_or(false, DefId::is_local))
     };
     if_chain! {
-        // Make sure we are only one level deep otherwise there are to many FP's
-        if in_macro(span);
-        if let Some((name, braces)) = find_matching_macro(span, &mac_braces.macro_braces);
+        if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind;
+        let name = &*mac_name.as_str();
+        if let Some(braces) = mac_braces.macro_braces.get(name);
         if let Some(snip) = snippet_opt(cx, span.ctxt().outer_expn_data().call_site);
         // we must check only invocation sites
         // https://github.com/rust-lang/rust-clippy/issues/7422
         if snip.starts_with(&format!("{}!", name));
         if unnested_or_local();
         // make formatting consistent
-        let c = snip.replace(" ", "");
+        let c = snip.replace(' ', "");
         if !c.starts_with(&format!("{}!{}", name, braces.0));
         if !mac_braces.done.contains(&span.ctxt().outer_expn_data().call_site);
         then {
-            Some((name, braces, snip))
+            Some((mac_name, braces, snip))
         } else {
             None
         }
     }
 }
 
-fn emit_help(cx: &EarlyContext<'_>, snip: String, braces: &(String, String), name: &str, span: Span) {
+fn emit_help(cx: &EarlyContext<'_>, snip: String, braces: &(String, String), name: Symbol, span: Span) {
     let with_space = &format!("! {}", braces.0);
     let without_space = &format!("!{}", braces.0);
     let mut help = snip;
@@ -144,15 +146,6 @@ fn emit_help(cx: &EarlyContext<'_>, snip: String, braces: &(String, String), nam
     );
 }
 
-fn find_matching_macro(
-    span: Span,
-    braces: &FxHashMap<String, (String, String)>,
-) -> Option<(&String, &(String, String))> {
-    braces
-        .iter()
-        .find(|(macro_name, _)| is_direct_expn_of(span, macro_name).is_some())
-}
-
 fn macro_braces(conf: FxHashSet<MacroMatcher>) -> FxHashMap<String, (String, String)> {
     let mut braces = vec![
         macro_matcher!(
diff --git a/src/tools/clippy/clippy_lints/src/octal_escapes.rs b/src/tools/clippy/clippy_lints/src/octal_escapes.rs
new file mode 100644
index 00000000000..9c971437645
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/octal_escapes.rs
@@ -0,0 +1,150 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_ast::ast::{Expr, ExprKind};
+use rustc_ast::token::{Lit, LitKind};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+use std::fmt::Write;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `\0` escapes in string and byte literals that look like octal
+    /// character escapes in C.
+    ///
+    /// ### Why is this bad?
+    ///
+    /// C and other languages support octal character escapes in strings, where
+    /// a backslash is followed by up to three octal digits. For example, `\033`
+    /// stands for the ASCII character 27 (ESC). Rust does not support this
+    /// notation, but has the escape code `\0` which stands for a null
+    /// byte/character, and any following digits do not form part of the escape
+    /// sequence. Therefore, `\033` is not a compiler error but the result may
+    /// be surprising.
+    ///
+    /// ### Known problems
+    /// The actual meaning can be the intended one. `\x00` can be used in these
+    /// cases to be unambigious.
+    ///
+    /// The lint does not trigger for format strings in `print!()`, `write!()`
+    /// and friends since the string is already preprocessed when Clippy lints
+    /// can see it.
+    ///
+    /// # Example
+    /// ```rust
+    /// // Bad
+    /// let one = "\033[1m Bold? \033[0m";  // \033 intended as escape
+    /// let two = "\033\0";                 // \033 intended as null-3-3
+    ///
+    /// // Good
+    /// let one = "\x1b[1mWill this be bold?\x1b[0m";
+    /// let two = "\x0033\x00";
+    /// ```
+    #[clippy::version = "1.58.0"]
+    pub OCTAL_ESCAPES,
+    suspicious,
+    "string escape sequences looking like octal characters"
+}
+
+declare_lint_pass!(OctalEscapes => [OCTAL_ESCAPES]);
+
+impl EarlyLintPass for OctalEscapes {
+    fn check_expr(&mut self, cx: &EarlyContext<'tcx>, expr: &Expr) {
+        if in_external_macro(cx.sess, expr.span) {
+            return;
+        }
+
+        if let ExprKind::Lit(lit) = &expr.kind {
+            if matches!(lit.token.kind, LitKind::Str) {
+                check_lit(cx, &lit.token, lit.span, true);
+            } else if matches!(lit.token.kind, LitKind::ByteStr) {
+                check_lit(cx, &lit.token, lit.span, false);
+            }
+        }
+    }
+}
+
+fn check_lit(cx: &EarlyContext<'tcx>, lit: &Lit, span: Span, is_string: bool) {
+    let contents = lit.symbol.as_str();
+    let mut iter = contents.char_indices().peekable();
+    let mut found = vec![];
+
+    // go through the string, looking for \0[0-7][0-7]?
+    while let Some((from, ch)) = iter.next() {
+        if ch == '\\' {
+            if let Some((_, '0')) = iter.next() {
+                // collect up to two further octal digits
+                if let Some((mut to, '0'..='7')) = iter.next() {
+                    if let Some((_, '0'..='7')) = iter.peek() {
+                        to += 1;
+                    }
+                    found.push((from, to + 1));
+                }
+            }
+        }
+    }
+
+    if found.is_empty() {
+        return;
+    }
+
+    // construct two suggestion strings, one with \x escapes with octal meaning
+    // as in C, and one with \x00 for null bytes.
+    let mut suggest_1 = if is_string { "\"" } else { "b\"" }.to_string();
+    let mut suggest_2 = suggest_1.clone();
+    let mut index = 0;
+    for (from, to) in found {
+        suggest_1.push_str(&contents[index..from]);
+        suggest_2.push_str(&contents[index..from]);
+
+        // construct a replacement escape
+        // the maximum value is \077, or \x3f, so u8 is sufficient here
+        if let Ok(n) = u8::from_str_radix(&contents[from + 1..to], 8) {
+            write!(&mut suggest_1, "\\x{:02x}", n).unwrap();
+        }
+
+        // append the null byte as \x00 and the following digits literally
+        suggest_2.push_str("\\x00");
+        suggest_2.push_str(&contents[from + 2..to]);
+
+        index = to;
+    }
+    suggest_1.push_str(&contents[index..]);
+    suggest_1.push('"');
+    suggest_2.push_str(&contents[index..]);
+    suggest_2.push('"');
+
+    span_lint_and_then(
+        cx,
+        OCTAL_ESCAPES,
+        span,
+        &format!(
+            "octal-looking escape in {} literal",
+            if is_string { "string" } else { "byte string" }
+        ),
+        |diag| {
+            diag.help(&format!(
+                "octal escapes are not supported, `\\0` is always a null {}",
+                if is_string { "character" } else { "byte" }
+            ));
+            // suggestion 1: equivalent hex escape
+            diag.span_suggestion(
+                span,
+                "if an octal escape was intended, use the hexadecimal representation instead",
+                suggest_1,
+                Applicability::MaybeIncorrect,
+            );
+            // suggestion 2: unambiguous null byte
+            diag.span_suggestion(
+                span,
+                &format!(
+                    "if the null {} is intended, disambiguate using",
+                    if is_string { "character" } else { "byte" }
+                ),
+                suggest_2,
+                Applicability::MaybeIncorrect,
+            );
+        },
+    );
+}
diff --git a/src/tools/clippy/clippy_lints/src/open_options.rs b/src/tools/clippy/clippy_lints/src/open_options.rs
index 5752342cf62..2c77100bdcf 100644
--- a/src/tools/clippy/clippy_lints/src/open_options.rs
+++ b/src/tools/clippy/clippy_lints/src/open_options.rs
@@ -22,6 +22,7 @@ declare_clippy_lint! {
     ///
     /// OpenOptions::new().read(true).truncate(true);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NONSENSICAL_OPEN_OPTIONS,
     correctness,
     "nonsensical combination of options for opening a file"
diff --git a/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs b/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs
index d7306628030..3f5286ba097 100644
--- a/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs
@@ -26,6 +26,7 @@ declare_clippy_lint! {
     /// ```rust,no_run
     /// let _ = env!("HOME");
     /// ```
+    #[clippy::version = "1.43.0"]
     pub OPTION_ENV_UNWRAP,
     correctness,
     "using `option_env!(...).unwrap()` to get environment variable"
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 cbe1c5d44d5..262be17f617 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
@@ -3,7 +3,7 @@ use clippy_utils::higher;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{
-    can_move_expr_to_closure, eager_or_lazy, in_constant, in_macro, is_else_clause, is_lang_ctor, peel_hir_expr_while,
+    can_move_expr_to_closure, eager_or_lazy, in_constant, is_else_clause, is_lang_ctor, peel_hir_expr_while,
     CaptureKind,
 };
 use if_chain::if_chain;
@@ -59,6 +59,7 @@ declare_clippy_lint! {
     ///     y*y
     /// }, |foo| foo);
     /// ```
+    #[clippy::version = "1.47.0"]
     pub OPTION_IF_LET_ELSE,
     nursery,
     "reimplementation of Option::map_or"
@@ -110,7 +111,7 @@ fn extract_body_from_expr<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
 fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String {
     format!(
         "{}{}",
-        Sugg::hir(cx, cond_expr, "..").maybe_par(),
+        Sugg::hir_with_macro_callsite(cx, cond_expr, "..").maybe_par(),
         if as_mut {
             ".as_mut()"
         } else if as_ref {
@@ -126,7 +127,7 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo
 /// this construct is found, or None if this construct is not found.
 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 !expr.span.from_expansion(); // Don't lint macros, because it behaves weirdly
         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);
@@ -146,11 +147,7 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) ->
             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::switch_to_eager_eval(cx, none_body) { "map_or" } else { "map_or_else" };
             let capture_name = id.name.to_ident_string();
             let (as_ref, as_mut) = match &let_expr.kind {
                 ExprKind::AddrOf(_, Mutability::Not, _) => (true, false),
@@ -183,8 +180,8 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) ->
             Some(OptionIfLetElseOccurence {
                 option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut),
                 method_sugg: method_sugg.to_string(),
-                some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir(cx, some_body, "..")),
-                none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")),
+                some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir_with_macro_callsite(cx, some_body, "..")),
+                none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir_with_macro_callsite(cx, none_body, "..")),
             })
         } else {
             None
diff --git a/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs b/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs
index 0f9e5ada3a8..6dabbd48031 100644
--- a/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs
+++ b/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs
@@ -19,6 +19,7 @@ declare_clippy_lint! {
     /// # let b = 2;
     /// a + b < a;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OVERFLOW_CHECK_CONDITIONAL,
     complexity,
     "overflow checks inspired by C which are likely to panic"
diff --git a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
index 583c42b6563..8769c045214 100644
--- a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
@@ -30,6 +30,7 @@ declare_clippy_lint! {
     ///     Err(String::from("error"))
     /// }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub PANIC_IN_RESULT_FN,
     restriction,
     "functions of type `Result<..>` that contain `panic!()`, `todo!()`, `unreachable()`, `unimplemented()` or assertion"
diff --git a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
index d8d9081d6f1..edfac824ded 100644
--- a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
+++ b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
@@ -17,6 +17,7 @@ declare_clippy_lint! {
     /// ```no_run
     /// panic!("even with a good reason");
     /// ```
+    #[clippy::version = "1.40.0"]
     pub PANIC,
     restriction,
     "usage of the `panic!` macro"
@@ -33,6 +34,7 @@ declare_clippy_lint! {
     /// ```no_run
     /// unimplemented!();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNIMPLEMENTED,
     restriction,
     "`unimplemented!` should not be present in production code"
@@ -49,6 +51,7 @@ declare_clippy_lint! {
     /// ```no_run
     /// todo!();
     /// ```
+    #[clippy::version = "1.40.0"]
     pub TODO,
     restriction,
     "`todo!` should not be present in production code"
@@ -65,6 +68,7 @@ declare_clippy_lint! {
     /// ```no_run
     /// unreachable!();
     /// ```
+    #[clippy::version = "1.40.0"]
     pub UNREACHABLE,
     restriction,
     "usage of the `unreachable!` macro"
diff --git a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
index 4ec493e5f45..e827cdaae87 100644
--- a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
@@ -25,6 +25,7 @@ declare_clippy_lint! {
     ///    fn ne(&self, other: &Foo) -> bool { !(self == other) }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub PARTIALEQ_NE_IMPL,
     complexity,
     "re-implementing `PartialEq::ne`"
diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
index 6229b9608b3..3092ab8392a 100644
--- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
+++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
@@ -65,6 +65,7 @@ declare_clippy_lint! {
     /// // Better
     /// fn foo(v: u32) {}
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TRIVIALLY_COPY_PASS_BY_REF,
     pedantic,
     "functions taking small copyable arguments by reference"
@@ -98,6 +99,7 @@ declare_clippy_lint! {
     /// // Good
     /// fn foo(v: &TooLarge) {}
     /// ```
+    #[clippy::version = "1.49.0"]
     pub LARGE_TYPES_PASSED_BY_VALUE,
     pedantic,
     "functions taking large arguments by value"
diff --git a/src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs b/src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs
index 3df7a72d295..8ebee9bd04d 100644
--- a/src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs
+++ b/src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs
@@ -35,6 +35,7 @@ declare_clippy_lint! {
     /// x.push("bar");
     /// assert_eq!(x, PathBuf::from("/foo/bar"));
     /// ```
+    #[clippy::version = "1.36.0"]
     pub PATH_BUF_PUSH_OVERWRITE,
     nursery,
     "calling `push` with file system root on `PathBuf` can overwrite it"
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 018e6d611db..c7d77d30927 100644
--- a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
+++ b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
@@ -73,6 +73,7 @@ declare_clippy_lint! {
     ///     *a += b;
     /// }
     /// ```
+    #[clippy::version = "1.47.0"]
     pub PATTERN_TYPE_MISMATCH,
     restriction,
     "type of pattern does not match the expression type"
diff --git a/src/tools/clippy/clippy_lints/src/precedence.rs b/src/tools/clippy/clippy_lints/src/precedence.rs
index 1a8da00d9d6..cc0533c9f5d 100644
--- a/src/tools/clippy/clippy_lints/src/precedence.rs
+++ b/src/tools/clippy/clippy_lints/src/precedence.rs
@@ -42,6 +42,7 @@ declare_clippy_lint! {
     /// ### Example
     /// * `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7
     /// * `-1i32.abs()` equals -1, while `(-1i32).abs()` equals 1
+    #[clippy::version = "pre 1.29.0"]
     pub PRECEDENCE,
     complexity,
     "operations where precedence may be unclear"
diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs
index 8a36e20fc97..c08a19d520b 100644
--- a/src/tools/clippy/clippy_lints/src/ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr.rs
@@ -70,6 +70,7 @@ declare_clippy_lint! {
     /// // Good
     /// fn foo(&[u32]) { .. }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub PTR_ARG,
     style,
     "fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively"
@@ -96,6 +97,7 @@ declare_clippy_lint! {
     ///     ..
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CMP_NULL,
     style,
     "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead"
@@ -121,6 +123,7 @@ declare_clippy_lint! {
     /// ```ignore
     /// fn foo(&Foo) -> &mut Bar { .. }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MUT_FROM_REF,
     correctness,
     "fns that create mutable refs from immutable ref args"
@@ -143,6 +146,7 @@ declare_clippy_lint! {
     /// // Good
     /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); }
     /// ```
+    #[clippy::version = "1.53.0"]
     pub INVALID_NULL_PTR_USAGE,
     correctness,
     "invalid usage of a null pointer, suggesting `NonNull::dangling()` instead"
diff --git a/src/tools/clippy/clippy_lints/src/ptr_eq.rs b/src/tools/clippy/clippy_lints/src/ptr_eq.rs
index 3258c9fb3fe..3c126fc1ca6 100644
--- a/src/tools/clippy/clippy_lints/src/ptr_eq.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr_eq.rs
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
 use clippy_utils::source::snippet_opt;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -30,6 +29,7 @@ declare_clippy_lint! {
     ///
     /// assert!(std::ptr::eq(a, b));
     /// ```
+    #[clippy::version = "1.49.0"]
     pub PTR_EQ,
     style,
     "use `std::ptr::eq` when comparing raw pointers"
@@ -41,7 +41,7 @@ static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers";
 
 impl LateLintPass<'_> for PtrEq {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if in_macro(expr.span) {
+        if expr.span.from_expansion() {
             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 cfb5287c667..964564b5794 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
@@ -38,6 +38,7 @@ declare_clippy_lint! {
     ///     ptr.add(offset);
     /// }
     /// ```
+    #[clippy::version = "1.30.0"]
     pub PTR_OFFSET_WITH_CAST,
     complexity,
     "unneeded pointer offset cast"
diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs
index f63ef163bcb..a5531993ee6 100644
--- a/src/tools/clippy/clippy_lints/src/question_mark.rs
+++ b/src/tools/clippy/clippy_lints/src/question_mark.rs
@@ -32,6 +32,7 @@ declare_clippy_lint! {
     /// ```ignore
     /// option?;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub QUESTION_MARK,
     style,
     "checks for expressions that could be replaced by the question mark operator"
diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs
index 4fa361fedaf..52c060bc42c 100644
--- a/src/tools/clippy/clippy_lints/src/ranges.rs
+++ b/src/tools/clippy/clippy_lints/src/ranges.rs
@@ -35,6 +35,7 @@ declare_clippy_lint! {
     /// # let x = vec![1];
     /// x.iter().enumerate();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub RANGE_ZIP_WITH_LEN,
     complexity,
     "zipping iterator with a range when `enumerate()` would do"
@@ -72,6 +73,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// for x..=y { .. }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub RANGE_PLUS_ONE,
     pedantic,
     "`x..(y+1)` reads better as `x..=y`"
@@ -100,6 +102,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// for x..y { .. }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub RANGE_MINUS_ONE,
     pedantic,
     "`x..=(y-1)` reads better as `x..y`"
@@ -132,6 +135,7 @@ declare_clippy_lint! {
     ///     let sub = &arr[1..3];
     /// }
     /// ```
+    #[clippy::version = "1.45.0"]
     pub REVERSED_EMPTY_RANGES,
     correctness,
     "reversing the limits of range expressions, resulting in empty ranges"
@@ -158,6 +162,7 @@ declare_clippy_lint! {
     ///# let x = 6;
     /// assert!((3..8).contains(&x));
     /// ```
+    #[clippy::version = "1.49.0"]
     pub MANUAL_RANGE_CONTAINS,
     style,
     "manually reimplementing {`Range`, `RangeInclusive`}`::contains`"
diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
index 0eba6633ee1..1cf349f8aa7 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
@@ -62,6 +62,7 @@ declare_clippy_lint! {
     ///
     /// Path::new("/a/b").join("c").to_path_buf();
     /// ```
+    #[clippy::version = "1.32.0"]
     pub REDUNDANT_CLONE,
     perf,
     "`clone()` of an owned value that is going to be dropped immediately"
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 90e3c3f4b3e..0de282542fc 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
@@ -30,6 +30,7 @@ declare_clippy_lint! {
     /// // Good
     /// let a = 42
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub REDUNDANT_CLOSURE_CALL,
     complexity,
     "throwaway closures called in the expression they are defined"
diff --git a/src/tools/clippy/clippy_lints/src/redundant_else.rs b/src/tools/clippy/clippy_lints/src/redundant_else.rs
index 68b256d2944..93dbe936d58 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_else.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_else.rs
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     ///     print!("Moving on...");
     /// }
     /// ```
+    #[clippy::version = "1.50.0"]
     pub REDUNDANT_ELSE,
     pedantic,
     "`else` branch that can be removed without changing semantics"
diff --git a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs
index 47df4917510..0dea4a784b2 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs
@@ -30,6 +30,7 @@ declare_clippy_lint! {
     /// ```ignore
     /// let foo = Foo { bar };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub REDUNDANT_FIELD_NAMES,
     style,
     "checks for fields in struct literals where shorthands could be used"
diff --git a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs
index 919d4e11e5a..2cee3c14d7f 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs
@@ -26,6 +26,7 @@ declare_clippy_lint! {
     ///     pub fn internal_fn() { }
     /// }
     /// ```
+    #[clippy::version = "1.44.0"]
     pub REDUNDANT_PUB_CRATE,
     nursery,
     "Using `pub(crate)` visibility on items that are not crate visible due to the visibility of the module that contains them."
diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs
index 290348c4509..b2bd0103d11 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::get_parent_expr;
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::ty::is_type_lang_item;
-use clippy_utils::{get_parent_expr, in_macro};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability};
@@ -34,6 +34,7 @@ declare_clippy_lint! {
     ///     x
     /// }
     /// ```
+    #[clippy::version = "1.51.0"]
     pub REDUNDANT_SLICING,
     complexity,
     "redundant slicing of the whole range of a type"
@@ -43,7 +44,7 @@ declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING]);
 
 impl LateLintPass<'_> for RedundantSlicing {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if in_macro(expr.span) {
+        if expr.span.from_expansion() {
             return;
         }
 
diff --git a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
index d5a1a61da6b..ea5064217ab 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
@@ -27,6 +27,7 @@ declare_clippy_lint! {
     ///  const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
     ///  static FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
     /// ```
+    #[clippy::version = "1.37.0"]
     pub REDUNDANT_STATIC_LIFETIMES,
     style,
     "Using explicit `'static` lifetime for constants or statics when elision rules would allow omitting them."
diff --git a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs
index d543832e314..909d6971a54 100644
--- a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs
@@ -28,6 +28,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// let x: Option<&u32> = Some(&0u32);
     /// ```
+    #[clippy::version = "1.49.0"]
     pub REF_OPTION_REF,
     pedantic,
     "use `Option<&T>` instead of `&Option<&T>`"
diff --git a/src/tools/clippy/clippy_lints/src/reference.rs b/src/tools/clippy/clippy_lints/src/reference.rs
index 77b6e60d893..22ae7a291d0 100644
--- a/src/tools/clippy/clippy_lints/src/reference.rs
+++ b/src/tools/clippy/clippy_lints/src/reference.rs
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
 use clippy_utils::source::{snippet_opt, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
 use if_chain::if_chain;
@@ -31,6 +30,7 @@ declare_clippy_lint! {
     /// let a = f(b);
     /// let c = d;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub DEREF_ADDROF,
     complexity,
     "use of `*&` or `*&mut` in an expression"
@@ -50,7 +50,7 @@ impl EarlyLintPass for DerefAddrOf {
         if_chain! {
             if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind;
             if let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind;
-            if !in_macro(addrof_target.span);
+            if !addrof_target.span.from_expansion();
             then {
                 let mut applicability = Applicability::MachineApplicable;
                 let sugg = if e.span.from_expansion() {
@@ -125,6 +125,7 @@ declare_clippy_lint! {
     /// # let point = Point(30, 20);
     /// let x = point.0;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub REF_IN_DEREF,
     complexity,
     "Use of reference in auto dereference expression."
diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs
index 5d08aee1e5f..8e5983b4773 100644
--- a/src/tools/clippy/clippy_lints/src/regex.rs
+++ b/src/tools/clippy/clippy_lints/src/regex.rs
@@ -22,6 +22,7 @@ declare_clippy_lint! {
     /// ```ignore
     /// Regex::new("|")
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub INVALID_REGEX,
     correctness,
     "invalid regular expressions"
@@ -46,6 +47,7 @@ declare_clippy_lint! {
     /// ```ignore
     /// Regex::new("^foobar")
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TRIVIAL_REGEX,
     nursery,
     "trivial regular expressions"
diff --git a/src/tools/clippy/clippy_lints/src/repeat_once.rs b/src/tools/clippy/clippy_lints/src/repeat_once.rs
index cf94c0e97d9..b5dd2de6337 100644
--- a/src/tools/clippy/clippy_lints/src/repeat_once.rs
+++ b/src/tools/clippy/clippy_lints/src/repeat_once.rs
@@ -1,6 +1,5 @@
 use clippy_utils::consts::{constant_context, Constant};
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
 use if_chain::if_chain;
@@ -36,6 +35,7 @@ declare_clippy_lint! {
     ///     let x = String::from("hello world").clone();
     /// }
     /// ```
+    #[clippy::version = "1.47.0"]
     pub REPEAT_ONCE,
     complexity,
     "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` "
@@ -49,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce {
             if let ExprKind::MethodCall(path, _, [receiver, count], _) = &expr.kind;
             if path.ident.name == sym!(repeat);
             if constant_context(cx, cx.typeck_results()).expr(count) == Some(Constant::Int(1));
-            if !in_macro(receiver.span);
+            if !receiver.span.from_expansion();
             then {
                 let ty = cx.typeck_results().expr_ty(receiver).peel_refs();
                 if ty.is_str() {
diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs
index ae85b7087e7..494bc7dda18 100644
--- a/src/tools/clippy/clippy_lints/src/returns.rs
+++ b/src/tools/clippy/clippy_lints/src/returns.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::snippet_opt;
-use clippy_utils::{fn_def_id, in_macro, path_to_local_id};
+use clippy_utils::{fn_def_id, path_to_local_id};
 use if_chain::if_chain;
 use rustc_ast::ast::Attribute;
 use rustc_errors::Applicability;
@@ -37,6 +37,7 @@ declare_clippy_lint! {
     ///     String::new()
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub LET_AND_RETURN,
     style,
     "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block"
@@ -62,6 +63,7 @@ declare_clippy_lint! {
     ///     x
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_RETURN,
     style,
     "using a return statement like `return expr;` where an expression would suffice"
@@ -90,8 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for Return {
             if !last_statement_borrows(cx, initexpr);
             if !in_external_macro(cx.sess(), initexpr.span);
             if !in_external_macro(cx.sess(), retexpr.span);
-            if !in_external_macro(cx.sess(), local.span);
-            if !in_macro(local.span);
+            if !local.span.from_expansion();
             then {
                 span_lint_and_then(
                     cx,
diff --git a/src/tools/clippy/clippy_lints/src/same_name_method.rs b/src/tools/clippy/clippy_lints/src/same_name_method.rs
index 737ff634e44..1bbaa104e60 100644
--- a/src/tools/clippy/clippy_lints/src/same_name_method.rs
+++ b/src/tools/clippy/clippy_lints/src/same_name_method.rs
@@ -11,7 +11,7 @@ use std::collections::{BTreeMap, BTreeSet};
 
 declare_clippy_lint! {
     /// ### What it does
-    /// It lints if a struct has two method with same time:
+    /// It lints if a struct has two methods with the same name:
     /// one from a trait, another not from trait.
     ///
     /// ### Why is this bad?
@@ -33,6 +33,7 @@ declare_clippy_lint! {
     ///     fn foo(&self) {}
     /// }
     /// ```
+    #[clippy::version = "1.57.0"]
     pub SAME_NAME_METHOD,
     restriction,
     "two method with same name"
@@ -99,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
                                         cx,
                                         SAME_NAME_METHOD,
                                         *impl_span,
-                                        "method's name is same to an existing method in a trait",
+                                        "method's name is the same as an existing method in a trait",
                                         |diag| {
                                             diag.span_note(
                                                 trait_method_span,
@@ -138,7 +139,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
                                         cx,
                                         SAME_NAME_METHOD,
                                         impl_span,
-                                        "method's name is same to an existing method in a trait",
+                                        "method's name is the same as an existing method in a trait",
                                         |diag| {
                                             // TODO should we `span_note` on every trait?
                                             // iterate on trait_spans?
diff --git a/src/tools/clippy/clippy_lints/src/self_assignment.rs b/src/tools/clippy/clippy_lints/src/self_assignment.rs
index fbd65fef7d1..b14f0518bdb 100644
--- a/src/tools/clippy/clippy_lints/src/self_assignment.rs
+++ b/src/tools/clippy/clippy_lints/src/self_assignment.rs
@@ -30,6 +30,7 @@ declare_clippy_lint! {
     ///     a.y = a.y;
     /// }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub SELF_ASSIGNMENT,
     correctness,
     "explicit self-assignment"
diff --git a/src/tools/clippy/clippy_lints/src/self_named_constructors.rs b/src/tools/clippy/clippy_lints/src/self_named_constructors.rs
index 9390378d789..d386663e498 100644
--- a/src/tools/clippy/clippy_lints/src/self_named_constructors.rs
+++ b/src/tools/clippy/clippy_lints/src/self_named_constructors.rs
@@ -32,6 +32,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.55.0"]
     pub SELF_NAMED_CONSTRUCTORS,
     style,
     "method should not have the same name as the type it is implemented for"
@@ -75,7 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for SelfNamedConstructors {
             let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did);
             if let Some(Node::Item(x)) = cx.tcx.hir().find(self_id);
             let type_name = x.ident.name.as_str().to_lowercase();
-            if impl_item.ident.name.as_str() == type_name || impl_item.ident.name.as_str().replace("_", "") == type_name;
+            if impl_item.ident.name.as_str() == type_name || impl_item.ident.name.as_str().replace('_', "") == type_name;
 
             then {
                 span_lint(
diff --git a/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs b/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs
index c0e4914efe0..0b3bbbc8155 100644
--- a/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs
+++ b/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs
@@ -29,6 +29,7 @@ declare_clippy_lint! {
     ///     println!("Hello world");
     /// }
     /// ```
+    #[clippy::version = "1.52.0"]
     pub SEMICOLON_IF_NOTHING_RETURNED,
     pedantic,
     "add a semicolon if nothing is returned"
@@ -44,7 +45,7 @@ impl LateLintPass<'_> for SemicolonIfNothingReturned {
             let t_expr = cx.typeck_results().expr_ty(expr);
             if t_expr.is_unit();
             if let snippet = snippet_with_macro_callsite(cx, expr.span, "}");
-            if !snippet.ends_with('}');
+            if !snippet.ends_with('}') && !snippet.ends_with(';');
             if cx.sess().source_map().is_multiline(block.span);
             then {
                 // filter out the desugared `for` loop
diff --git a/src/tools/clippy/clippy_lints/src/serde_api.rs b/src/tools/clippy/clippy_lints/src/serde_api.rs
index 2cd0f85999c..a38b3c4ab69 100644
--- a/src/tools/clippy/clippy_lints/src/serde_api.rs
+++ b/src/tools/clippy/clippy_lints/src/serde_api.rs
@@ -15,6 +15,7 @@ declare_clippy_lint! {
     /// ### Example
     /// Implementing `Visitor::visit_string` but not
     /// `Visitor::visit_str`.
+    #[clippy::version = "pre 1.29.0"]
     pub SERDE_API_MISUSE,
     correctness,
     "various things that will negatively affect your serde experience"
diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs
index 64841f33cc3..f6880af0cab 100644
--- a/src/tools/clippy/clippy_lints/src/shadow.rs
+++ b/src/tools/clippy/clippy_lints/src/shadow.rs
@@ -29,6 +29,7 @@ declare_clippy_lint! {
     /// // Good
     /// let y = &x; // use different variable name
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SHADOW_SAME,
     restriction,
     "rebinding a name to itself, e.g., `let mut x = &mut x`"
@@ -55,6 +56,7 @@ declare_clippy_lint! {
     /// let x = 2;
     /// let y = x + 1;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SHADOW_REUSE,
     restriction,
     "rebinding a name to an expression that re-uses the original value, e.g., `let x = x + 1`"
@@ -84,6 +86,7 @@ declare_clippy_lint! {
     /// // Good
     /// let w = z; // use different variable name
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SHADOW_UNRELATED,
     restriction,
     "rebinding a name without even using the original value"
@@ -102,11 +105,16 @@ impl<'tcx> LateLintPass<'tcx> for Shadow {
             PatKind::Binding(_, hir_id, ident, _) => (hir_id, ident),
             _ => return,
         };
+
+        if pat.span.desugaring_kind().is_some() {
+            return;
+        }
+
         if ident.span.from_expansion() || ident.span.is_dummy() {
             return;
         }
-        let HirId { owner, local_id } = id;
 
+        let HirId { owner, local_id } = id;
         // get (or insert) the list of items for this owner and symbol
         let data = self.bindings.last_mut().unwrap();
         let items_with_name = data.entry(ident.name).or_default();
diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
index f6487b8c46b..28d32203da9 100644
--- a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
-use clippy_utils::in_macro;
 use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind, VisibilityKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
@@ -28,6 +27,7 @@ declare_clippy_lint! {
     ///     regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
     /// }
     /// ```
+    #[clippy::version = "1.43.0"]
     pub SINGLE_COMPONENT_PATH_IMPORTS,
     style,
     "imports with single component path are redundant"
@@ -110,7 +110,7 @@ fn track_uses(
     single_use_usages: &mut Vec<(Symbol, Span, bool)>,
     macros: &mut Vec<Symbol>,
 ) {
-    if in_macro(item.span) || item.vis.kind.is_pub() {
+    if item.span.from_expansion() || item.vis.kind.is_pub() {
         return;
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs
index 3e4e4a8d0c0..df1e85afdd7 100644
--- a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs
+++ b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs
@@ -29,6 +29,7 @@ declare_clippy_lint! {
     /// let mut y = [2u8; SIZE];
     /// unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>() * SIZE) };
     /// ```
+    #[clippy::version = "1.50.0"]
     pub SIZE_OF_IN_ELEMENT_COUNT,
     correctness,
     "using `size_of::<T>` or `size_of_val::<T>` where a count of elements of `T` is expected"
diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
index 3608fe1472d..1ae772ef70b 100644
--- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
+++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     /// let mut vec1 = vec![0; len];
     /// let mut vec2 = vec![0; len];
     /// ```
+    #[clippy::version = "1.32.0"]
     pub SLOW_VECTOR_INITIALIZATION,
     perf,
     "slow vector initialization"
diff --git a/src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs b/src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs
index 4ea1293d504..953d21e07a3 100644
--- a/src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs
+++ b/src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs
@@ -28,6 +28,7 @@ declare_clippy_lint! {
     /// let mut vec = vec![2, 1, 3];
     /// vec.sort_unstable();
     /// ```
+    #[clippy::version = "1.47.0"]
     pub STABLE_SORT_PRIMITIVE,
     perf,
     "use of sort() when sort_unstable() is equivalent"
diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs
index 6435107b8b4..368274440d5 100644
--- a/src/tools/clippy/clippy_lints/src/strings.rs
+++ b/src/tools/clippy/clippy_lints/src/strings.rs
@@ -31,6 +31,7 @@ declare_clippy_lint! {
     /// x += ", World";
     /// x.push_str(", World");
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub STRING_ADD_ASSIGN,
     pedantic,
     "using `x = x + ..` where x is a `String` instead of `push_str()`"
@@ -58,6 +59,7 @@ declare_clippy_lint! {
     /// let x = "Hello".to_owned();
     /// x + ", World";
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub STRING_ADD,
     restriction,
     "using `x + ..` where x is a `String` instead of `push_str()`"
@@ -102,6 +104,7 @@ declare_clippy_lint! {
     /// // Good
     /// let bs = b"a byte string";
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub STRING_LIT_AS_BYTES,
     nursery,
     "calling `as_bytes` on a string literal instead of using a byte string literal"
@@ -125,6 +128,7 @@ declare_clippy_lint! {
     /// ```rust,should_panic
     /// &"Ölkanne"[1..];
     /// ```
+    #[clippy::version = "1.58.0"]
     pub STRING_SLICE,
     restriction,
     "slicing a string"
@@ -227,6 +231,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let _ = &"Hello World!"[6..11];
     /// ```
+    #[clippy::version = "1.50.0"]
     pub STRING_FROM_UTF8_AS_BYTES,
     complexity,
     "casting string slices to byte slices and back"
@@ -371,6 +376,7 @@ declare_clippy_lint! {
     /// // example code which does not raise clippy warning
     /// let _ = "str".to_owned();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub STR_TO_STRING,
     restriction,
     "using `to_string()` on a `&str`, which should be `to_owned()`"
@@ -420,6 +426,7 @@ declare_clippy_lint! {
     /// let msg = String::from("Hello World");
     /// let _ = msg.clone();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub STRING_TO_STRING,
     restriction,
     "using `to_string()` on a `String`, which should be `clone()`"
diff --git a/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs b/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs
index 516fa3d95b4..fee01fb0bd1 100644
--- a/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs
+++ b/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs
@@ -1,14 +1,14 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
-use clippy_utils::paths;
-use clippy_utils::source::snippet_with_macro_callsite;
-use clippy_utils::ty::{is_type_diagnostic_item, is_type_ref_to_diagnostic_item};
+use clippy_utils::source::snippet_with_context;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::visitors::is_expr_unsafe;
+use clippy_utils::{get_parent_node, match_libc_symbol};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir as hir;
+use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, Node, UnsafeSource};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::{sym, Symbol};
+use rustc_span::symbol::sym;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -31,6 +31,7 @@ declare_clippy_lint! {
     /// let cstring = CString::new("foo").expect("CString::new failed");
     /// let len = cstring.as_bytes().len();
     /// ```
+    #[clippy::version = "1.55.0"]
     pub STRLEN_ON_C_STRINGS,
     complexity,
     "using `libc::strlen` on a `CString` or `CStr` value, while `as_bytes().len()` or `to_bytes().len()` respectively can be used instead"
@@ -39,29 +40,35 @@ declare_clippy_lint! {
 declare_lint_pass!(StrlenOnCStrings => [STRLEN_ON_C_STRINGS]);
 
 impl LateLintPass<'tcx> for StrlenOnCStrings {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        if in_macro(expr.span) {
-            return;
-        }
-
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if_chain! {
-            if let hir::ExprKind::Call(func, [recv]) = expr.kind;
-            if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = func.kind;
-
-            if (&paths::LIBC_STRLEN).iter().map(|x| Symbol::intern(x)).eq(
-                path.segments.iter().map(|seg| seg.ident.name));
-            if let hir::ExprKind::MethodCall(path, _, args, _) = recv.kind;
-            if args.len() == 1;
-            if !args.iter().any(|e| e.span.from_expansion());
+            if !expr.span.from_expansion();
+            if let ExprKind::Call(func, [recv]) = expr.kind;
+            if let ExprKind::Path(path) = &func.kind;
+            if let Some(did) = cx.qpath_res(path, func.hir_id).opt_def_id();
+            if match_libc_symbol(cx, did, "strlen");
+            if let ExprKind::MethodCall(path, _, [self_arg], _) = recv.kind;
+            if !recv.span.from_expansion();
             if path.ident.name == sym::as_ptr;
             then {
-                let cstring = &args[0];
-                let ty = cx.typeck_results().expr_ty(cstring);
-                let val_name = snippet_with_macro_callsite(cx, cstring.span, "..");
-                let sugg = if is_type_diagnostic_item(cx, ty, sym::cstring_type){
-                    format!("{}.as_bytes().len()", val_name)
-                } else if is_type_ref_to_diagnostic_item(cx, ty, sym::CStr){
-                    format!("{}.to_bytes().len()", val_name)
+                let ctxt = expr.span.ctxt();
+                let span = match get_parent_node(cx.tcx, expr.hir_id) {
+                    Some(Node::Block(&Block {
+                        rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), span, ..
+                    }))
+                    if span.ctxt() == ctxt && !is_expr_unsafe(cx, self_arg) => {
+                        span
+                    }
+                    _ => expr.span,
+                };
+
+                let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
+                let mut app = Applicability::MachineApplicable;
+                let val_name = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
+                let method_name = if is_type_diagnostic_item(cx, ty, sym::cstring_type) {
+                    "as_bytes"
+                } else if is_type_diagnostic_item(cx, ty, sym::CStr) {
+                    "to_bytes"
                 } else {
                     return;
                 };
@@ -69,11 +76,11 @@ impl LateLintPass<'tcx> for StrlenOnCStrings {
                 span_lint_and_sugg(
                     cx,
                     STRLEN_ON_C_STRINGS,
-                    expr.span,
+                    span,
                     "using `libc::strlen` on a `CString` or `CStr` value",
-                    "try this (you might also need to get rid of `unsafe` block in some cases):",
-                    sugg,
-                    Applicability::Unspecified // Sometimes unnecessary `unsafe` block
+                    "try this",
+                    format!("{}.{}().len()", val_name, method_name),
+                    app,
                 );
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
index 201aa067824..faf43fd9fc1 100644
--- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
+++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
@@ -59,6 +59,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.50.0"]
     pub SUSPICIOUS_OPERATION_GROUPINGS,
     nursery,
     "groupings of binary operations that look suspiciously like typos"
diff --git a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs
index 682fad00a13..a3195de81d1 100644
--- a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs
@@ -25,6 +25,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SUSPICIOUS_ARITHMETIC_IMPL,
     suspicious,
     "suspicious use of operators in impl of arithmetic trait"
@@ -46,6 +47,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub SUSPICIOUS_OP_ASSIGN_IMPL,
     suspicious,
     "suspicious use of operators in impl of OpAssign trait"
diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs
index ef26de5b6b9..4c10b12437d 100644
--- a/src/tools/clippy/clippy_lints/src/swap.rs
+++ b/src/tools/clippy/clippy_lints/src/swap.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{can_mut_borrow_both, differing_macro_contexts, eq_expr_value};
+use clippy_utils::{can_mut_borrow_both, differing_macro_contexts, eq_expr_value, std_or_core};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
@@ -35,6 +35,7 @@ declare_clippy_lint! {
     /// let mut b = 2;
     /// std::mem::swap(&mut a, &mut b);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub MANUAL_SWAP,
     complexity,
     "manual swap of two variables"
@@ -60,6 +61,7 @@ declare_clippy_lint! {
     /// # let mut b = 2;
     /// std::mem::swap(&mut a, &mut b);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ALMOST_SWAPPED,
     correctness,
     "`foo = bar; bar = foo` sequence"
@@ -113,6 +115,8 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa
 
     let first = Sugg::hir_with_applicability(cx, e1, "..", &mut applicability);
     let second = Sugg::hir_with_applicability(cx, e2, "..", &mut applicability);
+    let Some(sugg) = std_or_core(cx) else { return };
+
     span_lint_and_then(
         cx,
         MANUAL_SWAP,
@@ -122,11 +126,11 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa
             diag.span_suggestion(
                 span,
                 "try",
-                format!("std::mem::swap({}, {})", first.mut_addr(), second.mut_addr()),
+                format!("{}::mem::swap({}, {})", sugg, first.mut_addr(), second.mut_addr()),
                 applicability,
             );
             if !is_xor_based {
-                diag.note("or maybe you should use `std::mem::replace`?");
+                diag.note(&format!("or maybe you should use `{}::mem::replace`?", sugg));
             }
         },
     );
@@ -187,26 +191,30 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) {
                 };
 
                 let span = first.span.to(second.span);
+                let Some(sugg) = std_or_core(cx) else { return };
 
                 span_lint_and_then(cx,
-                                   ALMOST_SWAPPED,
-                                   span,
-                                   &format!("this looks like you are trying to swap{}", what),
-                                   |diag| {
-                                       if !what.is_empty() {
-                                           diag.span_suggestion(
-                                               span,
-                                               "try",
-                                               format!(
-                                                   "std::mem::swap({}, {})",
-                                                   lhs,
-                                                   rhs,
-                                               ),
-                                               Applicability::MaybeIncorrect,
-                                           );
-                                           diag.note("or maybe you should use `std::mem::replace`?");
-                                       }
-                                   });
+                    ALMOST_SWAPPED,
+                    span,
+                    &format!("this looks like you are trying to swap{}", what),
+                    |diag| {
+                        if !what.is_empty() {
+                            diag.span_suggestion(
+                                span,
+                                "try",
+                                format!(
+                                    "{}::mem::swap({}, {})",
+                                    sugg,
+                                    lhs,
+                                    rhs,
+                                ),
+                                Applicability::MaybeIncorrect,
+                            );
+                            diag.note(
+                                &format!("or maybe you should use `{}::mem::replace`?", sugg)
+                            );
+                        }
+                    });
             }
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs b/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs
index 4a67cabf323..c9b4b245f4c 100644
--- a/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs
+++ b/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs
@@ -51,6 +51,7 @@ declare_clippy_lint! {
     ///    second_string: String,
     ///}
     /// ```
+    #[clippy::version = "1.41.0"]
     pub TABS_IN_DOC_COMMENTS,
     style,
     "using tabs in doc comments is not recommended"
diff --git a/src/tools/clippy/clippy_lints/src/temporary_assignment.rs b/src/tools/clippy/clippy_lints/src/temporary_assignment.rs
index a9da690339c..3766b8f8ed1 100644
--- a/src/tools/clippy/clippy_lints/src/temporary_assignment.rs
+++ b/src/tools/clippy/clippy_lints/src/temporary_assignment.rs
@@ -17,6 +17,7 @@ declare_clippy_lint! {
     /// ```rust
     /// (0, 0).0 = 1
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TEMPORARY_ASSIGNMENT,
     complexity,
     "assignments to temporaries"
diff --git a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs
index 1c14a919995..5eb58b47838 100644
--- a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs
+++ b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs
@@ -28,6 +28,7 @@ declare_clippy_lint! {
     /// # let radix = 10;
     /// let is_digit = c.is_digit(radix);
     /// ```
+    #[clippy::version = "1.41.0"]
     pub TO_DIGIT_IS_SOME,
     style,
     "`char.is_digit()` is clearer"
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 b7414cec87c..f8b6bdcd3e1 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
@@ -39,6 +39,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub TO_STRING_IN_DISPLAY,
     correctness,
     "`to_string` method used while implementing `Display` trait"
diff --git a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs
index c216a1f81ea..47c0a84cd46 100644
--- a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs
+++ b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs
@@ -28,6 +28,7 @@ declare_clippy_lint! {
     ///     last: [u32; 0],
     /// }
     /// ```
+    #[clippy::version = "1.58.0"]
     pub TRAILING_EMPTY_ARRAY,
     nursery,
     "struct with a trailing zero-sized array but without `#[repr(C)]` or another `repr` attribute"
diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
index 79367c4230c..fb4abceac25 100644
--- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs
+++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::source::{snippet, snippet_with_applicability};
-use clippy_utils::{in_macro, SpanlessHash};
+use clippy_utils::SpanlessHash;
 use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::unhash::UnhashMap;
@@ -28,6 +28,7 @@ declare_clippy_lint! {
     /// ```rust
     /// pub fn foo<T>(t: T) where T: Copy + Clone {}
     /// ```
+    #[clippy::version = "1.38.0"]
     pub TYPE_REPETITION_IN_BOUNDS,
     pedantic,
     "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
@@ -57,6 +58,7 @@ declare_clippy_lint! {
     /// ```rust
     /// fn func<T>(arg: T) where T: Clone + Default {}
     /// ```
+    #[clippy::version = "1.47.0"]
     pub TRAIT_DUPLICATION_IN_BOUNDS,
     pedantic,
     "Check if the same trait bounds are specified twice during a function declaration"
@@ -93,7 +95,7 @@ fn get_trait_res_span_from_bound(bound: &GenericBound<'_>) -> Option<(Res, Span)
 
 impl TraitBounds {
     fn check_type_repetition(self, cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
-        if in_macro(gen.span) {
+        if gen.span.from_expansion() {
             return;
         }
         let hash = |ty| -> u64 {
@@ -107,7 +109,7 @@ impl TraitBounds {
             if_chain! {
                 if let WherePredicate::BoundPredicate(ref p) = bound;
                 if p.bounds.len() as u64 <= self.max_trait_bounds;
-                if !in_macro(p.span);
+                if !p.span.from_expansion();
                 let h = hash(p.bounded_ty);
                 if let Some(ref v) = map.insert(h, p.bounds.iter().collect::<Vec<_>>());
 
@@ -151,7 +153,7 @@ impl TraitBounds {
 }
 
 fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
-    if in_macro(gen.span) || gen.params.is_empty() || gen.where_clause.predicates.is_empty() {
+    if gen.span.from_expansion() || gen.params.is_empty() || gen.where_clause.predicates.is_empty() {
         return;
     }
 
@@ -170,7 +172,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
     for predicate in gen.where_clause.predicates {
         if_chain! {
             if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
-            if !in_macro(bound_predicate.span);
+            if !bound_predicate.span.from_expansion();
             if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
             if let Some(segment) = segments.first();
             if let Some(trait_resolutions_direct) = map.get(&segment.ident);
diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
index e6acf1a94c9..3ad4ec74bf5 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     /// ```ignore
     /// let ptr: *const T = core::intrinsics::transmute('x')
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WRONG_TRANSMUTE,
     correctness,
     "transmutes that are confusing at best, undefined behaviour at worst and always useless"
@@ -55,6 +56,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// core::intrinsics::transmute(t); // where the result type is the same as `t`'s
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USELESS_TRANSMUTE,
     nursery,
     "transmutes that have the same to and from types or could be a cast/coercion"
@@ -80,6 +82,7 @@ declare_clippy_lint! {
     /// # let p: *const [i32] = &[];
     /// p as *const [u16];
     /// ```
+    #[clippy::version = "1.47.0"]
     pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
     complexity,
     "transmutes that could be a pointer cast"
@@ -98,6 +101,7 @@ declare_clippy_lint! {
     /// core::intrinsics::transmute(t) // where the result type is the same as
     ///                                // `*t` or `&t`'s
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub CROSSPOINTER_TRANSMUTE,
     complexity,
     "transmutes that have to or from types that are a pointer to the other"
@@ -125,6 +129,7 @@ declare_clippy_lint! {
     /// // can be written:
     /// let _: &T = &*p;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TRANSMUTE_PTR_TO_REF,
     complexity,
     "transmutes from a pointer to a reference type"
@@ -158,6 +163,7 @@ declare_clippy_lint! {
     /// // should be:
     /// let _ = std::char::from_u32(x).unwrap();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TRANSMUTE_INT_TO_CHAR,
     complexity,
     "transmutes from an integer to a `char`"
@@ -191,6 +197,7 @@ declare_clippy_lint! {
     /// // should be:
     /// let _ = std::str::from_utf8(b).unwrap();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TRANSMUTE_BYTES_TO_STR,
     complexity,
     "transmutes from a `&[u8]` to a `&str`"
@@ -213,6 +220,7 @@ declare_clippy_lint! {
     /// // should be:
     /// let _: bool = x != 0;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TRANSMUTE_INT_TO_BOOL,
     complexity,
     "transmutes from an integer to a `bool`"
@@ -235,6 +243,7 @@ declare_clippy_lint! {
     /// // should be:
     /// let _: f32 = f32::from_bits(1_u32);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TRANSMUTE_INT_TO_FLOAT,
     complexity,
     "transmutes from an integer to a float"
@@ -257,6 +266,7 @@ declare_clippy_lint! {
     /// // should be:
     /// let _: u32 = 1f32.to_bits();
     /// ```
+    #[clippy::version = "1.41.0"]
     pub TRANSMUTE_FLOAT_TO_INT,
     complexity,
     "transmutes from a float to an integer"
@@ -279,6 +289,7 @@ declare_clippy_lint! {
     /// // should be
     /// let x: [u8; 8] = 0i64.to_ne_bytes();
     /// ```
+    #[clippy::version = "1.58.0"]
     pub TRANSMUTE_NUM_TO_BYTES,
     complexity,
     "transmutes from a number to an array of `u8`"
@@ -306,6 +317,7 @@ declare_clippy_lint! {
     /// let _ = ptr as *const f32;
     /// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TRANSMUTE_PTR_TO_PTR,
     pedantic,
     "transmutes from a pointer to a pointer / a reference to a reference"
@@ -337,6 +349,7 @@ declare_clippy_lint! {
     /// ```rust
     /// vec![2_u16].into_iter().map(u32::from).collect::<Vec<_>>();
     /// ```
+    #[clippy::version = "1.40.0"]
     pub UNSOUND_COLLECTION_TRANSMUTE,
     correctness,
     "transmute between collections of layout-incompatible types"
diff --git a/src/tools/clippy/clippy_lints/src/transmuting_null.rs b/src/tools/clippy/clippy_lints/src/transmuting_null.rs
index ef80663d1da..7939dfedc3a 100644
--- a/src/tools/clippy/clippy_lints/src/transmuting_null.rs
+++ b/src/tools/clippy/clippy_lints/src/transmuting_null.rs
@@ -25,6 +25,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
     /// ```
+    #[clippy::version = "1.35.0"]
     pub TRANSMUTING_NULL,
     correctness,
     "transmutes from a null pointer to a reference, which is undefined behavior"
diff --git a/src/tools/clippy/clippy_lints/src/try_err.rs b/src/tools/clippy/clippy_lints/src/try_err.rs
index e9ec120a7f9..e0e7ec9a452 100644
--- a/src/tools/clippy/clippy_lints/src/try_err.rs
+++ b/src/tools/clippy/clippy_lints/src/try_err.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{snippet, snippet_with_macro_callsite};
+use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{differing_macro_contexts, get_parent_expr, in_macro, is_lang_ctor, match_def_path, paths};
+use clippy_utils::{get_parent_expr, is_lang_ctor, match_def_path, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::ResultErr;
@@ -10,7 +10,7 @@ use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, Ty};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
+use rustc_span::{hygiene, sym};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -41,6 +41,7 @@ declare_clippy_lint! {
     ///     Ok(0)
     /// }
     /// ```
+    #[clippy::version = "1.38.0"]
     pub TRY_ERR,
     style,
     "return errors explicitly rather than hiding them behind a `?`"
@@ -93,15 +94,9 @@ impl<'tcx> LateLintPass<'tcx> for TryErr {
                 };
 
                 let expr_err_ty = cx.typeck_results().expr_ty(err_arg);
-                let differing_contexts = differing_macro_contexts(expr.span, err_arg.span);
-
-                let origin_snippet = if in_macro(expr.span) && in_macro(err_arg.span) && differing_contexts {
-                    snippet(cx, err_arg.span.ctxt().outer_expn_data().call_site, "_")
-                } else if err_arg.span.from_expansion() && !in_macro(expr.span) {
-                    snippet_with_macro_callsite(cx, err_arg.span, "_")
-                } else {
-                    snippet(cx, err_arg.span, "_")
-                };
+                let span = hygiene::walk_chain(err_arg.span, try_arg.span.ctxt());
+                let mut applicability = Applicability::MachineApplicable;
+                let origin_snippet = snippet_with_applicability(cx, span, "_", &mut applicability);
                 let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) {
                     "" // already returns
                 } else {
@@ -120,7 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for TryErr {
                     "returning an `Err(_)` with the `?` operator",
                     "try this",
                     suggestion,
-                    Applicability::MachineApplicable
+                    applicability,
                 );
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs
index bbe07db5358..5a7ef760a30 100644
--- a/src/tools/clippy/clippy_lints/src/types/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/types/mod.rs
@@ -43,6 +43,7 @@ declare_clippy_lint! {
     ///     values: Vec<Foo>,
     /// }
     /// ```
+    #[clippy::version = "1.57.0"]
     pub BOX_COLLECTION,
     perf,
     "usage of `Box<Vec<T>>`, vector elements are already on the heap"
@@ -75,6 +76,7 @@ declare_clippy_lint! {
     ///     values: Vec<i32>,
     /// }
     /// ```
+    #[clippy::version = "1.33.0"]
     pub VEC_BOX,
     complexity,
     "usage of `Vec<Box<T>>` where T: Sized, vector elements are already on the heap"
@@ -113,6 +115,7 @@ declare_clippy_lint! {
     ///     Contents::None
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub OPTION_OPTION,
     pedantic,
     "usage of `Option<Option<T>>`"
@@ -152,6 +155,7 @@ declare_clippy_lint! {
     /// # use std::collections::LinkedList;
     /// let x: LinkedList<usize> = LinkedList::new();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub LINKEDLIST,
     pedantic,
     "usage of LinkedList, usually a vector is faster, or a more specialized data structure like a `VecDeque`"
@@ -176,6 +180,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// fn foo(bar: &T) { ... }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub BORROWED_BOX,
     complexity,
     "a borrow of a boxed type"
@@ -200,6 +205,7 @@ declare_clippy_lint! {
     /// ```rust
     /// fn foo(bar: &usize) {}
     /// ```
+    #[clippy::version = "1.44.0"]
     pub REDUNDANT_ALLOCATION,
     perf,
     "redundant allocation"
@@ -234,6 +240,7 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// fn foo(interned: Rc<str>) { ... }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub RC_BUFFER,
     restriction,
     "shared ownership of a buffer type"
@@ -255,6 +262,7 @@ declare_clippy_lint! {
     ///     inner: Rc<Vec<Vec<Box<(u32, u32, u32, u32)>>>>,
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub TYPE_COMPLEXITY,
     complexity,
     "usage of very complex types that might be better factored into `type` definitions"
@@ -287,6 +295,7 @@ declare_clippy_lint! {
     /// use std::cell::RefCell
     /// fn foo(interned: Rc<RefCell<i32>>) { ... }
     /// ```
+    #[clippy::version = "1.55.0"]
     pub RC_MUTEX,
     restriction,
     "usage of `Rc<Mutex<T>>`"
diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
index 11aef50991b..ccc49caf47c 100644
--- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
+++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::is_lint_allowed;
 use clippy_utils::source::{indent_of, reindent_multiline, snippet};
-use clippy_utils::{in_macro, is_lint_allowed};
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
 use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, HirId, Local, UnsafeSource};
@@ -39,6 +39,7 @@ declare_clippy_lint! {
     /// // Safety: references are guaranteed to be non-null.
     /// let ptr = unsafe { NonNull::new_unchecked(a) };
     /// ```
+    #[clippy::version = "1.58.0"]
     pub UNDOCUMENTED_UNSAFE_BLOCKS,
     restriction,
     "creating an unsafe block without explaining why it is safe"
@@ -134,12 +135,12 @@ impl UndocumentedUnsafeBlocks {
 
         let enclosing_scope_span = map.opt_span(enclosing_hir_id)?;
 
-        let between_span = if in_macro(block_span) {
+        let between_span = if block_span.from_expansion() {
             self.macro_expansion = true;
-            enclosing_scope_span.with_hi(block_span.hi())
+            enclosing_scope_span.with_hi(block_span.hi()).source_callsite()
         } else {
             self.macro_expansion = false;
-            enclosing_scope_span.to(block_span)
+            enclosing_scope_span.to(block_span).source_callsite()
         };
 
         let file_name = source_map.span_to_filename(between_span);
@@ -149,6 +150,8 @@ impl UndocumentedUnsafeBlocks {
         let lex_end = (between_span.hi().0 - source_file.start_pos.0) as usize;
         let src_str = source_file.src.as_ref()?[lex_start..lex_end].to_string();
 
+        let source_start_pos = source_file.start_pos.0 as usize + lex_start;
+
         let mut pos = 0;
         let mut comment = false;
 
@@ -171,7 +174,7 @@ impl UndocumentedUnsafeBlocks {
                     if comment {
                         // Get the line number of the "comment" (really wherever the trailing whitespace ended)
                         let comment_line_num = source_file
-                            .lookup_file_pos_with_col_display(BytePos((lex_start + pos).try_into().unwrap()))
+                            .lookup_file_pos(BytePos((source_start_pos + pos).try_into().unwrap()))
                             .0;
                         // Find the block/local's line number
                         let block_line_num = tcx.sess.source_map().lookup_char_pos(block_span.lo()).line;
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 09570616593..c58fa67a023 100644
--- a/src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs
+++ b/src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs
@@ -28,6 +28,7 @@ declare_clippy_lint! {
     ///     std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));
     /// }
     /// ```
+    #[clippy::version = "1.49.0"]
     pub UNDROPPED_MANUALLY_DROPS,
     correctness,
     "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value"
diff --git a/src/tools/clippy/clippy_lints/src/unicode.rs b/src/tools/clippy/clippy_lints/src/unicode.rs
index f49ce696a04..afd7be89a4e 100644
--- a/src/tools/clippy/clippy_lints/src/unicode.rs
+++ b/src/tools/clippy/clippy_lints/src/unicode.rs
@@ -20,6 +20,7 @@ declare_clippy_lint! {
     /// ### Example
     /// You don't see it, but there may be a zero-width space or soft hyphen
     /// some­where in this text.
+    #[clippy::version = "1.49.0"]
     pub INVISIBLE_CHARACTERS,
     correctness,
     "using an invisible character in a string literal, which is confusing"
@@ -27,7 +28,7 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for non-ASCII characters in string literals.
+    /// Checks for non-ASCII characters in string and char literals.
     ///
     /// ### Why is this bad?
     /// Yeah, we know, the 90's called and wanted their charset
@@ -44,6 +45,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let x = String::from("\u{20ac}");
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub NON_ASCII_LITERAL,
     restriction,
     "using any literal non-ASCII chars in a string literal instead of using the `\\u` escape"
@@ -62,6 +64,7 @@ declare_clippy_lint! {
     /// ### Example
     /// You may not see it, but "à"" and "à"" aren't the same string. The
     /// former when escaped is actually `"a\u{300}"` while the latter is `"\u{e0}"`.
+    #[clippy::version = "pre 1.29.0"]
     pub UNICODE_NOT_NFC,
     pedantic,
     "using a Unicode literal not in NFC normal form (see [Unicode tr15](http://www.unicode.org/reports/tr15/) for further information)"
@@ -72,7 +75,7 @@ declare_lint_pass!(Unicode => [INVISIBLE_CHARACTERS, NON_ASCII_LITERAL, UNICODE_
 impl LateLintPass<'_> for Unicode {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
         if let ExprKind::Lit(ref lit) = expr.kind {
-            if let LitKind::Str(_, _) = lit.node {
+            if let LitKind::Str(_, _) | LitKind::Char(_) = lit.node {
                 check_str(cx, lit.span, expr.hir_id);
             }
         }
@@ -103,9 +106,9 @@ fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) {
             "invisible character detected",
             "consider replacing the string with",
             string
-                .replace("\u{200B}", "\\u{200B}")
-                .replace("\u{ad}", "\\u{AD}")
-                .replace("\u{2060}", "\\u{2060}"),
+                .replace('\u{200B}', "\\u{200B}")
+                .replace('\u{ad}', "\\u{AD}")
+                .replace('\u{2060}', "\\u{2060}"),
             Applicability::MachineApplicable,
         );
     }
diff --git a/src/tools/clippy/clippy_lints/src/uninit_vec.rs b/src/tools/clippy/clippy_lints/src/uninit_vec.rs
index f3e8b688105..46cc76b150e 100644
--- a/src/tools/clippy/clippy_lints/src/uninit_vec.rs
+++ b/src/tools/clippy/clippy_lints/src/uninit_vec.rs
@@ -52,6 +52,7 @@ declare_clippy_lint! {
     ///    // perform initialization with `remaining`
     ///    vec.set_len(...);  // Safe to call `set_len()` on initialized part
     ///    ```
+    #[clippy::version = "1.58.0"]
     pub UNINIT_VEC,
     correctness,
     "Vec with uninitialized data"
diff --git a/src/tools/clippy/clippy_lints/src/unit_hash.rs b/src/tools/clippy/clippy_lints/src/unit_hash.rs
index a3a3f2d41c7..26b4e0f58a8 100644
--- a/src/tools/clippy/clippy_lints/src/unit_hash.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_hash.rs
@@ -39,6 +39,7 @@ declare_clippy_lint! {
     /// 	WithValue(x) => x.hash(&mut state),
     /// }
     /// ```
+    #[clippy::version = "1.58.0"]
     pub UNIT_HASH,
     correctness,
     "hashing a unit value, which does nothing"
diff --git a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs
index db0f412f2a1..9fb8f236899 100644
--- a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs
@@ -30,6 +30,7 @@ declare_clippy_lint! {
     /// let mut twins = vec!((1, 1), (2, 2));
     /// twins.sort_by_key(|x| { x.1; });
     /// ```
+    #[clippy::version = "1.47.0"]
     pub UNIT_RETURN_EXPECTING_ORD,
     correctness,
     "fn arguments of type Fn(...) -> Ord returning the unit type ()."
diff --git a/src/tools/clippy/clippy_lints/src/unit_types/mod.rs b/src/tools/clippy/clippy_lints/src/unit_types/mod.rs
index 66b1abbe50b..d9f5b53b413 100644
--- a/src/tools/clippy/clippy_lints/src/unit_types/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_types/mod.rs
@@ -21,6 +21,7 @@ declare_clippy_lint! {
     ///     1;
     /// };
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub LET_UNIT_VALUE,
     pedantic,
     "creating a `let` binding to a value of unit type, which usually can't be used afterwards"
@@ -68,6 +69,7 @@ declare_clippy_lint! {
     /// assert_eq!({ foo(); }, { bar(); });
     /// ```
     /// will always succeed
+    #[clippy::version = "pre 1.29.0"]
     pub UNIT_CMP,
     correctness,
     "comparing unit values"
@@ -88,6 +90,7 @@ declare_clippy_lint! {
     ///     baz(a);
     /// })
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNIT_ARG,
     complexity,
     "passing unit to a function"
diff --git a/src/tools/clippy/clippy_lints/src/unnamed_address.rs b/src/tools/clippy/clippy_lints/src/unnamed_address.rs
index 1eafdee0352..0bcafde658a 100644
--- a/src/tools/clippy/clippy_lints/src/unnamed_address.rs
+++ b/src/tools/clippy/clippy_lints/src/unnamed_address.rs
@@ -24,6 +24,7 @@ declare_clippy_lint! {
     ///     // ...
     /// }
     /// ```
+    #[clippy::version = "1.44.0"]
     pub FN_ADDRESS_COMPARISONS,
     correctness,
     "comparison with an address of a function item"
@@ -47,6 +48,7 @@ declare_clippy_lint! {
     ///     ...
     /// }
     /// ```
+    #[clippy::version = "1.44.0"]
     pub VTABLE_ADDRESS_COMPARISONS,
     correctness,
     "comparison with an address of a trait vtable"
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs b/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs
index 4cfd2df551f..839a4bdab09 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs
@@ -26,6 +26,7 @@ declare_clippy_lint! {
     /// ```rust
     /// use std::io;
     /// ```
+    #[clippy::version = "1.53.0"]
     pub UNNECESSARY_SELF_IMPORTS,
     restriction,
     "imports ending in `::{self}`, which can be omitted"
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 26b56e0f2f3..d024577f485 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs
@@ -39,6 +39,7 @@ declare_clippy_lint! {
     /// # let mut vec: Vec<A> = Vec::new();
     /// vec.sort_by_key(|a| a.foo());
     /// ```
+    #[clippy::version = "1.46.0"]
     pub UNNECESSARY_SORT_BY,
     complexity,
     "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer"
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
index c940cf077d1..1728533f18b 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
-use clippy_utils::{contains_return, in_macro, is_lang_ctor, return_ty, visitors::find_all_ret_expressions};
+use clippy_utils::{contains_return, is_lang_ctor, return_ty, visitors::find_all_ret_expressions};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
@@ -49,6 +49,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "1.50.0"]
     pub UNNECESSARY_WRAPS,
     pedantic,
     "functions that only return `Ok` or `Some`"
@@ -116,7 +117,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
         let mut suggs = Vec::new();
         let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| {
             if_chain! {
-                if !in_macro(ret_expr.span);
+                if !ret_expr.span.from_expansion();
                 // Check if a function call.
                 if let ExprKind::Call(func, [arg]) = ret_expr.kind;
                 // Check if OPTION_SOME or RESULT_OK, depending on return type.
diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
index d6cf7190abb..0bd151fed91 100644
--- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
@@ -39,6 +39,7 @@ declare_clippy_lint! {
     ///     if let Some(0 | 2) = Some(0) {}
     /// }
     /// ```
+    #[clippy::version = "1.46.0"]
     pub UNNESTED_OR_PATTERNS,
     pedantic,
     "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`"
diff --git a/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs b/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs
index 3c694af2b9d..44b1989dbc6 100644
--- a/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs
+++ b/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs
@@ -21,6 +21,7 @@ declare_clippy_lint! {
     /// extern crate crossbeam;
     /// use crossbeam::{spawn_unsafe as spawn};
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNSAFE_REMOVED_FROM_NAME,
     style,
     "`unsafe` removed from API names on import"
diff --git a/src/tools/clippy/clippy_lints/src/unused_async.rs b/src/tools/clippy/clippy_lints/src/unused_async.rs
index f4808682b69..1ccb78425c2 100644
--- a/src/tools/clippy/clippy_lints/src/unused_async.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_async.rs
@@ -29,6 +29,7 @@ declare_clippy_lint! {
     /// }
     /// let number_future = async { get_random_number_improved() };
     /// ```
+    #[clippy::version = "1.54.0"]
     pub UNUSED_ASYNC,
     pedantic,
     "finds async functions with no await statements"
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 031b182bd2f..d4b5c9770a2 100644
--- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
@@ -29,6 +29,7 @@ declare_clippy_lint! {
     ///     Ok(())
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNUSED_IO_AMOUNT,
     correctness,
     "unused written/read amount"
diff --git a/src/tools/clippy/clippy_lints/src/unused_self.rs b/src/tools/clippy/clippy_lints/src/unused_self.rs
index c961f995667..aa105580ee3 100644
--- a/src/tools/clippy/clippy_lints/src/unused_self.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_self.rs
@@ -29,6 +29,7 @@ declare_clippy_lint! {
     ///     fn method() {}
     /// }
     /// ```
+    #[clippy::version = "1.40.0"]
     pub UNUSED_SELF,
     pedantic,
     "methods that contain a `self` argument but don't use it"
diff --git a/src/tools/clippy/clippy_lints/src/unused_unit.rs b/src/tools/clippy/clippy_lints/src/unused_unit.rs
index 1164ac4938f..48c17fa2a40 100644
--- a/src/tools/clippy/clippy_lints/src/unused_unit.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_unit.rs
@@ -28,6 +28,7 @@ declare_clippy_lint! {
     /// ```rust
     /// fn return_unit() {}
     /// ```
+    #[clippy::version = "1.31.0"]
     pub UNUSED_UNIT,
     style,
     "needless unit expression"
diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs
index ebaa9dcbbf8..71771aae44b 100644
--- a/src/tools/clippy/clippy_lints/src/unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/unwrap.rs
@@ -39,6 +39,7 @@ declare_clippy_lint! {
     ///     do_something_with(value)
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub UNNECESSARY_UNWRAP,
     complexity,
     "checks for calls of `unwrap[_err]()` that cannot fail"
@@ -65,6 +66,7 @@ declare_clippy_lint! {
     /// ```
     ///
     /// This code will always panic. The if condition should probably be inverted.
+    #[clippy::version = "pre 1.29.0"]
     pub PANICKING_UNWRAP,
     correctness,
     "checks for calls of `unwrap[_err]()` that will always fail"
@@ -229,8 +231,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
         } else {
             // find `unwrap[_err]()` calls:
             if_chain! {
-                if let ExprKind::MethodCall(method_name, _, args, _) = expr.kind;
-                if let Some(id) = path_to_local(&args[0]);
+                if let ExprKind::MethodCall(method_name, _, [self_arg, ..], _) = expr.kind;
+                if let Some(id) = path_to_local(self_arg);
                 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()
diff --git a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs
index 6447e3fa2ca..994df85cb8a 100644
--- a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs
+++ b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs
@@ -51,6 +51,7 @@ declare_clippy_lint! {
     ///     Ok(())
     /// }
     /// ```
+    #[clippy::version = "1.48.0"]
     pub UNWRAP_IN_RESULT,
     restriction,
     "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`"
diff --git a/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs b/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs
index dbf335a70c8..4773e350760 100644
--- a/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs
+++ b/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs
@@ -33,6 +33,7 @@ declare_clippy_lint! {
     /// ```rust
     /// struct HttpResponse;
     /// ```
+    #[clippy::version = "1.51.0"]
     pub UPPER_CASE_ACRONYMS,
     style,
     "capitalized acronyms are against the naming convention"
diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs
index 9ae50e47ca4..059f7f647f8 100644
--- a/src/tools/clippy/clippy_lints/src/use_self.rs
+++ b/src/tools/clippy/clippy_lints/src/use_self.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::ty::same_type_and_consts;
-use clippy_utils::{in_macro, meets_msrv, msrvs};
+use clippy_utils::{meets_msrv, msrvs};
 use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -51,6 +51,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USE_SELF,
     nursery,
     "unnecessary structure name repetition whereas `Self` is applicable"
@@ -197,7 +198,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
 
     fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
         if_chain! {
-            if !in_macro(hir_ty.span);
+            if !hir_ty.span.from_expansion();
             if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
             if let Some(&StackItem::Check {
                 impl_id,
@@ -214,8 +215,8 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
             };
             if same_type_and_consts(ty, cx.tcx.type_of(impl_id));
             let hir = cx.tcx.hir();
-            let id = hir.get_parent_node(hir_ty.hir_id);
-            if !hir.opt_span(id).map_or(false, in_macro);
+            // prevents false positive on `#[derive(serde::Deserialize)]`
+            if !hir.span(hir.get_parent_node(hir_ty.hir_id)).in_derive_expansion();
             then {
                 span_lint(cx, hir_ty.span);
             }
@@ -224,7 +225,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
 
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
         if_chain! {
-            if !in_macro(expr.span);
+            if !expr.span.from_expansion();
             if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
             if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last();
             if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id);
diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs
index 88f11542072..0e4b32541c9 100644
--- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs
+++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs
@@ -28,6 +28,7 @@ declare_clippy_lint! {
     /// // Good
     /// let s: String = format!("hello");
     /// ```
+    #[clippy::version = "1.45.0"]
     pub USELESS_CONVERSION,
     complexity,
     "calls to `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` which perform useless conversions to the same type"
diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs
index f93d7782e25..d20bf341318 100644
--- a/src/tools/clippy/clippy_lints/src/utils/author.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/author.rs
@@ -1,16 +1,16 @@
 //! A group of attributes that can be attached to Rust code in order
 //! to generate a clippy lint detecting said code automatically.
 
-use clippy_utils::get_attr;
+use clippy_utils::{get_attr, higher};
 use rustc_ast::ast::{LitFloatType, LitKind};
-use rustc_ast::walk_list;
+use rustc_ast::LitIntType;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir as hir;
-use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
-use rustc_hir::{Block, Expr, ExprKind, Pat, PatKind, QPath, Stmt, StmtKind, TyKind};
+use rustc_hir::{ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::hir::map::Map;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::{Ident, Symbol};
+use std::fmt::{Display, Formatter, Write as _};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -53,6 +53,42 @@ declare_clippy_lint! {
 
 declare_lint_pass!(Author => [LINT_AUTHOR]);
 
+/// Writes a line of output with indentation added
+macro_rules! out {
+    ($($t:tt)*) => {
+        println!("    {}", format_args!($($t)*))
+    };
+}
+
+/// The variables passed in are replaced with `&Binding`s where the `value` field is set
+/// to the original value of the variable. The `name` field is set to the name of the variable
+/// (using `stringify!`) and is adjusted to avoid duplicate names.
+/// Note that the `Binding` may be printed directly to output the `name`.
+macro_rules! bind {
+    ($self:ident $(, $name:ident)+) => {
+        $(let $name = & $self.bind(stringify!($name), $name);)+
+    };
+}
+
+/// Transforms the given `Option<T>` varibles into `OptionPat<Binding<T>>`.
+/// This displays as `Some($name)` or `None` when printed. The name of the inner binding
+/// is set to the name of the variable passed to the macro.
+macro_rules! opt_bind {
+    ($self:ident $(, $name:ident)+) => {
+        $(let $name = OptionPat::new($name.map(|o| $self.bind(stringify!($name), o)));)+
+    };
+}
+
+/// Creates a `Binding` that accesses the field of an existing `Binding`
+macro_rules! field {
+    ($binding:ident.$field:ident) => {
+        &Binding {
+            name: $binding.name.to_string() + stringify!(.$field),
+            value: $binding.value.$field,
+        }
+    };
+}
+
 fn prelude() {
     println!("if_chain! {{");
 }
@@ -66,674 +102,594 @@ fn done() {
 
 impl<'tcx> LateLintPass<'tcx> for Author {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
-        if !has_attr(cx, item.hir_id()) {
-            return;
-        }
-        prelude();
-        PrintVisitor::new("item").visit_item(item);
-        done();
+        check_item(cx, item.hir_id());
     }
 
     fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
-        if !has_attr(cx, item.hir_id()) {
-            return;
-        }
-        prelude();
-        PrintVisitor::new("item").visit_impl_item(item);
-        done();
+        check_item(cx, item.hir_id());
     }
 
     fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
-        if !has_attr(cx, item.hir_id()) {
-            return;
-        }
-        prelude();
-        PrintVisitor::new("item").visit_trait_item(item);
-        done();
+        check_item(cx, item.hir_id());
     }
 
-    fn check_variant(&mut self, cx: &LateContext<'tcx>, var: &'tcx hir::Variant<'_>) {
-        if !has_attr(cx, var.id) {
-            return;
-        }
-        prelude();
-        let parent_hir_id = cx.tcx.hir().get_parent_node(var.id);
-        PrintVisitor::new("var").visit_variant(var, &hir::Generics::empty(), parent_hir_id);
-        done();
-    }
-
-    fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'_>) {
-        if !has_attr(cx, field.hir_id) {
-            return;
-        }
-        prelude();
-        PrintVisitor::new("field").visit_field_def(field);
-        done();
+    fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) {
+        check_node(cx, arm.hir_id, |v| {
+            v.arm(&v.bind("arm", arm));
+        });
     }
 
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        if !has_attr(cx, expr.hir_id) {
-            return;
-        }
-        prelude();
-        PrintVisitor::new("expr").visit_expr(expr);
-        done();
-    }
-
-    fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) {
-        if !has_attr(cx, arm.hir_id) {
-            return;
-        }
-        prelude();
-        PrintVisitor::new("arm").visit_arm(arm);
-        done();
+        check_node(cx, expr.hir_id, |v| {
+            v.expr(&v.bind("expr", expr));
+        });
     }
 
     fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) {
-        if !has_attr(cx, stmt.hir_id) {
-            return;
-        }
         match stmt.kind {
             StmtKind::Expr(e) | StmtKind::Semi(e) if has_attr(cx, e.hir_id) => return,
             _ => {},
         }
+        check_node(cx, stmt.hir_id, |v| {
+            v.stmt(&v.bind("stmt", stmt));
+        });
+    }
+}
+
+fn check_item(cx: &LateContext<'_>, hir_id: HirId) {
+    let hir = cx.tcx.hir();
+    if let Some(body_id) = hir.maybe_body_owned_by(hir_id) {
+        check_node(cx, hir_id, |v| {
+            v.expr(&v.bind("expr", &hir.body(body_id).value));
+        });
+    }
+}
+
+fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_, '_>)) {
+    if has_attr(cx, hir_id) {
         prelude();
-        PrintVisitor::new("stmt").visit_stmt(stmt);
+        f(&PrintVisitor::new(cx));
         done();
     }
+}
 
-    fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ForeignItem<'_>) {
-        if !has_attr(cx, item.hir_id()) {
-            return;
+struct Binding<T> {
+    name: String,
+    value: T,
+}
+
+impl<T> Display for Binding<T> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        f.write_str(&self.name)
+    }
+}
+
+struct OptionPat<T> {
+    pub opt: Option<T>,
+}
+
+impl<T> OptionPat<T> {
+    fn new(opt: Option<T>) -> Self {
+        Self { opt }
+    }
+
+    fn if_some(&self, f: impl Fn(&T)) {
+        if let Some(t) = &self.opt {
+            f(t);
+        }
+    }
+}
+
+impl<T: Display> Display for OptionPat<T> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        match &self.opt {
+            None => f.write_str("None"),
+            Some(node) => write!(f, "Some({node})"),
         }
-        prelude();
-        PrintVisitor::new("item").visit_foreign_item(item);
-        done();
     }
 }
 
-impl PrintVisitor {
-    #[must_use]
-    fn new(s: &'static str) -> Self {
+struct PrintVisitor<'a, 'tcx> {
+    cx: &'a LateContext<'tcx>,
+    /// Fields are the current index that needs to be appended to pattern
+    /// binding names
+    ids: std::cell::Cell<FxHashMap<&'static str, u32>>,
+}
+
+#[allow(clippy::unused_self)]
+impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
+    fn new(cx: &'a LateContext<'tcx>) -> Self {
         Self {
-            ids: FxHashMap::default(),
-            current: s.to_owned(),
+            cx,
+            ids: std::cell::Cell::default(),
         }
     }
 
-    fn next(&mut self, s: &'static str) -> String {
-        use std::collections::hash_map::Entry::{Occupied, Vacant};
-        match self.ids.entry(s) {
-            // already there: start numbering from `1`
-            Occupied(mut occ) => {
-                let val = occ.get_mut();
-                *val += 1;
-                format!("{}{}", s, *val)
-            },
-            // not there: insert and return name as given
-            Vacant(vac) => {
-                vac.insert(0);
-                s.to_owned()
+    fn next(&self, s: &'static str) -> String {
+        let mut ids = self.ids.take();
+        let out = match *ids.entry(s).and_modify(|n| *n += 1).or_default() {
+            // first usage of the name, use it as is
+            0 => s.to_string(),
+            // append a number starting with 1
+            n => format!("{s}{n}"),
+        };
+        self.ids.set(ids);
+        out
+    }
+
+    fn bind<T>(&self, name: &'static str, value: T) -> Binding<T> {
+        let name = self.next(name);
+        Binding { name, value }
+    }
+
+    fn option<T: Copy>(&self, option: &Binding<Option<T>>, name: &'static str, f: impl Fn(&Binding<T>)) {
+        match option.value {
+            None => out!("if {option}.is_none();"),
+            Some(value) => {
+                let value = &self.bind(name, value);
+                out!("if let Some({value}) = {option};");
+                f(value);
             },
         }
     }
 
-    fn print_qpath(&mut self, path: &QPath<'_>) {
-        if let QPath::LangItem(lang_item, _) = *path {
-            println!(
-                "    if matches!({}, QPath::LangItem(LangItem::{:?}, _));",
-                self.current, lang_item,
-            );
+    fn slice<T>(&self, slice: &Binding<&[T]>, f: impl Fn(&Binding<&T>)) {
+        if slice.value.is_empty() {
+            out!("if {slice}.is_empty();");
         } else {
-            print!("    if match_qpath({}, &[", self.current);
-            print_path(path, &mut true);
-            println!("]);");
+            out!("if {slice}.len() == {};", slice.value.len());
+            for (i, value) in slice.value.iter().enumerate() {
+                let name = format!("{slice}[{i}]");
+                f(&Binding { name, value });
+            }
         }
     }
-}
 
-struct PrintVisitor {
-    /// Fields are the current index that needs to be appended to pattern
-    /// binding names
-    ids: FxHashMap<&'static str, usize>,
-    /// the name that needs to be destructured
-    current: String,
-}
+    fn destination(&self, destination: &Binding<hir::Destination>) {
+        self.option(field!(destination.label), "label", |label| {
+            self.ident(field!(label.ident));
+        });
+    }
+
+    fn ident(&self, ident: &Binding<Ident>) {
+        out!("if {ident}.as_str() == {:?};", ident.value.as_str());
+    }
+
+    fn symbol(&self, symbol: &Binding<Symbol>) {
+        out!("if {symbol}.as_str() == {:?};", symbol.value.as_str());
+    }
 
-impl<'tcx> Visitor<'tcx> for PrintVisitor {
-    type Map = Map<'tcx>;
+    fn qpath(&self, qpath: &Binding<&QPath<'_>>) {
+        if let QPath::LangItem(lang_item, _) = *qpath.value {
+            out!("if matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _));");
+        } else {
+            out!("if match_qpath({qpath}, &[{}]);", path_to_string(qpath.value));
+        }
+    }
+
+    fn lit(&self, lit: &Binding<&Lit>) {
+        let kind = |kind| out!("if let LitKind::{kind} = {lit}.node;");
+        macro_rules! kind {
+            ($($t:tt)*) => (kind(format_args!($($t)*)));
+        }
+
+        match lit.value.node {
+            LitKind::Bool(val) => kind!("Bool({val:?})"),
+            LitKind::Char(c) => kind!("Char({c:?})"),
+            LitKind::Err(val) => kind!("Err({val})"),
+            LitKind::Byte(b) => kind!("Byte({b})"),
+            LitKind::Int(i, suffix) => {
+                let int_ty = match suffix {
+                    LitIntType::Signed(int_ty) => format!("LitIntType::Signed(IntTy::{int_ty:?})"),
+                    LitIntType::Unsigned(uint_ty) => format!("LitIntType::Unsigned(UintTy::{uint_ty:?})"),
+                    LitIntType::Unsuffixed => String::from("LitIntType::Unsuffixed"),
+                };
+                kind!("Int({i}, {int_ty})");
+            },
+            LitKind::Float(_, suffix) => {
+                let float_ty = match suffix {
+                    LitFloatType::Suffixed(suffix_ty) => format!("LitFloatType::Suffixed(FloatTy::{suffix_ty:?})"),
+                    LitFloatType::Unsuffixed => String::from("LitFloatType::Unsuffixed"),
+                };
+                kind!("Float(_, {float_ty})");
+            },
+            LitKind::ByteStr(ref vec) => {
+                bind!(self, vec);
+                kind!("ByteStr(ref {vec})");
+                out!("if let [{:?}] = **{vec};", vec.value);
+            },
+            LitKind::Str(s, _) => {
+                bind!(self, s);
+                kind!("Str({s}, _)");
+                self.symbol(s);
+            },
+        }
+    }
+
+    fn arm(&self, arm: &Binding<&hir::Arm<'_>>) {
+        self.pat(field!(arm.pat));
+        match arm.value.guard {
+            None => out!("if {arm}.guard.is_none();"),
+            Some(hir::Guard::If(expr)) => {
+                bind!(self, expr);
+                out!("if let Some(Guard::If({expr})) = {arm}.guard;");
+                self.expr(expr);
+            },
+            Some(hir::Guard::IfLet(pat, expr)) => {
+                bind!(self, pat, expr);
+                out!("if let Some(Guard::IfLet({pat}, {expr}) = {arm}.guard;");
+                self.pat(pat);
+                self.expr(expr);
+            },
+        }
+        self.expr(field!(arm.body));
+    }
 
     #[allow(clippy::too_many_lines)]
-    fn visit_expr(&mut self, expr: &Expr<'_>) {
-        print!("    if let ExprKind::");
-        let current = format!("{}.kind", self.current);
-        match expr.kind {
+    fn expr(&self, expr: &Binding<&hir::Expr<'_>>) {
+        if let Some(higher::While { condition, body }) = higher::While::hir(expr.value) {
+            bind!(self, condition, body);
+            out!(
+                "if let Some(higher::While {{ condition: {condition}, body: {body} }}) \
+                = higher::While::hir({expr});"
+            );
+            self.expr(condition);
+            self.expr(body);
+            return;
+        }
+
+        if let Some(higher::WhileLet {
+            let_pat,
+            let_expr,
+            if_then,
+        }) = higher::WhileLet::hir(expr.value)
+        {
+            bind!(self, let_pat, let_expr, if_then);
+            out!(
+                "if let Some(higher::WhileLet {{ let_pat: {let_pat}, let_expr: {let_expr}, if_then: {if_then} }}) \
+                = higher::WhileLet::hir({expr});"
+            );
+            self.pat(let_pat);
+            self.expr(let_expr);
+            self.expr(if_then);
+            return;
+        }
+
+        if let Some(higher::ForLoop { pat, arg, body, .. }) = higher::ForLoop::hir(expr.value) {
+            bind!(self, pat, arg, body);
+            out!(
+                "if let Some(higher::ForLoop {{ pat: {pat}, arg: {arg}, body: {body}, .. }}) \
+                = higher::ForLoop::hir({expr});"
+            );
+            self.pat(pat);
+            self.expr(arg);
+            self.expr(body);
+            return;
+        }
+
+        let kind = |kind| out!("if let ExprKind::{kind} = {expr}.kind;");
+        macro_rules! kind {
+            ($($t:tt)*) => (kind(format_args!($($t)*)));
+        }
+
+        match expr.value.kind {
             ExprKind::Let(pat, expr, _) => {
-                let let_pat = self.next("pat");
-                let let_expr = self.next("expr");
-                println!("    Let(ref {}, ref {}, _) = {};", let_pat, let_expr, current);
-                self.current = let_expr;
-                self.visit_expr(expr);
-                self.current = let_pat;
-                self.visit_pat(pat);
+                bind!(self, pat, expr);
+                kind!("Let({pat}, {expr}, _)");
+                self.pat(pat);
+                self.expr(expr);
             },
             ExprKind::Box(inner) => {
-                let inner_pat = self.next("inner");
-                println!("Box(ref {}) = {};", inner_pat, current);
-                self.current = inner_pat;
-                self.visit_expr(inner);
+                bind!(self, inner);
+                kind!("Box({inner})");
+                self.expr(inner);
             },
             ExprKind::Array(elements) => {
-                let elements_pat = self.next("elements");
-                println!("Array(ref {}) = {};", elements_pat, current);
-                println!("    if {}.len() == {};", elements_pat, elements.len());
-                for (i, element) in elements.iter().enumerate() {
-                    self.current = format!("{}[{}]", elements_pat, i);
-                    self.visit_expr(element);
-                }
+                bind!(self, elements);
+                kind!("Array({elements})");
+                self.slice(elements, |e| self.expr(e));
             },
             ExprKind::Call(func, args) => {
-                let func_pat = self.next("func");
-                let args_pat = self.next("args");
-                println!("Call(ref {}, ref {}) = {};", func_pat, args_pat, current);
-                self.current = func_pat;
-                self.visit_expr(func);
-                println!("    if {}.len() == {};", args_pat, args.len());
-                for (i, arg) in args.iter().enumerate() {
-                    self.current = format!("{}[{}]", args_pat, i);
-                    self.visit_expr(arg);
-                }
+                bind!(self, func, args);
+                kind!("Call({func}, {args})");
+                self.expr(func);
+                self.slice(args, |e| self.expr(e));
             },
-            ExprKind::MethodCall(_method_name, ref _generics, _args, ref _fn_span) => {
-                println!(
-                    "MethodCall(ref method_name, ref generics, ref args, ref fn_span) = {};",
-                    current
-                );
-                println!("    // unimplemented: `ExprKind::MethodCall` is not further destructured at the moment");
+            ExprKind::MethodCall(method_name, _, args, _) => {
+                bind!(self, method_name, args);
+                kind!("MethodCall({method_name}, _, {args}, _)");
+                self.ident(field!(method_name.ident));
+                self.slice(args, |e| self.expr(e));
             },
             ExprKind::Tup(elements) => {
-                let elements_pat = self.next("elements");
-                println!("Tup(ref {}) = {};", elements_pat, current);
-                println!("    if {}.len() == {};", elements_pat, elements.len());
-                for (i, element) in elements.iter().enumerate() {
-                    self.current = format!("{}[{}]", elements_pat, i);
-                    self.visit_expr(element);
-                }
-            },
-            ExprKind::Binary(ref op, left, right) => {
-                let op_pat = self.next("op");
-                let left_pat = self.next("left");
-                let right_pat = self.next("right");
-                println!(
-                    "Binary(ref {}, ref {}, ref {}) = {};",
-                    op_pat, left_pat, right_pat, current
-                );
-                println!("    if BinOpKind::{:?} == {}.node;", op.node, op_pat);
-                self.current = left_pat;
-                self.visit_expr(left);
-                self.current = right_pat;
-                self.visit_expr(right);
-            },
-            ExprKind::Unary(ref op, inner) => {
-                let inner_pat = self.next("inner");
-                println!("Unary(UnOp::{:?}, ref {}) = {};", op, inner_pat, current);
-                self.current = inner_pat;
-                self.visit_expr(inner);
+                bind!(self, elements);
+                kind!("Tup({elements})");
+                self.slice(elements, |e| self.expr(e));
+            },
+            ExprKind::Binary(op, left, right) => {
+                bind!(self, op, left, right);
+                kind!("Binary({op}, {left}, {right})");
+                out!("if BinOpKind::{:?} == {op}.node;", op.value.node);
+                self.expr(left);
+                self.expr(right);
+            },
+            ExprKind::Unary(op, inner) => {
+                bind!(self, inner);
+                kind!("Unary(UnOp::{op:?}, {inner})");
+                self.expr(inner);
             },
             ExprKind::Lit(ref lit) => {
-                let lit_pat = self.next("lit");
-                println!("Lit(ref {}) = {};", lit_pat, current);
-                match lit.node {
-                    LitKind::Bool(val) => println!("    if let LitKind::Bool({:?}) = {}.node;", val, lit_pat),
-                    LitKind::Char(c) => println!("    if let LitKind::Char({:?}) = {}.node;", c, lit_pat),
-                    LitKind::Err(val) => println!("    if let LitKind::Err({}) = {}.node;", val, lit_pat),
-                    LitKind::Byte(b) => println!("    if let LitKind::Byte({}) = {}.node;", b, lit_pat),
-                    // FIXME: also check int type
-                    LitKind::Int(i, _) => println!("    if let LitKind::Int({}, _) = {}.node;", i, lit_pat),
-                    LitKind::Float(_, LitFloatType::Suffixed(_)) => println!(
-                        "    if let LitKind::Float(_, LitFloatType::Suffixed(_)) = {}.node;",
-                        lit_pat
-                    ),
-                    LitKind::Float(_, LitFloatType::Unsuffixed) => println!(
-                        "    if let LitKind::Float(_, LitFloatType::Unsuffixed) = {}.node;",
-                        lit_pat
-                    ),
-                    LitKind::ByteStr(ref vec) => {
-                        let vec_pat = self.next("vec");
-                        println!("    if let LitKind::ByteStr(ref {}) = {}.node;", vec_pat, lit_pat);
-                        println!("    if let [{:?}] = **{};", vec, vec_pat);
-                    },
-                    LitKind::Str(ref text, _) => {
-                        let str_pat = self.next("s");
-                        println!("    if let LitKind::Str(ref {}, _) = {}.node;", str_pat, lit_pat);
-                        println!("    if {}.as_str() == {:?}", str_pat, &*text.as_str());
-                    },
+                bind!(self, lit);
+                kind!("Lit(ref {lit})");
+                self.lit(lit);
+            },
+            ExprKind::Cast(expr, cast_ty) => {
+                bind!(self, expr, cast_ty);
+                kind!("Cast({expr}, {cast_ty})");
+                if let TyKind::Path(ref qpath) = cast_ty.value.kind {
+                    bind!(self, qpath);
+                    out!("if let TyKind::Path(ref {qpath}) = {cast_ty}.kind;");
+                    self.qpath(qpath);
                 }
-            },
-            ExprKind::Cast(expr, ty) => {
-                let cast_pat = self.next("expr");
-                let cast_ty = self.next("cast_ty");
-                let qp_label = self.next("qp");
-
-                println!("Cast(ref {}, ref {}) = {};", cast_pat, cast_ty, current);
-                if let TyKind::Path(ref qp) = ty.kind {
-                    println!("    if let TyKind::Path(ref {}) = {}.kind;", qp_label, cast_ty);
-                    self.current = qp_label;
-                    self.print_qpath(qp);
-                }
-                self.current = cast_pat;
-                self.visit_expr(expr);
+                self.expr(expr);
             },
             ExprKind::Type(expr, _ty) => {
-                let cast_pat = self.next("expr");
-                println!("Type(ref {}, _) = {};", cast_pat, current);
-                self.current = cast_pat;
-                self.visit_expr(expr);
-            },
-            ExprKind::Loop(body, _, des, _) => {
-                let body_pat = self.next("body");
-                let label_pat = self.next("label");
-                println!(
-                    "Loop(ref {}, ref {}, LoopSource::{:?}) = {};",
-                    body_pat, label_pat, des, current
-                );
-                self.current = body_pat;
-                self.visit_block(body);
-            },
-            ExprKind::If(cond, then, ref opt_else) => {
-                let cond_pat = self.next("cond");
-                let then_pat = self.next("then");
-                if let Some(else_) = *opt_else {
-                    let else_pat = self.next("else_");
-                    println!(
-                        "If(ref {}, ref {}, Some(ref {})) = {};",
-                        cond_pat, then_pat, else_pat, current
-                    );
-                    self.current = else_pat;
-                    self.visit_expr(else_);
-                } else {
-                    println!("If(ref {}, ref {}, None) = {};", cond_pat, then_pat, current);
-                }
-                self.current = cond_pat;
-                self.visit_expr(cond);
-                self.current = then_pat;
-                self.visit_expr(then);
-            },
-            ExprKind::Match(expr, arms, des) => {
-                let expr_pat = self.next("expr");
-                let arms_pat = self.next("arms");
-                println!(
-                    "Match(ref {}, ref {}, MatchSource::{:?}) = {};",
-                    expr_pat, arms_pat, des, current
-                );
-                self.current = expr_pat;
-                self.visit_expr(expr);
-                println!("    if {}.len() == {};", arms_pat, arms.len());
-                for (i, arm) in arms.iter().enumerate() {
-                    self.current = format!("{}[{}].body", arms_pat, i);
-                    self.visit_expr(arm.body);
-                    if let Some(ref guard) = arm.guard {
-                        let guard_pat = self.next("guard");
-                        println!("    if let Some(ref {}) = {}[{}].guard;", guard_pat, arms_pat, i);
-                        match guard {
-                            hir::Guard::If(if_expr) => {
-                                let if_expr_pat = self.next("expr");
-                                println!("    if let Guard::If(ref {}) = {};", if_expr_pat, guard_pat);
-                                self.current = if_expr_pat;
-                                self.visit_expr(if_expr);
-                            },
-                            hir::Guard::IfLet(if_let_pat, if_let_expr) => {
-                                let if_let_pat_pat = self.next("pat");
-                                let if_let_expr_pat = self.next("expr");
-                                println!(
-                                    "    if let Guard::IfLet(ref {}, ref {}) = {};",
-                                    if_let_pat_pat, if_let_expr_pat, guard_pat
-                                );
-                                self.current = if_let_expr_pat;
-                                self.visit_expr(if_let_expr);
-                                self.current = if_let_pat_pat;
-                                self.visit_pat(if_let_pat);
-                            },
-                        }
-                    }
-                    self.current = format!("{}[{}].pat", arms_pat, i);
-                    self.visit_pat(arm.pat);
-                }
-            },
-            ExprKind::Closure(ref _capture_clause, _func, _, _, _) => {
-                println!("Closure(ref capture_clause, ref func, _, _, _) = {};", current);
-                println!("    // unimplemented: `ExprKind::Closure` is not further destructured at the moment");
-            },
-            ExprKind::Yield(sub, _) => {
-                let sub_pat = self.next("sub");
-                println!("Yield(ref sub) = {};", current);
-                self.current = sub_pat;
-                self.visit_expr(sub);
-            },
-            ExprKind::Block(block, _) => {
-                let block_pat = self.next("block");
-                println!("Block(ref {}) = {};", block_pat, current);
-                self.current = block_pat;
-                self.visit_block(block);
+                bind!(self, expr);
+                kind!("Type({expr}, _)");
+                self.expr(expr);
+            },
+            ExprKind::Loop(body, label, des, _) => {
+                bind!(self, body);
+                opt_bind!(self, label);
+                kind!("Loop({body}, {label}, LoopSource::{des:?}, _)");
+                self.block(body);
+                label.if_some(|l| self.ident(field!(l.ident)));
+            },
+            ExprKind::If(cond, then, else_expr) => {
+                bind!(self, cond, then);
+                opt_bind!(self, else_expr);
+                kind!("If({cond}, {then}, {else_expr})");
+                self.expr(cond);
+                self.expr(then);
+                else_expr.if_some(|e| self.expr(e));
+            },
+            ExprKind::Match(scrutinee, arms, des) => {
+                bind!(self, scrutinee, arms);
+                kind!("Match({scrutinee}, {arms}, MatchSource::{des:?})");
+                self.expr(scrutinee);
+                self.slice(arms, |arm| self.arm(arm));
+            },
+            ExprKind::Closure(capture_by, fn_decl, body_id, _, movability) => {
+                let movability = OptionPat::new(movability.map(|m| format!("Movability::{m:?}")));
+
+                let ret_ty = match fn_decl.output {
+                    FnRetTy::DefaultReturn(_) => "FnRetTy::DefaultReturn(_)",
+                    FnRetTy::Return(_) => "FnRetTy::Return(_ty)",
+                };
+
+                bind!(self, fn_decl, body_id);
+                kind!("Closure(CaptureBy::{capture_by:?}, {fn_decl}, {body_id}, _, {movability})");
+                out!("if let {ret_ty} = {fn_decl}.output;");
+                self.body(body_id);
+            },
+            ExprKind::Yield(sub, source) => {
+                bind!(self, sub);
+                kind!("Yield(sub, YieldSource::{source:?})");
+                self.expr(sub);
+            },
+            ExprKind::Block(block, label) => {
+                bind!(self, block);
+                opt_bind!(self, label);
+                kind!("Block({block}, {label})");
+                self.block(block);
+                label.if_some(|l| self.ident(field!(l.ident)));
             },
             ExprKind::Assign(target, value, _) => {
-                let target_pat = self.next("target");
-                let value_pat = self.next("value");
-                println!(
-                    "Assign(ref {}, ref {}, ref _span) = {};",
-                    target_pat, value_pat, current
-                );
-                self.current = target_pat;
-                self.visit_expr(target);
-                self.current = value_pat;
-                self.visit_expr(value);
-            },
-            ExprKind::AssignOp(ref op, target, value) => {
-                let op_pat = self.next("op");
-                let target_pat = self.next("target");
-                let value_pat = self.next("value");
-                println!(
-                    "AssignOp(ref {}, ref {}, ref {}) = {};",
-                    op_pat, target_pat, value_pat, current
-                );
-                println!("    if BinOpKind::{:?} == {}.node;", op.node, op_pat);
-                self.current = target_pat;
-                self.visit_expr(target);
-                self.current = value_pat;
-                self.visit_expr(value);
-            },
-            ExprKind::Field(object, ref field_ident) => {
-                let obj_pat = self.next("object");
-                let field_name_pat = self.next("field_name");
-                println!("Field(ref {}, ref {}) = {};", obj_pat, field_name_pat, current);
-                println!("    if {}.as_str() == {:?}", field_name_pat, field_ident.as_str());
-                self.current = obj_pat;
-                self.visit_expr(object);
+                bind!(self, target, value);
+                kind!("Assign({target}, {value}, _span)");
+                self.expr(target);
+                self.expr(value);
+            },
+            ExprKind::AssignOp(op, target, value) => {
+                bind!(self, op, target, value);
+                kind!("AssignOp({op}, {target}, {value})");
+                out!("if BinOpKind::{:?} == {op}.node;", op.value.node);
+                self.expr(target);
+                self.expr(value);
+            },
+            ExprKind::Field(object, field_name) => {
+                bind!(self, object, field_name);
+                kind!("Field({object}, {field_name})");
+                self.ident(field_name);
+                self.expr(object);
             },
             ExprKind::Index(object, index) => {
-                let object_pat = self.next("object");
-                let index_pat = self.next("index");
-                println!("Index(ref {}, ref {}) = {};", object_pat, index_pat, current);
-                self.current = object_pat;
-                self.visit_expr(object);
-                self.current = index_pat;
-                self.visit_expr(index);
-            },
-            ExprKind::Path(ref path) => {
-                let path_pat = self.next("path");
-                println!("Path(ref {}) = {};", path_pat, current);
-                self.current = path_pat;
-                self.print_qpath(path);
+                bind!(self, object, index);
+                kind!("Index({object}, {index})");
+                self.expr(object);
+                self.expr(index);
+            },
+            ExprKind::Path(ref qpath) => {
+                bind!(self, qpath);
+                kind!("Path(ref {qpath})");
+                self.qpath(qpath);
             },
             ExprKind::AddrOf(kind, mutability, inner) => {
-                let inner_pat = self.next("inner");
-                println!(
-                    "AddrOf(BorrowKind::{:?}, Mutability::{:?}, ref {}) = {};",
-                    kind, mutability, inner_pat, current
-                );
-                self.current = inner_pat;
-                self.visit_expr(inner);
-            },
-            ExprKind::Break(ref _destination, ref opt_value) => {
-                let destination_pat = self.next("destination");
-                if let Some(value) = *opt_value {
-                    let value_pat = self.next("value");
-                    println!("Break(ref {}, Some(ref {})) = {};", destination_pat, value_pat, current);
-                    self.current = value_pat;
-                    self.visit_expr(value);
-                } else {
-                    println!("Break(ref {}, None) = {};", destination_pat, current);
-                }
-                // FIXME: implement label printing
-            },
-            ExprKind::Continue(ref _destination) => {
-                let destination_pat = self.next("destination");
-                println!("Again(ref {}) = {};", destination_pat, current);
-                // FIXME: implement label printing
-            },
-            ExprKind::Ret(ref opt_value) => {
-                if let Some(value) = *opt_value {
-                    let value_pat = self.next("value");
-                    println!("Ret(Some(ref {})) = {};", value_pat, current);
-                    self.current = value_pat;
-                    self.visit_expr(value);
-                } else {
-                    println!("Ret(None) = {};", current);
-                }
+                bind!(self, inner);
+                kind!("AddrOf(BorrowKind::{kind:?}, Mutability::{mutability:?}, {inner})");
+                self.expr(inner);
+            },
+            ExprKind::Break(destination, value) => {
+                bind!(self, destination);
+                opt_bind!(self, value);
+                kind!("Break({destination}, {value})");
+                self.destination(destination);
+                value.if_some(|e| self.expr(e));
+            },
+            ExprKind::Continue(destination) => {
+                bind!(self, destination);
+                kind!("Continue({destination})");
+                self.destination(destination);
+            },
+            ExprKind::Ret(value) => {
+                opt_bind!(self, value);
+                kind!("Ret({value})");
+                value.if_some(|e| self.expr(e));
             },
             ExprKind::InlineAsm(_) => {
-                println!("InlineAsm(_) = {};", current);
-                println!("    // unimplemented: `ExprKind::InlineAsm` is not further destructured at the moment");
+                kind!("InlineAsm(_)");
+                out!("// unimplemented: `ExprKind::InlineAsm` is not further destructured at the moment");
             },
             ExprKind::LlvmInlineAsm(_) => {
-                println!("LlvmInlineAsm(_) = {};", current);
-                println!("    // unimplemented: `ExprKind::LlvmInlineAsm` is not further destructured at the moment");
-            },
-            ExprKind::Struct(path, fields, ref opt_base) => {
-                let path_pat = self.next("path");
-                let fields_pat = self.next("fields");
-                if let Some(base) = *opt_base {
-                    let base_pat = self.next("base");
-                    println!(
-                        "Struct(ref {}, ref {}, Some(ref {})) = {};",
-                        path_pat, fields_pat, base_pat, current
-                    );
-                    self.current = base_pat;
-                    self.visit_expr(base);
-                } else {
-                    println!("Struct(ref {}, ref {}, None) = {};", path_pat, fields_pat, current);
-                }
-                self.current = path_pat;
-                self.print_qpath(path);
-                println!("    if {}.len() == {};", fields_pat, fields.len());
-                println!("    // unimplemented: field checks");
-            },
-            ExprKind::ConstBlock(_) => {
-                let value_pat = self.next("value");
-                println!("Const({})", value_pat);
-                self.current = value_pat;
-            },
-            // FIXME: compute length (needs type info)
-            ExprKind::Repeat(value, _) => {
-                let value_pat = self.next("value");
-                println!("Repeat(ref {}, _) = {};", value_pat, current);
-                println!("// unimplemented: repeat count check");
-                self.current = value_pat;
-                self.visit_expr(value);
-            },
-            ExprKind::Err => {
-                println!("Err = {}", current);
-            },
+                kind!("LlvmInlineAsm(_)");
+                out!("// unimplemented: `ExprKind::LlvmInlineAsm` is not further destructured at the moment");
+            },
+            ExprKind::Struct(qpath, fields, base) => {
+                bind!(self, qpath, fields);
+                opt_bind!(self, base);
+                kind!("Struct({qpath}, {fields}, {base})");
+                self.qpath(qpath);
+                self.slice(fields, |field| {
+                    self.ident(field!(field.ident));
+                    self.expr(field!(field.expr));
+                });
+                base.if_some(|e| self.expr(e));
+            },
+            ExprKind::ConstBlock(_) => kind!("ConstBlock(_)"),
+            ExprKind::Repeat(value, length) => {
+                bind!(self, value, length);
+                kind!("Repeat({value}, {length})");
+                self.expr(value);
+                self.body(field!(length.body));
+            },
+            ExprKind::Err => kind!("Err"),
             ExprKind::DropTemps(expr) => {
-                let expr_pat = self.next("expr");
-                println!("DropTemps(ref {}) = {};", expr_pat, current);
-                self.current = expr_pat;
-                self.visit_expr(expr);
+                bind!(self, expr);
+                kind!("DropTemps({expr})");
+                self.expr(expr);
             },
         }
     }
 
-    fn visit_block(&mut self, block: &Block<'_>) {
-        println!("    if {}.stmts.len() == {};", self.current, block.stmts.len());
-        let block_name = self.current.clone();
-        for (i, stmt) in block.stmts.iter().enumerate() {
-            self.current = format!("{}.stmts[{}]", block_name, i);
-            self.visit_stmt(stmt);
-        }
-        if let Some(expr) = block.expr {
-            self.current = self.next("trailing_expr");
-            println!("    if let Some({}) = &{}.expr;", self.current, block_name);
-            self.visit_expr(expr);
-        } else {
-            println!("    if {}.expr.is_none();", block_name);
-        }
+    fn block(&self, block: &Binding<&hir::Block<'_>>) {
+        self.slice(field!(block.stmts), |stmt| self.stmt(stmt));
+        self.option(field!(block.expr), "trailing_expr", |expr| {
+            self.expr(expr);
+        });
     }
 
-    #[allow(clippy::too_many_lines)]
-    fn visit_pat(&mut self, pat: &Pat<'_>) {
-        print!("    if let PatKind::");
-        let current = format!("{}.kind", self.current);
-        match pat.kind {
-            PatKind::Wild => println!("Wild = {};", current),
-            PatKind::Binding(anno, .., ident, ref sub) => {
-                let anno_pat = &format!("BindingAnnotation::{:?}", anno);
-                let name_pat = self.next("name");
-                if let Some(sub) = *sub {
-                    let sub_pat = self.next("sub");
-                    println!(
-                        "Binding({}, _, {}, Some(ref {})) = {};",
-                        anno_pat, name_pat, sub_pat, current
-                    );
-                    self.current = sub_pat;
-                    self.visit_pat(sub);
-                } else {
-                    println!("Binding({}, _, {}, None) = {};", anno_pat, name_pat, current);
-                }
-                println!("    if {}.as_str() == \"{}\";", name_pat, ident.as_str());
-            },
-            PatKind::Struct(ref path, fields, ignore) => {
-                let path_pat = self.next("path");
-                let fields_pat = self.next("fields");
-                println!(
-                    "Struct(ref {}, ref {}, {}) = {};",
-                    path_pat, fields_pat, ignore, current
-                );
-                self.current = path_pat;
-                self.print_qpath(path);
-                println!("    if {}.len() == {};", fields_pat, fields.len());
-                println!("    // unimplemented: field checks");
+    fn body(&self, body_id: &Binding<hir::BodyId>) {
+        let expr = &self.cx.tcx.hir().body(body_id.value).value;
+        bind!(self, expr);
+        out!("let {expr} = &cx.tcx.hir().body({body_id}).value;");
+        self.expr(expr);
+    }
+
+    fn pat(&self, pat: &Binding<&hir::Pat<'_>>) {
+        let kind = |kind| out!("if let PatKind::{kind} = {pat}.kind;");
+        macro_rules! kind {
+            ($($t:tt)*) => (kind(format_args!($($t)*)));
+        }
+
+        match pat.value.kind {
+            PatKind::Wild => kind!("Wild"),
+            PatKind::Binding(anno, .., name, sub) => {
+                bind!(self, name);
+                opt_bind!(self, sub);
+                kind!("Binding(BindingAnnotation::{anno:?}, _, {name}, {sub})");
+                self.ident(name);
+                sub.if_some(|p| self.pat(p));
+            },
+            PatKind::Struct(ref qpath, fields, ignore) => {
+                bind!(self, qpath, fields);
+                kind!("Struct(ref {qpath}, {fields}, {ignore})");
+                self.qpath(qpath);
+                self.slice(fields, |field| {
+                    self.ident(field!(field.ident));
+                    self.pat(field!(field.pat));
+                });
             },
             PatKind::Or(fields) => {
-                let fields_pat = self.next("fields");
-                println!("Or(ref {}) = {};", fields_pat, current);
-                println!("    if {}.len() == {};", fields_pat, fields.len());
-                println!("    // unimplemented: field checks");
-            },
-            PatKind::TupleStruct(ref path, fields, skip_pos) => {
-                let path_pat = self.next("path");
-                let fields_pat = self.next("fields");
-                println!(
-                    "TupleStruct(ref {}, ref {}, {:?}) = {};",
-                    path_pat, fields_pat, skip_pos, current
-                );
-                self.current = path_pat;
-                self.print_qpath(path);
-                println!("    if {}.len() == {};", fields_pat, fields.len());
-                println!("    // unimplemented: field checks");
-            },
-            PatKind::Path(ref path) => {
-                let path_pat = self.next("path");
-                println!("Path(ref {}) = {};", path_pat, current);
-                self.current = path_pat;
-                self.print_qpath(path);
+                bind!(self, fields);
+                kind!("Or({fields})");
+                self.slice(fields, |pat| self.pat(pat));
+            },
+            PatKind::TupleStruct(ref qpath, fields, skip_pos) => {
+                bind!(self, qpath, fields);
+                kind!("TupleStruct(ref {qpath}, {fields}, {skip_pos:?})");
+                self.qpath(qpath);
+                self.slice(fields, |pat| self.pat(pat));
+            },
+            PatKind::Path(ref qpath) => {
+                bind!(self, qpath);
+                kind!("Path(ref {qpath})");
+                self.qpath(qpath);
             },
             PatKind::Tuple(fields, skip_pos) => {
-                let fields_pat = self.next("fields");
-                println!("Tuple(ref {}, {:?}) = {};", fields_pat, skip_pos, current);
-                println!("    if {}.len() == {};", fields_pat, fields.len());
-                println!("    // unimplemented: field checks");
+                bind!(self, fields);
+                kind!("Tuple({fields}, {skip_pos:?})");
+                self.slice(fields, |field| self.pat(field));
             },
             PatKind::Box(pat) => {
-                let pat_pat = self.next("pat");
-                println!("Box(ref {}) = {};", pat_pat, current);
-                self.current = pat_pat;
-                self.visit_pat(pat);
+                bind!(self, pat);
+                kind!("Box({pat})");
+                self.pat(pat);
             },
             PatKind::Ref(pat, muta) => {
-                let pat_pat = self.next("pat");
-                println!("Ref(ref {}, Mutability::{:?}) = {};", pat_pat, muta, current);
-                self.current = pat_pat;
-                self.visit_pat(pat);
+                bind!(self, pat);
+                kind!("Ref({pat}, Mutability::{muta:?})");
+                self.pat(pat);
             },
             PatKind::Lit(lit_expr) => {
-                let lit_expr_pat = self.next("lit_expr");
-                println!("Lit(ref {}) = {}", lit_expr_pat, current);
-                self.current = lit_expr_pat;
-                self.visit_expr(lit_expr);
-            },
-            PatKind::Range(ref start, ref end, end_kind) => {
-                let start_pat = self.next("start");
-                let end_pat = self.next("end");
-                println!(
-                    "Range(ref {}, ref {}, RangeEnd::{:?}) = {};",
-                    start_pat, end_pat, end_kind, current
-                );
-                self.current = start_pat;
-                walk_list!(self, visit_expr, start);
-                self.current = end_pat;
-                walk_list!(self, visit_expr, end);
-            },
-            PatKind::Slice(start, ref middle, end) => {
-                let start_pat = self.next("start");
-                let end_pat = self.next("end");
-                if let Some(middle) = middle {
-                    let middle_pat = self.next("middle");
-                    println!(
-                        "Slice(ref {}, Some(ref {}), ref {}) = {};",
-                        start_pat, middle_pat, end_pat, current
-                    );
-                    self.current = middle_pat;
-                    self.visit_pat(middle);
-                } else {
-                    println!("Slice(ref {}, None, ref {}) = {};", start_pat, end_pat, current);
-                }
-                println!("    if {}.len() == {};", start_pat, start.len());
-                for (i, pat) in start.iter().enumerate() {
-                    self.current = format!("{}[{}]", start_pat, i);
-                    self.visit_pat(pat);
-                }
-                println!("    if {}.len() == {};", end_pat, end.len());
-                for (i, pat) in end.iter().enumerate() {
-                    self.current = format!("{}[{}]", end_pat, i);
-                    self.visit_pat(pat);
-                }
+                bind!(self, lit_expr);
+                kind!("Lit({lit_expr})");
+                self.expr(lit_expr);
+            },
+            PatKind::Range(start, end, end_kind) => {
+                opt_bind!(self, start, end);
+                kind!("Range({start}, {end}, RangeEnd::{end_kind:?})");
+                start.if_some(|e| self.expr(e));
+                end.if_some(|e| self.expr(e));
+            },
+            PatKind::Slice(start, middle, end) => {
+                bind!(self, start, end);
+                opt_bind!(self, middle);
+                kind!("Slice({start}, {middle}, {end})");
+                middle.if_some(|p| self.pat(p));
+                self.slice(start, |pat| self.pat(pat));
+                self.slice(end, |pat| self.pat(pat));
             },
         }
     }
 
-    fn visit_stmt(&mut self, s: &Stmt<'_>) {
-        print!("    if let StmtKind::");
-        let current = format!("{}.kind", self.current);
-        match s.kind {
-            // A local (let) binding:
-            StmtKind::Local(local) => {
-                let local_pat = self.next("local");
-                println!("Local(ref {}) = {};", local_pat, current);
-                if let Some(init) = local.init {
-                    let init_pat = self.next("init");
-                    println!("    if let Some(ref {}) = {}.init;", init_pat, local_pat);
-                    self.current = init_pat;
-                    self.visit_expr(init);
-                }
-                self.current = format!("{}.pat", local_pat);
-                self.visit_pat(local.pat);
-            },
-            // An item binding:
-            StmtKind::Item(_) => {
-                println!("Item(item_id) = {};", current);
-            },
+    fn stmt(&self, stmt: &Binding<&hir::Stmt<'_>>) {
+        let kind = |kind| out!("if let StmtKind::{kind} = {stmt}.kind;");
+        macro_rules! kind {
+            ($($t:tt)*) => (kind(format_args!($($t)*)));
+        }
 
-            // Expr without trailing semi-colon (must have unit type):
+        match stmt.value.kind {
+            StmtKind::Local(local) => {
+                bind!(self, local);
+                kind!("Local({local})");
+                self.option(field!(local.init), "init", |init| {
+                    self.expr(init);
+                });
+                self.pat(field!(local.pat));
+            },
+            StmtKind::Item(_) => kind!("Item(item_id)"),
             StmtKind::Expr(e) => {
-                let e_pat = self.next("e");
-                println!("Expr(ref {}, _) = {}", e_pat, current);
-                self.current = e_pat;
-                self.visit_expr(e);
+                bind!(self, e);
+                kind!("Expr({e})");
+                self.expr(e);
             },
-
-            // Expr with trailing semi-colon (may have any type):
             StmtKind::Semi(e) => {
-                let e_pat = self.next("e");
-                println!("Semi(ref {}, _) = {}", e_pat, current);
-                self.current = e_pat;
-                self.visit_expr(e);
+                bind!(self, e);
+                kind!("Semi({e})");
+                self.expr(e);
             },
         }
     }
-
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::None
-    }
 }
 
 fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool {
@@ -741,30 +697,29 @@ fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool {
     get_attr(cx.sess(), attrs, "author").count() > 0
 }
 
-fn print_path(path: &QPath<'_>, first: &mut bool) {
-    match *path {
-        QPath::Resolved(_, path) => {
-            for segment in path.segments {
-                if *first {
-                    *first = false;
-                } else {
-                    print!(", ");
-                }
-                print!("{:?}", segment.ident.as_str());
-            }
-        },
-        QPath::TypeRelative(ty, segment) => match ty.kind {
-            hir::TyKind::Path(ref inner_path) => {
-                print_path(inner_path, first);
-                if *first {
-                    *first = false;
-                } else {
-                    print!(", ");
+fn path_to_string(path: &QPath<'_>) -> String {
+    fn inner(s: &mut String, path: &QPath<'_>) {
+        match *path {
+            QPath::Resolved(_, path) => {
+                for (i, segment) in path.segments.iter().enumerate() {
+                    if i > 0 {
+                        *s += ", ";
+                    }
+                    write!(s, "{:?}", segment.ident.as_str()).unwrap();
                 }
-                print!("{:?}", segment.ident.as_str());
             },
-            ref other => print!("/* unimplemented: {:?}*/", other),
-        },
-        QPath::LangItem(..) => panic!("print_path: called for lang item qpath"),
+            QPath::TypeRelative(ty, segment) => match &ty.kind {
+                hir::TyKind::Path(inner_path) => {
+                    inner(s, inner_path);
+                    *s += ", ";
+                    write!(s, "{:?}", segment.ident.as_str()).unwrap();
+                },
+                other => write!(s, "/* unimplemented: {:?}*/", other).unwrap(),
+            },
+            QPath::LangItem(..) => panic!("path_to_string: called for lang item qpath"),
+        }
     }
+    let mut s = String::new();
+    inner(&mut s, path);
+    s
 }
diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs
index 122a5ce3fc8..9c83d30eb9c 100644
--- a/src/tools/clippy/clippy_lints/src/utils/conf.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs
@@ -15,7 +15,7 @@ pub struct Rename {
     pub rename: String,
 }
 
-/// A single disallowed method, used by the `DISALLOWED_METHOD` lint.
+/// A single disallowed method, used by the `DISALLOWED_METHODS` lint.
 #[derive(Clone, Debug, Deserialize)]
 #[serde(untagged)]
 pub enum DisallowedMethod {
@@ -23,7 +23,7 @@ pub enum DisallowedMethod {
     WithReason { path: String, reason: Option<String> },
 }
 
-/// A single disallowed type, used by the `DISALLOWED_TYPE` lint.
+/// A single disallowed type, used by the `DISALLOWED_TYPES` lint.
 #[derive(Clone, Debug, Deserialize)]
 #[serde(untagged)]
 pub enum DisallowedType {
@@ -148,7 +148,7 @@ define_Conf! {
     ///
     /// Suppress lints whenever the suggested change would cause breakage for other crates.
     (avoid_breaking_exported_api: bool = true),
-    /// 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.
+    /// 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, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE.
     ///
     /// The minimum rust version that the project supports
     (msrv: Option<String> = None),
@@ -256,11 +256,11 @@ define_Conf! {
     ///
     /// Whether to allow certain wildcard imports (prelude, super in tests).
     (warn_on_all_wildcard_imports: bool = false),
-    /// Lint: DISALLOWED_METHOD.
+    /// Lint: DISALLOWED_METHODS.
     ///
     /// The list of disallowed methods, written as fully qualified paths.
     (disallowed_methods: Vec<crate::utils::conf::DisallowedMethod> = Vec::new()),
-    /// Lint: DISALLOWED_TYPE.
+    /// Lint: DISALLOWED_TYPES.
     ///
     /// The list of disallowed types, written as fully qualified paths.
     (disallowed_types: Vec<crate::utils::conf::DisallowedType> = Vec::new()),
@@ -296,6 +296,12 @@ define_Conf! {
     ///
     /// Whether to apply the raw pointer heuristic to determine if a type is `Send`.
     (enable_raw_pointer_heuristic_for_send: bool = true),
+    /// Lint: INDEX_REFUTABLE_SLICE.
+    ///
+    /// When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in
+    /// the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed.
+    /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements.
+    (max_suggested_slice_pattern_length: u64 = 3),
 }
 
 /// Search for the configuration file.
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 824ec53ab9c..1f97c8ba7e6 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
@@ -8,6 +8,7 @@ use clippy_utils::{
     paths, SpanlessEq,
 };
 use if_chain::if_chain;
+use rustc_ast as ast;
 use rustc_ast::ast::{Crate, ItemKind, LitKind, ModKind, NodeId};
 use rustc_ast::visit::FnKind;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@@ -25,10 +26,11 @@ use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintCon
 use rustc_middle::hir::map::Map;
 use rustc_middle::mir::interpret::ConstValue;
 use rustc_middle::ty;
+use rustc_semver::RustcVersion;
 use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{Symbol, SymbolStr};
-use rustc_span::{BytePos, Span};
+use rustc_span::{sym, BytePos, Span};
 use rustc_typeck::hir_ty_to_ty;
 
 use std::borrow::{Borrow, Cow};
@@ -314,6 +316,27 @@ declare_clippy_lint! {
     "non-idiomatic `if_chain!` usage"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for invalid `clippy::version` attributes.
+    ///
+    /// Valid values are:
+    /// * "pre 1.29.0"
+    /// * any valid semantic version
+    pub INVALID_CLIPPY_VERSION_ATTRIBUTE,
+    internal,
+    "found an invalid `clippy::version` attribute"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for declared clippy lints without the `clippy::version` attribute.
+    ///
+    pub MISSING_CLIPPY_VERSION_ATTRIBUTE,
+    internal,
+    "found clippy lint without `clippy::version` attribute"
+}
+
 declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
 
 impl EarlyLintPass for ClippyLintsInternal {
@@ -351,7 +374,7 @@ pub struct LintWithoutLintPass {
     registered_lints: FxHashSet<Symbol>,
 }
 
-impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS]);
+impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE]);
 
 impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
@@ -361,6 +384,8 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
 
         if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind {
             if is_lint_ref_type(cx, ty) {
+                check_invalid_clippy_version_attribute(cx, item);
+
                 let expr = &cx.tcx.hir().body(body_id).value;
                 if_chain! {
                     if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind;
@@ -458,6 +483,57 @@ fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &Ty<'_>) -> bool {
     false
 }
 
+fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) {
+    if let Some(value) = extract_clippy_version_value(cx, item) {
+        // The `sym!` macro doesn't work as it only expects a single token.
+        // It's better to keep it this way and have a direct `Symbol::intern` call here.
+        if value == Symbol::intern("pre 1.29.0") {
+            return;
+        }
+
+        if RustcVersion::parse(&*value.as_str()).is_err() {
+            span_lint_and_help(
+                cx,
+                INVALID_CLIPPY_VERSION_ATTRIBUTE,
+                item.span,
+                "this item has an invalid `clippy::version` attribute",
+                None,
+                "please use a valid sematic version, see `doc/adding_lints.md`",
+            );
+        }
+    } else {
+        span_lint_and_help(
+            cx,
+            MISSING_CLIPPY_VERSION_ATTRIBUTE,
+            item.span,
+            "this lint is missing the `clippy::version` attribute or version value",
+            None,
+            "please use a `clippy::version` attribute, see `doc/adding_lints.md`",
+        );
+    }
+}
+
+/// This function extracts the version value of a `clippy::version` attribute if the given value has
+/// one
+fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option<Symbol> {
+    let attrs = cx.tcx.hir().attrs(item.hir_id());
+    attrs.iter().find_map(|attr| {
+        if_chain! {
+            // Identify attribute
+            if let ast::AttrKind::Normal(ref attr_kind, _) = &attr.kind;
+            if let [tool_name, attr_name] = &attr_kind.path.segments[..];
+            if tool_name.ident.name == sym::clippy;
+            if attr_name.ident.name == sym::version;
+            if let Some(version) = attr.value_str();
+            then {
+                Some(version)
+            } else {
+                None
+            }
+        }
+    })
+}
+
 struct LintCollector<'a, 'tcx> {
     output: &'a mut FxHashSet<Symbol>,
     cx: &'a LateContext<'tcx>,
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 99cf4c1ed40..8051c58bad7 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
@@ -25,7 +25,7 @@ use std::fs::{self, OpenOptions};
 use std::io::prelude::*;
 use std::path::Path;
 
-use crate::utils::internal_lints::is_lint_ref_type;
+use crate::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type};
 use clippy_utils::{
     diagnostics::span_lint, last_path_segment, match_def_path, match_function_call, match_path, paths, ty::match_type,
     ty::walk_ptrs_ty_depth,
@@ -116,6 +116,8 @@ const DEPRECATED_LINT_TYPE: [&str; 3] = ["clippy_lints", "deprecated_lints", "Cl
 const APPLICABILITY_NAME_INDEX: usize = 2;
 /// This applicability will be set for unresolved applicability values.
 const APPLICABILITY_UNRESOLVED_STR: &str = "Unresolved";
+/// The version that will be displayed if none has been defined
+const VERION_DEFAULT_STR: &str = "Unknown";
 
 declare_clippy_lint! {
     /// ### What it does
@@ -144,6 +146,7 @@ declare_clippy_lint! {
     ///     "docs": " ### What it does\nCollects metadata about clippy lints for the website. [...] "
     /// }
     /// ```
+    #[clippy::version = "1.56.0"]
     pub INTERNAL_METADATA_COLLECTOR,
     internal_warn,
     "A busy bee collection metadata about lints"
@@ -215,18 +218,27 @@ struct LintMetadata {
     group: String,
     level: String,
     docs: String,
+    version: String,
     /// This field is only used in the output and will only be
     /// mapped shortly before the actual output.
     applicability: Option<ApplicabilityInfo>,
 }
 
 impl LintMetadata {
-    fn new(id: String, id_span: SerializableSpan, group: String, level: &'static str, docs: String) -> Self {
+    fn new(
+        id: String,
+        id_span: SerializableSpan,
+        group: String,
+        level: &'static str,
+        version: String,
+        docs: String,
+    ) -> Self {
         Self {
             id,
             id_span,
             group,
             level: level.to_string(),
+            version,
             docs,
             applicability: None,
         }
@@ -410,12 +422,14 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector {
                     if let Some(configuration_section) = self.get_lint_configs(&lint_name) {
                         docs.push_str(&configuration_section);
                     }
+                    let version = get_lint_version(cx, item);
 
                     self.lints.push(LintMetadata::new(
                         lint_name,
                         SerializableSpan::from_item(cx, item),
                         group,
                         level,
+                        version,
                         docs,
                     ));
                 }
@@ -429,11 +443,14 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector {
                 // Metadata the little we can get from a deprecated lint
                 if let Some(docs) = extract_attr_docs_or_lint(cx, item);
                 then {
+                    let version = get_lint_version(cx, item);
+
                     self.lints.push(LintMetadata::new(
                         lint_name,
                         SerializableSpan::from_item(cx, item),
                         DEPRECATED_LINT_GROUP_STR.to_string(),
                         DEPRECATED_LINT_LEVEL,
+                        version,
                         docs,
                     ));
                 }
@@ -552,6 +569,13 @@ fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
     Some(docs)
 }
 
+fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String {
+    extract_clippy_version_value(cx, item).map_or_else(
+        || VERION_DEFAULT_STR.to_string(),
+        |version| version.as_str().to_string(),
+    )
+}
+
 fn get_lint_group_and_level_or_lint(
     cx: &LateContext<'_>,
     lint_name: &str,
@@ -663,7 +687,6 @@ fn extract_emission_info<'hir>(
             applicability = resolve_applicability(cx, arg);
         } else if arg_ty.is_closure() {
             multi_part |= check_is_multi_part(cx, arg);
-            // TODO xFrednet 2021-03-01: don't use or_else but rather a comparison
             applicability = applicability.or_else(|| resolve_applicability(cx, arg));
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs
index d3234b5758a..79e7410c3a8 100644
--- a/src/tools/clippy/clippy_lints/src/vec.rs
+++ b/src/tools/clippy/clippy_lints/src/vec.rs
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     /// // Good
     /// foo(&[1, 2]);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USELESS_VEC,
     perf,
     "useless `vec!`"
diff --git a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
index b92b6ca4f43..1bc0eb6303c 100644
--- a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
+++ b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
@@ -27,6 +27,7 @@ declare_clippy_lint! {
     /// ```rust
     /// let v = vec![0];
     /// ```
+    #[clippy::version = "1.51.0"]
     pub VEC_INIT_THEN_PUSH,
     perf,
     "`push` immediately after `Vec` creation"
diff --git a/src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs b/src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs
index 5c0429db6b8..3441d9ccdfa 100644
--- a/src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs
@@ -20,6 +20,7 @@ declare_clippy_lint! {
     /// ```rust
     /// vec!(1, 2, 3, 4, 5).resize(0, 5)
     /// ```
+    #[clippy::version = "1.46.0"]
     pub VEC_RESIZE_TO_ZERO,
     correctness,
     "emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake"
diff --git a/src/tools/clippy/clippy_lints/src/verbose_file_reads.rs b/src/tools/clippy/clippy_lints/src/verbose_file_reads.rs
index e07c12f4f16..ebdaff1e676 100644
--- a/src/tools/clippy/clippy_lints/src/verbose_file_reads.rs
+++ b/src/tools/clippy/clippy_lints/src/verbose_file_reads.rs
@@ -27,6 +27,7 @@ declare_clippy_lint! {
     /// # use std::fs;
     /// let mut bytes = fs::read("foo.txt").unwrap();
     /// ```
+    #[clippy::version = "1.44.0"]
     pub VERBOSE_FILE_READS,
     restriction,
     "use of `File::read_to_end` or `File::read_to_string`"
diff --git a/src/tools/clippy/clippy_lints/src/wildcard_dependencies.rs b/src/tools/clippy/clippy_lints/src/wildcard_dependencies.rs
index d0c98b6bd79..80d7b8a1b6d 100644
--- a/src/tools/clippy/clippy_lints/src/wildcard_dependencies.rs
+++ b/src/tools/clippy/clippy_lints/src/wildcard_dependencies.rs
@@ -20,6 +20,7 @@ declare_clippy_lint! {
     /// [dependencies]
     /// regex = "*"
     /// ```
+    #[clippy::version = "1.32.0"]
     pub WILDCARD_DEPENDENCIES,
     cargo,
     "wildcard dependencies being used"
diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
index bafb9d3e3b1..832da66a536 100644
--- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_test_module_or_function;
 use clippy_utils::source::{snippet, snippet_with_applicability};
-use clippy_utils::{in_macro, is_test_module_or_function};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{
@@ -34,6 +34,7 @@ declare_clippy_lint! {
     /// use std::cmp::Ordering;
     /// foo(Ordering::Less)
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ENUM_GLOB_USE,
     pedantic,
     "use items that import all variants of an enum"
@@ -86,6 +87,7 @@ declare_clippy_lint! {
     ///
     /// foo();
     /// ```
+    #[clippy::version = "1.43.0"]
     pub WILDCARD_IMPORTS,
     pedantic,
     "lint `use _::*` statements"
@@ -196,7 +198,7 @@ impl LateLintPass<'_> for WildcardImports {
 
 impl WildcardImports {
     fn check_exceptions(&self, item: &Item<'_>, segments: &[PathSegment<'_>]) -> bool {
-        in_macro(item.span)
+        item.span.from_expansion()
             || is_prelude_import(segments)
             || (is_super_only_import(segments) && self.test_modules_deep > 0)
     }
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index b412e15ae4f..5bf0cffdbad 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -31,6 +31,7 @@ declare_clippy_lint! {
     /// // Good
     /// println!();
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub PRINTLN_EMPTY_STRING,
     style,
     "using `println!(\"\")` with an empty string"
@@ -55,6 +56,7 @@ declare_clippy_lint! {
     /// # let name = "World";
     /// println!("Hello {}!", name);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub PRINT_WITH_NEWLINE,
     style,
     "using `print!()` with a format string that ends in a single newline"
@@ -70,12 +72,19 @@ declare_clippy_lint! {
     /// application and might forget to remove those prints afterward.
     ///
     /// ### Known problems
-    /// Only catches `print!` and `println!` calls.
+    /// * Only catches `print!` and `println!` calls.
+    /// * The lint level is unaffected by crate attributes. The level can still
+    ///   be set for functions, modules and other items. To change the level for
+    ///   the entire crate, please use command line flags. More information and a
+    ///   configuration example can be found in [clippy#6610].
+    ///
+    /// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
     ///
     /// ### Example
     /// ```rust
     /// println!("Hello world!");
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub PRINT_STDOUT,
     restriction,
     "printing on stdout"
@@ -91,12 +100,19 @@ declare_clippy_lint! {
     /// application and might forget to remove those prints afterward.
     ///
     /// ### Known problems
-    /// Only catches `eprint!` and `eprintln!` calls.
+    /// * Only catches `eprint!` and `eprintln!` calls.
+    /// * The lint level is unaffected by crate attributes. The level can still
+    ///   be set for functions, modules and other items. To change the level for
+    ///   the entire crate, please use command line flags. More information and a
+    ///   configuration example can be found in [clippy#6610].
+    ///
+    /// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
     ///
     /// ### Example
     /// ```rust
     /// eprintln!("Hello world!");
     /// ```
+    #[clippy::version = "1.50.0"]
     pub PRINT_STDERR,
     restriction,
     "printing on stderr"
@@ -116,6 +132,7 @@ declare_clippy_lint! {
     /// # let foo = "bar";
     /// println!("{:?}", foo);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub USE_DEBUG,
     restriction,
     "use of `Debug`-based formatting"
@@ -142,6 +159,7 @@ declare_clippy_lint! {
     /// ```rust
     /// println!("foo");
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub PRINT_LITERAL,
     style,
     "printing a literal with a format string"
@@ -165,6 +183,7 @@ declare_clippy_lint! {
     /// // Good
     /// writeln!(buf);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WRITELN_EMPTY_STRING,
     style,
     "using `writeln!(buf, \"\")` with an empty string"
@@ -191,6 +210,7 @@ declare_clippy_lint! {
     /// // Good
     /// writeln!(buf, "Hello {}!", name);
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WRITE_WITH_NEWLINE,
     style,
     "using `write!()` with a format string that ends in a single newline"
@@ -219,6 +239,7 @@ declare_clippy_lint! {
     /// // Good
     /// writeln!(buf, "foo");
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub WRITE_LITERAL,
     style,
     "writing a literal with a format string"
@@ -562,10 +583,10 @@ impl Write {
             let replacement: String = match lit.token.kind {
                 LitKind::Integer | LitKind::Float | LitKind::Err => continue,
                 LitKind::StrRaw(_) | LitKind::ByteStrRaw(_) if matches!(fmtstr.style, StrStyle::Raw(_)) => {
-                    lit.token.symbol.as_str().replace("{", "{{").replace("}", "}}")
+                    lit.token.symbol.as_str().replace('{', "{{").replace('}', "}}")
                 },
                 LitKind::Str | LitKind::ByteStr if matches!(fmtstr.style, StrStyle::Cooked) => {
-                    lit.token.symbol.as_str().replace("{", "{{").replace("}", "}}")
+                    lit.token.symbol.as_str().replace('{', "{{").replace('}', "}}")
                 },
                 LitKind::StrRaw(_) | LitKind::Str | LitKind::ByteStrRaw(_) | LitKind::ByteStr => continue,
                 LitKind::Byte | LitKind::Char => match &*lit.token.symbol.as_str() {
diff --git a/src/tools/clippy/clippy_lints/src/zero_div_zero.rs b/src/tools/clippy/clippy_lints/src/zero_div_zero.rs
index e0746ce4d81..641681185a2 100644
--- a/src/tools/clippy/clippy_lints/src/zero_div_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/zero_div_zero.rs
@@ -20,6 +20,7 @@ declare_clippy_lint! {
     /// // Good
     /// let nan = f32::NAN;
     /// ```
+    #[clippy::version = "pre 1.29.0"]
     pub ZERO_DIVIDED_BY_ZERO,
     complexity,
     "usage of `0.0 / 0.0` to obtain NaN instead of `f32::NAN` or `f64::NAN`"
diff --git a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs
index aa6b2614bbc..eb8436a501d 100644
--- a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs
+++ b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     ///     todo!();
     /// }
     /// ```
+    #[clippy::version = "1.50.0"]
     pub ZERO_SIZED_MAP_VALUES,
     pedantic,
     "usage of map with zero-sized value type"
diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml
index d99a3d9359e..0ba0b59ed13 100644
--- a/src/tools/clippy/clippy_utils/Cargo.toml
+++ b/src/tools/clippy/clippy_utils/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_utils"
-version = "0.1.58"
+version = "0.1.59"
 edition = "2021"
 publish = false
 
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index 1b05a8a3504..8400bfbbd99 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -243,6 +243,7 @@ pub fn eq_item<K>(l: &Item<K>, r: &Item<K>, mut eq_kind: impl FnMut(&K, &K) -> b
     eq_id(l.ident, r.ident) && over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind)
 }
 
+#[allow(clippy::too_many_lines)] // Just a big match statement
 pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
     use ItemKind::*;
     match (l, r) {
@@ -250,8 +251,20 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
         (Use(l), Use(r)) => eq_use_tree(l, r),
         (Static(lt, lm, le), Static(rt, rm, re)) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re),
         (Const(ld, lt, le), Const(rd, rt, re)) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
-        (Fn(box ast::Fn { defaultness: ld, sig: lf, generics: lg, body: lb }),
-         Fn(box ast::Fn { defaultness: rd, sig: rf, generics: rg, body: rb })) => {
+        (
+            Fn(box ast::Fn {
+                defaultness: ld,
+                sig: lf,
+                generics: lg,
+                body: lb,
+            }),
+            Fn(box ast::Fn {
+                defaultness: rd,
+                sig: rf,
+                generics: rg,
+                body: rb,
+            }),
+        ) => {
             eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
         },
         (Mod(lu, lmk), Mod(ru, rmk)) => {
@@ -267,8 +280,20 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
         (ForeignMod(l), ForeignMod(r)) => {
             both(&l.abi, &r.abi, eq_str_lit) && over(&l.items, &r.items, |l, r| eq_item(l, r, eq_foreign_item_kind))
         },
-        (TyAlias(box ast::TyAlias { defaultness: ld, generics: lg, bounds: lb, ty: lt }),
-         TyAlias(box ast::TyAlias { defaultness: rd, generics: rg, bounds: rb, ty: rt })) => {
+        (
+            TyAlias(box ast::TyAlias {
+                defaultness: ld,
+                generics: lg,
+                bounds: lb,
+                ty: lt,
+            }),
+            TyAlias(box ast::TyAlias {
+                defaultness: rd,
+                generics: rg,
+                bounds: rb,
+                ty: rt,
+            }),
+        ) => {
             eq_defaultness(*ld, *rd)
                 && eq_generics(lg, rg)
                 && over(lb, rb, eq_generic_bound)
@@ -278,8 +303,22 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
         (Struct(lv, lg), Struct(rv, rg)) | (Union(lv, lg), Union(rv, rg)) => {
             eq_variant_data(lv, rv) && eq_generics(lg, rg)
         },
-        (Trait(box ast::Trait { is_auto: la, unsafety: lu, generics: lg, bounds: lb, items: li }),
-         Trait(box ast::Trait { is_auto: ra, unsafety: ru, generics: rg, bounds: rb, items: ri })) => {
+        (
+            Trait(box ast::Trait {
+                is_auto: la,
+                unsafety: lu,
+                generics: lg,
+                bounds: lb,
+                items: li,
+            }),
+            Trait(box ast::Trait {
+                is_auto: ra,
+                unsafety: ru,
+                generics: rg,
+                bounds: rb,
+                items: ri,
+            }),
+        ) => {
             la == ra
                 && matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No)
                 && eq_generics(lg, rg)
@@ -328,12 +367,36 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool {
     use ForeignItemKind::*;
     match (l, r) {
         (Static(lt, lm, le), Static(rt, rm, re)) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re),
-        (Fn(box ast::Fn { defaultness: ld, sig: lf, generics: lg, body: lb }),
-         Fn(box ast::Fn { defaultness: rd, sig: rf, generics: rg, body: rb })) => {
+        (
+            Fn(box ast::Fn {
+                defaultness: ld,
+                sig: lf,
+                generics: lg,
+                body: lb,
+            }),
+            Fn(box ast::Fn {
+                defaultness: rd,
+                sig: rf,
+                generics: rg,
+                body: rb,
+            }),
+        ) => {
             eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
         },
-        (TyAlias(box ast::TyAlias { defaultness: ld, generics: lg, bounds: lb, ty: lt }),
-         TyAlias(box ast::TyAlias { defaultness: rd, generics: rg, bounds: rb, ty: rt })) => {
+        (
+            TyAlias(box ast::TyAlias {
+                defaultness: ld,
+                generics: lg,
+                bounds: lb,
+                ty: lt,
+            }),
+            TyAlias(box ast::TyAlias {
+                defaultness: rd,
+                generics: rg,
+                bounds: rb,
+                ty: rt,
+            }),
+        ) => {
             eq_defaultness(*ld, *rd)
                 && eq_generics(lg, rg)
                 && over(lb, rb, eq_generic_bound)
@@ -348,12 +411,36 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
     use AssocItemKind::*;
     match (l, r) {
         (Const(ld, lt, le), Const(rd, rt, re)) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
-        (Fn(box ast::Fn { defaultness: ld, sig: lf, generics: lg, body: lb }),
-         Fn(box ast::Fn { defaultness: rd, sig: rf, generics: rg, body: rb })) => {
+        (
+            Fn(box ast::Fn {
+                defaultness: ld,
+                sig: lf,
+                generics: lg,
+                body: lb,
+            }),
+            Fn(box ast::Fn {
+                defaultness: rd,
+                sig: rf,
+                generics: rg,
+                body: rb,
+            }),
+        ) => {
             eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
         },
-        (TyAlias(box ast::TyAlias { defaultness: ld, generics: lg, bounds: lb, ty: lt }),
-         TyAlias(box ast::TyAlias { defaultness: rd, generics: rg, bounds: rb, ty: rt })) => {
+        (
+            TyAlias(box ast::TyAlias {
+                defaultness: ld,
+                generics: lg,
+                bounds: lb,
+                ty: lt,
+            }),
+            TyAlias(box ast::TyAlias {
+                defaultness: rd,
+                generics: rg,
+                bounds: rb,
+                ty: rt,
+            }),
+        ) => {
             eq_defaultness(*ld, *rd)
                 && eq_generics(lg, rg)
                 && over(lb, rb, eq_generic_bound)
diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs
index c19b558cd8c..7ae9615d560 100644
--- a/src/tools/clippy/clippy_utils/src/attrs.rs
+++ b/src/tools/clippy/clippy_utils/src/attrs.rs
@@ -14,15 +14,14 @@ pub enum DeprecationStatus {
     None,
 }
 
+#[rustfmt::skip]
 pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[
-    ("author", DeprecationStatus::None),
-    ("cognitive_complexity", DeprecationStatus::None),
-    (
-        "cyclomatic_complexity",
-        DeprecationStatus::Replaced("cognitive_complexity"),
-    ),
-    ("dump", DeprecationStatus::None),
-    ("msrv", DeprecationStatus::None),
+    ("author",                DeprecationStatus::None),
+    ("version",               DeprecationStatus::None),
+    ("cognitive_complexity",  DeprecationStatus::None),
+    ("cyclomatic_complexity", DeprecationStatus::Replaced("cognitive_complexity")),
+    ("dump",                  DeprecationStatus::None),
+    ("msrv",                  DeprecationStatus::None),
 ];
 
 pub struct LimitStack {
diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
index 1ea7ccfb752..c2645ac730a 100644
--- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
+++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
@@ -9,128 +9,227 @@
 //!  - or-fun-call
 //!  - option-if-let-else
 
-use crate::is_ctor_or_promotable_const_function;
-use crate::ty::is_type_diagnostic_item;
+use crate::ty::{all_predicates_of, is_copy};
+use crate::visitors::is_const_evaluatable;
 use rustc_hir::def::{DefKind, Res};
-
-use rustc_hir::intravisit;
-use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
-
-use rustc_hir::{Block, Expr, ExprKind, Path, QPath};
+use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor};
+use rustc_hir::{def_id::DefId, Block, Expr, ExprKind, QPath, UnOp};
 use rustc_lint::LateContext;
-use rustc_middle::hir::map::Map;
-use rustc_span::sym;
-
-/// Is the expr pure (is it free from side-effects)?
-/// This function is named so to stress that it isn't exhaustive and returns FNs.
-fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool {
-    match expr.kind {
-        ExprKind::Lit(..) | ExprKind::ConstBlock(..) | ExprKind::Path(..) | ExprKind::Field(..) => true,
-        ExprKind::AddrOf(_, _, addr_of_expr) => identify_some_pure_patterns(addr_of_expr),
-        ExprKind::Tup(tup_exprs) => tup_exprs.iter().all(identify_some_pure_patterns),
-        ExprKind::Struct(_, fields, expr) => {
-            fields.iter().all(|f| identify_some_pure_patterns(f.expr)) && expr.map_or(true, identify_some_pure_patterns)
-        },
-        ExprKind::Call(
-            &Expr {
-                kind:
-                    ExprKind::Path(QPath::Resolved(
-                        _,
-                        Path {
-                            res: Res::Def(DefKind::Ctor(..) | DefKind::Variant, ..),
-                            ..
-                        },
-                    )),
-                ..
-            },
-            args,
-        ) => args.iter().all(identify_some_pure_patterns),
-        ExprKind::Block(
-            &Block {
-                stmts,
-                expr: Some(expr),
-                ..
-            },
-            _,
-        ) => stmts.is_empty() && identify_some_pure_patterns(expr),
-        ExprKind::Box(..)
-        | ExprKind::Array(..)
-        | ExprKind::Call(..)
-        | ExprKind::MethodCall(..)
-        | ExprKind::Binary(..)
-        | ExprKind::Unary(..)
-        | ExprKind::Let(..)
-        | ExprKind::Cast(..)
-        | ExprKind::Type(..)
-        | ExprKind::DropTemps(..)
-        | ExprKind::Loop(..)
-        | ExprKind::If(..)
-        | ExprKind::Match(..)
-        | ExprKind::Closure(..)
-        | ExprKind::Block(..)
-        | ExprKind::Assign(..)
-        | ExprKind::AssignOp(..)
-        | ExprKind::Index(..)
-        | ExprKind::Break(..)
-        | ExprKind::Continue(..)
-        | ExprKind::Ret(..)
-        | ExprKind::InlineAsm(..)
-        | ExprKind::LlvmInlineAsm(..)
-        | ExprKind::Repeat(..)
-        | ExprKind::Yield(..)
-        | ExprKind::Err => false,
+use rustc_middle::ty::{self, PredicateKind};
+use rustc_span::{sym, Symbol};
+use std::cmp;
+use std::ops;
+
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+enum EagernessSuggestion {
+    // The expression is cheap and should be evaluated eagerly
+    Eager,
+    // The expression may be cheap, so don't suggested lazy evaluation; or the expression may not be safe to switch to
+    // eager evaluation.
+    NoChange,
+    // The expression is likely expensive and should be evaluated lazily.
+    Lazy,
+    // The expression cannot be placed into a closure.
+    ForceNoChange,
+}
+impl ops::BitOr for EagernessSuggestion {
+    type Output = Self;
+    fn bitor(self, rhs: Self) -> Self {
+        cmp::max(self, rhs)
     }
 }
-
-/// Identify some potentially computationally expensive patterns.
-/// This function is named so to stress that its implementation is non-exhaustive.
-/// It returns FNs and FPs.
-fn identify_some_potentially_expensive_patterns<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
-    // Searches an expression for method calls or function calls that aren't ctors
-    struct FunCallFinder<'a, 'tcx> {
-        cx: &'a LateContext<'tcx>,
-        found: bool,
+impl ops::BitOrAssign for EagernessSuggestion {
+    fn bitor_assign(&mut self, rhs: Self) {
+        *self = *self | rhs;
     }
+}
 
-    impl<'a, 'tcx> intravisit::Visitor<'tcx> for FunCallFinder<'a, 'tcx> {
-        type Map = Map<'tcx>;
-
-        fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
-            let call_found = match &expr.kind {
-                // ignore enum and struct constructors
-                ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr),
-                ExprKind::Index(obj, _) => {
-                    let ty = self.cx.typeck_results().expr_ty(obj);
-                    is_type_diagnostic_item(self.cx, ty, sym::HashMap)
-                        || is_type_diagnostic_item(self.cx, ty, sym::BTreeMap)
-                },
-                ExprKind::MethodCall(..) => true,
-                _ => false,
-            };
+/// Determine the eagerness of the given function call.
+fn fn_eagerness(cx: &LateContext<'tcx>, fn_id: DefId, name: Symbol, args: &'tcx [Expr<'_>]) -> EagernessSuggestion {
+    use EagernessSuggestion::{Eager, Lazy, NoChange};
+    let name = &*name.as_str();
 
-            if call_found {
-                self.found |= true;
-            }
+    let ty = match cx.tcx.impl_of_method(fn_id) {
+        Some(id) => cx.tcx.type_of(id),
+        None => return Lazy,
+    };
 
-            if !self.found {
-                intravisit::walk_expr(self, expr);
+    if (name.starts_with("as_") || name == "len" || name == "is_empty") && args.len() == 1 {
+        if matches!(
+            cx.tcx.crate_name(fn_id.krate),
+            sym::std | sym::core | sym::alloc | sym::proc_macro
+        ) {
+            Eager
+        } else {
+            NoChange
+        }
+    } else if let ty::Adt(def, subs) = ty.kind() {
+        // Types where the only fields are generic types (or references to) with no trait bounds other
+        // than marker traits.
+        // Due to the limited operations on these types functions should be fairly cheap.
+        if def
+            .variants
+            .iter()
+            .flat_map(|v| v.fields.iter())
+            .any(|x| matches!(cx.tcx.type_of(x.did).peel_refs().kind(), ty::Param(_)))
+            && all_predicates_of(cx.tcx, fn_id).all(|(pred, _)| match pred.kind().skip_binder() {
+                PredicateKind::Trait(pred) => cx.tcx.trait_def(pred.trait_ref.def_id).is_marker,
+                _ => true,
+            })
+            && subs.types().all(|x| matches!(x.peel_refs().kind(), ty::Param(_)))
+        {
+            // Limit the function to either `(self) -> bool` or `(&self) -> bool`
+            match &**cx.tcx.fn_sig(fn_id).skip_binder().inputs_and_output {
+                [arg, res] if !arg.is_mutable_ptr() && arg.peel_refs() == ty && res.is_bool() => NoChange,
+                _ => Lazy,
             }
+        } else {
+            Lazy
         }
+    } else {
+        Lazy
+    }
+}
 
+#[allow(clippy::too_many_lines)]
+fn expr_eagerness(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion {
+    struct V<'cx, 'tcx> {
+        cx: &'cx LateContext<'tcx>,
+        eagerness: EagernessSuggestion,
+    }
+
+    impl<'cx, 'tcx> Visitor<'tcx> for V<'cx, 'tcx> {
+        type Map = ErasedMap<'tcx>;
         fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
             NestedVisitorMap::None
         }
+
+        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
+            use EagernessSuggestion::{ForceNoChange, Lazy, NoChange};
+            if self.eagerness == ForceNoChange {
+                return;
+            }
+            match e.kind {
+                ExprKind::Call(
+                    &Expr {
+                        kind: ExprKind::Path(ref path),
+                        hir_id,
+                        ..
+                    },
+                    args,
+                ) => match self.cx.qpath_res(path, hir_id) {
+                    Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) => (),
+                    Res::Def(_, id) if self.cx.tcx.is_promotable_const_fn(id) => (),
+                    // No need to walk the arguments here, `is_const_evaluatable` already did
+                    Res::Def(..) if is_const_evaluatable(self.cx, e) => {
+                        self.eagerness |= NoChange;
+                        return;
+                    },
+                    Res::Def(_, id) => match path {
+                        QPath::Resolved(_, p) => {
+                            self.eagerness |= fn_eagerness(self.cx, id, p.segments.last().unwrap().ident.name, args);
+                        },
+                        QPath::TypeRelative(_, name) => {
+                            self.eagerness |= fn_eagerness(self.cx, id, name.ident.name, args);
+                        },
+                        QPath::LangItem(..) => self.eagerness = Lazy,
+                    },
+                    _ => self.eagerness = Lazy,
+                },
+                // No need to walk the arguments here, `is_const_evaluatable` already did
+                ExprKind::MethodCall(..) if is_const_evaluatable(self.cx, e) => {
+                    self.eagerness |= NoChange;
+                    return;
+                },
+                ExprKind::MethodCall(name, _, args, _) => {
+                    self.eagerness |= self
+                        .cx
+                        .typeck_results()
+                        .type_dependent_def_id(e.hir_id)
+                        .map_or(Lazy, |id| fn_eagerness(self.cx, id, name.ident.name, args));
+                },
+                ExprKind::Index(_, e) => {
+                    let ty = self.cx.typeck_results().expr_ty_adjusted(e);
+                    if is_copy(self.cx, ty) && !ty.is_ref() {
+                        self.eagerness |= NoChange;
+                    } else {
+                        self.eagerness = Lazy;
+                    }
+                },
+
+                // Dereferences should be cheap, but dereferencing a raw pointer earlier may not be safe.
+                ExprKind::Unary(UnOp::Deref, e) if !self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => (),
+                ExprKind::Unary(UnOp::Deref, _) => self.eagerness |= NoChange,
+
+                ExprKind::Unary(_, e)
+                    if matches!(
+                        self.cx.typeck_results().expr_ty(e).kind(),
+                        ty::Bool | ty::Int(_) | ty::Uint(_),
+                    ) => {},
+                ExprKind::Binary(_, lhs, rhs)
+                    if self.cx.typeck_results().expr_ty(lhs).is_primitive()
+                        && self.cx.typeck_results().expr_ty(rhs).is_primitive() => {},
+
+                // Can't be moved into a closure
+                ExprKind::Break(..)
+                | ExprKind::Continue(_)
+                | ExprKind::Ret(_)
+                | ExprKind::InlineAsm(_)
+                | ExprKind::LlvmInlineAsm(_)
+                | ExprKind::Yield(..)
+                | ExprKind::Err => {
+                    self.eagerness = ForceNoChange;
+                    return;
+                },
+
+                // Memory allocation, custom operator, loop, or call to an unknown function
+                ExprKind::Box(_)
+                | ExprKind::Unary(..)
+                | ExprKind::Binary(..)
+                | ExprKind::Loop(..)
+                | ExprKind::Call(..) => self.eagerness = Lazy,
+
+                ExprKind::ConstBlock(_)
+                | ExprKind::Array(_)
+                | ExprKind::Tup(_)
+                | ExprKind::Lit(_)
+                | ExprKind::Cast(..)
+                | ExprKind::Type(..)
+                | ExprKind::DropTemps(_)
+                | ExprKind::Let(..)
+                | ExprKind::If(..)
+                | ExprKind::Match(..)
+                | ExprKind::Closure(..)
+                | ExprKind::Field(..)
+                | ExprKind::Path(_)
+                | ExprKind::AddrOf(..)
+                | ExprKind::Struct(..)
+                | ExprKind::Repeat(..)
+                | ExprKind::Block(Block { stmts: [], .. }, _) => (),
+
+                // Assignment might be to a local defined earlier, so don't eagerly evaluate.
+                // Blocks with multiple statements might be expensive, so don't eagerly evaluate.
+                // TODO: Actually check if either of these are true here.
+                ExprKind::Assign(..) | ExprKind::AssignOp(..) | ExprKind::Block(..) => self.eagerness |= NoChange,
+            }
+            walk_expr(self, e);
+        }
     }
 
-    let mut finder = FunCallFinder { cx, found: false };
-    finder.visit_expr(expr);
-    finder.found
+    let mut v = V {
+        cx,
+        eagerness: EagernessSuggestion::Eager,
+    };
+    v.visit_expr(e);
+    v.eagerness
 }
 
-pub fn is_eagerness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
-    !identify_some_potentially_expensive_patterns(cx, expr) && identify_some_pure_patterns(expr)
+/// Whether the given expression should be changed to evaluate eagerly
+pub fn switch_to_eager_eval(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+    expr_eagerness(cx, expr) == EagernessSuggestion::Eager
 }
 
-pub fn is_lazyness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
-    identify_some_potentially_expensive_patterns(cx, expr)
+/// Whether the given expression should be changed to evaluate lazily
+pub fn switch_to_lazy_eval(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+    expr_eagerness(cx, expr) == EagernessSuggestion::Lazy
 }
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 086fbc9d3dd..f011380c127 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -1,6 +1,7 @@
 #![feature(box_patterns)]
 #![feature(in_band_lifetimes)]
 #![feature(iter_zip)]
+#![feature(let_else)]
 #![feature(rustc_private)]
 #![feature(control_flow_enum)]
 #![recursion_limit = "512"]
@@ -68,7 +69,7 @@ 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::intravisit::{walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor};
 use rustc_hir::itemlikevisit::ItemLikeVisitor;
 use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
 use rustc_hir::{
@@ -96,6 +97,7 @@ use rustc_target::abi::Integer;
 
 use crate::consts::{constant, Constant};
 use crate::ty::{can_partially_move_ty, is_copy, is_recursively_primitive_type};
+use crate::visitors::expr_visitor_no_bodies;
 
 pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
     if let Ok(version) = RustcVersion::parse(msrv) {
@@ -250,12 +252,6 @@ pub fn is_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem
     false
 }
 
-/// Returns `true` if this `span` was expanded by any macro.
-#[must_use]
-pub fn in_macro(span: Span) -> bool {
-    span.from_expansion() && !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..))
-}
-
 pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
     matches!(
         expr.kind,
@@ -1113,63 +1109,30 @@ pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool {
 
 /// Returns `true` if `expr` contains a return expression
 pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
-    struct RetCallFinder {
-        found: bool,
-    }
-
-    impl<'tcx> hir::intravisit::Visitor<'tcx> for RetCallFinder {
-        type Map = Map<'tcx>;
-
-        fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
-            if self.found {
-                return;
-            }
+    let mut found = false;
+    expr_visitor_no_bodies(|expr| {
+        if !found {
             if let hir::ExprKind::Ret(..) = &expr.kind {
-                self.found = true;
-            } else {
-                hir::intravisit::walk_expr(self, expr);
+                found = true;
             }
         }
-
-        fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
-            hir::intravisit::NestedVisitorMap::None
-        }
-    }
-
-    let mut visitor = RetCallFinder { found: false };
-    visitor.visit_expr(expr);
-    visitor.found
-}
-
-struct FindMacroCalls<'a, 'b> {
-    names: &'a [&'b str],
-    result: Vec<Span>,
-}
-
-impl<'a, 'b, 'tcx> Visitor<'tcx> for FindMacroCalls<'a, 'b> {
-    type Map = Map<'tcx>;
-
-    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
-        if self.names.iter().any(|fun| is_expn_of(expr.span, fun).is_some()) {
-            self.result.push(expr.span);
-        }
-        // and check sub-expressions
-        intravisit::walk_expr(self, expr);
-    }
-
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::None
-    }
+        !found
+    })
+    .visit_expr(expr);
+    found
 }
 
 /// Finds calls of the specified macros in a function body.
 pub fn find_macro_calls(names: &[&str], body: &Body<'_>) -> Vec<Span> {
-    let mut fmc = FindMacroCalls {
-        names,
-        result: Vec::new(),
-    };
-    fmc.visit_expr(&body.value);
-    fmc.result
+    let mut result = Vec::new();
+    expr_visitor_no_bodies(|expr| {
+        if names.iter().any(|fun| is_expn_of(expr.span, fun).is_some()) {
+            result.push(expr.span);
+        }
+        true
+    })
+    .visit_expr(&body.value);
+    result
 }
 
 /// Extends the span to the beginning of the spans line, incl. whitespaces.
@@ -1439,7 +1402,7 @@ pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>
 /// Checks for the `#[automatically_derived]` attribute all `#[derive]`d
 /// implementations have.
 pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool {
-    attrs.iter().any(|attr| attr.has_name(sym::automatically_derived))
+    has_attr(attrs, sym::automatically_derived)
 }
 
 /// Remove blocks around an expression.
@@ -1561,20 +1524,29 @@ pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::UintTy) -> u128 {
     (u << amt) >> amt
 }
 
-pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool {
+pub fn has_attr(attrs: &[ast::Attribute], symbol: Symbol) -> bool {
+    attrs.iter().any(|attr| attr.has_name(symbol))
+}
+
+pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
     let map = &tcx.hir();
     let mut prev_enclosing_node = None;
     let mut enclosing_node = node;
     while Some(enclosing_node) != prev_enclosing_node {
-        if is_automatically_derived(map.attrs(enclosing_node)) {
+        if has_attr(map.attrs(enclosing_node), symbol) {
             return true;
         }
         prev_enclosing_node = Some(enclosing_node);
         enclosing_node = map.get_parent_item(enclosing_node);
     }
+
     false
 }
 
+pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool {
+    any_parent_has_attr(tcx, node, sym::automatically_derived)
+}
+
 /// Matches a function call with the given path and returns the arguments.
 ///
 /// Usage:
@@ -1625,6 +1597,14 @@ pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -
     syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
 }
 
+/// Checks if the given `DefId` matches the `libc` item.
+pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
+    let path = cx.get_def_path(did);
+    // libc is meant to be used as a flat list of names, but they're all actually defined in different
+    // modules based on the target platform. Ignore everything but crate name and the item name.
+    path.first().map_or(false, |s| s.as_str() == "libc") && path.last().map_or(false, |s| s.as_str() == name)
+}
+
 pub fn match_panic_call(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
     if let ExprKind::Call(func, [arg]) = expr.kind {
         expr_path_res(cx, func)
@@ -1837,6 +1817,16 @@ pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
     matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..)))
 }
 
+pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
+    if !is_no_std_crate(cx) {
+        Some("std")
+    } else if !is_no_core_crate(cx) {
+        Some("core")
+    } else {
+        None
+    }
+}
+
 pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
     cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
         if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
@@ -1847,6 +1837,16 @@ pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
     })
 }
 
+pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
+    cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
+        if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
+            attr.path == sym::no_core
+        } else {
+            false
+        }
+    })
+}
+
 /// Check if parent of a hir node is a trait implementation block.
 /// For example, `f` in
 /// ```rust,ignore
diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs
index fa57dfbb57e..0cec7d6a5e4 100644
--- a/src/tools/clippy/clippy_utils/src/msrvs.rs
+++ b/src/tools/clippy/clippy_utils/src/msrvs.rs
@@ -19,7 +19,7 @@ msrv_aliases! {
     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,42,0 { MATCHES_MACRO, SLICE_PATTERNS }
     1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE }
     1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF }
     1,38,0 { POINTER_CAST }
@@ -27,7 +27,8 @@ msrv_aliases! {
     1,36,0 { ITERATOR_COPIED }
     1,35,0 { OPTION_COPIED, RANGE_CONTAINS }
     1,34,0 { TRY_FROM }
-    1,30,0 { ITERATOR_FIND_MAP }
+    1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES }
+    1,28,0 { FROM_BOOL }
     1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST }
     1,16,0 { STR_REPEAT }
 }
diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs
index 501b08a47f1..6171823abbb 100644
--- a/src/tools/clippy/clippy_utils/src/paths.rs
+++ b/src/tools/clippy/clippy_utils/src/paths.rs
@@ -28,6 +28,7 @@ pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
 pub(super) const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"];
 /// Preferably use the diagnostic item `sym::Borrow` where possible
 pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
+pub const BORROW_MUT_TRAIT: [&str; 3] = ["core", "borrow", "BorrowMut"];
 pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"];
 pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"];
 pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"];
@@ -85,7 +86,6 @@ pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tup
 pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
 #[cfg(feature = "internal-lints")]
 pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
-pub const LIBC_STRLEN: [&str; 2] = ["libc", "strlen"];
 #[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))]
 pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
 pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
@@ -110,6 +110,8 @@ pub(super) const PANICKING_PANIC: [&str; 3] = ["core", "panicking", "panic"];
 pub(super) const PANICKING_PANIC_FMT: [&str; 3] = ["core", "panicking", "panic_fmt"];
 pub(super) const PANICKING_PANIC_STR: [&str; 3] = ["core", "panicking", "panic_str"];
 pub(super) const PANIC_ANY: [&str; 3] = ["std", "panic", "panic_any"];
+pub const PARKING_LOT_RAWMUTEX: [&str; 3] = ["parking_lot", "raw_mutex", "RawMutex"];
+pub const PARKING_LOT_RAWRWLOCK: [&str; 3] = ["parking_lot", "raw_rwlock", "RawRwLock"];
 pub const PARKING_LOT_MUTEX_GUARD: [&str; 2] = ["parking_lot", "MutexGuard"];
 pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 2] = ["parking_lot", "RwLockReadGuard"];
 pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 2] = ["parking_lot", "RwLockWriteGuard"];
@@ -204,3 +206,4 @@ pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
 pub const WRITE_MACRO: [&str; 3] = ["core", "macros", "write"];
 #[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros
 pub const WRITELN_MACRO: [&str; 3] = ["core", "macros", "writeln"];
+pub const PTR_NON_NULL: [&str; 4] = ["core", "ptr", "non_null", "NonNull"];
diff --git a/src/tools/clippy/clippy_utils/src/ptr.rs b/src/tools/clippy/clippy_utils/src/ptr.rs
index 8adb6915952..17d9a505bc9 100644
--- a/src/tools/clippy/clippy_utils/src/ptr.rs
+++ b/src/tools/clippy/clippy_utils/src/ptr.rs
@@ -1,9 +1,9 @@
 use crate::source::snippet;
+use crate::visitors::expr_visitor_no_bodies;
 use crate::{path_to_local_id, strip_pat_refs};
-use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
-use rustc_hir::{Body, BodyId, Expr, ExprKind, HirId, PatKind};
+use rustc_hir::intravisit::Visitor;
+use rustc_hir::{Body, BodyId, ExprKind, HirId, PatKind};
 use rustc_lint::LateContext;
-use rustc_middle::hir::map::Map;
 use rustc_span::Span;
 use std::borrow::Cow;
 
@@ -30,50 +30,28 @@ fn extract_clone_suggestions<'tcx>(
     replace: &[(&'static str, &'static str)],
     body: &'tcx Body<'_>,
 ) -> Option<Vec<(Span, Cow<'static, str>)>> {
-    let mut visitor = PtrCloneVisitor {
-        cx,
-        id,
-        replace,
-        spans: vec![],
-        abort: false,
-    };
-    visitor.visit_body(body);
-    if visitor.abort { None } else { Some(visitor.spans) }
-}
-
-struct PtrCloneVisitor<'a, 'tcx> {
-    cx: &'a LateContext<'tcx>,
-    id: HirId,
-    replace: &'a [(&'static str, &'static str)],
-    spans: Vec<(Span, Cow<'static, str>)>,
-    abort: bool,
-}
-
-impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> {
-    type Map = Map<'tcx>;
-
-    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
-        if self.abort {
-            return;
+    let mut abort = false;
+    let mut spans = Vec::new();
+    expr_visitor_no_bodies(|expr| {
+        if abort {
+            return false;
         }
         if let ExprKind::MethodCall(seg, _, [recv], _) = expr.kind {
-            if path_to_local_id(recv, self.id) {
+            if path_to_local_id(recv, id) {
                 if seg.ident.name.as_str() == "capacity" {
-                    self.abort = true;
-                    return;
+                    abort = true;
+                    return false;
                 }
-                for &(fn_name, suffix) in self.replace {
+                for &(fn_name, suffix) in replace {
                     if seg.ident.name.as_str() == fn_name {
-                        self.spans.push((expr.span, snippet(self.cx, recv.span, "_") + suffix));
-                        return;
+                        spans.push((expr.span, snippet(cx, recv.span, "_") + suffix));
+                        return false;
                     }
                 }
             }
         }
-        walk_expr(self, expr);
-    }
-
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::None
-    }
+        !abort
+    })
+    .visit_body(body);
+    if abort { None } else { Some(spans) }
 }
diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs
index 789079510c5..d928317259d 100644
--- a/src/tools/clippy/clippy_utils/src/source.rs
+++ b/src/tools/clippy/clippy_utils/src/source.rs
@@ -128,7 +128,7 @@ pub fn reindent_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option<us
 fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>, ch: char) -> String {
     let x = s
         .lines()
-        .skip(ignore_first as usize)
+        .skip(usize::from(ignore_first))
         .filter_map(|l| {
             if l.is_empty() {
                 None
@@ -155,14 +155,22 @@ fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>,
         .join("\n")
 }
 
-/// Converts a span to a code snippet if available, otherwise use default.
+/// Converts a span to a code snippet if available, otherwise returns the default.
 ///
 /// This is useful if you want to provide suggestions for your lint or more generally, if you want
-/// to convert a given `Span` to a `str`.
+/// to convert a given `Span` to a `str`. To create suggestions consider using
+/// [`snippet_with_applicability`] to ensure that the applicability stays correct.
 ///
 /// # Example
 /// ```rust,ignore
-/// snippet(cx, expr.span, "..")
+/// // Given two spans one for `value` and one for the `init` expression.
+/// let value = Vec::new();
+/// //  ^^^^^   ^^^^^^^^^^
+/// //  span1   span2
+///
+/// // The snipped call would return the corresponding code snippet
+/// snippet(cx, span1, "..") // -> "value"
+/// snippet(cx, span2, "..") // -> "Vec::new()"
 /// ```
 pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
     snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from)
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index 01fb944cc36..872942685f0 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -1,19 +1,27 @@
 //! Contains utility functions to generate suggestions.
 #![deny(clippy::missing_docs_in_private_items)]
 
-use crate::higher;
-use crate::source::{snippet, snippet_opt, snippet_with_context, snippet_with_macro_callsite};
+use crate::source::{
+    snippet, snippet_opt, snippet_with_applicability, snippet_with_context, snippet_with_macro_callsite,
+};
+use crate::{get_parent_expr_for_hir, higher};
 use rustc_ast::util::parser::AssocOp;
 use rustc_ast::{ast, token};
 use rustc_ast_pretty::pprust::token_kind_to_string;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
+use rustc_hir::{ExprKind, HirId, MutTy, TyKind};
+use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{EarlyContext, LateContext, LintContext};
-use rustc_span::source_map::{CharPos, Span};
-use rustc_span::{BytePos, Pos, SyntaxContext};
+use rustc_middle::hir::place::ProjectionKind;
+use rustc_middle::mir::{FakeReadCause, Mutability};
+use rustc_middle::ty;
+use rustc_span::source_map::{BytePos, CharPos, Pos, Span, SyntaxContext};
+use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 use std::borrow::Cow;
 use std::convert::TryInto;
 use std::fmt::Display;
+use std::iter;
 use std::ops::{Add, Neg, Not, Sub};
 
 /// A helper type to build suggestion correctly handling parentheses.
@@ -716,6 +724,267 @@ impl<T: LintContext> DiagnosticBuilderExt<T> for rustc_errors::DiagnosticBuilder
     }
 }
 
+/// Suggestion results for handling closure
+/// args dereferencing and borrowing
+pub struct DerefClosure {
+    /// confidence on the built suggestion
+    pub applicability: Applicability,
+    /// gradually built suggestion
+    pub suggestion: String,
+}
+
+/// Build suggestion gradually by handling closure arg specific usages,
+/// such as explicit deref and borrowing cases.
+/// Returns `None` if no such use cases have been triggered in closure body
+///
+/// note: this only works on single line immutable closures with exactly one input parameter.
+pub fn deref_closure_args<'tcx>(cx: &LateContext<'_>, closure: &'tcx hir::Expr<'_>) -> Option<DerefClosure> {
+    if let hir::ExprKind::Closure(_, fn_decl, body_id, ..) = closure.kind {
+        let closure_body = cx.tcx.hir().body(body_id);
+        // is closure arg a type annotated double reference (i.e.: `|x: &&i32| ...`)
+        // a type annotation is present if param `kind` is different from `TyKind::Infer`
+        let closure_arg_is_type_annotated_double_ref = if let TyKind::Rptr(_, MutTy { ty, .. }) = fn_decl.inputs[0].kind
+        {
+            matches!(ty.kind, TyKind::Rptr(_, MutTy { .. }))
+        } else {
+            false
+        };
+
+        let mut visitor = DerefDelegate {
+            cx,
+            closure_span: closure.span,
+            closure_arg_is_type_annotated_double_ref,
+            next_pos: closure.span.lo(),
+            suggestion_start: String::new(),
+            applicability: Applicability::MaybeIncorrect,
+        };
+
+        let fn_def_id = cx.tcx.hir().local_def_id(closure.hir_id);
+        cx.tcx.infer_ctxt().enter(|infcx| {
+            ExprUseVisitor::new(&mut visitor, &infcx, fn_def_id, cx.param_env, cx.typeck_results())
+                .consume_body(closure_body);
+        });
+
+        if !visitor.suggestion_start.is_empty() {
+            return Some(DerefClosure {
+                applicability: visitor.applicability,
+                suggestion: visitor.finish(),
+            });
+        }
+    }
+    None
+}
+
+/// Visitor struct used for tracking down
+/// dereferencing and borrowing of closure's args
+struct DerefDelegate<'a, 'tcx> {
+    /// The late context of the lint
+    cx: &'a LateContext<'tcx>,
+    /// The span of the input closure to adapt
+    closure_span: Span,
+    /// Indicates if the arg of the closure is a type annotated double reference
+    closure_arg_is_type_annotated_double_ref: bool,
+    /// last position of the span to gradually build the suggestion
+    next_pos: BytePos,
+    /// starting part of the gradually built suggestion
+    suggestion_start: String,
+    /// confidence on the built suggestion
+    applicability: Applicability,
+}
+
+impl DerefDelegate<'_, 'tcx> {
+    /// build final suggestion:
+    /// - create the ending part of suggestion
+    /// - concatenate starting and ending parts
+    /// - potentially remove needless borrowing
+    pub fn finish(&mut self) -> String {
+        let end_span = Span::new(self.next_pos, self.closure_span.hi(), self.closure_span.ctxt(), None);
+        let end_snip = snippet_with_applicability(self.cx, end_span, "..", &mut self.applicability);
+        let sugg = format!("{}{}", self.suggestion_start, end_snip);
+        if self.closure_arg_is_type_annotated_double_ref {
+            sugg.replacen('&', "", 1)
+        } else {
+            sugg
+        }
+    }
+
+    /// indicates whether the function from `parent_expr` takes its args by double reference
+    fn func_takes_arg_by_double_ref(&self, parent_expr: &'tcx hir::Expr<'_>, cmt_hir_id: HirId) -> bool {
+        let (call_args, inputs) = match parent_expr.kind {
+            ExprKind::MethodCall(_, _, call_args, _) => {
+                if let Some(method_did) = self.cx.typeck_results().type_dependent_def_id(parent_expr.hir_id) {
+                    (call_args, self.cx.tcx.fn_sig(method_did).skip_binder().inputs())
+                } else {
+                    return false;
+                }
+            },
+            ExprKind::Call(func, call_args) => {
+                let typ = self.cx.typeck_results().expr_ty(func);
+                (call_args, typ.fn_sig(self.cx.tcx).skip_binder().inputs())
+            },
+            _ => return false,
+        };
+
+        iter::zip(call_args, inputs)
+            .any(|(arg, ty)| arg.hir_id == cmt_hir_id && matches!(ty.kind(), ty::Ref(_, inner, _) if inner.is_ref()))
+    }
+}
+
+impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
+    fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
+
+    #[allow(clippy::too_many_lines)]
+    fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {
+        if let PlaceBase::Local(id) = cmt.place.base {
+            let map = self.cx.tcx.hir();
+            let span = map.span(cmt.hir_id);
+            let start_span = Span::new(self.next_pos, span.lo(), span.ctxt(), None);
+            let mut start_snip = snippet_with_applicability(self.cx, start_span, "..", &mut self.applicability);
+
+            // identifier referring to the variable currently triggered (i.e.: `fp`)
+            let ident_str = map.name(id).to_string();
+            // full identifier that includes projection (i.e.: `fp.field`)
+            let ident_str_with_proj = snippet(self.cx, span, "..").to_string();
+
+            if cmt.place.projections.is_empty() {
+                // handle item without any projection, that needs an explicit borrowing
+                // i.e.: suggest `&x` instead of `x`
+                self.suggestion_start.push_str(&format!("{}&{}", start_snip, ident_str));
+            } else {
+                // cases where a parent `Call` or `MethodCall` is using the item
+                // i.e.: suggest `.contains(&x)` for `.find(|x| [1, 2, 3].contains(x)).is_none()`
+                //
+                // Note about method calls:
+                // - compiler automatically dereference references if the target type is a reference (works also for
+                //   function call)
+                // - `self` arguments in the case of `x.is_something()` are also automatically (de)referenced, and
+                //   no projection should be suggested
+                if let Some(parent_expr) = get_parent_expr_for_hir(self.cx, cmt.hir_id) {
+                    match &parent_expr.kind {
+                        // given expression is the self argument and will be handled completely by the compiler
+                        // i.e.: `|x| x.is_something()`
+                        ExprKind::MethodCall(_, _, [self_expr, ..], _) if self_expr.hir_id == cmt.hir_id => {
+                            self.suggestion_start
+                                .push_str(&format!("{}{}", start_snip, ident_str_with_proj));
+                            self.next_pos = span.hi();
+                            return;
+                        },
+                        // item is used in a call
+                        // i.e.: `Call`: `|x| please(x)` or `MethodCall`: `|x| [1, 2, 3].contains(x)`
+                        ExprKind::Call(_, [call_args @ ..]) | ExprKind::MethodCall(_, _, [_, call_args @ ..], _) => {
+                            let expr = self.cx.tcx.hir().expect_expr(cmt.hir_id);
+                            let arg_ty_kind = self.cx.typeck_results().expr_ty(expr).kind();
+
+                            if matches!(arg_ty_kind, ty::Ref(_, _, Mutability::Not)) {
+                                // suggest ampersand if call function is taking args by double reference
+                                let takes_arg_by_double_ref =
+                                    self.func_takes_arg_by_double_ref(parent_expr, cmt.hir_id);
+
+                                // compiler will automatically dereference field or index projection, so no need
+                                // to suggest ampersand, but full identifier that includes projection is required
+                                let has_field_or_index_projection =
+                                    cmt.place.projections.iter().any(|proj| {
+                                        matches!(proj.kind, ProjectionKind::Field(..) | ProjectionKind::Index)
+                                    });
+
+                                // no need to bind again if the function doesn't take arg by double ref
+                                // and if the item is already a double ref
+                                let ident_sugg = if !call_args.is_empty()
+                                    && !takes_arg_by_double_ref
+                                    && (self.closure_arg_is_type_annotated_double_ref || has_field_or_index_projection)
+                                {
+                                    let ident = if has_field_or_index_projection {
+                                        ident_str_with_proj
+                                    } else {
+                                        ident_str
+                                    };
+                                    format!("{}{}", start_snip, ident)
+                                } else {
+                                    format!("{}&{}", start_snip, ident_str)
+                                };
+                                self.suggestion_start.push_str(&ident_sugg);
+                                self.next_pos = span.hi();
+                                return;
+                            }
+
+                            self.applicability = Applicability::Unspecified;
+                        },
+                        _ => (),
+                    }
+                }
+
+                let mut replacement_str = ident_str;
+                let mut projections_handled = false;
+                cmt.place.projections.iter().enumerate().for_each(|(i, proj)| {
+                    match proj.kind {
+                        // Field projection like `|v| v.foo`
+                        // no adjustment needed here, as field projections are handled by the compiler
+                        ProjectionKind::Field(..) => match cmt.place.ty_before_projection(i).kind() {
+                            ty::Adt(..) | ty::Tuple(_) => {
+                                replacement_str = ident_str_with_proj.clone();
+                                projections_handled = true;
+                            },
+                            _ => (),
+                        },
+                        // Index projection like `|x| foo[x]`
+                        // the index is dropped so we can't get it to build the suggestion,
+                        // so the span is set-up again to get more code, using `span.hi()` (i.e.: `foo[x]`)
+                        // instead of `span.lo()` (i.e.: `foo`)
+                        ProjectionKind::Index => {
+                            let start_span = Span::new(self.next_pos, span.hi(), span.ctxt(), None);
+                            start_snip = snippet_with_applicability(self.cx, start_span, "..", &mut self.applicability);
+                            replacement_str.clear();
+                            projections_handled = true;
+                        },
+                        // note: unable to trigger `Subslice` kind in tests
+                        ProjectionKind::Subslice => (),
+                        ProjectionKind::Deref => {
+                            // Explicit derefs are typically handled later on, but
+                            // some items do not need explicit deref, such as array accesses,
+                            // so we mark them as already processed
+                            // i.e.: don't suggest `*sub[1..4].len()` for `|sub| sub[1..4].len() == 3`
+                            if let ty::Ref(_, inner, _) = cmt.place.ty_before_projection(i).kind() {
+                                if matches!(inner.kind(), ty::Ref(_, innermost, _) if innermost.is_array()) {
+                                    projections_handled = true;
+                                }
+                            }
+                        },
+                    }
+                });
+
+                // handle `ProjectionKind::Deref` by removing one explicit deref
+                // if no special case was detected (i.e.: suggest `*x` instead of `**x`)
+                if !projections_handled {
+                    let last_deref = cmt
+                        .place
+                        .projections
+                        .iter()
+                        .rposition(|proj| proj.kind == ProjectionKind::Deref);
+
+                    if let Some(pos) = last_deref {
+                        let mut projections = cmt.place.projections.clone();
+                        projections.truncate(pos);
+
+                        for item in projections {
+                            if item.kind == ProjectionKind::Deref {
+                                replacement_str = format!("*{}", replacement_str);
+                            }
+                        }
+                    }
+                }
+
+                self.suggestion_start
+                    .push_str(&format!("{}{}", start_snip, replacement_str));
+            }
+            self.next_pos = span.hi();
+        }
+    }
+
+    fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
+
+    fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {}
+}
+
 #[cfg(test)]
 mod test {
     use super::Sugg;
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index ca64ac7de3e..438c39bea0a 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -10,15 +10,16 @@ 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, AdtDef, IntTy, Ty, TyCtxt, TypeFoldable, UintTy};
-use rustc_span::sym;
-use rustc_span::symbol::{Ident, Symbol};
-use rustc_span::DUMMY_SP;
+use rustc_middle::ty::{self, AdtDef, IntTy, Predicate, Ty, TyCtxt, TypeFoldable, UintTy};
+use rustc_span::symbol::Ident;
+use rustc_span::{sym, Span, Symbol, DUMMY_SP};
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::query::normalize::AtExt;
+use std::iter;
 
 use crate::{match_def_path, must_use_attr};
 
+// Checks if the given type implements copy.
 pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
     ty.is_copy_modulo_regions(cx.tcx.at(DUMMY_SP), cx.param_env)
 }
@@ -114,7 +115,12 @@ pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<
 
 /// Checks whether a type implements a trait.
 /// The function returns false in case the type contains an inference variable.
-/// See also [`get_trait_def_id`](super::get_trait_def_id).
+///
+/// See:
+/// * [`get_trait_def_id`](super::get_trait_def_id) to get a trait [`DefId`].
+/// * [Common tools for writing lints] for an example how to use this function and other options.
+///
+/// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait
 pub fn implements_trait<'tcx>(
     cx: &LateContext<'tcx>,
     ty: Ty<'tcx>,
@@ -254,9 +260,17 @@ pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_ite
     }
 }
 
-/// Checks if the type is equal to a diagnostic item
+/// Checks if the type is equal to a diagnostic item. To check if a type implements a
+/// trait marked with a diagnostic item use [`implements_trait`].
+///
+/// For a further exploitation what diagnostic items are see [diagnostic items] in
+/// rustc-dev-guide.
+///
+/// ---
 ///
 /// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
+///
+/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
 pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
     match ty.kind() {
         ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did),
@@ -377,3 +391,16 @@ pub fn is_uninit_value_valid_for_ty(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
         _ => false,
     }
 }
+
+/// Gets an iterator over all predicates which apply to the given item.
+pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(Predicate<'_>, Span)> {
+    let mut next_id = Some(id);
+    iter::from_fn(move || {
+        next_id.take().map(|id| {
+            let preds = tcx.predicates_of(id);
+            next_id = preds.parent;
+            preds.predicates.iter()
+        })
+    })
+    .flatten()
+}
diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs
index 34206b5ae2b..dfe8a66c2a1 100644
--- a/src/tools/clippy/clippy_utils/src/usage.rs
+++ b/src/tools/clippy/clippy_utils/src/usage.rs
@@ -1,7 +1,7 @@
 use crate as utils;
+use crate::visitors::{expr_visitor, expr_visitor_no_bodies};
 use rustc_hir as hir;
-use rustc_hir::intravisit;
-use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
+use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::HirIdSet;
 use rustc_hir::{Expr, ExprKind, HirId};
 use rustc_infer::infer::TyCtxtInferExt;
@@ -148,96 +148,47 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> {
     }
 }
 
-struct ReturnBreakContinueMacroVisitor {
-    seen_return_break_continue: bool,
-}
-
-impl ReturnBreakContinueMacroVisitor {
-    fn new() -> ReturnBreakContinueMacroVisitor {
-        ReturnBreakContinueMacroVisitor {
-            seen_return_break_continue: false,
-        }
-    }
-}
-
-impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor {
-    type Map = Map<'tcx>;
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::None
-    }
-
-    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
-        if self.seen_return_break_continue {
-            // No need to look farther if we've already seen one of them
-            return;
+pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
+    let mut seen_return_break_continue = false;
+    expr_visitor_no_bodies(|ex| {
+        if seen_return_break_continue {
+            return false;
         }
         match &ex.kind {
             ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => {
-                self.seen_return_break_continue = true;
+                seen_return_break_continue = true;
             },
             // Something special could be done here to handle while or for loop
             // desugaring, as this will detect a break if there's a while loop
             // or a for loop inside the expression.
             _ => {
-                if utils::in_macro(ex.span) {
-                    self.seen_return_break_continue = true;
-                } else {
-                    rustc_hir::intravisit::walk_expr(self, ex);
+                if ex.span.from_expansion() {
+                    seen_return_break_continue = true;
                 }
             },
         }
-    }
-}
-
-pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
-    let mut recursive_visitor = ReturnBreakContinueMacroVisitor::new();
-    recursive_visitor.visit_expr(expression);
-    recursive_visitor.seen_return_break_continue
-}
-
-pub struct UsedAfterExprVisitor<'a, 'tcx> {
-    cx: &'a LateContext<'tcx>,
-    expr: &'tcx Expr<'tcx>,
-    definition: HirId,
-    past_expr: bool,
-    used_after_expr: bool,
-}
-impl<'a, 'tcx> UsedAfterExprVisitor<'a, 'tcx> {
-    pub fn is_found(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
-        utils::path_to_local(expr).map_or(false, |definition| {
-            let mut visitor = UsedAfterExprVisitor {
-                cx,
-                expr,
-                definition,
-                past_expr: false,
-                used_after_expr: false,
-            };
-            utils::get_enclosing_block(cx, definition).map_or(false, |block| {
-                visitor.visit_block(block);
-                visitor.used_after_expr
-            })
-        })
-    }
-}
-
-impl<'a, 'tcx> intravisit::Visitor<'tcx> for UsedAfterExprVisitor<'a, 'tcx> {
-    type Map = Map<'tcx>;
-
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
-    }
-
-    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
-        if self.used_after_expr {
-            return;
+        !seen_return_break_continue
+    })
+    .visit_expr(expression);
+    seen_return_break_continue
+}
+
+pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr<'_>) -> bool {
+    let Some(block) = utils::get_enclosing_block(cx, local_id) else { return false };
+    let mut used_after_expr = false;
+    let mut past_expr = false;
+    expr_visitor(cx, |expr| {
+        if used_after_expr {
+            return false;
         }
 
-        if expr.hir_id == self.expr.hir_id {
-            self.past_expr = true;
-        } else if self.past_expr && utils::path_to_local_id(expr, self.definition) {
-            self.used_after_expr = true;
-        } else {
-            intravisit::walk_expr(self, expr);
+        if expr.hir_id == after.hir_id {
+            past_expr = true;
+        } else if past_expr && utils::path_to_local_id(expr, local_id) {
+            used_after_expr = true;
         }
-    }
+        !used_after_expr
+    })
+    .visit_block(block);
+    used_after_expr
 }
diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs
index 503effbdad5..4bfd3c64b9c 100644
--- a/src/tools/clippy/clippy_utils/src/visitors.rs
+++ b/src/tools/clippy/clippy_utils/src/visitors.rs
@@ -1,38 +1,70 @@
 use crate::path_to_local_id;
 use rustc_hir as hir;
-use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor};
-use rustc_hir::{def::Res, Arm, Block, Body, BodyId, Destination, Expr, ExprKind, HirId, Stmt};
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::intravisit::{self, walk_block, walk_expr, NestedVisitorMap, Visitor};
+use rustc_hir::{
+    Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, Unsafety,
+};
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
-use std::ops::ControlFlow;
+use rustc_middle::ty;
 
-/// returns `true` if expr contains match expr desugared from try
-fn contains_try(expr: &hir::Expr<'_>) -> bool {
-    struct TryFinder {
-        found: bool,
+/// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
+/// bodies (i.e. closures) are visited.
+/// If the callback returns `true`, the expr just provided to the callback is walked.
+#[must_use]
+pub fn expr_visitor<'tcx>(cx: &LateContext<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> {
+    struct V<'tcx, F> {
+        hir: Map<'tcx>,
+        f: F,
     }
+    impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<'tcx, F> {
+        type Map = Map<'tcx>;
+        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+            NestedVisitorMap::OnlyBodies(self.hir)
+        }
 
-    impl<'hir> intravisit::Visitor<'hir> for TryFinder {
-        type Map = Map<'hir>;
+        fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+            if (self.f)(expr) {
+                walk_expr(self, expr);
+            }
+        }
+    }
+    V { hir: cx.tcx.hir(), f }
+}
 
-        fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
-            intravisit::NestedVisitorMap::None
+/// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
+/// bodies (i.e. closures) are not visited.
+/// If the callback returns `true`, the expr just provided to the callback is walked.
+#[must_use]
+pub fn expr_visitor_no_bodies<'tcx>(f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> {
+    struct V<F>(F);
+    impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<F> {
+        type Map = intravisit::ErasedMap<'tcx>;
+        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+            NestedVisitorMap::None
         }
 
-        fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
-            if self.found {
-                return;
-            }
-            match expr.kind {
-                hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar) => self.found = true,
-                _ => intravisit::walk_expr(self, expr),
+        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
+            if (self.0)(e) {
+                walk_expr(self, e);
             }
         }
     }
+    V(f)
+}
 
-    let mut visitor = TryFinder { found: false };
-    visitor.visit_expr(expr);
-    visitor.found
+/// returns `true` if expr contains match expr desugared from try
+fn contains_try(expr: &hir::Expr<'_>) -> bool {
+    let mut found = false;
+    expr_visitor_no_bodies(|e| {
+        if !found {
+            found = matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar));
+        }
+        !found
+    })
+    .visit_expr(expr);
+    found
 }
 
 pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool
@@ -165,103 +197,186 @@ visitable_ref!(Stmt, visit_stmt);
 //     }
 // }
 
-/// Calls the given function for each break expression.
-pub fn visit_break_exprs<'tcx>(
-    node: impl Visitable<'tcx>,
-    f: impl FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>),
-) {
-    struct V<F>(F);
-    impl<'tcx, F: FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>)> Visitor<'tcx> for V<F> {
-        type Map = ErasedMap<'tcx>;
-        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-            NestedVisitorMap::None
+/// Checks if the given resolved path is used in the given body.
+pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
+    let mut found = false;
+    expr_visitor(cx, |e| {
+        if found {
+            return false;
         }
 
-        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
-            if let ExprKind::Break(dest, sub_expr) = e.kind {
-                self.0(e, dest, sub_expr);
+        if let ExprKind::Path(p) = &e.kind {
+            if cx.qpath_res(p, e.hir_id) == res {
+                found = true;
             }
-            walk_expr(self, e);
         }
-    }
+        !found
+    })
+    .visit_expr(&cx.tcx.hir().body(body).value);
+    found
+}
 
-    node.visit(&mut V(f));
+/// Checks if the given local is used.
+pub fn is_local_used(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
+    let mut is_used = false;
+    let mut visitor = expr_visitor(cx, |expr| {
+        if !is_used {
+            is_used = path_to_local_id(expr, id);
+        }
+        !is_used
+    });
+    visitable.visit(&mut visitor);
+    drop(visitor);
+    is_used
 }
 
-/// Checks if the given resolved path is used in the given body.
-pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
+/// Checks if the given expression is a constant.
+pub fn is_const_evaluatable(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
     struct V<'a, 'tcx> {
         cx: &'a LateContext<'tcx>,
-        res: Res,
-        found: bool,
+        is_const: bool,
     }
-    impl Visitor<'tcx> for V<'_, 'tcx> {
+    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
         type Map = Map<'tcx>;
         fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
             NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
         }
 
         fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
-            if self.found {
+            if !self.is_const {
                 return;
             }
+            match e.kind {
+                ExprKind::ConstBlock(_) => return,
+                ExprKind::Call(
+                    &Expr {
+                        kind: ExprKind::Path(ref p),
+                        hir_id,
+                        ..
+                    },
+                    _,
+                ) if self
+                    .cx
+                    .qpath_res(p, hir_id)
+                    .opt_def_id()
+                    .map_or(false, |id| self.cx.tcx.is_const_fn_raw(id)) => {},
+                ExprKind::MethodCall(..)
+                    if self
+                        .cx
+                        .typeck_results()
+                        .type_dependent_def_id(e.hir_id)
+                        .map_or(false, |id| self.cx.tcx.is_const_fn_raw(id)) => {},
+                ExprKind::Binary(_, lhs, rhs)
+                    if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty()
+                        && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {},
+                ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_ref() => (),
+                ExprKind::Unary(_, e) if self.cx.typeck_results().expr_ty(e).peel_refs().is_primitive_ty() => (),
+                ExprKind::Index(base, _)
+                    if matches!(
+                        self.cx.typeck_results().expr_ty(base).peel_refs().kind(),
+                        ty::Slice(_) | ty::Array(..)
+                    ) => {},
+                ExprKind::Path(ref p)
+                    if matches!(
+                        self.cx.qpath_res(p, e.hir_id),
+                        Res::Def(
+                            DefKind::Const
+                                | DefKind::AssocConst
+                                | DefKind::AnonConst
+                                | DefKind::ConstParam
+                                | DefKind::Ctor(..)
+                                | DefKind::Fn
+                                | DefKind::AssocFn,
+                            _
+                        ) | Res::SelfCtor(_)
+                    ) => {},
 
-            if let ExprKind::Path(p) = &e.kind {
-                if self.cx.qpath_res(p, e.hir_id) == self.res {
-                    self.found = true;
-                }
-            } else {
-                walk_expr(self, e);
+                ExprKind::AddrOf(..)
+                | ExprKind::Array(_)
+                | ExprKind::Block(..)
+                | ExprKind::Cast(..)
+                | ExprKind::DropTemps(_)
+                | ExprKind::Field(..)
+                | ExprKind::If(..)
+                | ExprKind::Let(..)
+                | ExprKind::Lit(_)
+                | ExprKind::Match(..)
+                | ExprKind::Repeat(..)
+                | ExprKind::Struct(..)
+                | ExprKind::Tup(_)
+                | ExprKind::Type(..) => (),
+
+                _ => {
+                    self.is_const = false;
+                    return;
+                },
             }
+            walk_expr(self, e);
         }
     }
 
-    let mut v = V { cx, res, found: false };
-    v.visit_expr(&cx.tcx.hir().body(body).value);
-    v.found
+    let mut v = V { cx, is_const: true };
+    v.visit_expr(e);
+    v.is_const
 }
 
-/// 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>,
+/// Checks if the given expression performs an unsafe operation outside of an unsafe block.
+pub fn is_expr_unsafe(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
+    struct V<'a, 'tcx> {
+        cx: &'a LateContext<'tcx>,
+        is_unsafe: bool,
     }
-    impl<'tcx, B, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>> Visitor<'tcx> for V<'tcx, B, F> {
+    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
         type Map = Map<'tcx>;
         fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-            NestedVisitorMap::OnlyBodies(self.map)
+            NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
         }
-
         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);
-                }
+            if self.is_unsafe {
+                return;
+            }
+            match e.kind {
+                ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => {
+                    self.is_unsafe = true;
+                },
+                ExprKind::MethodCall(..)
+                    if self
+                        .cx
+                        .typeck_results()
+                        .type_dependent_def_id(e.hir_id)
+                        .map_or(false, |id| self.cx.tcx.fn_sig(id).unsafety() == Unsafety::Unsafe) =>
+                {
+                    self.is_unsafe = true;
+                },
+                ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() {
+                    ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
+                    ty::FnPtr(sig) if sig.unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
+                    _ => walk_expr(self, e),
+                },
+                ExprKind::Path(ref p)
+                    if self
+                        .cx
+                        .qpath_res(p, e.hir_id)
+                        .opt_def_id()
+                        .map_or(false, |id| self.cx.tcx.is_mutable_static(id)) =>
+                {
+                    self.is_unsafe = true;
+                },
+                _ => walk_expr(self, e),
+            }
+        }
+        fn visit_block(&mut self, b: &'tcx Block<'_>) {
+            if !matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) {
+                walk_block(self, b);
+            }
+        }
+        fn visit_nested_item(&mut self, id: ItemId) {
+            if let ItemKind::Impl(i) = &self.cx.tcx.hir().item(id).kind {
+                self.is_unsafe = i.unsafety == Unsafety::Unsafe;
             }
         }
     }
-
-    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()
+    let mut v = V { cx, is_unsafe: false };
+    v.visit_expr(e);
+    v.is_unsafe
 }
diff --git a/src/tools/clippy/doc/adding_lints.md b/src/tools/clippy/doc/adding_lints.md
index bd32696d6db..cf16a1d5d3d 100644
--- a/src/tools/clippy/doc/adding_lints.md
+++ b/src/tools/clippy/doc/adding_lints.md
@@ -157,7 +157,7 @@ Manually testing against an example file can be useful if you have added some
 your local modifications, run
 
 ```
-env __CLIPPY_INTERNAL_TESTS=true cargo run --bin clippy-driver -- -L ./target/debug input.rs
+cargo dev lint input.rs
 ```
 
 from the working copy root. With tests in place, let's have a look at
@@ -189,6 +189,7 @@ declare_clippy_lint! {
     /// ```rust
     /// // example code
     /// ```
+    #[clippy::version = "1.29.0"]
     pub FOO_FUNCTIONS,
     pedantic,
     "function named `foo`, which is not a descriptive name"
@@ -199,6 +200,10 @@ declare_clippy_lint! {
   section. This is the default documentation style and will be displayed
   [like this][example_lint_page]. To render and open this documentation locally
   in a browser, run `cargo dev serve`.
+* The `#[clippy::version]` attribute will be rendered as part of the lint documentation.
+  The value should be set to the current Rust version that the lint is developed in,
+  it can be retrieved by running `rustc -vV` in the rust-clippy directory. The version
+  is listed under *release*. (Use the version without the `-nightly`) suffix.
 * `FOO_FUNCTIONS` is the name of our lint. Be sure to follow the
   [lint naming guidelines][lint_naming] here when naming your lint.
   In short, the name should state the thing that is being checked for and
@@ -503,6 +508,7 @@ declare_clippy_lint! {
     /// // Good
     /// Insert a short example of improved code that doesn't trigger the lint
     /// ```
+    #[clippy::version = "1.29.0"]
     pub FOO_FUNCTIONS,
     pedantic,
     "function named `foo`, which is not a descriptive name"
@@ -634,7 +640,7 @@ in the following steps:
 Here are some pointers to things you are likely going to need for every lint:
 
 * [Clippy utils][utils] - Various helper functions. Maybe the function you need
-  is already in here (`implements_trait`, `match_def_path`, `snippet`, etc)
+  is already in here ([`is_type_diagnostic_item`], [`implements_trait`], [`snippet`], etc)
 * [Clippy diagnostics][diagnostics]
 * [The `if_chain` macro][if_chain]
 * [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro]
@@ -660,7 +666,10 @@ documentation currently. This is unfortunate, but in most cases you can probably
 get away with copying things from existing similar lints. If you are stuck,
 don't hesitate to ask on [Zulip] or in the issue/PR.
 
-[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/lib.rs
+[utils]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/index.html
+[`is_type_diagnostic_item`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.is_type_diagnostic_item.html
+[`implements_trait`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.implements_trait.html
+[`snippet`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/source/fn.snippet.html
 [if_chain]: https://docs.rs/if_chain/*/if_chain/
 [from_expansion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion
 [in_external_macro]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html
diff --git a/src/tools/clippy/doc/changelog_update.md b/src/tools/clippy/doc/changelog_update.md
index 115848c4804..0cbad2c0924 100644
--- a/src/tools/clippy/doc/changelog_update.md
+++ b/src/tools/clippy/doc/changelog_update.md
@@ -32,7 +32,7 @@ bullet points might be helpful:
   need to check out the Rust release tag of the stable release.
   [Link][rust_stable_tools]
 
-Usually you want to wirte the changelog of the **upcoming stable release**. Make
+Usually you want to write the changelog of the **upcoming stable release**. Make
 sure though, that `beta` was already branched in the Rust repository.
 
 To find the commit hash, issue the following command when in a `rust-lang/rust` checkout:
diff --git a/src/tools/clippy/doc/common_tools_writing_lints.md b/src/tools/clippy/doc/common_tools_writing_lints.md
index 1a6b7c8cb47..c7e51d53f51 100644
--- a/src/tools/clippy/doc/common_tools_writing_lints.md
+++ b/src/tools/clippy/doc/common_tools_writing_lints.md
@@ -4,10 +4,11 @@ You may need following tooltips to catch up with common operations.
 
 - [Common tools for writing lints](#common-tools-for-writing-lints)
   - [Retrieving the type of an expression](#retrieving-the-type-of-an-expression)
-  - [Checking if an expression is calling a specific method](#checking-if-an-expr-is-calling-a-specific-method)
+  - [Checking if an expr is calling a specific method](#checking-if-an-expr-is-calling-a-specific-method)
+  - [Checking for a specific type](#checking-for-a-specific-type)
   - [Checking if a type implements a specific trait](#checking-if-a-type-implements-a-specific-trait)
   - [Checking if a type defines a specific method](#checking-if-a-type-defines-a-specific-method)
-  - [Dealing with macros](#dealing-with-macros)
+  - [Dealing with macros](#dealing-with-macros-and-expansions)
 
 Useful Rustc dev guide links:
 - [Stages of compilation](https://rustc-dev-guide.rust-lang.org/compiler-src.html#the-main-stages-of-compilation)
@@ -15,7 +16,7 @@ Useful Rustc dev guide links:
 - [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html)
 - [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html)
 
-# Retrieving the type of an expression
+## Retrieving the type of an expression
 
 Sometimes you may want to retrieve the type `Ty` of an expression `Expr`, for example to answer following questions:
 
@@ -54,7 +55,7 @@ Two noticeable items here:
   created by type checking step, it includes useful information such as types
   of expressions, ways to resolve methods and so on.
 
-# Checking if an expr is calling a specific method
+## Checking if an expr is calling a specific method
 
 Starting with an `expr`, you can check whether it is calling a specific method `some_method`:
 
@@ -63,9 +64,11 @@ impl LateLintPass<'_> for MyStructLint {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
         if_chain! {
             // Check our expr is calling a method
-            if let hir::ExprKind::MethodCall(path, _, _args, _) = &expr.kind;
+            if let hir::ExprKind::MethodCall(path, _, [_self_arg, ..], _) = &expr.kind;
             // Check the name of this method is `some_method`
             if path.ident.name == sym!(some_method);
+            // Optionally, check the type of the self argument.
+            // - See "Checking for a specific type"
             then {
                 // ...
             }
@@ -74,7 +77,45 @@ impl LateLintPass<'_> for MyStructLint {
 }
 ```
 
-# Checking if a type implements a specific trait
+## Checking for a specific type
+
+There are three ways to check if an expression type is a specific type we want to check for.
+All of these methods only check for the base type, generic arguments have to be checked separately.
+
+```rust
+use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
+use clippy_utils::{paths, match_def_path};
+use rustc_span::symbol::sym;
+use rustc_hir::LangItem;
+
+impl LateLintPass<'_> for MyStructLint {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        // Getting the expression type
+        let ty = cx.typeck_results().expr_ty(expr);
+
+        // 1. Using diagnostic items
+        // The last argument is the diagnostic item to check for
+        if is_type_diagnostic_item(cx, ty, sym::Option) {
+            // The type is an `Option`
+        }
+
+        // 2. Using lang items
+        if is_type_lang_item(cx, ty, LangItem::RangeFull) {
+            // The type is a full range like `.drain(..)`
+        }
+
+        // 3. Using the type path
+        // This method should be avoided if possible
+        if match_def_path(cx, def_id, &paths::RESULT) {
+            // The type is a `core::result::Result`
+        }
+    }
+}
+```
+
+Prefer using diagnostic items and lang items where possible.
+
+## Checking if a type implements a specific trait
 
 There are three ways to do this, depending on if the target trait has a diagnostic item, lang item or neither.
 
@@ -102,6 +143,7 @@ impl LateLintPass<'_> for MyStructLint {
 
         // 3. Using the type path with the expression
         // we use `match_trait_method` function from Clippy's utils
+        // (This method should be avoided if possible)
         if match_trait_method(cx, expr, &paths::INTO) {
             // `expr` implements `Into` trait
         }
@@ -114,7 +156,7 @@ impl LateLintPass<'_> for MyStructLint {
 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
+## Checking if a type defines a specific method
 
 To check if our type defines a method called `some_method`:
 
@@ -140,64 +182,78 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl {
 }
 ```
 
-# Dealing with macros
+## Dealing with macros and expansions
 
-There are several helpers in [`clippy_utils`][utils] to deal with macros:
+Keep in mind that macros are already expanded and desugaring is already applied
+to the code representation that you are working with in Clippy. This unfortunately causes a lot of
+false positives because macro expansions are "invisible" unless you actively check for them.
+Generally speaking, code with macro expansions should just be ignored by Clippy because that code can be
+dynamic in ways that are difficult or impossible to see.
+Use the following functions to deal with macros:
 
-- `in_macro()`: detect if the given span is expanded by a macro
+- `span.from_expansion()`: detects if a span is from macro expansion or desugaring.
+  Checking this is a common first step in a lint.
 
-You may want to use this for example to not start linting in any macro.
+   ```rust
+   if expr.span.from_expansion() {
+       // just forget it
+       return;
+   }
+   ```
 
-```rust
-macro_rules! foo {
-    ($param:expr) => {
-        match $param {
-            "bar" => println!("whatever"),
-            _ => ()
-        }
-    };
-}
+- `span.ctxt()`: the span's context represents whether it is from expansion, and if so, which macro call expanded it.
+   It is sometimes useful to check if the context of two spans are equal.
 
-foo!("bar");
+   ```rust
+   // expands to `1 + 0`, but don't lint
+   1 + mac!()
+   ```
+   ```rust
+   if left.span.ctxt() != right.span.ctxt() {
+       // the coder most likely cannot modify this expression
+       return;
+   }
+   ```
+  Note: Code that is not from expansion is in the "root" context. So any spans where `from_expansion` returns `true` can
+  be assumed to have the same context. And so just using `span.from_expansion()` is often good enough.
 
-// if we lint the `match` of `foo` call and test its span
-assert_eq!(in_macro(match_span), true);
-```
 
-- `in_external_macro()`: detect if the given span is from an external macro, defined in a foreign crate
+- `in_external_macro(span)`: detect if the given span is from a macro defined in a foreign crate.
+   If you want the lint to work with macro-generated code, this is the next line of defense to avoid macros
+   not defined in the current crate. It doesn't make sense to lint code that the coder can't change.
 
-You may want to use it for example to not start linting in macros from other crates
+   You may want to use it for example to not start linting in macros from other crates
 
-```rust
-#[macro_use]
-extern crate a_crate_with_macros;
+   ```rust
+   #[macro_use]
+   extern crate a_crate_with_macros;
 
-// `foo` is defined in `a_crate_with_macros`
-foo!("bar");
+   // `foo` is defined in `a_crate_with_macros`
+   foo!("bar");
 
-// if we lint the `match` of `foo` call and test its span
-assert_eq!(in_external_macro(cx.sess(), match_span), true);
-```
+   // if we lint the `match` of `foo` call and test its span
+   assert_eq!(in_external_macro(cx.sess(), match_span), true);
+   ```
 
 - `differing_macro_contexts()`: returns true if the two given spans are not from the same context
 
-```rust
-macro_rules! m {
-    ($a:expr, $b:expr) => {
-        if $a.is_some() {
-            $b;
-        }
-    }
-}
+   ```rust
+   macro_rules! m {
+       ($a:expr, $b:expr) => {
+           if $a.is_some() {
+               $b;
+           }
+       }
+   }
 
-let x: Option<u32> = Some(42);
-m!(x, x.unwrap());
+   let x: Option<u32> = Some(42);
+   m!(x, x.unwrap());
 
-// These spans are not from the same context
-// x.is_some() is from inside the macro
-// x.unwrap() is from outside the macro
-assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true);
-```
+   // These spans are not from the same context
+   // x.is_some() is from inside the macro
+   // x.unwrap() is from outside the macro
+   assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true);
+   ```
 
 [TyS]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyS.html
 [TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html
@@ -207,4 +263,3 @@ assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true);
 [TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html
 [pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.pat_ty
 [paths]: ../clippy_utils/src/paths.rs
-[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/lib.rs
diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs
index 97d3794fb84..41de2576e28 100644
--- a/src/tools/clippy/lintcheck/src/main.rs
+++ b/src/tools/clippy/lintcheck/src/main.rs
@@ -563,7 +563,7 @@ fn parse_json_message(json_message: &str, krate: &Crate) -> ClippyWarning {
     }
 }
 
-/// Generate a short list of occuring lints-types and their count
+/// Generate a short list of occurring lints-types and their count
 fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String, usize>) {
     // count lint type occurrences
     let mut counter: HashMap<&String, usize> = HashMap::new();
@@ -718,7 +718,7 @@ pub fn main() {
             // quarter of the time which might result in a longer wall clock runtime
 
             // This helps when we check many small crates with dep-trees that don't have a lot of branches in
-            // order to achive some kind of parallelism
+            // order to achieve some kind of parallelism
 
             // by default, use a single thread
             let num_cpus = config.max_jobs;
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index 09554c08987..27969b0d655 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2021-11-04"
-components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
+channel = "nightly-2021-12-02"
+components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs
index 0c82f37d6a2..a8aa3a76abc 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -108,7 +108,7 @@ impl rustc_driver::Callbacks for ClippyCallbacks {
 
             let conf = clippy_lints::read_conf(sess);
             clippy_lints::register_plugins(lint_store, sess, &conf);
-            clippy_lints::register_pre_expansion_lints(lint_store);
+            clippy_lints::register_pre_expansion_lints(lint_store, sess, &conf);
             clippy_lints::register_renamed(lint_store);
         }));
 
diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs
index f25cf1d3ef5..a2d58491872 100644
--- a/src/tools/clippy/tests/compile-test.rs
+++ b/src/tools/clippy/tests/compile-test.rs
@@ -28,6 +28,7 @@ static TEST_DEPENDENCIES: &[&str] = &[
     "serde",
     "serde_derive",
     "syn",
+    "parking_lot",
 ];
 
 // Test dependencies may need an `extern crate` here to ensure that they show up
@@ -41,6 +42,8 @@ extern crate if_chain;
 #[allow(unused_extern_crates)]
 extern crate itertools;
 #[allow(unused_extern_crates)]
+extern crate parking_lot;
+#[allow(unused_extern_crates)]
 extern crate quote;
 #[allow(unused_extern_crates)]
 extern crate syn;
diff --git a/src/tools/clippy/tests/fmt.rs b/src/tools/clippy/tests/fmt.rs
index be42f1fbb20..383702dd439 100644
--- a/src/tools/clippy/tests/fmt.rs
+++ b/src/tools/clippy/tests/fmt.rs
@@ -11,10 +11,7 @@ fn fmt() {
     }
 
     // Skip this test if nightly rustfmt is unavailable
-    let rustup_output = Command::new("rustup")
-        .args(&["component", "list", "--toolchain", "nightly"])
-        .output()
-        .unwrap();
+    let rustup_output = Command::new("rustup").args(&["component", "list"]).output().unwrap();
     assert!(rustup_output.status.success());
     let component_output = String::from_utf8_lossy(&rustup_output.stdout);
     if !component_output.contains("rustfmt") {
diff --git a/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.rs b/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.rs
new file mode 100644
index 00000000000..31acac89cc6
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.rs
@@ -0,0 +1,87 @@
+#![deny(clippy::internal)]
+#![feature(rustc_private)]
+
+#[macro_use]
+extern crate rustc_middle;
+#[macro_use]
+extern crate rustc_session;
+extern crate rustc_lint;
+
+///////////////////////
+// Valid descriptions
+///////////////////////
+declare_tool_lint! {
+    #[clippy::version = "pre 1.29.0"]
+    pub clippy::VALID_ONE,
+    Warn,
+    "One",
+    report_in_external_macro: true
+}
+
+declare_tool_lint! {
+    #[clippy::version = "1.29.0"]
+    pub clippy::VALID_TWO,
+    Warn,
+    "Two",
+    report_in_external_macro: true
+}
+
+declare_tool_lint! {
+    #[clippy::version = "1.59.0"]
+    pub clippy::VALID_THREE,
+    Warn,
+    "Three",
+    report_in_external_macro: true
+}
+
+///////////////////////
+// Invalid attributes
+///////////////////////
+declare_tool_lint! {
+    #[clippy::version = "1.2.3.4.5.6"]
+    pub clippy::INVALID_ONE,
+    Warn,
+    "One",
+    report_in_external_macro: true
+}
+
+declare_tool_lint! {
+    #[clippy::version = "I'm a string"]
+    pub clippy::INVALID_TWO,
+    Warn,
+    "Two",
+    report_in_external_macro: true
+}
+
+///////////////////////
+// Missing attribute test
+///////////////////////
+declare_tool_lint! {
+    #[clippy::version]
+    pub clippy::MISSING_ATTRIBUTE_ONE,
+    Warn,
+    "Two",
+    report_in_external_macro: true
+}
+
+declare_tool_lint! {
+    pub clippy::MISSING_ATTRIBUTE_TWO,
+    Warn,
+    "Two",
+    report_in_external_macro: true
+}
+
+#[allow(clippy::missing_clippy_version_attribute)]
+mod internal_clippy_lints {
+    declare_tool_lint! {
+        pub clippy::ALLOW_MISSING_ATTRIBUTE_ONE,
+        Warn,
+        "Two",
+        report_in_external_macro: true
+    }
+}
+
+use crate::internal_clippy_lints::ALLOW_MISSING_ATTRIBUTE_ONE;
+declare_lint_pass!(Pass2 => [VALID_ONE, VALID_TWO, VALID_THREE, INVALID_ONE, INVALID_TWO, MISSING_ATTRIBUTE_ONE, MISSING_ATTRIBUTE_TWO, ALLOW_MISSING_ATTRIBUTE_ONE]);
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.stderr b/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.stderr
new file mode 100644
index 00000000000..9302e02ccb9
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/check_clippy_version_attribute.stderr
@@ -0,0 +1,73 @@
+error: this item has an invalid `clippy::version` attribute
+  --> $DIR/check_clippy_version_attribute.rs:40:1
+   |
+LL | / declare_tool_lint! {
+LL | |     #[clippy::version = "1.2.3.4.5.6"]
+LL | |     pub clippy::INVALID_ONE,
+LL | |     Warn,
+LL | |     "One",
+LL | |     report_in_external_macro: true
+LL | | }
+   | |_^
+   |
+note: the lint level is defined here
+  --> $DIR/check_clippy_version_attribute.rs:1:9
+   |
+LL | #![deny(clippy::internal)]
+   |         ^^^^^^^^^^^^^^^^
+   = note: `#[deny(clippy::invalid_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]`
+   = help: please use a valid sematic version, see `doc/adding_lints.md`
+   = note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: this item has an invalid `clippy::version` attribute
+  --> $DIR/check_clippy_version_attribute.rs:48:1
+   |
+LL | / declare_tool_lint! {
+LL | |     #[clippy::version = "I'm a string"]
+LL | |     pub clippy::INVALID_TWO,
+LL | |     Warn,
+LL | |     "Two",
+LL | |     report_in_external_macro: true
+LL | | }
+   | |_^
+   |
+   = help: please use a valid sematic version, see `doc/adding_lints.md`
+   = note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: this lint is missing the `clippy::version` attribute or version value
+  --> $DIR/check_clippy_version_attribute.rs:59:1
+   |
+LL | / declare_tool_lint! {
+LL | |     #[clippy::version]
+LL | |     pub clippy::MISSING_ATTRIBUTE_ONE,
+LL | |     Warn,
+LL | |     "Two",
+LL | |     report_in_external_macro: true
+LL | | }
+   | |_^
+   |
+note: the lint level is defined here
+  --> $DIR/check_clippy_version_attribute.rs:1:9
+   |
+LL | #![deny(clippy::internal)]
+   |         ^^^^^^^^^^^^^^^^
+   = note: `#[deny(clippy::missing_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]`
+   = help: please use a `clippy::version` attribute, see `doc/adding_lints.md`
+   = note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: this lint is missing the `clippy::version` attribute or version value
+  --> $DIR/check_clippy_version_attribute.rs:67:1
+   |
+LL | / declare_tool_lint! {
+LL | |     pub clippy::MISSING_ATTRIBUTE_TWO,
+LL | |     Warn,
+LL | |     "Two",
+LL | |     report_in_external_macro: true
+LL | | }
+   | |_^
+   |
+   = help: please use a `clippy::version` attribute, see `doc/adding_lints.md`
+   = note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed
index 7764cc8da78..a5a6f20ddd5 100644
--- a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed
+++ b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed
@@ -1,5 +1,6 @@
 // run-rustfix
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 extern crate clippy_utils;
diff --git a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs
index bdd296db832..6d783aa0786 100644
--- a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs
+++ b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs
@@ -1,5 +1,6 @@
 // run-rustfix
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 extern crate clippy_utils;
diff --git a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr
index 0632b038577..558d1299160 100644
--- a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr
+++ b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr
@@ -1,5 +1,5 @@
 error: this call is collapsible
-  --> $DIR/collapsible_span_lint_calls.rs:35:9
+  --> $DIR/collapsible_span_lint_calls.rs:36:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable);
@@ -14,7 +14,7 @@ LL | #![deny(clippy::internal)]
    = note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]`
 
 error: this call is collapsible
-  --> $DIR/collapsible_span_lint_calls.rs:38:9
+  --> $DIR/collapsible_span_lint_calls.rs:39:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.span_help(expr.span, help_msg);
@@ -22,7 +22,7 @@ LL | |         });
    | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)`
 
 error: this call is collapsible
-  --> $DIR/collapsible_span_lint_calls.rs:41:9
+  --> $DIR/collapsible_span_lint_calls.rs:42:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.help(help_msg);
@@ -30,7 +30,7 @@ LL | |         });
    | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)`
 
 error: this call is collspible
-  --> $DIR/collapsible_span_lint_calls.rs:44:9
+  --> $DIR/collapsible_span_lint_calls.rs:45:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.span_note(expr.span, note_msg);
@@ -38,7 +38,7 @@ LL | |         });
    | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)`
 
 error: this call is collspible
-  --> $DIR/collapsible_span_lint_calls.rs:47:9
+  --> $DIR/collapsible_span_lint_calls.rs:48:9
    |
 LL | /         span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
 LL | |             db.note(note_msg);
diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.rs b/src/tools/clippy/tests/ui-internal/custom_ice_message.rs
index 5b30c9d5721..5057a018300 100644
--- a/src/tools/clippy/tests/ui-internal/custom_ice_message.rs
+++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.rs
@@ -4,6 +4,7 @@
 // normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints"
 
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 
 fn it_looks_like_you_are_trying_to_kill_clippy() {}
 
diff --git a/src/tools/clippy/tests/ui-internal/default_lint.rs b/src/tools/clippy/tests/ui-internal/default_lint.rs
index 053faae02ce..da29aedb2a3 100644
--- a/src/tools/clippy/tests/ui-internal/default_lint.rs
+++ b/src/tools/clippy/tests/ui-internal/default_lint.rs
@@ -1,4 +1,5 @@
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 #[macro_use]
diff --git a/src/tools/clippy/tests/ui-internal/default_lint.stderr b/src/tools/clippy/tests/ui-internal/default_lint.stderr
index 4735573a47d..af6735f4e4d 100644
--- a/src/tools/clippy/tests/ui-internal/default_lint.stderr
+++ b/src/tools/clippy/tests/ui-internal/default_lint.stderr
@@ -1,5 +1,5 @@
 error: the lint `TEST_LINT_DEFAULT` has the default lint description
-  --> $DIR/default_lint.rs:17:1
+  --> $DIR/default_lint.rs:18:1
    |
 LL | / declare_tool_lint! {
 LL | |     pub clippy::TEST_LINT_DEFAULT,
diff --git a/src/tools/clippy/tests/ui-internal/if_chain_style.rs b/src/tools/clippy/tests/ui-internal/if_chain_style.rs
index 8e871707aa8..b0d89e038aa 100644
--- a/src/tools/clippy/tests/ui-internal/if_chain_style.rs
+++ b/src/tools/clippy/tests/ui-internal/if_chain_style.rs
@@ -1,5 +1,5 @@
 #![warn(clippy::if_chain_style)]
-#![allow(clippy::no_effect)]
+#![allow(clippy::no_effect, clippy::nonminimal_bool, clippy::missing_clippy_version_attribute)]
 
 extern crate if_chain;
 
diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed
index 9ab845a573a..6b7fd6efe39 100644
--- a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed
+++ b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed
@@ -1,5 +1,6 @@
 // run-rustfix
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 extern crate rustc_span;
diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs
index a58e182971d..98d7d7adad1 100644
--- a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs
+++ b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs
@@ -1,5 +1,6 @@
 // run-rustfix
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 extern crate rustc_span;
diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr
index 50c1c268eb1..4e99636e683 100644
--- a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr
+++ b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr
@@ -1,5 +1,5 @@
 error: interning a defined symbol
-  --> $DIR/interning_defined_symbol.rs:17:13
+  --> $DIR/interning_defined_symbol.rs:18:13
    |
 LL |     let _ = Symbol::intern("f32");
    |             ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::f32`
@@ -12,19 +12,19 @@ LL | #![deny(clippy::internal)]
    = note: `#[deny(clippy::interning_defined_symbol)]` implied by `#[deny(clippy::internal)]`
 
 error: interning a defined symbol
-  --> $DIR/interning_defined_symbol.rs:20:13
+  --> $DIR/interning_defined_symbol.rs:21:13
    |
 LL |     let _ = sym!(f32);
    |             ^^^^^^^^^ help: try: `rustc_span::sym::f32`
 
 error: interning a defined symbol
-  --> $DIR/interning_defined_symbol.rs:23:13
+  --> $DIR/interning_defined_symbol.rs:24:13
    |
 LL |     let _ = Symbol::intern("proc-macro");
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::proc_dash_macro`
 
 error: interning a defined symbol
-  --> $DIR/interning_defined_symbol.rs:26:13
+  --> $DIR/interning_defined_symbol.rs:27:13
    |
 LL |     let _ = Symbol::intern("self");
    |             ^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::kw::SelfLower`
diff --git a/src/tools/clippy/tests/ui-internal/invalid_paths.rs b/src/tools/clippy/tests/ui-internal/invalid_paths.rs
index a3b19c2e394..b823ff7fe37 100644
--- a/src/tools/clippy/tests/ui-internal/invalid_paths.rs
+++ b/src/tools/clippy/tests/ui-internal/invalid_paths.rs
@@ -1,4 +1,5 @@
 #![warn(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 
 mod paths {
     // Good path
diff --git a/src/tools/clippy/tests/ui-internal/invalid_paths.stderr b/src/tools/clippy/tests/ui-internal/invalid_paths.stderr
index 20aa81b98a0..0a8e5427978 100644
--- a/src/tools/clippy/tests/ui-internal/invalid_paths.stderr
+++ b/src/tools/clippy/tests/ui-internal/invalid_paths.stderr
@@ -1,5 +1,5 @@
 error: invalid path
-  --> $DIR/invalid_paths.rs:17:5
+  --> $DIR/invalid_paths.rs:18:5
    |
 LL |     pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -7,7 +7,7 @@ LL |     pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
    = note: `-D clippy::invalid-paths` implied by `-D warnings`
 
 error: invalid path
-  --> $DIR/invalid_paths.rs:20:5
+  --> $DIR/invalid_paths.rs:21:5
    |
 LL |     pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs b/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs
index beaef79a340..1fd03cfe36d 100644
--- a/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs
+++ b/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs
@@ -1,4 +1,5 @@
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 #[macro_use]
diff --git a/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.stderr b/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.stderr
index e308e13da13..de04920b8e6 100644
--- a/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.stderr
+++ b/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.stderr
@@ -1,5 +1,5 @@
 error: the lint `TEST_LINT` is not added to any `LintPass`
-  --> $DIR/lint_without_lint_pass.rs:11:1
+  --> $DIR/lint_without_lint_pass.rs:12:1
    |
 LL | / declare_tool_lint! {
 LL | |     pub clippy::TEST_LINT,
diff --git a/src/tools/clippy/tests/ui-internal/match_type_on_diag_item.rs b/src/tools/clippy/tests/ui-internal/match_type_on_diag_item.rs
index be7b7a9af19..4b41ff15e80 100644
--- a/src/tools/clippy/tests/ui-internal/match_type_on_diag_item.rs
+++ b/src/tools/clippy/tests/ui-internal/match_type_on_diag_item.rs
@@ -1,4 +1,5 @@
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 extern crate clippy_utils;
diff --git a/src/tools/clippy/tests/ui-internal/match_type_on_diag_item.stderr b/src/tools/clippy/tests/ui-internal/match_type_on_diag_item.stderr
index bf1d67e6054..e3cb6b6c22e 100644
--- a/src/tools/clippy/tests/ui-internal/match_type_on_diag_item.stderr
+++ b/src/tools/clippy/tests/ui-internal/match_type_on_diag_item.stderr
@@ -1,5 +1,5 @@
 error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
-  --> $DIR/match_type_on_diag_item.rs:30:17
+  --> $DIR/match_type_on_diag_item.rs:31:17
    |
 LL |         let _ = match_type(cx, ty, &OPTION);
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Option)`
@@ -12,13 +12,13 @@ LL | #![deny(clippy::internal)]
    = note: `#[deny(clippy::match_type_on_diagnostic_item)]` implied by `#[deny(clippy::internal)]`
 
 error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
-  --> $DIR/match_type_on_diag_item.rs:31:17
+  --> $DIR/match_type_on_diag_item.rs:32:17
    |
 LL |         let _ = match_type(cx, ty, &["core", "result", "Result"]);
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Result)`
 
 error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
-  --> $DIR/match_type_on_diag_item.rs:34:17
+  --> $DIR/match_type_on_diag_item.rs:35:17
    |
 LL |         let _ = clippy_utils::ty::match_type(cx, ty, rc_path);
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Rc)`
diff --git a/src/tools/clippy/tests/ui-internal/outer_expn_data.fixed b/src/tools/clippy/tests/ui-internal/outer_expn_data.fixed
index b0b3498f057..bb82faf0c90 100644
--- a/src/tools/clippy/tests/ui-internal/outer_expn_data.fixed
+++ b/src/tools/clippy/tests/ui-internal/outer_expn_data.fixed
@@ -1,6 +1,7 @@
 // run-rustfix
 
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 extern crate rustc_hir;
diff --git a/src/tools/clippy/tests/ui-internal/outer_expn_data.rs b/src/tools/clippy/tests/ui-internal/outer_expn_data.rs
index 55a3fed00d0..187d468b392 100644
--- a/src/tools/clippy/tests/ui-internal/outer_expn_data.rs
+++ b/src/tools/clippy/tests/ui-internal/outer_expn_data.rs
@@ -1,6 +1,7 @@
 // run-rustfix
 
 #![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
 #![feature(rustc_private)]
 
 extern crate rustc_hir;
diff --git a/src/tools/clippy/tests/ui-internal/outer_expn_data.stderr b/src/tools/clippy/tests/ui-internal/outer_expn_data.stderr
index 56b6ce1f78e..afef696785e 100644
--- a/src/tools/clippy/tests/ui-internal/outer_expn_data.stderr
+++ b/src/tools/clippy/tests/ui-internal/outer_expn_data.stderr
@@ -1,5 +1,5 @@
 error: usage of `outer_expn().expn_data()`
-  --> $DIR/outer_expn_data.rs:24:34
+  --> $DIR/outer_expn_data.rs:25:34
    |
 LL |         let _ = expr.span.ctxt().outer_expn().expn_data();
    |                                  ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `outer_expn_data()`
diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed
index 95b8c6dfe89..4f5336663a8 100644
--- a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed
+++ b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed
@@ -1,7 +1,11 @@
 // run-rustfix
 #![feature(rustc_private)]
 #![deny(clippy::internal)]
-#![allow(clippy::unnecessary_operation, unused_must_use)]
+#![allow(
+    clippy::unnecessary_operation,
+    unused_must_use,
+    clippy::missing_clippy_version_attribute
+)]
 
 extern crate rustc_span;
 
diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs
index ad6937cf60a..894aa1d3bc6 100644
--- a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs
+++ b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs
@@ -1,7 +1,11 @@
 // run-rustfix
 #![feature(rustc_private)]
 #![deny(clippy::internal)]
-#![allow(clippy::unnecessary_operation, unused_must_use)]
+#![allow(
+    clippy::unnecessary_operation,
+    unused_must_use,
+    clippy::missing_clippy_version_attribute
+)]
 
 extern crate rustc_span;
 
diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr
index 12e05eaa7a0..75367bf4bc5 100644
--- a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr
+++ b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr
@@ -1,5 +1,5 @@
 error: unnecessary `Symbol` to string conversion
-  --> $DIR/unnecessary_symbol_str.rs:11:5
+  --> $DIR/unnecessary_symbol_str.rs:15:5
    |
 LL |     Symbol::intern("foo").as_str() == "clippy";
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::sym::clippy`
@@ -12,25 +12,25 @@ LL | #![deny(clippy::internal)]
    = note: `#[deny(clippy::unnecessary_symbol_str)]` implied by `#[deny(clippy::internal)]`
 
 error: unnecessary `Symbol` to string conversion
-  --> $DIR/unnecessary_symbol_str.rs:12:5
+  --> $DIR/unnecessary_symbol_str.rs:16:5
    |
 LL |     Symbol::intern("foo").to_string() == "self";
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower`
 
 error: unnecessary `Symbol` to string conversion
-  --> $DIR/unnecessary_symbol_str.rs:13:5
+  --> $DIR/unnecessary_symbol_str.rs:17:5
    |
 LL |     Symbol::intern("foo").to_ident_string() != "Self";
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper`
 
 error: unnecessary `Symbol` to string conversion
-  --> $DIR/unnecessary_symbol_str.rs:14:5
+  --> $DIR/unnecessary_symbol_str.rs:18:5
    |
 LL |     &*Ident::empty().as_str() == "clippy";
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::empty().name == rustc_span::sym::clippy`
 
 error: unnecessary `Symbol` to string conversion
-  --> $DIR/unnecessary_symbol_str.rs:15:5
+  --> $DIR/unnecessary_symbol_str.rs:19:5
    |
 LL |     "clippy" == Ident::empty().to_string();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name`
diff --git a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/clippy.toml b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/clippy.toml
new file mode 100644
index 00000000000..78c7e63b410
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/clippy.toml
@@ -0,0 +1 @@
+max-suggested-slice-pattern-length = 8
diff --git a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs
new file mode 100644
index 00000000000..21849a14fa9
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs
@@ -0,0 +1,23 @@
+#![deny(clippy::index_refutable_slice)]
+
+fn below_limit() {
+    let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+    if let Some(slice) = slice {
+        // This would usually not be linted but is included now due to the
+        // index limit in the config file
+        println!("{}", slice[7]);
+    }
+}
+
+fn above_limit() {
+    let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+    if let Some(slice) = slice {
+        // This will not be linted as 8 is above the limit
+        println!("{}", slice[8]);
+    }
+}
+
+fn main() {
+    below_limit();
+    above_limit();
+}
diff --git a/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr
new file mode 100644
index 00000000000..d319e65d06c
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.stderr
@@ -0,0 +1,22 @@
+error: this binding can be a slice pattern to avoid indexing
+  --> $DIR/index_refutable_slice.rs:5:17
+   |
+LL |     if let Some(slice) = slice {
+   |                 ^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/index_refutable_slice.rs:1:9
+   |
+LL | #![deny(clippy::index_refutable_slice)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try using a slice pattern here
+   |
+LL |     if let Some([_, _, _, _, _, _, _, slice_7, ..]) = slice {
+   |                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: and replace the index expressions here
+   |
+LL |         println!("{}", slice_7);
+   |                        ~~~~~~~
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.rs b/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.rs
index bc41efa42a1..8e104926524 100644
--- a/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.rs
+++ b/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.rs
@@ -59,10 +59,20 @@ fn manual_strip_msrv() {
     }
 }
 
+fn check_index_refutable_slice() {
+    // This shouldn't trigger `clippy::index_refutable_slice` as the suggestion
+    // would only be valid from 1.42.0 onward
+    let slice: Option<&[u32]> = Some(&[1]);
+    if let Some(slice) = slice {
+        println!("{}", slice[0]);
+    }
+}
+
 fn main() {
     option_as_ref_deref();
     match_like_matches();
     match_same_arms();
     match_same_arms2();
     manual_strip_msrv();
+    check_index_refutable_slice();
 }
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_method/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml
index f1d4a4619c5..f1d4a4619c5 100644
--- a/src/tools/clippy/tests/ui-toml/toml_disallowed_method/clippy.toml
+++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.rs b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs
index 1901a99377e..cb449b45bde 100644
--- a/src/tools/clippy/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.rs
+++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs
@@ -1,4 +1,4 @@
-#![warn(clippy::disallowed_method)]
+#![warn(clippy::disallowed_methods)]
 
 extern crate regex;
 use regex::Regex;
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr
index 38123220a43..999ead10d51 100644
--- a/src/tools/clippy/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr
+++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr
@@ -1,13 +1,13 @@
 error: use of a disallowed method `regex::Regex::new`
-  --> $DIR/conf_disallowed_method.rs:7:14
+  --> $DIR/conf_disallowed_methods.rs:7:14
    |
 LL |     let re = Regex::new(r"ab.*c").unwrap();
    |              ^^^^^^^^^^^^^^^^^^^^
    |
-   = note: `-D clippy::disallowed-method` implied by `-D warnings`
+   = note: `-D clippy::disallowed-methods` implied by `-D warnings`
 
 error: use of a disallowed method `regex::Regex::is_match`
-  --> $DIR/conf_disallowed_method.rs:8:5
+  --> $DIR/conf_disallowed_methods.rs:8:5
    |
 LL |     re.is_match("abc");
    |     ^^^^^^^^^^^^^^^^^^
@@ -15,7 +15,7 @@ LL |     re.is_match("abc");
    = note: no matching allowed (from clippy.toml)
 
 error: use of a disallowed method `std::iter::Iterator::sum`
-  --> $DIR/conf_disallowed_method.rs:11:5
+  --> $DIR/conf_disallowed_methods.rs:11:5
    |
 LL |     a.iter().sum::<i32>();
    |     ^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_type/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/clippy.toml
index 6cb9e2ef954..6cb9e2ef954 100644
--- a/src/tools/clippy/tests/ui-toml/toml_disallowed_type/clippy.toml
+++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/clippy.toml
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs
index 410f0765055..7f28efd676f 100644
--- a/src/tools/clippy/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs
+++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs
@@ -1,4 +1,4 @@
-#![warn(clippy::disallowed_type)]
+#![warn(clippy::disallowed_types)]
 
 extern crate quote;
 extern crate syn;
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr
index 08a400a8367..e3ece799c7c 100644
--- a/src/tools/clippy/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr
+++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr
@@ -1,67 +1,67 @@
 error: `std::sync::atomic::AtomicU32` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:7:1
+  --> $DIR/conf_disallowed_types.rs:7:1
    |
 LL | use std::sync::atomic::AtomicU32;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: `-D clippy::disallowed-type` implied by `-D warnings`
+   = note: `-D clippy::disallowed-types` implied by `-D warnings`
 
 error: `std::time::Instant` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:8:1
+  --> $DIR/conf_disallowed_types.rs:8:1
    |
 LL | use std::time::Instant as Sneaky;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `std::time::Instant` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:12:33
+  --> $DIR/conf_disallowed_types.rs:12:33
    |
 LL | fn bad_return_type() -> fn() -> Sneaky {
    |                                 ^^^^^^
 
 error: `std::time::Instant` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:16:28
+  --> $DIR/conf_disallowed_types.rs:16:28
    |
 LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {}
    |                            ^^^^^^
 
 error: `std::sync::atomic::AtomicU32` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:16:39
+  --> $DIR/conf_disallowed_types.rs:16:39
    |
 LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {}
    |                                       ^^^^^^^^^^^^^^^^^^^^^^
 
 error: `std::io::Read` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:18:22
+  --> $DIR/conf_disallowed_types.rs:18:22
    |
 LL | fn trait_obj(_: &dyn std::io::Read) {}
    |                      ^^^^^^^^^^^^^
 
 error: `usize` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:20:33
+  --> $DIR/conf_disallowed_types.rs:20:33
    |
 LL | fn full_and_single_path_prim(_: usize, _: bool) {}
    |                                 ^^^^^
 
 error: `bool` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:20:43
+  --> $DIR/conf_disallowed_types.rs:20:43
    |
 LL | fn full_and_single_path_prim(_: usize, _: bool) {}
    |                                           ^^^^
 
 error: `usize` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:22:28
+  --> $DIR/conf_disallowed_types.rs:22:28
    |
 LL | fn const_generics<const C: usize>() {}
    |                            ^^^^^
 
 error: `usize` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:24:24
+  --> $DIR/conf_disallowed_types.rs:24:24
    |
 LL | struct GenArg<const U: usize>([u8; U]);
    |                        ^^^^^
 
 error: `std::net::Ipv4Addr` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:28:10
+  --> $DIR/conf_disallowed_types.rs:28:10
    |
 LL | fn ip(_: std::net::Ipv4Addr) {}
    |          ^^^^^^^^^^^^^^^^^^
@@ -69,61 +69,61 @@ LL | fn ip(_: std::net::Ipv4Addr) {}
    = note: no IPv4 allowed (from clippy.toml)
 
 error: `std::net::TcpListener` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:30:16
+  --> $DIR/conf_disallowed_types.rs:30:16
    |
 LL | fn listener(_: std::net::TcpListener) {}
    |                ^^^^^^^^^^^^^^^^^^^^^
 
 error: `std::collections::HashMap` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:34:48
+  --> $DIR/conf_disallowed_types.rs:34:48
    |
 LL |     let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
    |                                                ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `std::collections::HashMap` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:34:12
+  --> $DIR/conf_disallowed_types.rs:34:12
    |
 LL |     let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `std::time::Instant` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:35:13
+  --> $DIR/conf_disallowed_types.rs:35:13
    |
 LL |     let _ = Sneaky::now();
    |             ^^^^^^
 
 error: `std::sync::atomic::AtomicU32` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:36:13
+  --> $DIR/conf_disallowed_types.rs:36:13
    |
 LL |     let _ = foo::atomic::AtomicU32::new(0);
    |             ^^^^^^^^^^^^^^^^^^^^^^
 
 error: `std::sync::atomic::AtomicU32` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:37:17
+  --> $DIR/conf_disallowed_types.rs:37:17
    |
 LL |     static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `std::sync::atomic::AtomicU32` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:37:48
+  --> $DIR/conf_disallowed_types.rs:37:48
    |
 LL |     static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
    |                                                ^^^^^^^^^^^^^^^^^^^^^^
 
 error: `syn::TypePath` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:38:43
+  --> $DIR/conf_disallowed_types.rs:38:43
    |
 LL |     let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default();
    |                                           ^^^^^^^^^^^^^
 
 error: `syn::Ident` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:39:13
+  --> $DIR/conf_disallowed_types.rs:39:13
    |
 LL |     let _ = syn::Ident::new("", todo!());
    |             ^^^^^^^^^^
 
 error: `usize` is not allowed according to config
-  --> $DIR/conf_disallowed_type.rs:41:12
+  --> $DIR/conf_disallowed_types.rs:41:12
    |
 LL |     let _: usize = 64_usize;
    |            ^^^^^
diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
index 97bab1308aa..00ddbd608a7 100644
--- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
+++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
@@ -1,4 +1,4 @@
-error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `third-party` at line 5 column 1
+error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `third-party` at line 5 column 1
 
 error: aborting due to previous error
 
diff --git a/src/tools/clippy/tests/ui/author.stdout b/src/tools/clippy/tests/ui/author.stdout
index 211d11c7c1a..3125863036b 100644
--- a/src/tools/clippy/tests/ui/author.stdout
+++ b/src/tools/clippy/tests/ui/author.stdout
@@ -1,11 +1,11 @@
 if_chain! {
-    if let StmtKind::Local(ref local) = stmt.kind;
-    if let Some(ref init) = local.init;
-    if let ExprKind::Cast(ref expr, ref cast_ty) = init.kind;
-    if let TyKind::Path(ref qp) = cast_ty.kind;
-    if match_qpath(qp, &["char"]);
+    if let StmtKind::Local(local) = stmt.kind;
+    if let Some(init) = local.init;
+    if let ExprKind::Cast(expr, cast_ty) = init.kind;
+    if let TyKind::Path(ref qpath) = cast_ty.kind;
+    if match_qpath(qpath, &["char"]);
     if let ExprKind::Lit(ref lit) = expr.kind;
-    if let LitKind::Int(69, _) = lit.node;
+    if let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node;
     if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local.pat.kind;
     if name.as_str() == "x";
     then {
diff --git a/src/tools/clippy/tests/ui/author/blocks.rs b/src/tools/clippy/tests/ui/author/blocks.rs
index c8465cd59aa..a7335c01baa 100644
--- a/src/tools/clippy/tests/ui/author/blocks.rs
+++ b/src/tools/clippy/tests/ui/author/blocks.rs
@@ -1,10 +1,16 @@
+// edition:2018
+
 #![allow(redundant_semicolons, clippy::no_effect)]
+#![feature(stmt_expr_attributes)]
+#![feature(async_closure)]
 
 #[rustfmt::skip]
 fn main() {
     #[clippy::author]
     {
         let x = 42i32;
+        let _t = 1f32;
+
         -x;
     };
     #[clippy::author]
@@ -12,4 +18,7 @@ fn main() {
         let expr = String::new();
         drop(expr)
     };
+
+    #[clippy::author]
+    async move || {};
 }
diff --git a/src/tools/clippy/tests/ui/author/blocks.stdout b/src/tools/clippy/tests/ui/author/blocks.stdout
index 854bc28083a..2fc4a7d1f7f 100644
--- a/src/tools/clippy/tests/ui/author/blocks.stdout
+++ b/src/tools/clippy/tests/ui/author/blocks.stdout
@@ -1,39 +1,63 @@
 if_chain! {
-    if let ExprKind::Block(ref block) = expr.kind;
-    if block.stmts.len() == 2;
-    if let StmtKind::Local(ref local) = block.stmts[0].kind;
-    if let Some(ref init) = local.init;
+    if let ExprKind::Block(block, None) = expr.kind;
+    if block.stmts.len() == 3;
+    if let StmtKind::Local(local) = block.stmts[0].kind;
+    if let Some(init) = local.init;
     if let ExprKind::Lit(ref lit) = init.kind;
-    if let LitKind::Int(42, _) = lit.node;
+    if let LitKind::Int(42, LitIntType::Signed(IntTy::I32)) = lit.node;
     if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local.pat.kind;
     if name.as_str() == "x";
-    if let StmtKind::Semi(ref e, _) = block.stmts[1].kind
-    if let ExprKind::Unary(UnOp::Neg, ref inner) = e.kind;
-    if let ExprKind::Path(ref path) = inner.kind;
-    if match_qpath(path, &["x"]);
+    if let StmtKind::Local(local1) = block.stmts[1].kind;
+    if let Some(init1) = local1.init;
+    if let ExprKind::Lit(ref lit1) = init1.kind;
+    if let LitKind::Float(_, LitFloatType::Suffixed(FloatTy::F32)) = lit1.node;
+    if let PatKind::Binding(BindingAnnotation::Unannotated, _, name1, None) = local1.pat.kind;
+    if name1.as_str() == "_t";
+    if let StmtKind::Semi(e) = block.stmts[2].kind;
+    if let ExprKind::Unary(UnOp::Neg, inner) = e.kind;
+    if let ExprKind::Path(ref qpath) = inner.kind;
+    if match_qpath(qpath, &["x"]);
     if block.expr.is_none();
     then {
         // report your lint here
     }
 }
 if_chain! {
-    if let ExprKind::Block(ref block) = expr.kind;
+    if let ExprKind::Block(block, None) = expr.kind;
     if block.stmts.len() == 1;
-    if let StmtKind::Local(ref local) = block.stmts[0].kind;
-    if let Some(ref init) = local.init;
-    if let ExprKind::Call(ref func, ref args) = init.kind;
-    if let ExprKind::Path(ref path) = func.kind;
-    if match_qpath(path, &["String", "new"]);
-    if args.len() == 0;
+    if let StmtKind::Local(local) = block.stmts[0].kind;
+    if let Some(init) = local.init;
+    if let ExprKind::Call(func, args) = init.kind;
+    if let ExprKind::Path(ref qpath) = func.kind;
+    if match_qpath(qpath, &["String", "new"]);
+    if args.is_empty();
     if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local.pat.kind;
     if name.as_str() == "expr";
-    if let Some(trailing_expr) = &block.expr;
-    if let ExprKind::Call(ref func1, ref args1) = trailing_expr.kind;
-    if let ExprKind::Path(ref path1) = func1.kind;
-    if match_qpath(path1, &["drop"]);
+    if let Some(trailing_expr) = block.expr;
+    if let ExprKind::Call(func1, args1) = trailing_expr.kind;
+    if let ExprKind::Path(ref qpath1) = func1.kind;
+    if match_qpath(qpath1, &["drop"]);
     if args1.len() == 1;
-    if let ExprKind::Path(ref path2) = args1[0].kind;
-    if match_qpath(path2, &["expr"]);
+    if let ExprKind::Path(ref qpath2) = args1[0].kind;
+    if match_qpath(qpath2, &["expr"]);
+    then {
+        // report your lint here
+    }
+}
+if_chain! {
+    if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kind;
+    if let FnRetTy::DefaultReturn(_) = fn_decl.output;
+    let expr1 = &cx.tcx.hir().body(body_id).value;
+    if let ExprKind::Call(func, args) = expr1.kind;
+    if let ExprKind::Path(ref qpath) = func.kind;
+    if matches!(qpath, QPath::LangItem(LangItem::FromGenerator, _));
+    if args.len() == 1;
+    if let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = args[0].kind;
+    if let FnRetTy::DefaultReturn(_) = fn_decl1.output;
+    let expr2 = &cx.tcx.hir().body(body_id1).value;
+    if let ExprKind::Block(block, None) = expr2.kind;
+    if block.stmts.is_empty();
+    if block.expr.is_none();
     then {
         // report your lint here
     }
diff --git a/src/tools/clippy/tests/ui/author/call.stdout b/src/tools/clippy/tests/ui/author/call.stdout
index 4dccf666631..266312d63e5 100644
--- a/src/tools/clippy/tests/ui/author/call.stdout
+++ b/src/tools/clippy/tests/ui/author/call.stdout
@@ -1,14 +1,14 @@
 if_chain! {
-    if let StmtKind::Local(ref local) = stmt.kind;
-    if let Some(ref init) = local.init;
-    if let ExprKind::Call(ref func, ref args) = init.kind;
-    if let ExprKind::Path(ref path) = func.kind;
-    if match_qpath(path, &["{{root}}", "std", "cmp", "min"]);
+    if let StmtKind::Local(local) = stmt.kind;
+    if let Some(init) = local.init;
+    if let ExprKind::Call(func, args) = init.kind;
+    if let ExprKind::Path(ref qpath) = func.kind;
+    if match_qpath(qpath, &["{{root}}", "std", "cmp", "min"]);
     if args.len() == 2;
     if let ExprKind::Lit(ref lit) = args[0].kind;
-    if let LitKind::Int(3, _) = lit.node;
+    if let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node;
     if let ExprKind::Lit(ref lit1) = args[1].kind;
-    if let LitKind::Int(4, _) = lit1.node;
+    if let LitKind::Int(4, LitIntType::Unsuffixed) = lit1.node;
     if let PatKind::Wild = local.pat.kind;
     then {
         // report your lint here
diff --git a/src/tools/clippy/tests/ui/author/for_loop.rs b/src/tools/clippy/tests/ui/author/for_loop.rs
deleted file mode 100644
index b3dec876535..00000000000
--- a/src/tools/clippy/tests/ui/author/for_loop.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-#![feature(stmt_expr_attributes)]
-
-fn main() {
-    #[clippy::author]
-    for y in 0..10 {
-        let z = y;
-    }
-}
diff --git a/src/tools/clippy/tests/ui/author/for_loop.stdout b/src/tools/clippy/tests/ui/author/for_loop.stdout
deleted file mode 100644
index 4d0e13c833f..00000000000
--- a/src/tools/clippy/tests/ui/author/for_loop.stdout
+++ /dev/null
@@ -1,49 +0,0 @@
-if_chain! {
-    if let ExprKind::DropTemps(ref expr) = expr.kind;
-    if let ExprKind::Match(ref expr1, ref arms, MatchSource::ForLoopDesugar) = expr.kind;
-    if let ExprKind::Call(ref func, ref args) = expr1.kind;
-    if let ExprKind::Path(ref path) = func.kind;
-    if matches!(path, QPath::LangItem(LangItem::IntoIterIntoIter, _));
-    if args.len() == 1;
-    if let ExprKind::Struct(ref path1, ref fields, None) = args[0].kind;
-    if matches!(path1, QPath::LangItem(LangItem::Range, _));
-    if fields.len() == 2;
-    // unimplemented: field checks
-    if arms.len() == 1;
-    if let ExprKind::Loop(ref body, ref label, LoopSource::ForLoop) = arms[0].body.kind;
-    if body.stmts.len() == 1;
-    if let StmtKind::Expr(ref e, _) = body.stmts[0].kind
-    if let ExprKind::Match(ref expr2, ref arms1, MatchSource::ForLoopDesugar) = e.kind;
-    if let ExprKind::Call(ref func1, ref args1) = expr2.kind;
-    if let ExprKind::Path(ref path2) = func1.kind;
-    if matches!(path2, QPath::LangItem(LangItem::IteratorNext, _));
-    if args1.len() == 1;
-    if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, ref inner) = args1[0].kind;
-    if let ExprKind::Path(ref path3) = inner.kind;
-    if match_qpath(path3, &["iter"]);
-    if arms1.len() == 2;
-    if let ExprKind::Break(ref destination, None) = arms1[0].body.kind;
-    if let PatKind::Struct(ref path4, ref fields1, false) = arms1[0].pat.kind;
-    if matches!(path4, QPath::LangItem(LangItem::OptionNone, _));
-    if fields1.len() == 0;
-    // unimplemented: field checks
-    if let ExprKind::Block(ref block) = arms1[1].body.kind;
-    if block.stmts.len() == 1;
-    if let StmtKind::Local(ref local) = block.stmts[0].kind;
-    if let Some(ref init) = local.init;
-    if let ExprKind::Path(ref path5) = init.kind;
-    if match_qpath(path5, &["y"]);
-    if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local.pat.kind;
-    if name.as_str() == "z";
-    if block.expr.is_none();
-    if let PatKind::Struct(ref path6, ref fields2, false) = arms1[1].pat.kind;
-    if matches!(path6, QPath::LangItem(LangItem::OptionSome, _));
-    if fields2.len() == 1;
-    // unimplemented: field checks
-    if body.expr.is_none();
-    if let PatKind::Binding(BindingAnnotation::Mutable, _, name1, None) = arms[0].pat.kind;
-    if name1.as_str() == "iter";
-    then {
-        // report your lint here
-    }
-}
diff --git a/src/tools/clippy/tests/ui/author/if.rs b/src/tools/clippy/tests/ui/author/if.rs
index 2e9cb1466d0..946088ab346 100644
--- a/src/tools/clippy/tests/ui/author/if.rs
+++ b/src/tools/clippy/tests/ui/author/if.rs
@@ -7,4 +7,11 @@ fn main() {
     } else {
         2 == 2;
     };
+
+    let a = true;
+
+    #[clippy::author]
+    if let true = a {
+    } else {
+    };
 }
diff --git a/src/tools/clippy/tests/ui/author/if.stdout b/src/tools/clippy/tests/ui/author/if.stdout
index 1653de9a6f2..75ff3faf29a 100644
--- a/src/tools/clippy/tests/ui/author/if.stdout
+++ b/src/tools/clippy/tests/ui/author/if.stdout
@@ -1,32 +1,50 @@
 if_chain! {
-    if let StmtKind::Local(ref local) = stmt.kind;
-    if let Some(ref init) = local.init;
-    if let ExprKind::If(ref cond, ref then, Some(ref else_)) = init.kind;
-    if let ExprKind::Block(ref block) = else_.kind;
+    if let StmtKind::Local(local) = stmt.kind;
+    if let Some(init) = local.init;
+    if let ExprKind::If(cond, then, Some(else_expr)) = init.kind;
+    if let ExprKind::DropTemps(expr) = cond.kind;
+    if let ExprKind::Lit(ref lit) = expr.kind;
+    if let LitKind::Bool(true) = lit.node;
+    if let ExprKind::Block(block, None) = then.kind;
     if block.stmts.len() == 1;
-    if let StmtKind::Semi(ref e, _) = block.stmts[0].kind
-    if let ExprKind::Binary(ref op, ref left, ref right) = e.kind;
+    if let StmtKind::Semi(e) = block.stmts[0].kind;
+    if let ExprKind::Binary(op, left, right) = e.kind;
     if BinOpKind::Eq == op.node;
-    if let ExprKind::Lit(ref lit) = left.kind;
-    if let LitKind::Int(2, _) = lit.node;
-    if let ExprKind::Lit(ref lit1) = right.kind;
-    if let LitKind::Int(2, _) = lit1.node;
+    if let ExprKind::Lit(ref lit1) = left.kind;
+    if let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node;
+    if let ExprKind::Lit(ref lit2) = right.kind;
+    if let LitKind::Int(1, LitIntType::Unsuffixed) = lit2.node;
     if block.expr.is_none();
-    if let ExprKind::DropTemps(ref expr) = cond.kind;
-    if let ExprKind::Lit(ref lit2) = expr.kind;
-    if let LitKind::Bool(true) = lit2.node;
-    if let ExprKind::Block(ref block1) = then.kind;
+    if let ExprKind::Block(block1, None) = else_expr.kind;
     if block1.stmts.len() == 1;
-    if let StmtKind::Semi(ref e1, _) = block1.stmts[0].kind
-    if let ExprKind::Binary(ref op1, ref left1, ref right1) = e1.kind;
+    if let StmtKind::Semi(e1) = block1.stmts[0].kind;
+    if let ExprKind::Binary(op1, left1, right1) = e1.kind;
     if BinOpKind::Eq == op1.node;
     if let ExprKind::Lit(ref lit3) = left1.kind;
-    if let LitKind::Int(1, _) = lit3.node;
+    if let LitKind::Int(2, LitIntType::Unsuffixed) = lit3.node;
     if let ExprKind::Lit(ref lit4) = right1.kind;
-    if let LitKind::Int(1, _) = lit4.node;
+    if let LitKind::Int(2, LitIntType::Unsuffixed) = lit4.node;
     if block1.expr.is_none();
     if let PatKind::Wild = local.pat.kind;
     then {
         // report your lint here
     }
 }
+if_chain! {
+    if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind;
+    if let ExprKind::Let(pat, expr1, _) = cond.kind;
+    if let PatKind::Lit(lit_expr) = pat.kind;
+    if let ExprKind::Lit(ref lit) = lit_expr.kind;
+    if let LitKind::Bool(true) = lit.node;
+    if let ExprKind::Path(ref qpath) = expr1.kind;
+    if match_qpath(qpath, &["a"]);
+    if let ExprKind::Block(block, None) = then.kind;
+    if block.stmts.is_empty();
+    if block.expr.is_none();
+    if let ExprKind::Block(block1, None) = else_expr.kind;
+    if block1.stmts.is_empty();
+    if block1.expr.is_none();
+    then {
+        // report your lint here
+    }
+}
diff --git a/src/tools/clippy/tests/ui/author/issue_3849.stdout b/src/tools/clippy/tests/ui/author/issue_3849.stdout
index 65f93f3cdc0..bce4bc70273 100644
--- a/src/tools/clippy/tests/ui/author/issue_3849.stdout
+++ b/src/tools/clippy/tests/ui/author/issue_3849.stdout
@@ -1,12 +1,12 @@
 if_chain! {
-    if let StmtKind::Local(ref local) = stmt.kind;
-    if let Some(ref init) = local.init;
-    if let ExprKind::Call(ref func, ref args) = init.kind;
-    if let ExprKind::Path(ref path) = func.kind;
-    if match_qpath(path, &["std", "mem", "transmute"]);
+    if let StmtKind::Local(local) = stmt.kind;
+    if let Some(init) = local.init;
+    if let ExprKind::Call(func, args) = init.kind;
+    if let ExprKind::Path(ref qpath) = func.kind;
+    if match_qpath(qpath, &["std", "mem", "transmute"]);
     if args.len() == 1;
-    if let ExprKind::Path(ref path1) = args[0].kind;
-    if match_qpath(path1, &["ZPTR"]);
+    if let ExprKind::Path(ref qpath1) = args[0].kind;
+    if match_qpath(qpath1, &["ZPTR"]);
     if let PatKind::Wild = local.pat.kind;
     then {
         // report your lint here
diff --git a/src/tools/clippy/tests/ui/author/loop.rs b/src/tools/clippy/tests/ui/author/loop.rs
new file mode 100644
index 00000000000..d6de21631e2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/loop.rs
@@ -0,0 +1,36 @@
+#![feature(stmt_expr_attributes)]
+#![allow(clippy::never_loop, clippy::while_immutable_condition)]
+
+fn main() {
+    #[clippy::author]
+    for y in 0..10 {
+        let z = y;
+    }
+
+    #[clippy::author]
+    for _ in 0..10 {
+        break;
+    }
+
+    #[clippy::author]
+    'label: for _ in 0..10 {
+        break 'label;
+    }
+
+    let a = true;
+
+    #[clippy::author]
+    while a {
+        break;
+    }
+
+    #[clippy::author]
+    while let true = a {
+        break;
+    }
+
+    #[clippy::author]
+    loop {
+        break;
+    }
+}
diff --git a/src/tools/clippy/tests/ui/author/loop.stdout b/src/tools/clippy/tests/ui/author/loop.stdout
new file mode 100644
index 00000000000..3d9560f697a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/loop.stdout
@@ -0,0 +1,113 @@
+if_chain! {
+    if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr);
+    if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = pat.kind;
+    if name.as_str() == "y";
+    if let ExprKind::Struct(qpath, fields, None) = arg.kind;
+    if matches!(qpath, QPath::LangItem(LangItem::Range, _));
+    if fields.len() == 2;
+    if fields[0].ident.as_str() == "start";
+    if let ExprKind::Lit(ref lit) = fields[0].expr.kind;
+    if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node;
+    if fields[1].ident.as_str() == "end";
+    if let ExprKind::Lit(ref lit1) = fields[1].expr.kind;
+    if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node;
+    if let ExprKind::Block(block, None) = body.kind;
+    if block.stmts.len() == 1;
+    if let StmtKind::Local(local) = block.stmts[0].kind;
+    if let Some(init) = local.init;
+    if let ExprKind::Path(ref qpath1) = init.kind;
+    if match_qpath(qpath1, &["y"]);
+    if let PatKind::Binding(BindingAnnotation::Unannotated, _, name1, None) = local.pat.kind;
+    if name1.as_str() == "z";
+    if block.expr.is_none();
+    then {
+        // report your lint here
+    }
+}
+if_chain! {
+    if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr);
+    if let PatKind::Wild = pat.kind;
+    if let ExprKind::Struct(qpath, fields, None) = arg.kind;
+    if matches!(qpath, QPath::LangItem(LangItem::Range, _));
+    if fields.len() == 2;
+    if fields[0].ident.as_str() == "start";
+    if let ExprKind::Lit(ref lit) = fields[0].expr.kind;
+    if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node;
+    if fields[1].ident.as_str() == "end";
+    if let ExprKind::Lit(ref lit1) = fields[1].expr.kind;
+    if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node;
+    if let ExprKind::Block(block, None) = body.kind;
+    if block.stmts.len() == 1;
+    if let StmtKind::Semi(e) = block.stmts[0].kind;
+    if let ExprKind::Break(destination, None) = e.kind;
+    if destination.label.is_none();
+    if block.expr.is_none();
+    then {
+        // report your lint here
+    }
+}
+if_chain! {
+    if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr);
+    if let PatKind::Wild = pat.kind;
+    if let ExprKind::Struct(qpath, fields, None) = arg.kind;
+    if matches!(qpath, QPath::LangItem(LangItem::Range, _));
+    if fields.len() == 2;
+    if fields[0].ident.as_str() == "start";
+    if let ExprKind::Lit(ref lit) = fields[0].expr.kind;
+    if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node;
+    if fields[1].ident.as_str() == "end";
+    if let ExprKind::Lit(ref lit1) = fields[1].expr.kind;
+    if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node;
+    if let ExprKind::Block(block, None) = body.kind;
+    if block.stmts.len() == 1;
+    if let StmtKind::Semi(e) = block.stmts[0].kind;
+    if let ExprKind::Break(destination, None) = e.kind;
+    if let Some(label) = destination.label;
+    if label.ident.as_str() == "'label";
+    if block.expr.is_none();
+    then {
+        // report your lint here
+    }
+}
+if_chain! {
+    if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr);
+    if let ExprKind::Path(ref qpath) = condition.kind;
+    if match_qpath(qpath, &["a"]);
+    if let ExprKind::Block(block, None) = body.kind;
+    if block.stmts.len() == 1;
+    if let StmtKind::Semi(e) = block.stmts[0].kind;
+    if let ExprKind::Break(destination, None) = e.kind;
+    if destination.label.is_none();
+    if block.expr.is_none();
+    then {
+        // report your lint here
+    }
+}
+if_chain! {
+    if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr);
+    if let PatKind::Lit(lit_expr) = let_pat.kind;
+    if let ExprKind::Lit(ref lit) = lit_expr.kind;
+    if let LitKind::Bool(true) = lit.node;
+    if let ExprKind::Path(ref qpath) = let_expr.kind;
+    if match_qpath(qpath, &["a"]);
+    if let ExprKind::Block(block, None) = if_then.kind;
+    if block.stmts.len() == 1;
+    if let StmtKind::Semi(e) = block.stmts[0].kind;
+    if let ExprKind::Break(destination, None) = e.kind;
+    if destination.label.is_none();
+    if block.expr.is_none();
+    then {
+        // report your lint here
+    }
+}
+if_chain! {
+    if let ExprKind::Loop(body, None, LoopSource::Loop, _) = expr.kind;
+    if body.stmts.len() == 1;
+    if let StmtKind::Semi(e) = body.stmts[0].kind;
+    if let ExprKind::Break(destination, None) = e.kind;
+    if destination.label.is_none();
+    if body.expr.is_none();
+    then {
+        // report your lint here
+    }
+}
diff --git a/src/tools/clippy/tests/ui/author/matches.stdout b/src/tools/clippy/tests/ui/author/matches.stdout
index 68cc2b214eb..38444a0094c 100644
--- a/src/tools/clippy/tests/ui/author/matches.stdout
+++ b/src/tools/clippy/tests/ui/author/matches.stdout
@@ -1,32 +1,35 @@
 if_chain! {
-    if let StmtKind::Local(ref local) = stmt.kind;
-    if let Some(ref init) = local.init;
-    if let ExprKind::Match(ref expr, ref arms, MatchSource::Normal) = init.kind;
-    if let ExprKind::Lit(ref lit) = expr.kind;
-    if let LitKind::Int(42, _) = lit.node;
+    if let StmtKind::Local(local) = stmt.kind;
+    if let Some(init) = local.init;
+    if let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = init.kind;
+    if let ExprKind::Lit(ref lit) = scrutinee.kind;
+    if let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node;
     if arms.len() == 3;
-    if let ExprKind::Lit(ref lit1) = arms[0].body.kind;
-    if let LitKind::Int(5, _) = lit1.node;
-    if let PatKind::Lit(ref lit_expr) = arms[0].pat.kind
-    if let ExprKind::Lit(ref lit2) = lit_expr.kind;
-    if let LitKind::Int(16, _) = lit2.node;
-    if let ExprKind::Block(ref block) = arms[1].body.kind;
+    if let PatKind::Lit(lit_expr) = arms[0].pat.kind;
+    if let ExprKind::Lit(ref lit1) = lit_expr.kind;
+    if let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node;
+    if arms[0].guard.is_none();
+    if let ExprKind::Lit(ref lit2) = arms[0].body.kind;
+    if let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node;
+    if let PatKind::Lit(lit_expr1) = arms[1].pat.kind;
+    if let ExprKind::Lit(ref lit3) = lit_expr1.kind;
+    if let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node;
+    if arms[1].guard.is_none();
+    if let ExprKind::Block(block, None) = arms[1].body.kind;
     if block.stmts.len() == 1;
-    if let StmtKind::Local(ref local1) = block.stmts[0].kind;
-    if let Some(ref init1) = local1.init;
-    if let ExprKind::Lit(ref lit3) = init1.kind;
-    if let LitKind::Int(3, _) = lit3.node;
+    if let StmtKind::Local(local1) = block.stmts[0].kind;
+    if let Some(init1) = local1.init;
+    if let ExprKind::Lit(ref lit4) = init1.kind;
+    if let LitKind::Int(3, LitIntType::Unsuffixed) = lit4.node;
     if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local1.pat.kind;
     if name.as_str() == "x";
-    if let Some(trailing_expr) = &block.expr;
-    if let ExprKind::Path(ref path) = trailing_expr.kind;
-    if match_qpath(path, &["x"]);
-    if let PatKind::Lit(ref lit_expr1) = arms[1].pat.kind
-    if let ExprKind::Lit(ref lit4) = lit_expr1.kind;
-    if let LitKind::Int(17, _) = lit4.node;
-    if let ExprKind::Lit(ref lit5) = arms[2].body.kind;
-    if let LitKind::Int(1, _) = lit5.node;
+    if let Some(trailing_expr) = block.expr;
+    if let ExprKind::Path(ref qpath) = trailing_expr.kind;
+    if match_qpath(qpath, &["x"]);
     if let PatKind::Wild = arms[2].pat.kind;
+    if arms[2].guard.is_none();
+    if let ExprKind::Lit(ref lit5) = arms[2].body.kind;
+    if let LitKind::Int(1, LitIntType::Unsuffixed) = lit5.node;
     if let PatKind::Binding(BindingAnnotation::Unannotated, _, name1, None) = local.pat.kind;
     if name1.as_str() == "a";
     then {
diff --git a/src/tools/clippy/tests/ui/author/repeat.rs b/src/tools/clippy/tests/ui/author/repeat.rs
new file mode 100644
index 00000000000..d8e9d589e68
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/repeat.rs
@@ -0,0 +1,5 @@
+#[allow(clippy::no_effect)]
+fn main() {
+    #[clippy::author]
+    [1_u8; 5];
+}
diff --git a/src/tools/clippy/tests/ui/author/repeat.stdout b/src/tools/clippy/tests/ui/author/repeat.stdout
new file mode 100644
index 00000000000..f16350e4b5e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/repeat.stdout
@@ -0,0 +1,11 @@
+if_chain! {
+    if let ExprKind::Repeat(value, length) = expr.kind;
+    if let ExprKind::Lit(ref lit) = value.kind;
+    if let LitKind::Int(1, LitIntType::Unsigned(UintTy::U8)) = lit.node;
+    let expr1 = &cx.tcx.hir().body(length.body).value;
+    if let ExprKind::Lit(ref lit1) = expr1.kind;
+    if let LitKind::Int(5, LitIntType::Unsuffixed) = lit1.node;
+    then {
+        // report your lint here
+    }
+}
diff --git a/src/tools/clippy/tests/ui/author/struct.rs b/src/tools/clippy/tests/ui/author/struct.rs
new file mode 100644
index 00000000000..5fdf3433a37
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/struct.rs
@@ -0,0 +1,40 @@
+#[allow(clippy::unnecessary_operation, clippy::single_match)]
+fn main() {
+    struct Test {
+        field: u32,
+    }
+
+    #[clippy::author]
+    Test {
+        field: if true { 1 } else { 0 },
+    };
+
+    let test = Test { field: 1 };
+
+    match test {
+        #[clippy::author]
+        Test { field: 1 } => {},
+        _ => {},
+    }
+
+    struct TestTuple(u32);
+
+    let test_tuple = TestTuple(1);
+
+    match test_tuple {
+        #[clippy::author]
+        TestTuple(1) => {},
+        _ => {},
+    }
+
+    struct TestMethodCall(u32);
+
+    impl TestMethodCall {
+        fn test(&self) {}
+    }
+
+    let test_method_call = TestMethodCall(1);
+
+    #[clippy::author]
+    test_method_call.test();
+}
diff --git a/src/tools/clippy/tests/ui/author/struct.stdout b/src/tools/clippy/tests/ui/author/struct.stdout
new file mode 100644
index 00000000000..ded5abd8d33
--- /dev/null
+++ b/src/tools/clippy/tests/ui/author/struct.stdout
@@ -0,0 +1,64 @@
+if_chain! {
+    if let ExprKind::Struct(qpath, fields, None) = expr.kind;
+    if match_qpath(qpath, &["Test"]);
+    if fields.len() == 1;
+    if fields[0].ident.as_str() == "field";
+    if let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind;
+    if let ExprKind::DropTemps(expr1) = cond.kind;
+    if let ExprKind::Lit(ref lit) = expr1.kind;
+    if let LitKind::Bool(true) = lit.node;
+    if let ExprKind::Block(block, None) = then.kind;
+    if block.stmts.is_empty();
+    if let Some(trailing_expr) = block.expr;
+    if let ExprKind::Lit(ref lit1) = trailing_expr.kind;
+    if let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node;
+    if let ExprKind::Block(block1, None) = else_expr.kind;
+    if block1.stmts.is_empty();
+    if let Some(trailing_expr1) = block1.expr;
+    if let ExprKind::Lit(ref lit2) = trailing_expr1.kind;
+    if let LitKind::Int(0, LitIntType::Unsuffixed) = lit2.node;
+    then {
+        // report your lint here
+    }
+}
+if_chain! {
+    if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind;
+    if match_qpath(qpath, &["Test"]);
+    if fields.len() == 1;
+    if fields[0].ident.as_str() == "field";
+    if let PatKind::Lit(lit_expr) = fields[0].pat.kind;
+    if let ExprKind::Lit(ref lit) = lit_expr.kind;
+    if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node;
+    if arm.guard.is_none();
+    if let ExprKind::Block(block, None) = arm.body.kind;
+    if block.stmts.is_empty();
+    if block.expr.is_none();
+    then {
+        // report your lint here
+    }
+}
+if_chain! {
+    if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind;
+    if match_qpath(qpath, &["TestTuple"]);
+    if fields.len() == 1;
+    if let PatKind::Lit(lit_expr) = fields[0].kind;
+    if let ExprKind::Lit(ref lit) = lit_expr.kind;
+    if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node;
+    if arm.guard.is_none();
+    if let ExprKind::Block(block, None) = arm.body.kind;
+    if block.stmts.is_empty();
+    if block.expr.is_none();
+    then {
+        // report your lint here
+    }
+}
+if_chain! {
+    if let ExprKind::MethodCall(method_name, _, args, _) = expr.kind;
+    if method_name.ident.as_str() == "test";
+    if args.len() == 1;
+    if let ExprKind::Path(ref qpath) = args[0].kind;
+    if match_qpath(qpath, &["test_method_call"]);
+    then {
+        // report your lint here
+    }
+}
diff --git a/src/tools/clippy/tests/ui/cast_lossless_bool.fixed b/src/tools/clippy/tests/ui/cast_lossless_bool.fixed
new file mode 100644
index 00000000000..9e2da45c378
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_lossless_bool.fixed
@@ -0,0 +1,42 @@
+// run-rustfix
+
+#![allow(dead_code)]
+#![warn(clippy::cast_lossless)]
+
+fn main() {
+    // Test clippy::cast_lossless with casts to integer types
+    let _ = u8::from(true);
+    let _ = u16::from(true);
+    let _ = u32::from(true);
+    let _ = u64::from(true);
+    let _ = u128::from(true);
+    let _ = usize::from(true);
+
+    let _ = i8::from(true);
+    let _ = i16::from(true);
+    let _ = i32::from(true);
+    let _ = i64::from(true);
+    let _ = i128::from(true);
+    let _ = isize::from(true);
+
+    // Test with an expression wrapped in parens
+    let _ = u16::from(true | false);
+}
+
+// The lint would suggest using `u32::from(input)` here but the `XX::from` function is not const,
+// so we skip the lint if the expression is in a const fn.
+// See #3656
+const fn abc(input: bool) -> u32 {
+    input as u32
+}
+
+// Same as the above issue. We can't suggest `::from` in const fns in impls
+mod cast_lossless_in_impl {
+    struct A;
+
+    impl A {
+        pub const fn convert(x: bool) -> u64 {
+            x as u64
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/cast_lossless_bool.rs b/src/tools/clippy/tests/ui/cast_lossless_bool.rs
new file mode 100644
index 00000000000..b6f6c59a01f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_lossless_bool.rs
@@ -0,0 +1,42 @@
+// run-rustfix
+
+#![allow(dead_code)]
+#![warn(clippy::cast_lossless)]
+
+fn main() {
+    // Test clippy::cast_lossless with casts to integer types
+    let _ = true as u8;
+    let _ = true as u16;
+    let _ = true as u32;
+    let _ = true as u64;
+    let _ = true as u128;
+    let _ = true as usize;
+
+    let _ = true as i8;
+    let _ = true as i16;
+    let _ = true as i32;
+    let _ = true as i64;
+    let _ = true as i128;
+    let _ = true as isize;
+
+    // Test with an expression wrapped in parens
+    let _ = (true | false) as u16;
+}
+
+// The lint would suggest using `u32::from(input)` here but the `XX::from` function is not const,
+// so we skip the lint if the expression is in a const fn.
+// See #3656
+const fn abc(input: bool) -> u32 {
+    input as u32
+}
+
+// Same as the above issue. We can't suggest `::from` in const fns in impls
+mod cast_lossless_in_impl {
+    struct A;
+
+    impl A {
+        pub const fn convert(x: bool) -> u64 {
+            x as u64
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/cast_lossless_bool.stderr b/src/tools/clippy/tests/ui/cast_lossless_bool.stderr
new file mode 100644
index 00000000000..6b148336011
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_lossless_bool.stderr
@@ -0,0 +1,82 @@
+error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)`
+  --> $DIR/cast_lossless_bool.rs:8:13
+   |
+LL |     let _ = true as u8;
+   |             ^^^^^^^^^^ help: try: `u8::from(true)`
+   |
+   = note: `-D clippy::cast-lossless` implied by `-D warnings`
+
+error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)`
+  --> $DIR/cast_lossless_bool.rs:9:13
+   |
+LL |     let _ = true as u16;
+   |             ^^^^^^^^^^^ help: try: `u16::from(true)`
+
+error: casting `bool` to `u32` is more cleanly stated with `u32::from(_)`
+  --> $DIR/cast_lossless_bool.rs:10:13
+   |
+LL |     let _ = true as u32;
+   |             ^^^^^^^^^^^ help: try: `u32::from(true)`
+
+error: casting `bool` to `u64` is more cleanly stated with `u64::from(_)`
+  --> $DIR/cast_lossless_bool.rs:11:13
+   |
+LL |     let _ = true as u64;
+   |             ^^^^^^^^^^^ help: try: `u64::from(true)`
+
+error: casting `bool` to `u128` is more cleanly stated with `u128::from(_)`
+  --> $DIR/cast_lossless_bool.rs:12:13
+   |
+LL |     let _ = true as u128;
+   |             ^^^^^^^^^^^^ help: try: `u128::from(true)`
+
+error: casting `bool` to `usize` is more cleanly stated with `usize::from(_)`
+  --> $DIR/cast_lossless_bool.rs:13:13
+   |
+LL |     let _ = true as usize;
+   |             ^^^^^^^^^^^^^ help: try: `usize::from(true)`
+
+error: casting `bool` to `i8` is more cleanly stated with `i8::from(_)`
+  --> $DIR/cast_lossless_bool.rs:15:13
+   |
+LL |     let _ = true as i8;
+   |             ^^^^^^^^^^ help: try: `i8::from(true)`
+
+error: casting `bool` to `i16` is more cleanly stated with `i16::from(_)`
+  --> $DIR/cast_lossless_bool.rs:16:13
+   |
+LL |     let _ = true as i16;
+   |             ^^^^^^^^^^^ help: try: `i16::from(true)`
+
+error: casting `bool` to `i32` is more cleanly stated with `i32::from(_)`
+  --> $DIR/cast_lossless_bool.rs:17:13
+   |
+LL |     let _ = true as i32;
+   |             ^^^^^^^^^^^ help: try: `i32::from(true)`
+
+error: casting `bool` to `i64` is more cleanly stated with `i64::from(_)`
+  --> $DIR/cast_lossless_bool.rs:18:13
+   |
+LL |     let _ = true as i64;
+   |             ^^^^^^^^^^^ help: try: `i64::from(true)`
+
+error: casting `bool` to `i128` is more cleanly stated with `i128::from(_)`
+  --> $DIR/cast_lossless_bool.rs:19:13
+   |
+LL |     let _ = true as i128;
+   |             ^^^^^^^^^^^^ help: try: `i128::from(true)`
+
+error: casting `bool` to `isize` is more cleanly stated with `isize::from(_)`
+  --> $DIR/cast_lossless_bool.rs:20:13
+   |
+LL |     let _ = true as isize;
+   |             ^^^^^^^^^^^^^ help: try: `isize::from(true)`
+
+error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)`
+  --> $DIR/cast_lossless_bool.rs:23:13
+   |
+LL |     let _ = (true | false) as u16;
+   |             ^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::from(true | false)`
+
+error: aborting due to 13 previous errors
+
diff --git a/src/tools/clippy/tests/ui/crashes/auxiliary/ice-7934-aux.rs b/src/tools/clippy/tests/ui/crashes/auxiliary/ice-7934-aux.rs
new file mode 100644
index 00000000000..4afbf027b61
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/auxiliary/ice-7934-aux.rs
@@ -0,0 +1,4 @@
+fn zero() {
+    // SAFETY:
+    unsafe { 0 };
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7934.rs b/src/tools/clippy/tests/ui/crashes/ice-7934.rs
new file mode 100644
index 00000000000..a4691c4131b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-7934.rs
@@ -0,0 +1,7 @@
+#![warn(clippy::undocumented_unsafe_blocks)]
+#![allow(clippy::no_effect)]
+
+#[path = "auxiliary/ice-7934-aux.rs"]
+mod zero;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.rs b/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.rs
new file mode 100644
index 00000000000..d3571eaf0d7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.rs
@@ -0,0 +1,14 @@
+#![no_std]
+#![feature(lang_items, start, libc)]
+#![crate_type = "lib"]
+
+use core::panic::PanicInfo;
+
+#[warn(clippy::all)]
+fn main() {
+    let mut a = 42;
+    let mut b = 1337;
+
+    a = b;
+    b = a;
+}
diff --git a/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.stderr b/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.stderr
new file mode 100644
index 00000000000..48152d8ad77
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crate_level_checks/no_std_swap.stderr
@@ -0,0 +1,12 @@
+error: this looks like you are trying to swap `a` and `b`
+  --> $DIR/no_std_swap.rs:12:5
+   |
+LL | /     a = b;
+LL | |     b = a;
+   | |_________^ help: try: `core::mem::swap(&mut a, &mut b)`
+   |
+   = note: `-D clippy::almost-swapped` implied by `-D warnings`
+   = note: or maybe you should use `core::mem::replace`?
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
index 31132f86edb..40345370c04 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
@@ -2,183 +2,332 @@ error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:9:9
    |
 LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
-   |         ^^^^^^^ help: try: ``foo_bar``
+   |         ^^^^^^^
    |
    = note: `-D clippy::doc-markdown` implied by `-D warnings`
+help: try
+   |
+LL | /// The `foo_bar` function does _nothing_. See also foo::bar. (note the dot there)
+   |         ~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:9:51
    |
 LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
-   |                                                   ^^^^^^^^ help: try: ``foo::bar``
+   |                                                   ^^^^^^^^
+   |
+help: try
+   |
+LL | /// The foo_bar function does _nothing_. See also `foo::bar`. (note the dot there)
+   |                                                   ~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:10:83
    |
 LL | /// Markdown is _weird_. I mean _really weird_. This /_ is ok. So is `_`. But not Foo::some_fun
-   |                                                                                   ^^^^^^^^^^^^^ help: try: ``Foo::some_fun``
+   |                                                                                   ^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// Markdown is _weird_. I mean _really weird_. This /_ is ok. So is `_`. But not `Foo::some_fun`
+   |                                                                                   ~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:12:13
    |
 LL | /// Here be ::a::global:path, and _::another::global::path_.  :: is not a path though.
-   |             ^^^^^^^^^^^^^^^^ help: try: ``::a::global:path``
+   |             ^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// Here be `::a::global:path`, and _::another::global::path_.  :: is not a path though.
+   |             ~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:12:36
    |
 LL | /// Here be ::a::global:path, and _::another::global::path_.  :: is not a path though.
-   |                                    ^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``::another::global::path``
+   |                                    ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// Here be ::a::global:path, and _`::another::global::path`_.  :: is not a path though.
+   |                                    ~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:13:25
    |
 LL | /// Import an item from ::awesome::global::blob:: (Intended postfix)
-   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``::awesome::global::blob::``
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// Import an item from `::awesome::global::blob::` (Intended postfix)
+   |                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:14:31
    |
 LL | /// These are the options for ::Cat: (Intended trailing single colon, shouldn't be linted)
-   |                               ^^^^^ help: try: ``::Cat``
+   |                               ^^^^^
+   |
+help: try
+   |
+LL | /// These are the options for `::Cat`: (Intended trailing single colon, shouldn't be linted)
+   |                               ~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:15:22
    |
 LL | /// That's not code ~NotInCodeBlock~.
-   |                      ^^^^^^^^^^^^^^ help: try: ``NotInCodeBlock``
+   |                      ^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// That's not code ~`NotInCodeBlock`~.
+   |                      ~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:16:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:30:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:37:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:51:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:74:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:78:22
    |
 LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823.
-   |                      ^^^^^^^^^^^^^^^^^^^^^ help: try: ``link_with_underscores``
+   |                      ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// This test has [a `link_with_underscores`][chunked-example] inside it. See #823.
+   |                      ~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:81:21
    |
 LL | /// It can also be [inline_link2].
-   |                     ^^^^^^^^^^^^ help: try: ``inline_link2``
+   |                     ^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// It can also be [`inline_link2`].
+   |                     ~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:91:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:99:8
    |
 LL | /// ## CamelCaseThing
-   |        ^^^^^^^^^^^^^^ help: try: ``CamelCaseThing``
+   |        ^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// ## `CamelCaseThing`
+   |        ~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:102:7
    |
 LL | /// # CamelCaseThing
-   |       ^^^^^^^^^^^^^^ help: try: ``CamelCaseThing``
+   |       ^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// # `CamelCaseThing`
+   |       ~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:104:22
    |
 LL | /// Not a title #897 CamelCaseThing
-   |                      ^^^^^^^^^^^^^^ help: try: ``CamelCaseThing``
+   |                      ^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// Not a title #897 `CamelCaseThing`
+   |                      ~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:105:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:112:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:125:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:136:43
    |
 LL | /** E.g., serialization of an empty list: FooBar
-   |                                           ^^^^^^ help: try: ``FooBar``
+   |                                           ^^^^^^
+   |
+help: try
+   |
+LL | /** E.g., serialization of an empty list: `FooBar`
+   |                                           ~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:141:5
    |
 LL | And BarQuz too.
-   |     ^^^^^^ help: try: ``BarQuz``
+   |     ^^^^^^
+   |
+help: try
+   |
+LL | And `BarQuz` too.
+   |     ~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:142:1
    |
 LL | be_sure_we_got_to_the_end_of_it
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | `be_sure_we_got_to_the_end_of_it`
+   |
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:147:43
    |
 LL | /** E.g., serialization of an empty list: FooBar
-   |                                           ^^^^^^ help: try: ``FooBar``
+   |                                           ^^^^^^
+   |
+help: try
+   |
+LL | /** E.g., serialization of an empty list: `FooBar`
+   |                                           ~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:152:5
    |
 LL | And BarQuz too.
-   |     ^^^^^^ help: try: ``BarQuz``
+   |     ^^^^^^
+   |
+help: try
+   |
+LL | And `BarQuz` too.
+   |     ~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:153:1
    |
 LL | be_sure_we_got_to_the_end_of_it
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | `be_sure_we_got_to_the_end_of_it`
+   |
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:164:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
   --> $DIR/doc-fixable.rs:183:22
    |
 LL | /// An iterator over mycrate::Collection's values.
-   |                      ^^^^^^^^^^^^^^^^^^^ help: try: ``mycrate::Collection``
+   |                      ^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// An iterator over `mycrate::Collection`'s values.
+   |                      ~~~~~~~~~~~~~~~~~~~~~
 
 error: aborting due to 30 previous errors
 
diff --git a/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr b/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr
index 9670e5c24fb..a462b98871a 100644
--- a/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr
+++ b/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr
@@ -22,7 +22,12 @@ error: item in documentation is missing backticks
   --> $DIR/unbalanced_ticks.rs:15:32
    |
 LL | /// This paragraph is fine and should_be linted normally.
-   |                                ^^^^^^^^^ help: try: ``should_be``
+   |                                ^^^^^^^^^
+   |
+help: try
+   |
+LL | /// This paragraph is fine and `should_be` linted normally.
+   |                                ~~~~~~~~~~~
 
 error: backticks are unbalanced
   --> $DIR/unbalanced_ticks.rs:17:1
@@ -36,7 +41,12 @@ error: item in documentation is missing backticks
   --> $DIR/unbalanced_ticks.rs:30:8
    |
 LL | /// ## not_fine
-   |        ^^^^^^^^ help: try: ``not_fine``
+   |        ^^^^^^^^
+   |
+help: try
+   |
+LL | /// ## `not_fine`
+   |        ~~~~~~~~~~
 
 error: backticks are unbalanced
   --> $DIR/unbalanced_ticks.rs:32:1
@@ -58,7 +68,12 @@ error: item in documentation is missing backticks
   --> $DIR/unbalanced_ticks.rs:35:23
    |
 LL | /// - This item needs backticks_here
-   |                       ^^^^^^^^^^^^^^ help: try: ``backticks_here``
+   |                       ^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL | /// - This item needs `backticks_here`
+   |                       ~~~~~~~~~~~~~~~~
 
 error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui/explicit_counter_loop.rs b/src/tools/clippy/tests/ui/explicit_counter_loop.rs
index 81d8221bd13..aa966761feb 100644
--- a/src/tools/clippy/tests/ui/explicit_counter_loop.rs
+++ b/src/tools/clippy/tests/ui/explicit_counter_loop.rs
@@ -158,3 +158,33 @@ mod issue_4677 {
         }
     }
 }
+
+mod issue_7920 {
+    pub fn test() {
+        let slice = &[1, 2, 3];
+
+        let index_usize: usize = 0;
+        let mut idx_usize: usize = 0;
+
+        // should suggest `enumerate`
+        for _item in slice {
+            if idx_usize == index_usize {
+                break;
+            }
+
+            idx_usize += 1;
+        }
+
+        let index_u32: u32 = 0;
+        let mut idx_u32: u32 = 0;
+
+        // should suggest `zip`
+        for _item in slice {
+            if idx_u32 == index_u32 {
+                break;
+            }
+
+            idx_u32 += 1;
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/explicit_counter_loop.stderr b/src/tools/clippy/tests/ui/explicit_counter_loop.stderr
index 4cbacffe87b..9edddea651c 100644
--- a/src/tools/clippy/tests/ui/explicit_counter_loop.stderr
+++ b/src/tools/clippy/tests/ui/explicit_counter_loop.stderr
@@ -42,5 +42,19 @@ error: the variable `count` is used as a loop counter
 LL |         for _i in 3..10 {
    |         ^^^^^^^^^^^^^^^ help: consider using: `for (count, _i) in (3..10).enumerate()`
 
-error: aborting due to 7 previous errors
+error: the variable `idx_usize` is used as a loop counter
+  --> $DIR/explicit_counter_loop.rs:170:9
+   |
+LL |         for _item in slice {
+   |         ^^^^^^^^^^^^^^^^^^ help: consider using: `for (idx_usize, _item) in slice.into_iter().enumerate()`
+
+error: the variable `idx_u32` is used as a loop counter
+  --> $DIR/explicit_counter_loop.rs:182:9
+   |
+LL |         for _item in slice {
+   |         ^^^^^^^^^^^^^^^^^^ help: consider using: `for (idx_u32, _item) in (0_u32..).zip(slice.into_iter())`
+   |
+   = note: `idx_u32` is of type `u32`, making it ineligible for `Iterator::enumerate`
+
+error: aborting due to 9 previous errors
 
diff --git a/src/tools/clippy/tests/ui/floating_point_abs.fixed b/src/tools/clippy/tests/ui/floating_point_abs.fixed
index cea727257c4..ca747fefc64 100644
--- a/src/tools/clippy/tests/ui/floating_point_abs.fixed
+++ b/src/tools/clippy/tests/ui/floating_point_abs.fixed
@@ -1,6 +1,12 @@
 // run-rustfix
+#![feature(const_fn_floating_point_arithmetic)]
 #![warn(clippy::suboptimal_flops)]
 
+/// Allow suboptimal ops in constant context
+pub const fn in_const_context(num: f64) -> f64 {
+    if num >= 0.0 { num } else { -num }
+}
+
 struct A {
     a: f64,
     b: f64,
diff --git a/src/tools/clippy/tests/ui/floating_point_abs.rs b/src/tools/clippy/tests/ui/floating_point_abs.rs
index ba8a8f18fa2..e4b60657497 100644
--- a/src/tools/clippy/tests/ui/floating_point_abs.rs
+++ b/src/tools/clippy/tests/ui/floating_point_abs.rs
@@ -1,6 +1,12 @@
 // run-rustfix
+#![feature(const_fn_floating_point_arithmetic)]
 #![warn(clippy::suboptimal_flops)]
 
+/// Allow suboptimal ops in constant context
+pub const fn in_const_context(num: f64) -> f64 {
+    if num >= 0.0 { num } else { -num }
+}
+
 struct A {
     a: f64,
     b: f64,
diff --git a/src/tools/clippy/tests/ui/floating_point_abs.stderr b/src/tools/clippy/tests/ui/floating_point_abs.stderr
index 35af70201fa..db8290423ae 100644
--- a/src/tools/clippy/tests/ui/floating_point_abs.stderr
+++ b/src/tools/clippy/tests/ui/floating_point_abs.stderr
@@ -1,5 +1,5 @@
 error: manual implementation of `abs` method
-  --> $DIR/floating_point_abs.rs:10:5
+  --> $DIR/floating_point_abs.rs:16:5
    |
 LL |     if num >= 0.0 { num } else { -num }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.abs()`
@@ -7,43 +7,43 @@ LL |     if num >= 0.0 { num } else { -num }
    = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
 
 error: manual implementation of `abs` method
-  --> $DIR/floating_point_abs.rs:14:5
+  --> $DIR/floating_point_abs.rs:20:5
    |
 LL |     if 0.0 < num { num } else { -num }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.abs()`
 
 error: manual implementation of `abs` method
-  --> $DIR/floating_point_abs.rs:18:5
+  --> $DIR/floating_point_abs.rs:24:5
    |
 LL |     if a.a > 0.0 { a.a } else { -a.a }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.a.abs()`
 
 error: manual implementation of `abs` method
-  --> $DIR/floating_point_abs.rs:22:5
+  --> $DIR/floating_point_abs.rs:28:5
    |
 LL |     if 0.0 >= num { -num } else { num }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.abs()`
 
 error: manual implementation of `abs` method
-  --> $DIR/floating_point_abs.rs:26:5
+  --> $DIR/floating_point_abs.rs:32:5
    |
 LL |     if a.a < 0.0 { -a.a } else { a.a }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.a.abs()`
 
 error: manual implementation of negation of `abs` method
-  --> $DIR/floating_point_abs.rs:30:5
+  --> $DIR/floating_point_abs.rs:36:5
    |
 LL |     if num < 0.0 { num } else { -num }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-num.abs()`
 
 error: manual implementation of negation of `abs` method
-  --> $DIR/floating_point_abs.rs:34:5
+  --> $DIR/floating_point_abs.rs:40:5
    |
 LL |     if 0.0 >= num { num } else { -num }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-num.abs()`
 
 error: manual implementation of negation of `abs` method
-  --> $DIR/floating_point_abs.rs:39:12
+  --> $DIR/floating_point_abs.rs:45:12
    |
 LL |         a: if a.a >= 0.0 { -a.a } else { a.a },
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-a.a.abs()`
diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.fixed b/src/tools/clippy/tests/ui/floating_point_mul_add.fixed
index 911700bab00..169ec02f82b 100644
--- a/src/tools/clippy/tests/ui/floating_point_mul_add.fixed
+++ b/src/tools/clippy/tests/ui/floating_point_mul_add.fixed
@@ -1,6 +1,17 @@
 // run-rustfix
+#![feature(const_fn_floating_point_arithmetic)]
 #![warn(clippy::suboptimal_flops)]
 
+/// Allow suboptimal_ops in constant context
+pub const fn in_const_context() {
+    let a: f64 = 1234.567;
+    let b: f64 = 45.67834;
+    let c: f64 = 0.0004;
+
+    let _ = a * b + c;
+    let _ = c + a * b;
+}
+
 fn main() {
     let a: f64 = 1234.567;
     let b: f64 = 45.67834;
diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.rs b/src/tools/clippy/tests/ui/floating_point_mul_add.rs
index d202385fc8a..5338d4fc2b7 100644
--- a/src/tools/clippy/tests/ui/floating_point_mul_add.rs
+++ b/src/tools/clippy/tests/ui/floating_point_mul_add.rs
@@ -1,6 +1,17 @@
 // run-rustfix
+#![feature(const_fn_floating_point_arithmetic)]
 #![warn(clippy::suboptimal_flops)]
 
+/// Allow suboptimal_ops in constant context
+pub const fn in_const_context() {
+    let a: f64 = 1234.567;
+    let b: f64 = 45.67834;
+    let c: f64 = 0.0004;
+
+    let _ = a * b + c;
+    let _ = c + a * b;
+}
+
 fn main() {
     let a: f64 = 1234.567;
     let b: f64 = 45.67834;
diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.stderr b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr
index ac8d0c0cae0..e637bbf90ca 100644
--- a/src/tools/clippy/tests/ui/floating_point_mul_add.stderr
+++ b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr
@@ -1,5 +1,5 @@
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> $DIR/floating_point_mul_add.rs:10:13
+  --> $DIR/floating_point_mul_add.rs:21:13
    |
 LL |     let _ = a * b + c;
    |             ^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
@@ -7,55 +7,55 @@ LL |     let _ = a * b + c;
    = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> $DIR/floating_point_mul_add.rs:11:13
+  --> $DIR/floating_point_mul_add.rs:22:13
    |
 LL |     let _ = c + a * b;
    |             ^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> $DIR/floating_point_mul_add.rs:12:13
+  --> $DIR/floating_point_mul_add.rs:23:13
    |
 LL |     let _ = a + 2.0 * 4.0;
    |             ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, a)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> $DIR/floating_point_mul_add.rs:13:13
+  --> $DIR/floating_point_mul_add.rs:24:13
    |
 LL |     let _ = a + 2. * 4.;
    |             ^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4., a)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> $DIR/floating_point_mul_add.rs:15:13
+  --> $DIR/floating_point_mul_add.rs:26:13
    |
 LL |     let _ = (a * b) + c;
    |             ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> $DIR/floating_point_mul_add.rs:16:13
+  --> $DIR/floating_point_mul_add.rs:27:13
    |
 LL |     let _ = c + (a * b);
    |             ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> $DIR/floating_point_mul_add.rs:17:13
+  --> $DIR/floating_point_mul_add.rs:28:13
    |
 LL |     let _ = a * b * c + d;
    |             ^^^^^^^^^^^^^ help: consider using: `(a * b).mul_add(c, d)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> $DIR/floating_point_mul_add.rs:19:13
+  --> $DIR/floating_point_mul_add.rs:30:13
    |
 LL |     let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c))`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> $DIR/floating_point_mul_add.rs:20:13
+  --> $DIR/floating_point_mul_add.rs:31:13
    |
 LL |     let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> $DIR/floating_point_mul_add.rs:22:13
+  --> $DIR/floating_point_mul_add.rs:33:13
    |
 LL |     let _ = (a * a + b).sqrt();
    |             ^^^^^^^^^^^ help: consider using: `a.mul_add(a, b)`
diff --git a/src/tools/clippy/tests/ui/floating_point_rad.fixed b/src/tools/clippy/tests/ui/floating_point_rad.fixed
index 92480c5db8b..a35bb1c27f3 100644
--- a/src/tools/clippy/tests/ui/floating_point_rad.fixed
+++ b/src/tools/clippy/tests/ui/floating_point_rad.fixed
@@ -1,6 +1,13 @@
 // run-rustfix
+#![feature(const_fn_floating_point_arithmetic)]
 #![warn(clippy::suboptimal_flops)]
 
+/// Allow suboptimal_flops in constant context
+pub const fn const_context() {
+    let x = 3f32;
+    let _ = x * 180f32 / std::f32::consts::PI;
+}
+
 fn main() {
     let x = 3f32;
     let _ = x.to_degrees();
diff --git a/src/tools/clippy/tests/ui/floating_point_rad.rs b/src/tools/clippy/tests/ui/floating_point_rad.rs
index 062e7c3fdc1..834db4be533 100644
--- a/src/tools/clippy/tests/ui/floating_point_rad.rs
+++ b/src/tools/clippy/tests/ui/floating_point_rad.rs
@@ -1,6 +1,13 @@
 // run-rustfix
+#![feature(const_fn_floating_point_arithmetic)]
 #![warn(clippy::suboptimal_flops)]
 
+/// Allow suboptimal_flops in constant context
+pub const fn const_context() {
+    let x = 3f32;
+    let _ = x * 180f32 / std::f32::consts::PI;
+}
+
 fn main() {
     let x = 3f32;
     let _ = x * 180f32 / std::f32::consts::PI;
diff --git a/src/tools/clippy/tests/ui/floating_point_rad.stderr b/src/tools/clippy/tests/ui/floating_point_rad.stderr
index a6ffdca64ee..acecddbca53 100644
--- a/src/tools/clippy/tests/ui/floating_point_rad.stderr
+++ b/src/tools/clippy/tests/ui/floating_point_rad.stderr
@@ -1,5 +1,5 @@
 error: conversion to degrees can be done more accurately
-  --> $DIR/floating_point_rad.rs:6:13
+  --> $DIR/floating_point_rad.rs:13:13
    |
 LL |     let _ = x * 180f32 / std::f32::consts::PI;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_degrees()`
@@ -7,7 +7,7 @@ LL |     let _ = x * 180f32 / std::f32::consts::PI;
    = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
 
 error: conversion to radians can be done more accurately
-  --> $DIR/floating_point_rad.rs:7:13
+  --> $DIR/floating_point_rad.rs:14:13
    |
 LL |     let _ = x * std::f32::consts::PI / 180f32;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_radians()`
diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.rs b/src/tools/clippy/tests/ui/if_then_some_else_none.rs
index 54789bf9320..3bc3a039524 100644
--- a/src/tools/clippy/tests/ui/if_then_some_else_none.rs
+++ b/src/tools/clippy/tests/ui/if_then_some_else_none.rs
@@ -102,3 +102,14 @@ fn into_some<T>(v: T) -> Option<T> {
 fn into_none<T>() -> Option<T> {
     None
 }
+
+// Should not warn
+fn f(b: bool, v: Option<()>) -> Option<()> {
+    if b {
+        v?; // This is a potential early return, is not equivalent with `bool::then`
+
+        Some(())
+    } else {
+        None
+    }
+}
diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs
new file mode 100644
index 00000000000..c2c0c520dc6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.rs
@@ -0,0 +1,166 @@
+#![deny(clippy::index_refutable_slice)]
+
+enum SomeEnum<T> {
+    One(T),
+    Two(T),
+    Three(T),
+    Four(T),
+}
+
+fn lintable_examples() {
+    // Try with reference
+    let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+    if let Some(slice) = slice {
+        println!("{}", slice[0]);
+    }
+
+    // Try with copy
+    let slice: Option<[u32; 3]> = Some([1, 2, 3]);
+    if let Some(slice) = slice {
+        println!("{}", slice[0]);
+    }
+
+    // Try with long slice and small indices
+    let slice: Option<[u32; 9]> = Some([1, 2, 3, 4, 5, 6, 7, 8, 9]);
+    if let Some(slice) = slice {
+        println!("{}", slice[2]);
+        println!("{}", slice[0]);
+    }
+
+    // Multiple bindings
+    let slice_wrapped: SomeEnum<[u32; 3]> = SomeEnum::One([5, 6, 7]);
+    if let SomeEnum::One(slice) | SomeEnum::Three(slice) = slice_wrapped {
+        println!("{}", slice[0]);
+    }
+
+    // Two lintable slices in one if let
+    let a_wrapped: SomeEnum<[u32; 3]> = SomeEnum::One([9, 5, 1]);
+    let b_wrapped: Option<[u32; 2]> = Some([4, 6]);
+    if let (SomeEnum::Three(a), Some(b)) = (a_wrapped, b_wrapped) {
+        println!("{} -> {}", a[2], b[1]);
+    }
+
+    // This requires the slice values to be borrowed as the slice values can only be
+    // borrowed and `String` doesn't implement copy
+    let slice: Option<[String; 2]> = Some([String::from("1"), String::from("2")]);
+    if let Some(ref slice) = slice {
+        println!("{:?}", slice[1]);
+    }
+    println!("{:?}", slice);
+
+    // This should not suggest using the `ref` keyword as the scrutinee is already
+    // a reference
+    let slice: Option<[String; 2]> = Some([String::from("1"), String::from("2")]);
+    if let Some(slice) = &slice {
+        println!("{:?}", slice[0]);
+    }
+    println!("{:?}", slice);
+}
+
+fn slice_index_above_limit() {
+    let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+
+    if let Some(slice) = slice {
+        // Would cause a panic, IDK
+        println!("{}", slice[7]);
+    }
+}
+
+fn slice_is_used() {
+    let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+    if let Some(slice) = slice {
+        println!("{:?}", slice.len());
+    }
+
+    let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+    if let Some(slice) = slice {
+        println!("{:?}", slice.to_vec());
+    }
+
+    let opt: Option<[String; 2]> = Some([String::from("Hello"), String::from("world")]);
+    if let Some(slice) = opt {
+        if !slice.is_empty() {
+            println!("first: {}", slice[0]);
+        }
+    }
+}
+
+/// The slice is used by an external function and should therefore not be linted
+fn check_slice_as_arg() {
+    fn is_interesting<T>(slice: &[T; 2]) -> bool {
+        !slice.is_empty()
+    }
+
+    let slice_wrapped: Option<[String; 2]> = Some([String::from("Hello"), String::from("world")]);
+    if let Some(slice) = &slice_wrapped {
+        if is_interesting(slice) {
+            println!("This is interesting {}", slice[0]);
+        }
+    }
+    println!("{:?}", slice_wrapped);
+}
+
+fn check_slice_in_struct() {
+    #[derive(Debug)]
+    struct Wrapper<'a> {
+        inner: Option<&'a [String]>,
+        is_awesome: bool,
+    }
+
+    impl<'a> Wrapper<'a> {
+        fn is_super_awesome(&self) -> bool {
+            self.is_awesome
+        }
+    }
+
+    let inner = &[String::from("New"), String::from("World")];
+    let wrap = Wrapper {
+        inner: Some(inner),
+        is_awesome: true,
+    };
+
+    // Test 1: Field access
+    if let Some(slice) = wrap.inner {
+        if wrap.is_awesome {
+            println!("This is awesome! {}", slice[0]);
+        }
+    }
+
+    // Test 2: function access
+    if let Some(slice) = wrap.inner {
+        if wrap.is_super_awesome() {
+            println!("This is super awesome! {}", slice[0]);
+        }
+    }
+    println!("Complete wrap: {:?}", wrap);
+}
+
+/// This would be a nice additional feature to have in the future, but adding it
+/// now would make the PR too large. This is therefore only a test that we don't
+/// lint cases we can't make a reasonable suggestion for
+fn mutable_slice_index() {
+    // Mut access
+    let mut slice: Option<[String; 1]> = Some([String::from("Penguin")]);
+    if let Some(ref mut slice) = slice {
+        slice[0] = String::from("Mr. Penguin");
+    }
+    println!("Use after modification: {:?}", slice);
+
+    // Mut access on reference
+    let mut slice: Option<[String; 1]> = Some([String::from("Cat")]);
+    if let Some(slice) = &mut slice {
+        slice[0] = String::from("Lord Meow Meow");
+    }
+    println!("Use after modification: {:?}", slice);
+}
+
+/// The lint will ignore bindings with sub patterns as it would be hard
+/// to build correct suggestions for these instances :)
+fn binding_with_sub_pattern() {
+    let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+    if let Some(slice @ [_, _, _]) = slice {
+        println!("{:?}", slice[2]);
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.stderr b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.stderr
new file mode 100644
index 00000000000..a607df9b876
--- /dev/null
+++ b/src/tools/clippy/tests/ui/index_refutable_slice/if_let_slice_binding.stderr
@@ -0,0 +1,158 @@
+error: this binding can be a slice pattern to avoid indexing
+  --> $DIR/if_let_slice_binding.rs:13:17
+   |
+LL |     if let Some(slice) = slice {
+   |                 ^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/if_let_slice_binding.rs:1:9
+   |
+LL | #![deny(clippy::index_refutable_slice)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try using a slice pattern here
+   |
+LL |     if let Some([slice_0, ..]) = slice {
+   |                 ~~~~~~~~~~~~~
+help: and replace the index expressions here
+   |
+LL |         println!("{}", slice_0);
+   |                        ~~~~~~~
+
+error: this binding can be a slice pattern to avoid indexing
+  --> $DIR/if_let_slice_binding.rs:19:17
+   |
+LL |     if let Some(slice) = slice {
+   |                 ^^^^^
+   |
+help: try using a slice pattern here
+   |
+LL |     if let Some([slice_0, ..]) = slice {
+   |                 ~~~~~~~~~~~~~
+help: and replace the index expressions here
+   |
+LL |         println!("{}", slice_0);
+   |                        ~~~~~~~
+
+error: this binding can be a slice pattern to avoid indexing
+  --> $DIR/if_let_slice_binding.rs:25:17
+   |
+LL |     if let Some(slice) = slice {
+   |                 ^^^^^
+   |
+help: try using a slice pattern here
+   |
+LL |     if let Some([slice_0, _, slice_2, ..]) = slice {
+   |                 ~~~~~~~~~~~~~~~~~~~~~~~~~
+help: and replace the index expressions here
+   |
+LL ~         println!("{}", slice_2);
+LL ~         println!("{}", slice_0);
+   |
+
+error: this binding can be a slice pattern to avoid indexing
+  --> $DIR/if_let_slice_binding.rs:32:26
+   |
+LL |     if let SomeEnum::One(slice) | SomeEnum::Three(slice) = slice_wrapped {
+   |                          ^^^^^
+   |
+help: try using a slice pattern here
+   |
+LL |     if let SomeEnum::One([slice_0, ..]) | SomeEnum::Three([slice_0, ..]) = slice_wrapped {
+   |                          ~~~~~~~~~~~~~                    ~~~~~~~~~~~~~
+help: and replace the index expressions here
+   |
+LL |         println!("{}", slice_0);
+   |                        ~~~~~~~
+
+error: this binding can be a slice pattern to avoid indexing
+  --> $DIR/if_let_slice_binding.rs:39:29
+   |
+LL |     if let (SomeEnum::Three(a), Some(b)) = (a_wrapped, b_wrapped) {
+   |                             ^
+   |
+help: try using a slice pattern here
+   |
+LL |     if let (SomeEnum::Three([_, _, a_2, ..]), Some(b)) = (a_wrapped, b_wrapped) {
+   |                             ~~~~~~~~~~~~~~~
+help: and replace the index expressions here
+   |
+LL |         println!("{} -> {}", a_2, b[1]);
+   |                              ~~~
+
+error: this binding can be a slice pattern to avoid indexing
+  --> $DIR/if_let_slice_binding.rs:39:38
+   |
+LL |     if let (SomeEnum::Three(a), Some(b)) = (a_wrapped, b_wrapped) {
+   |                                      ^
+   |
+help: try using a slice pattern here
+   |
+LL |     if let (SomeEnum::Three(a), Some([_, b_1, ..])) = (a_wrapped, b_wrapped) {
+   |                                      ~~~~~~~~~~~~
+help: and replace the index expressions here
+   |
+LL |         println!("{} -> {}", a[2], b_1);
+   |                                    ~~~
+
+error: this binding can be a slice pattern to avoid indexing
+  --> $DIR/if_let_slice_binding.rs:46:21
+   |
+LL |     if let Some(ref slice) = slice {
+   |                     ^^^^^
+   |
+help: try using a slice pattern here
+   |
+LL |     if let Some([_, ref slice_1, ..]) = slice {
+   |                 ~~~~~~~~~~~~~~~~~~~~
+help: and replace the index expressions here
+   |
+LL |         println!("{:?}", slice_1);
+   |                          ~~~~~~~
+
+error: this binding can be a slice pattern to avoid indexing
+  --> $DIR/if_let_slice_binding.rs:54:17
+   |
+LL |     if let Some(slice) = &slice {
+   |                 ^^^^^
+   |
+help: try using a slice pattern here
+   |
+LL |     if let Some([slice_0, ..]) = &slice {
+   |                 ~~~~~~~~~~~~~
+help: and replace the index expressions here
+   |
+LL |         println!("{:?}", slice_0);
+   |                          ~~~~~~~
+
+error: this binding can be a slice pattern to avoid indexing
+  --> $DIR/if_let_slice_binding.rs:123:17
+   |
+LL |     if let Some(slice) = wrap.inner {
+   |                 ^^^^^
+   |
+help: try using a slice pattern here
+   |
+LL |     if let Some([slice_0, ..]) = wrap.inner {
+   |                 ~~~~~~~~~~~~~
+help: and replace the index expressions here
+   |
+LL |             println!("This is awesome! {}", slice_0);
+   |                                             ~~~~~~~
+
+error: this binding can be a slice pattern to avoid indexing
+  --> $DIR/if_let_slice_binding.rs:130:17
+   |
+LL |     if let Some(slice) = wrap.inner {
+   |                 ^^^^^
+   |
+help: try using a slice pattern here
+   |
+LL |     if let Some([slice_0, ..]) = wrap.inner {
+   |                 ~~~~~~~~~~~~~
+help: and replace the index expressions here
+   |
+LL |             println!("This is super awesome! {}", slice_0);
+   |                                                   ~~~~~~~
+
+error: aborting due to 10 previous errors
+
diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs b/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs
new file mode 100644
index 00000000000..406e82083f8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.rs
@@ -0,0 +1,28 @@
+#![deny(clippy::index_refutable_slice)]
+
+extern crate if_chain;
+use if_chain::if_chain;
+
+macro_rules! if_let_slice_macro {
+    () => {
+        // This would normally be linted
+        let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+        if let Some(slice) = slice {
+            println!("{}", slice[0]);
+        }
+    };
+}
+
+fn main() {
+    // Don't lint this
+    if_let_slice_macro!();
+
+    // Do lint this
+    if_chain! {
+        let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+        if let Some(slice) = slice;
+        then {
+            println!("{}", slice[0]);
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr b/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr
new file mode 100644
index 00000000000..11b19428b4f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/index_refutable_slice/slice_indexing_in_macro.stderr
@@ -0,0 +1,22 @@
+error: this binding can be a slice pattern to avoid indexing
+  --> $DIR/slice_indexing_in_macro.rs:23:21
+   |
+LL |         if let Some(slice) = slice;
+   |                     ^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/slice_indexing_in_macro.rs:1:9
+   |
+LL | #![deny(clippy::index_refutable_slice)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try using a slice pattern here
+   |
+LL |         if let Some([slice_0, ..]) = slice;
+   |                     ~~~~~~~~~~~~~
+help: and replace the index expressions here
+   |
+LL |             println!("{}", slice_0);
+   |                            ~~~~~~~
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/iter_cloned_collect.fixed b/src/tools/clippy/tests/ui/iter_cloned_collect.fixed
index 39cc58cd298..9b862133580 100644
--- a/src/tools/clippy/tests/ui/iter_cloned_collect.fixed
+++ b/src/tools/clippy/tests/ui/iter_cloned_collect.fixed
@@ -23,4 +23,7 @@ fn main() {
     // Issue #6808
     let arr: [u8; 64] = [0; 64];
     let _: Vec<_> = arr.to_vec();
+
+    // Issue #6703
+    let _: Vec<isize> = v.to_vec();
 }
diff --git a/src/tools/clippy/tests/ui/iter_cloned_collect.rs b/src/tools/clippy/tests/ui/iter_cloned_collect.rs
index c2a036ec09f..639f50665f2 100644
--- a/src/tools/clippy/tests/ui/iter_cloned_collect.rs
+++ b/src/tools/clippy/tests/ui/iter_cloned_collect.rs
@@ -26,4 +26,7 @@ fn main() {
     // Issue #6808
     let arr: [u8; 64] = [0; 64];
     let _: Vec<_> = arr.iter().cloned().collect();
+
+    // Issue #6703
+    let _: Vec<isize> = v.iter().copied().collect();
 }
diff --git a/src/tools/clippy/tests/ui/iter_cloned_collect.stderr b/src/tools/clippy/tests/ui/iter_cloned_collect.stderr
index e1df61794ce..b2cc497bf43 100644
--- a/src/tools/clippy/tests/ui/iter_cloned_collect.stderr
+++ b/src/tools/clippy/tests/ui/iter_cloned_collect.stderr
@@ -28,5 +28,11 @@ error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling
 LL |     let _: Vec<_> = arr.iter().cloned().collect();
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()`
 
-error: aborting due to 4 previous errors
+error: called `iter().copied().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable
+  --> $DIR/iter_cloned_collect.rs:31:26
+   |
+LL |     let _: Vec<isize> = v.iter().copied().collect();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()`
+
+error: aborting due to 5 previous errors
 
diff --git a/src/tools/clippy/tests/ui/let_if_seq.rs b/src/tools/clippy/tests/ui/let_if_seq.rs
index 2d8f3c2f0e7..c5cb2eb1fe1 100644
--- a/src/tools/clippy/tests/ui/let_if_seq.rs
+++ b/src/tools/clippy/tests/ui/let_if_seq.rs
@@ -3,7 +3,8 @@
     unused_assignments,
     clippy::similar_names,
     clippy::blacklisted_name,
-    clippy::branches_sharing_code
+    clippy::branches_sharing_code,
+    clippy::needless_late_init
 )]
 #![warn(clippy::useless_let_if_seq)]
 
diff --git a/src/tools/clippy/tests/ui/let_if_seq.stderr b/src/tools/clippy/tests/ui/let_if_seq.stderr
index 9cf2e10a5ee..271ccce681c 100644
--- a/src/tools/clippy/tests/ui/let_if_seq.stderr
+++ b/src/tools/clippy/tests/ui/let_if_seq.stderr
@@ -1,5 +1,5 @@
 error: `if _ { .. } else { .. }` is an expression
-  --> $DIR/let_if_seq.rs:65:5
+  --> $DIR/let_if_seq.rs:66:5
    |
 LL | /     let mut foo = 0;
 LL | |     if f() {
@@ -11,7 +11,7 @@ LL | |     }
    = note: you might not need `mut` at all
 
 error: `if _ { .. } else { .. }` is an expression
-  --> $DIR/let_if_seq.rs:70:5
+  --> $DIR/let_if_seq.rs:71:5
    |
 LL | /     let mut bar = 0;
 LL | |     if f() {
@@ -25,7 +25,7 @@ LL | |     }
    = note: you might not need `mut` at all
 
 error: `if _ { .. } else { .. }` is an expression
-  --> $DIR/let_if_seq.rs:78:5
+  --> $DIR/let_if_seq.rs:79:5
    |
 LL | /     let quz;
 LL | |     if f() {
@@ -36,7 +36,7 @@ LL | |     }
    | |_____^ help: it is more idiomatic to write: `let quz = if f() { 42 } else { 0 };`
 
 error: `if _ { .. } else { .. }` is an expression
-  --> $DIR/let_if_seq.rs:107:5
+  --> $DIR/let_if_seq.rs:108:5
    |
 LL | /     let mut baz = 0;
 LL | |     if f() {
diff --git a/src/tools/clippy/tests/ui/let_underscore_lock.rs b/src/tools/clippy/tests/ui/let_underscore_lock.rs
index 88fb216a743..539d74d1d4c 100644
--- a/src/tools/clippy/tests/ui/let_underscore_lock.rs
+++ b/src/tools/clippy/tests/ui/let_underscore_lock.rs
@@ -1,5 +1,7 @@
 #![warn(clippy::let_underscore_lock)]
 
+extern crate parking_lot;
+
 fn main() {
     let m = std::sync::Mutex::new(());
     let rw = std::sync::RwLock::new(());
@@ -10,4 +12,16 @@ fn main() {
     let _ = m.try_lock();
     let _ = rw.try_read();
     let _ = rw.try_write();
+
+    use parking_lot::{lock_api::RawMutex, Mutex, RwLock};
+
+    let p_m: Mutex<()> = Mutex::const_new(RawMutex::INIT, ());
+    let _ = p_m.lock();
+
+    let p_m1 = Mutex::new(0);
+    let _ = p_m1.lock();
+
+    let p_rw = RwLock::new(0);
+    let _ = p_rw.read();
+    let _ = p_rw.write();
 }
diff --git a/src/tools/clippy/tests/ui/let_underscore_lock.stderr b/src/tools/clippy/tests/ui/let_underscore_lock.stderr
index 5d5f6059ef1..3a2bc17bf7b 100644
--- a/src/tools/clippy/tests/ui/let_underscore_lock.stderr
+++ b/src/tools/clippy/tests/ui/let_underscore_lock.stderr
@@ -1,5 +1,5 @@
 error: non-binding let on a synchronization lock
-  --> $DIR/let_underscore_lock.rs:7:5
+  --> $DIR/let_underscore_lock.rs:9:5
    |
 LL |     let _ = m.lock();
    |     ^^^^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL |     let _ = m.lock();
    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 
 error: non-binding let on a synchronization lock
-  --> $DIR/let_underscore_lock.rs:8:5
+  --> $DIR/let_underscore_lock.rs:10:5
    |
 LL |     let _ = rw.read();
    |     ^^^^^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL |     let _ = rw.read();
    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 
 error: non-binding let on a synchronization lock
-  --> $DIR/let_underscore_lock.rs:9:5
+  --> $DIR/let_underscore_lock.rs:11:5
    |
 LL |     let _ = rw.write();
    |     ^^^^^^^^^^^^^^^^^^^
@@ -24,7 +24,7 @@ LL |     let _ = rw.write();
    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 
 error: non-binding let on a synchronization lock
-  --> $DIR/let_underscore_lock.rs:10:5
+  --> $DIR/let_underscore_lock.rs:12:5
    |
 LL |     let _ = m.try_lock();
    |     ^^^^^^^^^^^^^^^^^^^^^
@@ -32,7 +32,7 @@ LL |     let _ = m.try_lock();
    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 
 error: non-binding let on a synchronization lock
-  --> $DIR/let_underscore_lock.rs:11:5
+  --> $DIR/let_underscore_lock.rs:13:5
    |
 LL |     let _ = rw.try_read();
    |     ^^^^^^^^^^^^^^^^^^^^^^
@@ -40,12 +40,44 @@ LL |     let _ = rw.try_read();
    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 
 error: non-binding let on a synchronization lock
-  --> $DIR/let_underscore_lock.rs:12:5
+  --> $DIR/let_underscore_lock.rs:14:5
    |
 LL |     let _ = rw.try_write();
    |     ^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 
-error: aborting due to 6 previous errors
+error: non-binding let on a synchronization lock
+  --> $DIR/let_underscore_lock.rs:19:5
+   |
+LL |     let _ = p_m.lock();
+   |     ^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: non-binding let on a synchronization lock
+  --> $DIR/let_underscore_lock.rs:22:5
+   |
+LL |     let _ = p_m1.lock();
+   |     ^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: non-binding let on a synchronization lock
+  --> $DIR/let_underscore_lock.rs:25:5
+   |
+LL |     let _ = p_rw.read();
+   |     ^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: non-binding let on a synchronization lock
+  --> $DIR/let_underscore_lock.rs:26:5
+   |
+LL |     let _ = p_rw.write();
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: aborting due to 10 previous errors
 
diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed b/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed
index 11fe06c5724..6c2a25c37d8 100644
--- a/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed
+++ b/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed
@@ -2,7 +2,9 @@
 // [edition2018] edition:2018
 // [edition2021] edition:2021
 // run-rustfix
+
 #![warn(clippy::manual_assert)]
+#![allow(clippy::nonminimal_bool)]
 
 fn main() {
     let a = vec![1, 2, 3];
diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr b/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr
index 03c03472f90..77511631e44 100644
--- a/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr
+++ b/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr
@@ -1,5 +1,5 @@
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:22:5
+  --> $DIR/manual_assert.rs:24:5
    |
 LL | /     if !a.is_empty() {
 LL | |         panic!("qaqaq{:?}", a);
@@ -9,7 +9,7 @@ LL | |     }
    = note: `-D clippy::manual-assert` implied by `-D warnings`
 
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:25:5
+  --> $DIR/manual_assert.rs:27:5
    |
 LL | /     if !a.is_empty() {
 LL | |         panic!("qwqwq");
@@ -17,7 +17,7 @@ LL | |     }
    | |_____^ help: try: `assert!(a.is_empty(), "qwqwq");`
 
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:42:5
+  --> $DIR/manual_assert.rs:44:5
    |
 LL | /     if b.is_empty() {
 LL | |         panic!("panic1");
@@ -25,7 +25,7 @@ LL | |     }
    | |_____^ help: try: `assert!(!b.is_empty(), "panic1");`
 
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:45:5
+  --> $DIR/manual_assert.rs:47:5
    |
 LL | /     if b.is_empty() && a.is_empty() {
 LL | |         panic!("panic2");
@@ -33,7 +33,7 @@ LL | |     }
    | |_____^ help: try: `assert!(!(b.is_empty() && a.is_empty()), "panic2");`
 
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:48:5
+  --> $DIR/manual_assert.rs:50:5
    |
 LL | /     if a.is_empty() && !b.is_empty() {
 LL | |         panic!("panic3");
@@ -41,7 +41,7 @@ LL | |     }
    | |_____^ help: try: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");`
 
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:51:5
+  --> $DIR/manual_assert.rs:53:5
    |
 LL | /     if b.is_empty() || a.is_empty() {
 LL | |         panic!("panic4");
@@ -49,7 +49,7 @@ LL | |     }
    | |_____^ help: try: `assert!(!(b.is_empty() || a.is_empty()), "panic4");`
 
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:54:5
+  --> $DIR/manual_assert.rs:56:5
    |
 LL | /     if a.is_empty() || !b.is_empty() {
 LL | |         panic!("panic5");
diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2021.fixed b/src/tools/clippy/tests/ui/manual_assert.edition2021.fixed
index 11fe06c5724..6c2a25c37d8 100644
--- a/src/tools/clippy/tests/ui/manual_assert.edition2021.fixed
+++ b/src/tools/clippy/tests/ui/manual_assert.edition2021.fixed
@@ -2,7 +2,9 @@
 // [edition2018] edition:2018
 // [edition2021] edition:2021
 // run-rustfix
+
 #![warn(clippy::manual_assert)]
+#![allow(clippy::nonminimal_bool)]
 
 fn main() {
     let a = vec![1, 2, 3];
diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2021.stderr b/src/tools/clippy/tests/ui/manual_assert.edition2021.stderr
index 03c03472f90..77511631e44 100644
--- a/src/tools/clippy/tests/ui/manual_assert.edition2021.stderr
+++ b/src/tools/clippy/tests/ui/manual_assert.edition2021.stderr
@@ -1,5 +1,5 @@
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:22:5
+  --> $DIR/manual_assert.rs:24:5
    |
 LL | /     if !a.is_empty() {
 LL | |         panic!("qaqaq{:?}", a);
@@ -9,7 +9,7 @@ LL | |     }
    = note: `-D clippy::manual-assert` implied by `-D warnings`
 
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:25:5
+  --> $DIR/manual_assert.rs:27:5
    |
 LL | /     if !a.is_empty() {
 LL | |         panic!("qwqwq");
@@ -17,7 +17,7 @@ LL | |     }
    | |_____^ help: try: `assert!(a.is_empty(), "qwqwq");`
 
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:42:5
+  --> $DIR/manual_assert.rs:44:5
    |
 LL | /     if b.is_empty() {
 LL | |         panic!("panic1");
@@ -25,7 +25,7 @@ LL | |     }
    | |_____^ help: try: `assert!(!b.is_empty(), "panic1");`
 
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:45:5
+  --> $DIR/manual_assert.rs:47:5
    |
 LL | /     if b.is_empty() && a.is_empty() {
 LL | |         panic!("panic2");
@@ -33,7 +33,7 @@ LL | |     }
    | |_____^ help: try: `assert!(!(b.is_empty() && a.is_empty()), "panic2");`
 
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:48:5
+  --> $DIR/manual_assert.rs:50:5
    |
 LL | /     if a.is_empty() && !b.is_empty() {
 LL | |         panic!("panic3");
@@ -41,7 +41,7 @@ LL | |     }
    | |_____^ help: try: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");`
 
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:51:5
+  --> $DIR/manual_assert.rs:53:5
    |
 LL | /     if b.is_empty() || a.is_empty() {
 LL | |         panic!("panic4");
@@ -49,7 +49,7 @@ LL | |     }
    | |_____^ help: try: `assert!(!(b.is_empty() || a.is_empty()), "panic4");`
 
 error: only a `panic!` in `if`-then statement
-  --> $DIR/manual_assert.rs:54:5
+  --> $DIR/manual_assert.rs:56:5
    |
 LL | /     if a.is_empty() || !b.is_empty() {
 LL | |         panic!("panic5");
diff --git a/src/tools/clippy/tests/ui/manual_assert.fixed b/src/tools/clippy/tests/ui/manual_assert.fixed
index 11fe06c5724..6c2a25c37d8 100644
--- a/src/tools/clippy/tests/ui/manual_assert.fixed
+++ b/src/tools/clippy/tests/ui/manual_assert.fixed
@@ -2,7 +2,9 @@
 // [edition2018] edition:2018
 // [edition2021] edition:2021
 // run-rustfix
+
 #![warn(clippy::manual_assert)]
+#![allow(clippy::nonminimal_bool)]
 
 fn main() {
     let a = vec![1, 2, 3];
diff --git a/src/tools/clippy/tests/ui/manual_assert.rs b/src/tools/clippy/tests/ui/manual_assert.rs
index 8713426fc88..d3e0897488f 100644
--- a/src/tools/clippy/tests/ui/manual_assert.rs
+++ b/src/tools/clippy/tests/ui/manual_assert.rs
@@ -2,7 +2,9 @@
 // [edition2018] edition:2018
 // [edition2021] edition:2021
 // run-rustfix
+
 #![warn(clippy::manual_assert)]
+#![allow(clippy::nonminimal_bool)]
 
 fn main() {
     let a = vec![1, 2, 3];
diff --git a/src/tools/clippy/tests/ui/manual_map_option_2.fixed b/src/tools/clippy/tests/ui/manual_map_option_2.fixed
index 8cc12149403..ebf3f8cabd4 100644
--- a/src/tools/clippy/tests/ui/manual_map_option_2.fixed
+++ b/src/tools/clippy/tests/ui/manual_map_option_2.fixed
@@ -47,4 +47,14 @@ fn main() {
     let _ = s.as_ref().map(|x| {
             if let Some(ref s) = s { (x.clone(), s) } else { panic!() }
         });
+
+    // Issue #7820
+    unsafe fn f(x: u32) -> u32 {
+        x
+    }
+    unsafe {
+        let _ = Some(0).map(|x| f(x));
+    }
+    let _ = Some(0).map(|x| unsafe { f(x) });
+    let _ = Some(0).map(|x| unsafe { f(x) });
 }
diff --git a/src/tools/clippy/tests/ui/manual_map_option_2.rs b/src/tools/clippy/tests/ui/manual_map_option_2.rs
index 0862b201ead..1382d9af0aa 100644
--- a/src/tools/clippy/tests/ui/manual_map_option_2.rs
+++ b/src/tools/clippy/tests/ui/manual_map_option_2.rs
@@ -53,4 +53,23 @@ fn main() {
         }),
         None => None,
     };
+
+    // Issue #7820
+    unsafe fn f(x: u32) -> u32 {
+        x
+    }
+    unsafe {
+        let _ = match Some(0) {
+            Some(x) => Some(f(x)),
+            None => None,
+        };
+    }
+    let _ = match Some(0) {
+        Some(x) => unsafe { Some(f(x)) },
+        None => None,
+    };
+    let _ = match Some(0) {
+        Some(x) => Some(unsafe { f(x) }),
+        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
index 711ff6c4a4b..d35b6252fb8 100644
--- a/src/tools/clippy/tests/ui/manual_map_option_2.stderr
+++ b/src/tools/clippy/tests/ui/manual_map_option_2.stderr
@@ -39,5 +39,35 @@ LL +             if let Some(ref s) = s { (x.clone(), s) } else { panic!() }
 LL ~         });
    |
 
-error: aborting due to 2 previous errors
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option_2.rs:62:17
+   |
+LL |           let _ = match Some(0) {
+   |  _________________^
+LL | |             Some(x) => Some(f(x)),
+LL | |             None => None,
+LL | |         };
+   | |_________^ help: try this: `Some(0).map(|x| f(x))`
+
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option_2.rs:67:13
+   |
+LL |       let _ = match Some(0) {
+   |  _____________^
+LL | |         Some(x) => unsafe { Some(f(x)) },
+LL | |         None => None,
+LL | |     };
+   | |_____^ help: try this: `Some(0).map(|x| unsafe { f(x) })`
+
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option_2.rs:71:13
+   |
+LL |       let _ = match Some(0) {
+   |  _____________^
+LL | |         Some(x) => Some(unsafe { f(x) }),
+LL | |         None => None,
+LL | |     };
+   | |_____^ help: try this: `Some(0).map(|x| unsafe { f(x) })`
+
+error: aborting due to 5 previous errors
 
diff --git a/src/tools/clippy/tests/ui/manual_split_once.fixed b/src/tools/clippy/tests/ui/manual_split_once.fixed
index 992baf1f185..d5113df569a 100644
--- a/src/tools/clippy/tests/ui/manual_split_once.fixed
+++ b/src/tools/clippy/tests/ui/manual_split_once.fixed
@@ -2,7 +2,7 @@
 
 #![feature(custom_inner_attributes)]
 #![warn(clippy::manual_split_once)]
-#![allow(clippy::iter_skip_next, clippy::iter_nth_zero)]
+#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::needless_splitn)]
 
 extern crate itertools;
 
@@ -38,8 +38,8 @@ fn main() {
     let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
 
     // `rsplitn` gives the results in the reverse order of `rsplit_once`
-    let _ = "key=value".rsplit_once('=').unwrap().1;
-    let _ = "key=value".rsplit_once('=').map_or("key=value", |x| x.0);
+    let _ = "key=value".rsplitn(2, '=').next().unwrap();
+    let _ = "key=value".rsplit_once('=').unwrap().0;
     let _ = "key=value".rsplit_once('=').map(|x| x.1);
     let (_, _) = "key=value".rsplit_once('=').map(|(x, y)| (y, x)).unwrap();
 }
diff --git a/src/tools/clippy/tests/ui/manual_split_once.rs b/src/tools/clippy/tests/ui/manual_split_once.rs
index 4f92ab6b812..80e02952dbd 100644
--- a/src/tools/clippy/tests/ui/manual_split_once.rs
+++ b/src/tools/clippy/tests/ui/manual_split_once.rs
@@ -2,7 +2,7 @@
 
 #![feature(custom_inner_attributes)]
 #![warn(clippy::manual_split_once)]
-#![allow(clippy::iter_skip_next, clippy::iter_nth_zero)]
+#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::needless_splitn)]
 
 extern crate itertools;
 
diff --git a/src/tools/clippy/tests/ui/manual_split_once.stderr b/src/tools/clippy/tests/ui/manual_split_once.stderr
index 7bea2303d92..af9c7a2d41b 100644
--- a/src/tools/clippy/tests/ui/manual_split_once.stderr
+++ b/src/tools/clippy/tests/ui/manual_split_once.stderr
@@ -73,16 +73,10 @@ LL |         let _ = s.splitn(2, "key=value").skip(1).next()?;
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1`
 
 error: manual implementation of `rsplit_once`
-  --> $DIR/manual_split_once.rs:41:13
-   |
-LL |     let _ = "key=value".rsplitn(2, '=').next().unwrap();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().1`
-
-error: manual implementation of `rsplit_once`
   --> $DIR/manual_split_once.rs:42:13
    |
 LL |     let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map_or("key=value", |x| x.0)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().0`
 
 error: manual implementation of `rsplit_once`
   --> $DIR/manual_split_once.rs:43:13
@@ -102,5 +96,5 @@ error: manual implementation of `split_once`
 LL |     let _ = "key=value".splitn(2, '=').nth(1).unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
 
-error: aborting due to 17 previous errors
+error: aborting due to 16 previous errors
 
diff --git a/src/tools/clippy/tests/ui/match_overlapping_arm.rs b/src/tools/clippy/tests/ui/match_overlapping_arm.rs
index 845986a4ead..2f85e635713 100644
--- a/src/tools/clippy/tests/ui/match_overlapping_arm.rs
+++ b/src/tools/clippy/tests/ui/match_overlapping_arm.rs
@@ -100,6 +100,15 @@ fn overlapping() {
         _ => (),
     }
 
+    // Issue #7816 - overlap after included range
+    match 42 {
+        5..=10 => (),
+        0..=20 => (),
+        21..=30 => (),
+        21..=40 => (),
+        _ => (),
+    }
+
     // Issue #7829
     match 0 {
         -1..=1 => (),
@@ -107,6 +116,15 @@ fn overlapping() {
         _ => (),
     }
 
+    // Only warn about the first if there are multiple overlaps
+    match 42u128 {
+        0..=0x0000_0000_0000_00ff => (),
+        0..=0x0000_0000_0000_ffff => (),
+        0..=0x0000_0000_ffff_ffff => (),
+        0..=0xffff_ffff_ffff_ffff => (),
+        _ => (),
+    }
+
     if let None = Some(42) {
         // nothing
     } else if let None = Some(42) {
diff --git a/src/tools/clippy/tests/ui/match_overlapping_arm.stderr b/src/tools/clippy/tests/ui/match_overlapping_arm.stderr
index c2b3f173c2b..b81bb1ecfae 100644
--- a/src/tools/clippy/tests/ui/match_overlapping_arm.stderr
+++ b/src/tools/clippy/tests/ui/match_overlapping_arm.stderr
@@ -71,5 +71,29 @@ note: overlaps with this
 LL |         ..26 => println!("..26"),
    |         ^^^^
 
-error: aborting due to 6 previous errors
+error: some ranges overlap
+  --> $DIR/match_overlapping_arm.rs:107:9
+   |
+LL |         21..=30 => (),
+   |         ^^^^^^^
+   |
+note: overlaps with this
+  --> $DIR/match_overlapping_arm.rs:108:9
+   |
+LL |         21..=40 => (),
+   |         ^^^^^^^
+
+error: some ranges overlap
+  --> $DIR/match_overlapping_arm.rs:121:9
+   |
+LL |         0..=0x0000_0000_0000_00ff => (),
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: overlaps with this
+  --> $DIR/match_overlapping_arm.rs:122:9
+   |
+LL |         0..=0x0000_0000_0000_ffff => (),
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui/min_max.rs b/src/tools/clippy/tests/ui/min_max.rs
index f7ed72a11cf..b2bc97f4744 100644
--- a/src/tools/clippy/tests/ui/min_max.rs
+++ b/src/tools/clippy/tests/ui/min_max.rs
@@ -19,8 +19,7 @@ impl NotOrd {
 }
 
 fn main() {
-    let x;
-    x = 2usize;
+    let x = 2usize;
     min(1, max(3, x));
     min(max(3, x), 1);
     max(min(x, 1), 3);
@@ -35,9 +34,7 @@ fn main() {
     let y = 2isize;
     min(max(y, -1), 3);
 
-    let s;
-    s = "Hello";
-
+    let s = "Hello";
     min("Apple", max("Zoo", s));
     max(min(s, "Apple"), "Zoo");
 
diff --git a/src/tools/clippy/tests/ui/min_max.stderr b/src/tools/clippy/tests/ui/min_max.stderr
index 9f8e26fa406..c70b77eabbd 100644
--- a/src/tools/clippy/tests/ui/min_max.stderr
+++ b/src/tools/clippy/tests/ui/min_max.stderr
@@ -1,5 +1,5 @@
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:24:5
+  --> $DIR/min_max.rs:23:5
    |
 LL |     min(1, max(3, x));
    |     ^^^^^^^^^^^^^^^^^
@@ -7,73 +7,73 @@ LL |     min(1, max(3, x));
    = note: `-D clippy::min-max` implied by `-D warnings`
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:25:5
+  --> $DIR/min_max.rs:24:5
    |
 LL |     min(max(3, x), 1);
    |     ^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:26:5
+  --> $DIR/min_max.rs:25:5
    |
 LL |     max(min(x, 1), 3);
    |     ^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:27:5
+  --> $DIR/min_max.rs:26:5
    |
 LL |     max(3, min(x, 1));
    |     ^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:29:5
+  --> $DIR/min_max.rs:28:5
    |
 LL |     my_max(3, my_min(x, 1));
    |     ^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:41:5
+  --> $DIR/min_max.rs:38:5
    |
 LL |     min("Apple", max("Zoo", s));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:42:5
+  --> $DIR/min_max.rs:39:5
    |
 LL |     max(min(s, "Apple"), "Zoo");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:47:5
+  --> $DIR/min_max.rs:44:5
    |
 LL |     x.min(1).max(3);
    |     ^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:48:5
+  --> $DIR/min_max.rs:45:5
    |
 LL |     x.max(3).min(1);
    |     ^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:49:5
+  --> $DIR/min_max.rs:46:5
    |
 LL |     f.max(3f32).min(1f32);
    |     ^^^^^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:55:5
+  --> $DIR/min_max.rs:52:5
    |
 LL |     max(x.min(1), 3);
    |     ^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:58:5
+  --> $DIR/min_max.rs:55:5
    |
 LL |     s.max("Zoo").min("Apple");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:59:5
+  --> $DIR/min_max.rs:56:5
    |
 LL |     s.min("Apple").max("Zoo");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
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 8d9fc5a864d..c5f221220ec 100644
--- a/src/tools/clippy/tests/ui/min_rust_version_attr.rs
+++ b/src/tools/clippy/tests/ui/min_rust_version_attr.rs
@@ -137,6 +137,14 @@ fn unnest_or_patterns() {
     if let TS(0, x) | TS(1, x) = TS(0, 0) {}
 }
 
+#[cfg_attr(rustfmt, rustfmt_skip)]
+fn deprecated_cfg_attr() {}
+
+#[warn(clippy::cast_lossless)]
+fn int_from_bool() -> u8 {
+    true as u8
+}
+
 fn main() {
     filter_map_next();
     checked_conversion();
@@ -153,11 +161,12 @@ fn main() {
     map_unwrap_or();
     missing_const_for_fn();
     unnest_or_patterns();
+    int_from_bool();
 }
 
-mod meets_msrv {
+mod just_under_msrv {
     #![feature(custom_inner_attributes)]
-    #![clippy::msrv = "1.45.0"]
+    #![clippy::msrv = "1.44.0"]
 
     fn main() {
         let s = "hello, world!";
@@ -167,9 +176,9 @@ mod meets_msrv {
     }
 }
 
-mod just_under_msrv {
+mod meets_msrv {
     #![feature(custom_inner_attributes)]
-    #![clippy::msrv = "1.46.0"]
+    #![clippy::msrv = "1.45.0"]
 
     fn main() {
         let s = "hello, world!";
@@ -181,7 +190,7 @@ mod just_under_msrv {
 
 mod just_above_msrv {
     #![feature(custom_inner_attributes)]
-    #![clippy::msrv = "1.44.0"]
+    #![clippy::msrv = "1.46.0"]
 
     fn main() {
         let s = "hello, world!";
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 360dcfb230c..6b3fdb0844b 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:165:24
+  --> $DIR/min_rust_version_attr.rs:186: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:164:9
+  --> $DIR/min_rust_version_attr.rs:185: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:177:24
+  --> $DIR/min_rust_version_attr.rs:198:24
    |
 LL |             assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
    |                        ^^^^^^^^^^^^^^^^^^^^
    |
 note: the prefix was tested here
-  --> $DIR/min_rust_version_attr.rs:176:9
+  --> $DIR/min_rust_version_attr.rs:197:9
    |
 LL |         if s.starts_with("hello, ") {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/needless_borrow.fixed b/src/tools/clippy/tests/ui/needless_borrow.fixed
index 42c2bb9f414..9e37fb92559 100644
--- a/src/tools/clippy/tests/ui/needless_borrow.fixed
+++ b/src/tools/clippy/tests/ui/needless_borrow.fixed
@@ -1,9 +1,10 @@
 // run-rustfix
 
 #[warn(clippy::all, clippy::needless_borrow)]
-#[allow(unused_variables)]
+#[allow(unused_variables, clippy::unnecessary_mut_passed)]
 fn main() {
     let a = 5;
+    let ref_a = &a;
     let _ = x(&a); // no warning
     let _ = x(&a); // warn
 
@@ -21,11 +22,29 @@ fn main() {
         44 => &a,
         45 => {
             println!("foo");
-            &&a // FIXME: this should lint, too
+            &a
         },
         46 => &a,
+        47 => {
+            println!("foo");
+            loop {
+                println!("{}", a);
+                if a == 25 {
+                    break ref_a;
+                }
+            }
+        },
         _ => panic!(),
     };
+
+    let _ = x(&a);
+    let _ = x(&a);
+    let _ = x(&mut b);
+    let _ = x(ref_a);
+    {
+        let b = &mut b;
+        x(b);
+    }
 }
 
 #[allow(clippy::needless_borrowed_reference)]
diff --git a/src/tools/clippy/tests/ui/needless_borrow.rs b/src/tools/clippy/tests/ui/needless_borrow.rs
index 31977416bc7..093277784be 100644
--- a/src/tools/clippy/tests/ui/needless_borrow.rs
+++ b/src/tools/clippy/tests/ui/needless_borrow.rs
@@ -1,9 +1,10 @@
 // run-rustfix
 
 #[warn(clippy::all, clippy::needless_borrow)]
-#[allow(unused_variables)]
+#[allow(unused_variables, clippy::unnecessary_mut_passed)]
 fn main() {
     let a = 5;
+    let ref_a = &a;
     let _ = x(&a); // no warning
     let _ = x(&&a); // warn
 
@@ -21,11 +22,29 @@ fn main() {
         44 => &a,
         45 => {
             println!("foo");
-            &&a // FIXME: this should lint, too
+            &&a
         },
         46 => &&a,
+        47 => {
+            println!("foo");
+            loop {
+                println!("{}", a);
+                if a == 25 {
+                    break &ref_a;
+                }
+            }
+        },
         _ => panic!(),
     };
+
+    let _ = x(&&&a);
+    let _ = x(&mut &&a);
+    let _ = x(&&&mut b);
+    let _ = x(&&ref_a);
+    {
+        let b = &mut b;
+        x(&b);
+    }
 }
 
 #[allow(clippy::needless_borrowed_reference)]
diff --git a/src/tools/clippy/tests/ui/needless_borrow.stderr b/src/tools/clippy/tests/ui/needless_borrow.stderr
index 012d62e2871..03a5b3d260e 100644
--- a/src/tools/clippy/tests/ui/needless_borrow.stderr
+++ b/src/tools/clippy/tests/ui/needless_borrow.stderr
@@ -1,5 +1,5 @@
 error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
-  --> $DIR/needless_borrow.rs:8:15
+  --> $DIR/needless_borrow.rs:9:15
    |
 LL |     let _ = x(&&a); // warn
    |               ^^^ help: change this to: `&a`
@@ -7,16 +7,58 @@ LL |     let _ = x(&&a); // warn
    = note: `-D clippy::needless-borrow` implied by `-D warnings`
 
 error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler
-  --> $DIR/needless_borrow.rs:12:13
+  --> $DIR/needless_borrow.rs:13:13
    |
 LL |     mut_ref(&mut &mut b); // warn
    |             ^^^^^^^^^^^ help: change this to: `&mut b`
 
 error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
-  --> $DIR/needless_borrow.rs:26:15
+  --> $DIR/needless_borrow.rs:25:13
+   |
+LL |             &&a
+   |             ^^^ help: change this to: `&a`
+
+error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
+  --> $DIR/needless_borrow.rs:27:15
    |
 LL |         46 => &&a,
    |               ^^^ help: change this to: `&a`
 
-error: aborting due to 3 previous errors
+error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
+  --> $DIR/needless_borrow.rs:33:27
+   |
+LL |                     break &ref_a;
+   |                           ^^^^^^ help: change this to: `ref_a`
+
+error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
+  --> $DIR/needless_borrow.rs:40:15
+   |
+LL |     let _ = x(&&&a);
+   |               ^^^^ help: change this to: `&a`
+
+error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
+  --> $DIR/needless_borrow.rs:41:15
+   |
+LL |     let _ = x(&mut &&a);
+   |               ^^^^^^^^ help: change this to: `&a`
+
+error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler
+  --> $DIR/needless_borrow.rs:42:15
+   |
+LL |     let _ = x(&&&mut b);
+   |               ^^^^^^^^ help: change this to: `&mut b`
+
+error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
+  --> $DIR/needless_borrow.rs:43:15
+   |
+LL |     let _ = x(&&ref_a);
+   |               ^^^^^^^ help: change this to: `ref_a`
+
+error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler
+  --> $DIR/needless_borrow.rs:46:11
+   |
+LL |         x(&b);
+   |           ^^ help: change this to: `b`
+
+error: aborting due to 10 previous errors
 
diff --git a/src/tools/clippy/tests/ui/needless_collect_indirect.rs b/src/tools/clippy/tests/ui/needless_collect_indirect.rs
index 2c94235b8f5..1f11d1f8d56 100644
--- a/src/tools/clippy/tests/ui/needless_collect_indirect.rs
+++ b/src/tools/clippy/tests/ui/needless_collect_indirect.rs
@@ -76,6 +76,37 @@ mod issue7110 {
     }
 }
 
+mod issue7975 {
+    use super::*;
+
+    fn direct_mapping_with_used_mutable_reference() -> Vec<()> {
+        let test_vec: Vec<()> = vec![];
+        let mut vec_2: Vec<()> = vec![];
+        let mut_ref = &mut vec_2;
+        let collected_vec: Vec<_> = test_vec.into_iter().map(|_| mut_ref.push(())).collect();
+        collected_vec.into_iter().map(|_| mut_ref.push(())).collect()
+    }
+
+    fn indirectly_mapping_with_used_mutable_reference() -> Vec<()> {
+        let test_vec: Vec<()> = vec![];
+        let mut vec_2: Vec<()> = vec![];
+        let mut_ref = &mut vec_2;
+        let collected_vec: Vec<_> = test_vec.into_iter().map(|_| mut_ref.push(())).collect();
+        let iter = collected_vec.into_iter();
+        iter.map(|_| mut_ref.push(())).collect()
+    }
+
+    fn indirect_collect_after_indirect_mapping_with_used_mutable_reference() -> Vec<()> {
+        let test_vec: Vec<()> = vec![];
+        let mut vec_2: Vec<()> = vec![];
+        let mut_ref = &mut vec_2;
+        let collected_vec: Vec<_> = test_vec.into_iter().map(|_| mut_ref.push(())).collect();
+        let iter = collected_vec.into_iter();
+        let mapped_iter = iter.map(|_| mut_ref.push(()));
+        mapped_iter.collect()
+    }
+}
+
 fn allow_test() {
     #[allow(clippy::needless_collect)]
     let v = [1].iter().collect::<Vec<_>>();
diff --git a/src/tools/clippy/tests/ui/needless_late_init.rs b/src/tools/clippy/tests/ui/needless_late_init.rs
new file mode 100644
index 00000000000..6fdb4cc566f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_late_init.rs
@@ -0,0 +1,167 @@
+#![allow(unused)]
+
+fn main() {
+    let a;
+    let n = 1;
+    match n {
+        1 => a = "one",
+        _ => {
+            a = "two";
+        },
+    }
+
+    let b;
+    if n == 3 {
+        b = "four";
+    } else {
+        b = "five"
+    }
+
+    let c;
+    if let Some(n) = Some(5) {
+        c = n;
+    } else {
+        c = -50;
+    }
+
+    let d;
+    if true {
+        let temp = 5;
+        d = temp;
+    } else {
+        d = 15;
+    }
+
+    let e;
+    if true {
+        e = format!("{} {}", a, b);
+    } else {
+        e = format!("{}", c);
+    }
+
+    println!("{}", a);
+}
+
+async fn in_async() -> &'static str {
+    async fn f() -> &'static str {
+        "one"
+    }
+
+    let a;
+    let n = 1;
+    match n {
+        1 => a = f().await,
+        _ => {
+            a = "two";
+        },
+    }
+
+    a
+}
+
+const fn in_const() -> &'static str {
+    const fn f() -> &'static str {
+        "one"
+    }
+
+    let a;
+    let n = 1;
+    match n {
+        1 => a = f(),
+        _ => {
+            a = "two";
+        },
+    }
+
+    a
+}
+
+fn does_not_lint() {
+    let z;
+    if false {
+        z = 1;
+    }
+
+    let x;
+    let y;
+    if true {
+        x = 1;
+    } else {
+        y = 1;
+    }
+
+    let mut x;
+    if true {
+        x = 5;
+        x = 10 / x;
+    } else {
+        x = 2;
+    }
+
+    let x;
+    let _ = match 1 {
+        1 => x = 10,
+        _ => x = 20,
+    };
+
+    // using tuples would be possible, but not always preferable
+    let x;
+    let y;
+    if true {
+        x = 1;
+        y = 2;
+    } else {
+        x = 3;
+        y = 4;
+    }
+
+    // could match with a smarter heuristic to avoid multiple assignments
+    let x;
+    if true {
+        let mut y = 5;
+        y = 6;
+        x = y;
+    } else {
+        x = 2;
+    }
+
+    let (x, y);
+    if true {
+        x = 1;
+    } else {
+        x = 2;
+    }
+    y = 3;
+
+    macro_rules! assign {
+        ($i:ident) => {
+            $i = 1;
+        };
+    }
+    let x;
+    assign!(x);
+
+    let x;
+    if true {
+        assign!(x);
+    } else {
+        x = 2;
+    }
+
+    macro_rules! in_macro {
+        () => {
+            let x;
+            x = 1;
+
+            let x;
+            if true {
+                x = 1;
+            } else {
+                x = 2;
+            }
+        };
+    }
+    in_macro!();
+
+    println!("{}", x);
+}
diff --git a/src/tools/clippy/tests/ui/needless_late_init.stderr b/src/tools/clippy/tests/ui/needless_late_init.stderr
new file mode 100644
index 00000000000..cbb7538c63b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_late_init.stderr
@@ -0,0 +1,150 @@
+error: unneeded late initalization
+  --> $DIR/needless_late_init.rs:4:5
+   |
+LL |     let a;
+   |     ^^^^^^
+   |
+   = note: `-D clippy::needless-late-init` implied by `-D warnings`
+help: declare `a` here
+   |
+LL |     let a = match n {
+   |     +++++++
+help: remove the assignments from the `match` arms
+   |
+LL ~         1 => "one",
+LL |         _ => {
+LL ~             "two"
+   |
+help: add a semicolon after the `match` expression
+   |
+LL |     };
+   |      +
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init.rs:13:5
+   |
+LL |     let b;
+   |     ^^^^^^
+   |
+help: declare `b` here
+   |
+LL |     let b = if n == 3 {
+   |     +++++++
+help: remove the assignments from the branches
+   |
+LL ~         "four"
+LL |     } else {
+LL ~         "five"
+   |
+help: add a semicolon after the `if` expression
+   |
+LL |     };
+   |      +
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init.rs:20:5
+   |
+LL |     let c;
+   |     ^^^^^^
+   |
+help: declare `c` here
+   |
+LL |     let c = if let Some(n) = Some(5) {
+   |     +++++++
+help: remove the assignments from the branches
+   |
+LL ~         n
+LL |     } else {
+LL ~         -50
+   |
+help: add a semicolon after the `if` expression
+   |
+LL |     };
+   |      +
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init.rs:27:5
+   |
+LL |     let d;
+   |     ^^^^^^
+   |
+help: declare `d` here
+   |
+LL |     let d = if true {
+   |     +++++++
+help: remove the assignments from the branches
+   |
+LL ~         temp
+LL |     } else {
+LL ~         15
+   |
+help: add a semicolon after the `if` expression
+   |
+LL |     };
+   |      +
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init.rs:35:5
+   |
+LL |     let e;
+   |     ^^^^^^
+   |
+help: declare `e` here
+   |
+LL |     let e = if true {
+   |     +++++++
+help: remove the assignments from the branches
+   |
+LL ~         format!("{} {}", a, b)
+LL |     } else {
+LL ~         format!("{}", c)
+   |
+help: add a semicolon after the `if` expression
+   |
+LL |     };
+   |      +
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init.rs:50:5
+   |
+LL |     let a;
+   |     ^^^^^^
+   |
+help: declare `a` here
+   |
+LL |     let a = match n {
+   |     +++++++
+help: remove the assignments from the `match` arms
+   |
+LL ~         1 => f().await,
+LL |         _ => {
+LL ~             "two"
+   |
+help: add a semicolon after the `match` expression
+   |
+LL |     };
+   |      +
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init.rs:67:5
+   |
+LL |     let a;
+   |     ^^^^^^
+   |
+help: declare `a` here
+   |
+LL |     let a = match n {
+   |     +++++++
+help: remove the assignments from the `match` arms
+   |
+LL ~         1 => f(),
+LL |         _ => {
+LL ~             "two"
+   |
+help: add a semicolon after the `match` expression
+   |
+LL |     };
+   |      +
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_late_init_fixable.fixed b/src/tools/clippy/tests/ui/needless_late_init_fixable.fixed
new file mode 100644
index 00000000000..32d5d04fde4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_late_init_fixable.fixed
@@ -0,0 +1,38 @@
+// run-rustfix
+
+#![allow(unused, clippy::assign_op_pattern)]
+
+fn main() {
+    
+    let a = "zero";
+
+    
+    
+    let b = 1;
+    let c = 2;
+
+    
+    let d: usize = 1;
+
+    
+    let mut e = 1;
+    e = 2;
+
+    
+    let f = match 1 {
+        1 => "three",
+        _ => return,
+    }; // has semi
+
+    
+    let g: usize = if true {
+        5
+    } else {
+        panic!();
+    };
+
+    
+    let h = format!("{}", e);
+
+    println!("{}", a);
+}
diff --git a/src/tools/clippy/tests/ui/needless_late_init_fixable.rs b/src/tools/clippy/tests/ui/needless_late_init_fixable.rs
new file mode 100644
index 00000000000..6bc85f68632
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_late_init_fixable.rs
@@ -0,0 +1,38 @@
+// run-rustfix
+
+#![allow(unused, clippy::assign_op_pattern)]
+
+fn main() {
+    let a;
+    a = "zero";
+
+    let b;
+    let c;
+    b = 1;
+    c = 2;
+
+    let d: usize;
+    d = 1;
+
+    let mut e;
+    e = 1;
+    e = 2;
+
+    let f;
+    match 1 {
+        1 => f = "three",
+        _ => return,
+    }; // has semi
+
+    let g: usize;
+    if true {
+        g = 5;
+    } else {
+        panic!();
+    }
+
+    let h;
+    h = format!("{}", e);
+
+    println!("{}", a);
+}
diff --git a/src/tools/clippy/tests/ui/needless_late_init_fixable.stderr b/src/tools/clippy/tests/ui/needless_late_init_fixable.stderr
new file mode 100644
index 00000000000..a0ce4f812f4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_late_init_fixable.stderr
@@ -0,0 +1,103 @@
+error: unneeded late initalization
+  --> $DIR/needless_late_init_fixable.rs:6:5
+   |
+LL |     let a;
+   |     ^^^^^^
+   |
+   = note: `-D clippy::needless-late-init` implied by `-D warnings`
+help: declare `a` here
+   |
+LL |     let a = "zero";
+   |     ~~~~~
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init_fixable.rs:9:5
+   |
+LL |     let b;
+   |     ^^^^^^
+   |
+help: declare `b` here
+   |
+LL |     let b = 1;
+   |     ~~~~~
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init_fixable.rs:10:5
+   |
+LL |     let c;
+   |     ^^^^^^
+   |
+help: declare `c` here
+   |
+LL |     let c = 2;
+   |     ~~~~~
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init_fixable.rs:14:5
+   |
+LL |     let d: usize;
+   |     ^^^^^^^^^^^^^
+   |
+help: declare `d` here
+   |
+LL |     let d: usize = 1;
+   |     ~~~~~~~~~~~~
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init_fixable.rs:17:5
+   |
+LL |     let mut e;
+   |     ^^^^^^^^^^
+   |
+help: declare `e` here
+   |
+LL |     let mut e = 1;
+   |     ~~~~~~~~~
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init_fixable.rs:21:5
+   |
+LL |     let f;
+   |     ^^^^^^
+   |
+help: declare `f` here
+   |
+LL |     let f = match 1 {
+   |     +++++++
+help: remove the assignments from the `match` arms
+   |
+LL |         1 => "three",
+   |              ~~~~~~~
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init_fixable.rs:27:5
+   |
+LL |     let g: usize;
+   |     ^^^^^^^^^^^^^
+   |
+help: declare `g` here
+   |
+LL |     let g: usize = if true {
+   |     ++++++++++++++
+help: remove the assignments from the branches
+   |
+LL |         5
+   |
+help: add a semicolon after the `if` expression
+   |
+LL |     };
+   |      +
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init_fixable.rs:34:5
+   |
+LL |     let h;
+   |     ^^^^^^
+   |
+help: declare `h` here
+   |
+LL |     let h = format!("{}", e);
+   |     ~~~~~
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_question_mark.stderr b/src/tools/clippy/tests/ui/needless_question_mark.stderr
index 02bf50d077a..57c3d48c761 100644
--- a/src/tools/clippy/tests/ui/needless_question_mark.stderr
+++ b/src/tools/clippy/tests/ui/needless_question_mark.stderr
@@ -2,7 +2,7 @@ error: question mark operator is useless here
   --> $DIR/needless_question_mark.rs:23:12
    |
 LL |     return Some(to.magic?);
-   |            ^^^^^^^^^^^^^^^ help: try: `to.magic`
+   |            ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
    |
    = note: `-D clippy::needless-question-mark` implied by `-D warnings`
 
@@ -10,67 +10,67 @@ error: question mark operator is useless here
   --> $DIR/needless_question_mark.rs:31:12
    |
 LL |     return Some(to.magic?)
-   |            ^^^^^^^^^^^^^^^ help: try: `to.magic`
+   |            ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
 
 error: question mark operator is useless here
   --> $DIR/needless_question_mark.rs:36:5
    |
 LL |     Some(to.magic?)
-   |     ^^^^^^^^^^^^^^^ help: try: `to.magic`
+   |     ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
 
 error: question mark operator is useless here
   --> $DIR/needless_question_mark.rs:41:21
    |
 LL |     to.and_then(|t| Some(t.magic?))
-   |                     ^^^^^^^^^^^^^^ help: try: `t.magic`
+   |                     ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic`
 
 error: question mark operator is useless here
   --> $DIR/needless_question_mark.rs:50:9
    |
 LL |         Some(t.magic?)
-   |         ^^^^^^^^^^^^^^ help: try: `t.magic`
+   |         ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic`
 
 error: question mark operator is useless here
   --> $DIR/needless_question_mark.rs:55:12
    |
 LL |     return Ok(tr.magic?);
-   |            ^^^^^^^^^^^^^ help: try: `tr.magic`
+   |            ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic`
 
 error: question mark operator is useless here
   --> $DIR/needless_question_mark.rs:62:12
    |
 LL |     return Ok(tr.magic?)
-   |            ^^^^^^^^^^^^^ help: try: `tr.magic`
+   |            ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic`
 
 error: question mark operator is useless here
   --> $DIR/needless_question_mark.rs:66:5
    |
 LL |     Ok(tr.magic?)
-   |     ^^^^^^^^^^^^^ help: try: `tr.magic`
+   |     ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic`
 
 error: question mark operator is useless here
   --> $DIR/needless_question_mark.rs:70:21
    |
 LL |     tr.and_then(|t| Ok(t.magic?))
-   |                     ^^^^^^^^^^^^ help: try: `t.magic`
+   |                     ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic`
 
 error: question mark operator is useless here
   --> $DIR/needless_question_mark.rs:78:9
    |
 LL |         Ok(t.magic?)
-   |         ^^^^^^^^^^^^ help: try: `t.magic`
+   |         ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic`
 
 error: question mark operator is useless here
   --> $DIR/needless_question_mark.rs:85:16
    |
 LL |         return Ok(t.magic?);
-   |                ^^^^^^^^^^^^ help: try: `t.magic`
+   |                ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic`
 
 error: question mark operator is useless here
   --> $DIR/needless_question_mark.rs:120:27
    |
 LL |         || -> Option<_> { Some(Some($expr)?) }()
-   |                           ^^^^^^^^^^^^^^^^^^ help: try: `Some($expr)`
+   |                           ^^^^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `Some($expr)`
 ...
 LL |     let _x = some_and_qmark_in_macro!(x?);
    |              ---------------------------- in this macro invocation
diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed
index 812ce7163cd..83f467c8400 100644
--- a/src/tools/clippy/tests/ui/needless_return.fixed
+++ b/src/tools/clippy/tests/ui/needless_return.fixed
@@ -108,6 +108,7 @@ fn test_return_in_macro() {
 }
 
 mod issue6501 {
+    #[allow(clippy::unnecessary_lazy_evaluations)]
     fn foo(bar: Result<(), ()>) {
         bar.unwrap_or_else(|_| {})
     }
diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs
index c42567b517c..341caf18bd6 100644
--- a/src/tools/clippy/tests/ui/needless_return.rs
+++ b/src/tools/clippy/tests/ui/needless_return.rs
@@ -108,6 +108,7 @@ fn test_return_in_macro() {
 }
 
 mod issue6501 {
+    #[allow(clippy::unnecessary_lazy_evaluations)]
     fn foo(bar: Result<(), ()>) {
         bar.unwrap_or_else(|_| return)
     }
diff --git a/src/tools/clippy/tests/ui/needless_return.stderr b/src/tools/clippy/tests/ui/needless_return.stderr
index 74dda971fda..c0abc2c63dd 100644
--- a/src/tools/clippy/tests/ui/needless_return.stderr
+++ b/src/tools/clippy/tests/ui/needless_return.stderr
@@ -85,109 +85,109 @@ LL |         return String::new();
    |         ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:112:32
+  --> $DIR/needless_return.rs:113:32
    |
 LL |         bar.unwrap_or_else(|_| return)
    |                                ^^^^^^ help: replace `return` with an empty block: `{}`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:117:13
+  --> $DIR/needless_return.rs:118:13
    |
 LL |             return;
    |             ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:119:20
+  --> $DIR/needless_return.rs:120:20
    |
 LL |         let _ = || return;
    |                    ^^^^^^ help: replace `return` with an empty block: `{}`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:125:32
+  --> $DIR/needless_return.rs:126:32
    |
 LL |         res.unwrap_or_else(|_| return Foo)
    |                                ^^^^^^^^^^ help: remove `return`: `Foo`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:134:5
+  --> $DIR/needless_return.rs:135:5
    |
 LL |     return true;
    |     ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:138:5
+  --> $DIR/needless_return.rs:139:5
    |
 LL |     return true;
    |     ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:143:9
+  --> $DIR/needless_return.rs:144:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:145:9
+  --> $DIR/needless_return.rs:146:9
    |
 LL |         return false;
    |         ^^^^^^^^^^^^^ help: remove `return`: `false`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:151:17
+  --> $DIR/needless_return.rs:152:17
    |
 LL |         true => return false,
    |                 ^^^^^^^^^^^^ help: remove `return`: `false`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:153:13
+  --> $DIR/needless_return.rs:154:13
    |
 LL |             return true;
    |             ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:160:9
+  --> $DIR/needless_return.rs:161:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:162:16
+  --> $DIR/needless_return.rs:163:16
    |
 LL |     let _ = || return true;
    |                ^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:170:5
+  --> $DIR/needless_return.rs:171:5
    |
 LL |     return;
    |     ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:175:9
+  --> $DIR/needless_return.rs:176:9
    |
 LL |         return;
    |         ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:177:9
+  --> $DIR/needless_return.rs:178:9
    |
 LL |         return;
    |         ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:184:14
+  --> $DIR/needless_return.rs:185:14
    |
 LL |         _ => return,
    |              ^^^^^^ help: replace `return` with an empty block: `{}`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:199:9
+  --> $DIR/needless_return.rs:200:9
    |
 LL |         return String::from("test");
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:201:9
+  --> $DIR/needless_return.rs:202:9
    |
 LL |         return String::new();
    |         ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
diff --git a/src/tools/clippy/tests/ui/needless_splitn.fixed b/src/tools/clippy/tests/ui/needless_splitn.fixed
new file mode 100644
index 00000000000..f6a4b2f17d3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_splitn.fixed
@@ -0,0 +1,27 @@
+// run-rustfix
+// edition:2018
+
+#![feature(custom_inner_attributes)]
+#![warn(clippy::needless_splitn)]
+#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::manual_split_once)]
+
+extern crate itertools;
+
+#[allow(unused_imports)]
+use itertools::Itertools;
+
+fn main() {
+    let str = "key=value=end";
+    let _ = str.split('=').next();
+    let _ = str.split('=').nth(0);
+    let _ = str.splitn(2, '=').nth(1);
+    let (_, _) = str.splitn(2, '=').next_tuple().unwrap();
+    let (_, _) = str.split('=').next_tuple().unwrap();
+    let _: Vec<&str> = str.splitn(3, '=').collect();
+
+    let _ = str.rsplit('=').next();
+    let _ = str.rsplit('=').nth(0);
+    let _ = str.rsplitn(2, '=').nth(1);
+    let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap();
+    let (_, _) = str.rsplit('=').next_tuple().unwrap();
+}
diff --git a/src/tools/clippy/tests/ui/needless_splitn.rs b/src/tools/clippy/tests/ui/needless_splitn.rs
new file mode 100644
index 00000000000..6ba32255bb2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_splitn.rs
@@ -0,0 +1,27 @@
+// run-rustfix
+// edition:2018
+
+#![feature(custom_inner_attributes)]
+#![warn(clippy::needless_splitn)]
+#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::manual_split_once)]
+
+extern crate itertools;
+
+#[allow(unused_imports)]
+use itertools::Itertools;
+
+fn main() {
+    let str = "key=value=end";
+    let _ = str.splitn(2, '=').next();
+    let _ = str.splitn(2, '=').nth(0);
+    let _ = str.splitn(2, '=').nth(1);
+    let (_, _) = str.splitn(2, '=').next_tuple().unwrap();
+    let (_, _) = str.splitn(3, '=').next_tuple().unwrap();
+    let _: Vec<&str> = str.splitn(3, '=').collect();
+
+    let _ = str.rsplitn(2, '=').next();
+    let _ = str.rsplitn(2, '=').nth(0);
+    let _ = str.rsplitn(2, '=').nth(1);
+    let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap();
+    let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap();
+}
diff --git a/src/tools/clippy/tests/ui/needless_splitn.stderr b/src/tools/clippy/tests/ui/needless_splitn.stderr
new file mode 100644
index 00000000000..66de2256554
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_splitn.stderr
@@ -0,0 +1,40 @@
+error: unnecessary use of `splitn`
+  --> $DIR/needless_splitn.rs:15:13
+   |
+LL |     let _ = str.splitn(2, '=').next();
+   |             ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
+   |
+   = note: `-D clippy::needless-splitn` implied by `-D warnings`
+
+error: unnecessary use of `splitn`
+  --> $DIR/needless_splitn.rs:16:13
+   |
+LL |     let _ = str.splitn(2, '=').nth(0);
+   |             ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
+
+error: unnecessary use of `splitn`
+  --> $DIR/needless_splitn.rs:19:18
+   |
+LL |     let (_, _) = str.splitn(3, '=').next_tuple().unwrap();
+   |                  ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
+
+error: unnecessary use of `rsplitn`
+  --> $DIR/needless_splitn.rs:22:13
+   |
+LL |     let _ = str.rsplitn(2, '=').next();
+   |             ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
+
+error: unnecessary use of `rsplitn`
+  --> $DIR/needless_splitn.rs:23:13
+   |
+LL |     let _ = str.rsplitn(2, '=').nth(0);
+   |             ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
+
+error: unnecessary use of `rsplitn`
+  --> $DIR/needless_splitn.rs:26:18
+   |
+LL |     let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap();
+   |                  ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/no_effect.rs b/src/tools/clippy/tests/ui/no_effect.rs
index 7bcc4cad0d3..5427c88faf3 100644
--- a/src/tools/clippy/tests/ui/no_effect.rs
+++ b/src/tools/clippy/tests/ui/no_effect.rs
@@ -1,4 +1,4 @@
-#![feature(box_syntax)]
+#![feature(box_syntax, fn_traits, unboxed_closures)]
 #![warn(clippy::no_effect_underscore_binding)]
 #![allow(dead_code)]
 #![allow(path_statements)]
@@ -58,6 +58,36 @@ unsafe fn unsafe_fn() -> i32 {
     0
 }
 
+struct GreetStruct1;
+
+impl FnOnce<(&str,)> for GreetStruct1 {
+    type Output = ();
+
+    extern "rust-call" fn call_once(self, (who,): (&str,)) -> Self::Output {
+        println!("hello {}", who);
+    }
+}
+
+struct GreetStruct2();
+
+impl FnOnce<(&str,)> for GreetStruct2 {
+    type Output = ();
+
+    extern "rust-call" fn call_once(self, (who,): (&str,)) -> Self::Output {
+        println!("hello {}", who);
+    }
+}
+
+struct GreetStruct3 {}
+
+impl FnOnce<(&str,)> for GreetStruct3 {
+    type Output = ();
+
+    extern "rust-call" fn call_once(self, (who,): (&str,)) -> Self::Output {
+        println!("hello {}", who);
+    }
+}
+
 fn main() {
     let s = get_struct();
     let s2 = get_struct();
@@ -108,4 +138,7 @@ fn main() {
     DropTuple(0);
     DropEnum::Tuple(0);
     DropEnum::Struct { field: 0 };
+    GreetStruct1("world");
+    GreetStruct2()("world");
+    GreetStruct3 {}("world");
 }
diff --git a/src/tools/clippy/tests/ui/no_effect.stderr b/src/tools/clippy/tests/ui/no_effect.stderr
index a5dbc9fef45..06b88bb5bee 100644
--- a/src/tools/clippy/tests/ui/no_effect.stderr
+++ b/src/tools/clippy/tests/ui/no_effect.stderr
@@ -1,5 +1,5 @@
 error: statement with no effect
-  --> $DIR/no_effect.rs:65:5
+  --> $DIR/no_effect.rs:95:5
    |
 LL |     0;
    |     ^^
@@ -7,157 +7,157 @@ LL |     0;
    = note: `-D clippy::no-effect` implied by `-D warnings`
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:66:5
+  --> $DIR/no_effect.rs:96:5
    |
 LL |     s2;
    |     ^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:67:5
+  --> $DIR/no_effect.rs:97:5
    |
 LL |     Unit;
    |     ^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:68:5
+  --> $DIR/no_effect.rs:98:5
    |
 LL |     Tuple(0);
    |     ^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:69:5
+  --> $DIR/no_effect.rs:99:5
    |
 LL |     Struct { field: 0 };
    |     ^^^^^^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:70:5
+  --> $DIR/no_effect.rs:100:5
    |
 LL |     Struct { ..s };
    |     ^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:71:5
+  --> $DIR/no_effect.rs:101:5
    |
 LL |     Union { a: 0 };
    |     ^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:72:5
+  --> $DIR/no_effect.rs:102:5
    |
 LL |     Enum::Tuple(0);
    |     ^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:73:5
+  --> $DIR/no_effect.rs:103:5
    |
 LL |     Enum::Struct { field: 0 };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:74:5
+  --> $DIR/no_effect.rs:104:5
    |
 LL |     5 + 6;
    |     ^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:75:5
+  --> $DIR/no_effect.rs:105:5
    |
 LL |     *&42;
    |     ^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:76:5
+  --> $DIR/no_effect.rs:106:5
    |
 LL |     &6;
    |     ^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:77:5
+  --> $DIR/no_effect.rs:107:5
    |
 LL |     (5, 6, 7);
    |     ^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:78:5
+  --> $DIR/no_effect.rs:108:5
    |
 LL |     box 42;
    |     ^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:79:5
+  --> $DIR/no_effect.rs:109:5
    |
 LL |     ..;
    |     ^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:80:5
+  --> $DIR/no_effect.rs:110:5
    |
 LL |     5..;
    |     ^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:81:5
+  --> $DIR/no_effect.rs:111:5
    |
 LL |     ..5;
    |     ^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:82:5
+  --> $DIR/no_effect.rs:112:5
    |
 LL |     5..6;
    |     ^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:83:5
+  --> $DIR/no_effect.rs:113:5
    |
 LL |     5..=6;
    |     ^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:84:5
+  --> $DIR/no_effect.rs:114:5
    |
 LL |     [42, 55];
    |     ^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:85:5
+  --> $DIR/no_effect.rs:115:5
    |
 LL |     [42, 55][1];
    |     ^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:86:5
+  --> $DIR/no_effect.rs:116:5
    |
 LL |     (42, 55).1;
    |     ^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:87:5
+  --> $DIR/no_effect.rs:117:5
    |
 LL |     [42; 55];
    |     ^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:88:5
+  --> $DIR/no_effect.rs:118:5
    |
 LL |     [42; 55][13];
    |     ^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:90:5
+  --> $DIR/no_effect.rs:120:5
    |
 LL |     || x += 5;
    |     ^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:92:5
+  --> $DIR/no_effect.rs:122:5
    |
 LL |     FooString { s: s };
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: binding to `_` prefixed variable with no side-effect
-  --> $DIR/no_effect.rs:93:5
+  --> $DIR/no_effect.rs:123:5
    |
 LL |     let _unused = 1;
    |     ^^^^^^^^^^^^^^^^
@@ -165,19 +165,19 @@ LL |     let _unused = 1;
    = note: `-D clippy::no-effect-underscore-binding` implied by `-D warnings`
 
 error: binding to `_` prefixed variable with no side-effect
-  --> $DIR/no_effect.rs:94:5
+  --> $DIR/no_effect.rs:124:5
    |
 LL |     let _penguin = || println!("Some helpful closure");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: binding to `_` prefixed variable with no side-effect
-  --> $DIR/no_effect.rs:95:5
+  --> $DIR/no_effect.rs:125:5
    |
 LL |     let _duck = Struct { field: 0 };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: binding to `_` prefixed variable with no side-effect
-  --> $DIR/no_effect.rs:96:5
+  --> $DIR/no_effect.rs:126:5
    |
 LL |     let _cat = [2, 4, 6, 8][2];
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.rs b/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.rs
index eca7f5e5655..828248d922f 100644
--- a/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.rs
+++ b/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.rs
@@ -69,6 +69,11 @@ pub enum MyOption<T> {
 
 unsafe impl<T> Send for MyOption<T> {}
 
+// Test types that contain `NonNull` instead of raw pointers (#8045)
+pub struct WrappedNonNull(UnsafeCell<NonNull<()>>);
+
+unsafe impl Send for WrappedNonNull {}
+
 // Multiple type parameters
 pub struct MultiParam<A, B> {
     vec: Vec<(A, B)>,
diff --git a/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.stderr b/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.stderr
index 8b8a1d16d9b..3c4da36b3e0 100644
--- a/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.stderr
+++ b/src/tools/clippy/tests/ui/non_send_fields_in_send_ty.stderr
@@ -103,65 +103,65 @@ LL |     MySome(T),
    = help: add `T: Send` bound in `Send` impl
 
 error: this implementation is unsound, as some fields in `MultiParam<A, B>` are `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:77:1
+  --> $DIR/non_send_fields_in_send_ty.rs:82:1
    |
 LL | unsafe impl<A, B> Send for MultiParam<A, B> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: the type of field `vec` is `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:74:5
+  --> $DIR/non_send_fields_in_send_ty.rs:79:5
    |
 LL |     vec: Vec<(A, B)>,
    |     ^^^^^^^^^^^^^^^^
    = help: add bounds on type parameters `A, B` that satisfy `Vec<(A, B)>: Send`
 
 error: this implementation is unsound, as some fields in `HeuristicTest` are `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:95:1
+  --> $DIR/non_send_fields_in_send_ty.rs:100:1
    |
 LL | unsafe impl Send for HeuristicTest {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: the type of field `field4` is `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:90:5
+  --> $DIR/non_send_fields_in_send_ty.rs:95:5
    |
 LL |     field4: (*const NonSend, Rc<u8>),
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: use a thread-safe type that implements `Send`
 
 error: this implementation is unsound, as some fields in `AttrTest3<T>` are `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:114:1
+  --> $DIR/non_send_fields_in_send_ty.rs:119:1
    |
 LL | unsafe impl<T> Send for AttrTest3<T> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: the type of field `0` is `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:109:11
+  --> $DIR/non_send_fields_in_send_ty.rs:114:11
    |
 LL |     Enum2(T),
    |           ^
    = help: add `T: Send` bound in `Send` impl
 
 error: this implementation is unsound, as some fields in `Complex<P, u32>` are `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:122:1
+  --> $DIR/non_send_fields_in_send_ty.rs:127:1
    |
 LL | unsafe impl<P> Send for Complex<P, u32> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: the type of field `field1` is `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:118:5
+  --> $DIR/non_send_fields_in_send_ty.rs:123:5
    |
 LL |     field1: A,
    |     ^^^^^^^^^
    = help: add `P: Send` bound in `Send` impl
 
 error: this implementation is unsound, as some fields in `Complex<Q, MutexGuard<'static, bool>>` are `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:125:1
+  --> $DIR/non_send_fields_in_send_ty.rs:130:1
    |
 LL | unsafe impl<Q: Send> Send for Complex<Q, MutexGuard<'static, bool>> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: the type of field `field2` is `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:119:5
+  --> $DIR/non_send_fields_in_send_ty.rs:124:5
    |
 LL |     field2: B,
    |     ^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/octal_escapes.rs b/src/tools/clippy/tests/ui/octal_escapes.rs
new file mode 100644
index 00000000000..53145ef0fd2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/octal_escapes.rs
@@ -0,0 +1,20 @@
+#![warn(clippy::octal_escapes)]
+
+fn main() {
+    let _bad1 = "\033[0m";
+    let _bad2 = b"\033[0m";
+    let _bad3 = "\\\033[0m";
+    // maximum 3 digits (\012 is the escape)
+    let _bad4 = "\01234567";
+    let _bad5 = "\0\03";
+    let _bad6 = "Text-\055\077-MoreText";
+    let _bad7 = "EvenMoreText-\01\02-ShortEscapes";
+    let _bad8 = "锈\01锈";
+    let _bad9 = "锈\011锈";
+
+    let _good1 = "\\033[0m";
+    let _good2 = "\0\\0";
+    let _good3 = "\0\0";
+    let _good4 = "X\0\0X";
+    let _good5 = "锈\0锈";
+}
diff --git a/src/tools/clippy/tests/ui/octal_escapes.stderr b/src/tools/clippy/tests/ui/octal_escapes.stderr
new file mode 100644
index 00000000000..54f5bbb0fc4
--- /dev/null
+++ b/src/tools/clippy/tests/ui/octal_escapes.stderr
@@ -0,0 +1,131 @@
+error: octal-looking escape in string literal
+  --> $DIR/octal_escapes.rs:4:17
+   |
+LL |     let _bad1 = "/033[0m";
+   |                 ^^^^^^^^^
+   |
+   = note: `-D clippy::octal-escapes` implied by `-D warnings`
+   = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+   |
+LL |     let _bad1 = "/x1b[0m";
+   |                 ~~~~~~~~~
+help: if the null character is intended, disambiguate using
+   |
+LL |     let _bad1 = "/x0033[0m";
+   |                 ~~~~~~~~~~~
+
+error: octal-looking escape in byte string literal
+  --> $DIR/octal_escapes.rs:5:17
+   |
+LL |     let _bad2 = b"/033[0m";
+   |                 ^^^^^^^^^^
+   |
+   = help: octal escapes are not supported, `/0` is always a null byte
+help: if an octal escape was intended, use the hexadecimal representation instead
+   |
+LL |     let _bad2 = b"/x1b[0m";
+   |                 ~~~~~~~~~~
+help: if the null byte is intended, disambiguate using
+   |
+LL |     let _bad2 = b"/x0033[0m";
+   |                 ~~~~~~~~~~~~
+
+error: octal-looking escape in string literal
+  --> $DIR/octal_escapes.rs:6:17
+   |
+LL |     let _bad3 = "//033[0m";
+   |                 ^^^^^^^^^^^
+   |
+   = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+   |
+LL |     let _bad3 = "//x1b[0m";
+   |                 ~~~~~~~~~~~
+help: if the null character is intended, disambiguate using
+   |
+LL |     let _bad3 = "//x0033[0m";
+   |                 ~~~~~~~~~~~~~
+
+error: octal-looking escape in string literal
+  --> $DIR/octal_escapes.rs:8:17
+   |
+LL |     let _bad4 = "/01234567";
+   |                 ^^^^^^^^^^^
+   |
+   = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+   |
+LL |     let _bad4 = "/x0a34567";
+   |                 ~~~~~~~~~~~
+help: if the null character is intended, disambiguate using
+   |
+LL |     let _bad4 = "/x001234567";
+   |                 ~~~~~~~~~~~~~
+
+error: octal-looking escape in string literal
+  --> $DIR/octal_escapes.rs:10:17
+   |
+LL |     let _bad6 = "Text-/055/077-MoreText";
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+   |
+LL |     let _bad6 = "Text-/x2d/x3f-MoreText";
+   |                 ~~~~~~~~~~~~~~~~~~~~~~~~
+help: if the null character is intended, disambiguate using
+   |
+LL |     let _bad6 = "Text-/x0055/x0077-MoreText";
+   |                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: octal-looking escape in string literal
+  --> $DIR/octal_escapes.rs:11:17
+   |
+LL |     let _bad7 = "EvenMoreText-/01/02-ShortEscapes";
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+   |
+LL |     let _bad7 = "EvenMoreText-/x01/x02-ShortEscapes";
+   |                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: if the null character is intended, disambiguate using
+   |
+LL |     let _bad7 = "EvenMoreText-/x001/x002-ShortEscapes";
+   |                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: octal-looking escape in string literal
+  --> $DIR/octal_escapes.rs:12:17
+   |
+LL |     let _bad8 = "锈/01锈";
+   |                 ^^^^^^^^^
+   |
+   = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+   |
+LL |     let _bad8 = "锈/x01锈";
+   |                 ~~~~~~~~~~
+help: if the null character is intended, disambiguate using
+   |
+LL |     let _bad8 = "锈/x001锈";
+   |                 ~~~~~~~~~~~
+
+error: octal-looking escape in string literal
+  --> $DIR/octal_escapes.rs:13:17
+   |
+LL |     let _bad9 = "锈/011锈";
+   |                 ^^^^^^^^^^
+   |
+   = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+   |
+LL |     let _bad9 = "锈/x09锈";
+   |                 ~~~~~~~~~~
+help: if the null character is intended, disambiguate using
+   |
+LL |     let _bad9 = "锈/x0011锈";
+   |                 ~~~~~~~~~~~~
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/option_env_unwrap.rs b/src/tools/clippy/tests/ui/option_env_unwrap.rs
index 642c77460a3..0141fb7856d 100644
--- a/src/tools/clippy/tests/ui/option_env_unwrap.rs
+++ b/src/tools/clippy/tests/ui/option_env_unwrap.rs
@@ -1,5 +1,6 @@
 // aux-build:macro_rules.rs
 #![warn(clippy::option_env_unwrap)]
+#![allow(clippy::map_flatten)]
 
 #[macro_use]
 extern crate macro_rules;
diff --git a/src/tools/clippy/tests/ui/option_env_unwrap.stderr b/src/tools/clippy/tests/ui/option_env_unwrap.stderr
index e6a58b0b2b7..885ac096cc8 100644
--- a/src/tools/clippy/tests/ui/option_env_unwrap.stderr
+++ b/src/tools/clippy/tests/ui/option_env_unwrap.stderr
@@ -1,5 +1,5 @@
 error: this will panic at run-time if the environment variable doesn't exist at compile-time
-  --> $DIR/option_env_unwrap.rs:17:13
+  --> $DIR/option_env_unwrap.rs:18:13
    |
 LL |     let _ = option_env!("PATH").unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL |     let _ = option_env!("PATH").unwrap();
    = help: consider using the `env!` macro instead
 
 error: this will panic at run-time if the environment variable doesn't exist at compile-time
-  --> $DIR/option_env_unwrap.rs:18:13
+  --> $DIR/option_env_unwrap.rs:19:13
    |
 LL |     let _ = option_env!("PATH").expect("environment variable PATH isn't set");
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL |     let _ = option_env!("PATH").expect("environment variable PATH isn't set
    = help: consider using the `env!` macro instead
 
 error: this will panic at run-time if the environment variable doesn't exist at compile-time
-  --> $DIR/option_env_unwrap.rs:9:9
+  --> $DIR/option_env_unwrap.rs:10:9
    |
 LL |         option_env!($env).unwrap()
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -28,7 +28,7 @@ LL |     let _ = option_env_unwrap!("PATH");
    = note: this error originates in the macro `option_env_unwrap` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: this will panic at run-time if the environment variable doesn't exist at compile-time
-  --> $DIR/option_env_unwrap.rs:12:9
+  --> $DIR/option_env_unwrap.rs:13:9
    |
 LL |         option_env!($env).expect($message)
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -40,7 +40,7 @@ LL |     let _ = option_env_unwrap!("PATH", "environment variable PATH isn't set
    = note: this error originates in the macro `option_env_unwrap` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: this will panic at run-time if the environment variable doesn't exist at compile-time
-  --> $DIR/option_env_unwrap.rs:21:13
+  --> $DIR/option_env_unwrap.rs:22:13
    |
 LL |     let _ = option_env_unwrap_external!("PATH");
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -49,7 +49,7 @@ LL |     let _ = option_env_unwrap_external!("PATH");
    = note: this error originates in the macro `option_env_unwrap_external` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: this will panic at run-time if the environment variable doesn't exist at compile-time
-  --> $DIR/option_env_unwrap.rs:22:13
+  --> $DIR/option_env_unwrap.rs:23:13
    |
 LL |     let _ = option_env_unwrap_external!("PATH", "environment variable PATH isn't set");
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/option_filter_map.fixed b/src/tools/clippy/tests/ui/option_filter_map.fixed
index f9d1825ade0..b20f73f3110 100644
--- a/src/tools/clippy/tests/ui/option_filter_map.fixed
+++ b/src/tools/clippy/tests/ui/option_filter_map.fixed
@@ -1,8 +1,6 @@
-#![warn(clippy::option_filter_map)]
 // run-rustfix
-fn odds_out(x: i32) -> Option<i32> {
-    if x % 2 == 0 { Some(x) } else { None }
-}
+#![warn(clippy::option_filter_map)]
+#![allow(clippy::map_flatten)]
 
 fn main() {
     let _ = Some(Some(1)).flatten();
@@ -21,3 +19,7 @@ fn main() {
         .map(odds_out)
         .flatten();
 }
+
+fn odds_out(x: i32) -> Option<i32> {
+    if x % 2 == 0 { Some(x) } else { None }
+}
diff --git a/src/tools/clippy/tests/ui/option_filter_map.rs b/src/tools/clippy/tests/ui/option_filter_map.rs
index 588e1ccccce..7abaaa0fb83 100644
--- a/src/tools/clippy/tests/ui/option_filter_map.rs
+++ b/src/tools/clippy/tests/ui/option_filter_map.rs
@@ -1,8 +1,6 @@
-#![warn(clippy::option_filter_map)]
 // run-rustfix
-fn odds_out(x: i32) -> Option<i32> {
-    if x % 2 == 0 { Some(x) } else { None }
-}
+#![warn(clippy::option_filter_map)]
+#![allow(clippy::map_flatten)]
 
 fn main() {
     let _ = Some(Some(1)).filter(Option::is_some).map(Option::unwrap);
@@ -23,3 +21,7 @@ fn main() {
         .filter(|o| o.is_some())
         .map(|o| o.unwrap());
 }
+
+fn odds_out(x: i32) -> Option<i32> {
+    if x % 2 == 0 { Some(x) } else { None }
+}
diff --git a/src/tools/clippy/tests/ui/option_filter_map.stderr b/src/tools/clippy/tests/ui/option_filter_map.stderr
index 31a82969d5a..4a030ac9ab0 100644
--- a/src/tools/clippy/tests/ui/option_filter_map.stderr
+++ b/src/tools/clippy/tests/ui/option_filter_map.stderr
@@ -1,5 +1,5 @@
 error: `filter` for `Some` followed by `unwrap`
-  --> $DIR/option_filter_map.rs:8:27
+  --> $DIR/option_filter_map.rs:6:27
    |
 LL |     let _ = Some(Some(1)).filter(Option::is_some).map(Option::unwrap);
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
@@ -7,37 +7,37 @@ LL |     let _ = Some(Some(1)).filter(Option::is_some).map(Option::unwrap);
    = note: `-D clippy::option-filter-map` implied by `-D warnings`
 
 error: `filter` for `Some` followed by `unwrap`
-  --> $DIR/option_filter_map.rs:9:27
+  --> $DIR/option_filter_map.rs:7:27
    |
 LL |     let _ = Some(Some(1)).filter(|o| o.is_some()).map(|o| o.unwrap());
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
 
 error: `filter` for `Some` followed by `unwrap`
-  --> $DIR/option_filter_map.rs:10:35
+  --> $DIR/option_filter_map.rs:8:35
    |
 LL |     let _ = Some(1).map(odds_out).filter(Option::is_some).map(Option::unwrap);
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
 
 error: `filter` for `Some` followed by `unwrap`
-  --> $DIR/option_filter_map.rs:11:35
+  --> $DIR/option_filter_map.rs:9:35
    |
 LL |     let _ = Some(1).map(odds_out).filter(|o| o.is_some()).map(|o| o.unwrap());
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
 
 error: `filter` for `Some` followed by `unwrap`
-  --> $DIR/option_filter_map.rs:13:39
+  --> $DIR/option_filter_map.rs:11:39
    |
 LL |     let _ = vec![Some(1)].into_iter().filter(Option::is_some).map(Option::unwrap);
    |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
 
 error: `filter` for `Some` followed by `unwrap`
-  --> $DIR/option_filter_map.rs:14:39
+  --> $DIR/option_filter_map.rs:12:39
    |
 LL |     let _ = vec![Some(1)].into_iter().filter(|o| o.is_some()).map(|o| o.unwrap());
    |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
 
 error: `filter` for `Some` followed by `unwrap`
-  --> $DIR/option_filter_map.rs:18:10
+  --> $DIR/option_filter_map.rs:16:10
    |
 LL |           .filter(Option::is_some)
    |  __________^
@@ -45,7 +45,7 @@ LL | |         .map(Option::unwrap);
    | |____________________________^ help: consider using `flatten` instead: `flatten()`
 
 error: `filter` for `Some` followed by `unwrap`
-  --> $DIR/option_filter_map.rs:23:10
+  --> $DIR/option_filter_map.rs:21:10
    |
 LL |           .filter(|o| o.is_some())
    |  __________^
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 9cb6a9d1ecc..ce3093c542a 100644
--- a/src/tools/clippy/tests/ui/option_if_let_else.fixed
+++ b/src/tools/clippy/tests/ui/option_if_let_else.fixed
@@ -75,6 +75,17 @@ fn negative_tests(arg: Option<u32>) -> u32 {
     7
 }
 
+// #7973
+fn pattern_to_vec(pattern: &str) -> Vec<String> {
+    pattern
+        .trim_matches('/')
+        .split('/')
+        .flat_map(|s| {
+            s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])
+        })
+        .collect::<Vec<_>>()
+}
+
 fn main() {
     let optional = Some(5);
     let _ = optional.map_or(5, |x| x + 2);
@@ -110,7 +121,7 @@ fn main() {
 
     let s = String::new();
     // Lint, both branches immutably borrow `s`.
-    let _ = Some(0).map_or_else(|| s.len(), |x| s.len() + x);
+    let _ = Some(0).map_or(s.len(), |x| s.len() + x);
 
     let s = String::new();
     // Lint, `Some` branch consumes `s`, but else branch doesn't use `s`.
@@ -146,4 +157,6 @@ fn main() {
         // Don't lint. `await` can't be moved into a closure.
         let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 };
     }
+
+    let _ = pattern_to_vec("hello world");
 }
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 b3ba5eb870a..c228b2f43d1 100644
--- a/src/tools/clippy/tests/ui/option_if_let_else.rs
+++ b/src/tools/clippy/tests/ui/option_if_let_else.rs
@@ -94,6 +94,21 @@ fn negative_tests(arg: Option<u32>) -> u32 {
     7
 }
 
+// #7973
+fn pattern_to_vec(pattern: &str) -> Vec<String> {
+    pattern
+        .trim_matches('/')
+        .split('/')
+        .flat_map(|s| {
+            if let Some(idx) = s.find('.') {
+                vec![s[..idx].to_string(), s[idx..].to_string()]
+            } else {
+                vec![s.to_string()]
+            }
+        })
+        .collect::<Vec<_>>()
+}
+
 fn main() {
     let optional = Some(5);
     let _ = if let Some(x) = optional { x + 2 } else { 5 };
@@ -171,4 +186,6 @@ fn main() {
         // Don't lint. `await` can't be moved into a closure.
         let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 };
     }
+
+    let _ = pattern_to_vec("hello world");
 }
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 685bb48ea37..4e64cd7cdb1 100644
--- a/src/tools/clippy/tests/ui/option_if_let_else.stderr
+++ b/src/tools/clippy/tests/ui/option_if_let_else.stderr
@@ -142,14 +142,24 @@ LL +         y
 LL ~     }, |x| x * x * x * x);
    |
 
+error: use Option::map_or_else instead of an if let/else
+  --> $DIR/option_if_let_else.rs:103:13
+   |
+LL | /             if let Some(idx) = s.find('.') {
+LL | |                 vec![s[..idx].to_string(), s[idx..].to_string()]
+LL | |             } else {
+LL | |                 vec![s.to_string()]
+LL | |             }
+   | |_____________^ help: try: `s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])`
+
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:99:13
+  --> $DIR/option_if_let_else.rs:114:13
    |
 LL |     let _ = if let Some(x) = optional { x + 2 } else { 5 };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)`
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:108:13
+  --> $DIR/option_if_let_else.rs:123:13
    |
 LL |       let _ = if let Some(x) = Some(0) {
    |  _____________^
@@ -170,14 +180,14 @@ LL +             }
 LL ~         });
    |
 
-error: use Option::map_or_else instead of an if let/else
-  --> $DIR/option_if_let_else.rs:136:13
+error: use Option::map_or instead of an if let/else
+  --> $DIR/option_if_let_else.rs:151: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)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)`
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:140:13
+  --> $DIR/option_if_let_else.rs:155:13
    |
 LL |       let _ = if let Some(x) = Some(0) {
    |  _____________^
@@ -196,5 +206,5 @@ LL +         s.len() + x
 LL ~     });
    |
 
-error: aborting due to 14 previous errors
+error: aborting due to 15 previous errors
 
diff --git a/src/tools/clippy/tests/ui/option_map_or_none.fixed b/src/tools/clippy/tests/ui/option_map_or_none.fixed
index d80c3c7c1b7..04bfac7735f 100644
--- a/src/tools/clippy/tests/ui/option_map_or_none.fixed
+++ b/src/tools/clippy/tests/ui/option_map_or_none.fixed
@@ -4,13 +4,23 @@
 
 fn main() {
     let opt = Some(1);
+    let r: Result<i32, i32> = Ok(1);
+    let bar = |_| Some(1);
 
     // Check `OPTION_MAP_OR_NONE`.
     // Single line case.
-    let _ = opt.and_then(|x| Some(x + 1));
+    let _: Option<i32> = opt.map(|x| x + 1);
     // Multi-line case.
     #[rustfmt::skip]
-    let _ = opt.and_then(|x| {
-                        Some(x + 1)
-                       });
+    let _: Option<i32> = opt.map(|x| x + 1);
+    // function returning `Option`
+    let _: Option<i32> = opt.and_then(bar);
+    let _: Option<i32> = opt.and_then(|x| {
+        let offset = 0;
+        let height = x;
+        Some(offset + height)
+    });
+
+    // Check `RESULT_MAP_OR_INTO_OPTION`.
+    let _: Option<i32> = r.ok();
 }
diff --git a/src/tools/clippy/tests/ui/option_map_or_none.rs b/src/tools/clippy/tests/ui/option_map_or_none.rs
index 629842419e5..bb84f8a48f4 100644
--- a/src/tools/clippy/tests/ui/option_map_or_none.rs
+++ b/src/tools/clippy/tests/ui/option_map_or_none.rs
@@ -4,13 +4,25 @@
 
 fn main() {
     let opt = Some(1);
+    let r: Result<i32, i32> = Ok(1);
+    let bar = |_| Some(1);
 
     // Check `OPTION_MAP_OR_NONE`.
     // Single line case.
-    let _ = opt.map_or(None, |x| Some(x + 1));
+    let _: Option<i32> = opt.map_or(None, |x| Some(x + 1));
     // Multi-line case.
     #[rustfmt::skip]
-    let _ = opt.map_or(None, |x| {
+    let _: Option<i32> = opt.map_or(None, |x| {
                         Some(x + 1)
                        });
+    // function returning `Option`
+    let _: Option<i32> = opt.map_or(None, bar);
+    let _: Option<i32> = opt.map_or(None, |x| {
+        let offset = 0;
+        let height = x;
+        Some(offset + height)
+    });
+
+    // Check `RESULT_MAP_OR_INTO_OPTION`.
+    let _: Option<i32> = r.map_or(None, Some);
 }
diff --git a/src/tools/clippy/tests/ui/option_map_or_none.stderr b/src/tools/clippy/tests/ui/option_map_or_none.stderr
index 27d68b85e6f..7befcb89086 100644
--- a/src/tools/clippy/tests/ui/option_map_or_none.stderr
+++ b/src/tools/clippy/tests/ui/option_map_or_none.stderr
@@ -1,26 +1,53 @@
-error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `and_then(..)` instead
-  --> $DIR/option_map_or_none.rs:10:13
+error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `map(..)` instead
+  --> $DIR/option_map_or_none.rs:12:26
    |
-LL |     let _ = opt.map_or(None, |x| Some(x + 1));
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `opt.and_then(|x| Some(x + 1))`
+LL |     let _: Option<i32> = opt.map_or(None, |x| Some(x + 1));
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `map` instead: `opt.map(|x| x + 1)`
    |
    = note: `-D clippy::option-map-or-none` implied by `-D warnings`
 
-error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `and_then(..)` instead
-  --> $DIR/option_map_or_none.rs:13:13
+error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `map(..)` instead
+  --> $DIR/option_map_or_none.rs:15:26
    |
-LL |       let _ = opt.map_or(None, |x| {
-   |  _____________^
+LL |       let _: Option<i32> = opt.map_or(None, |x| {
+   |  __________________________^
 LL | |                         Some(x + 1)
 LL | |                        });
-   | |_________________________^
+   | |_________________________^ help: try using `map` instead: `opt.map(|x| x + 1)`
+
+error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `and_then(..)` instead
+  --> $DIR/option_map_or_none.rs:19:26
+   |
+LL |     let _: Option<i32> = opt.map_or(None, bar);
+   |                          ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `opt.and_then(bar)`
+
+error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `and_then(..)` instead
+  --> $DIR/option_map_or_none.rs:20:26
+   |
+LL |       let _: Option<i32> = opt.map_or(None, |x| {
+   |  __________________________^
+LL | |         let offset = 0;
+LL | |         let height = x;
+LL | |         Some(offset + height)
+LL | |     });
+   | |______^
    |
 help: try using `and_then` instead
    |
-LL ~     let _ = opt.and_then(|x| {
-LL +                         Some(x + 1)
-LL ~                        });
+LL ~     let _: Option<i32> = opt.and_then(|x| {
+LL +         let offset = 0;
+LL +         let height = x;
+LL +         Some(offset + height)
+LL ~     });
+   |
+
+error: called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling `ok()` instead
+  --> $DIR/option_map_or_none.rs:27:26
+   |
+LL |     let _: Option<i32> = r.map_or(None, Some);
+   |                          ^^^^^^^^^^^^^^^^^^^^ help: try using `ok` instead: `r.ok()`
    |
+   = note: `-D clippy::result-map-or-into-option` implied by `-D warnings`
 
-error: aborting due to 2 previous errors
+error: aborting due to 5 previous errors
 
diff --git a/src/tools/clippy/tests/ui/or_fun_call.fixed b/src/tools/clippy/tests/ui/or_fun_call.fixed
index c2f94d0e857..d6d6ab49734 100644
--- a/src/tools/clippy/tests/ui/or_fun_call.fixed
+++ b/src/tools/clippy/tests/ui/or_fun_call.fixed
@@ -43,7 +43,7 @@ fn or_fun_call() {
     with_enum.unwrap_or(Enum::A(5));
 
     let with_const_fn = Some(Duration::from_secs(1));
-    with_const_fn.unwrap_or_else(|| Duration::from_secs(5));
+    with_const_fn.unwrap_or(Duration::from_secs(5));
 
     let with_constructor = Some(vec![1]);
     with_constructor.unwrap_or_else(make);
@@ -79,16 +79,16 @@ fn or_fun_call() {
     without_default.unwrap_or_else(Foo::new);
 
     let mut map = HashMap::<u64, String>::new();
-    map.entry(42).or_insert_with(String::new);
+    map.entry(42).or_insert(String::new());
 
     let mut map_vec = HashMap::<u64, Vec<i32>>::new();
-    map_vec.entry(42).or_insert_with(Vec::new);
+    map_vec.entry(42).or_insert(vec![]);
 
     let mut btree = BTreeMap::<u64, String>::new();
-    btree.entry(42).or_insert_with(String::new);
+    btree.entry(42).or_insert(String::new());
 
     let mut btree_vec = BTreeMap::<u64, Vec<i32>>::new();
-    btree_vec.entry(42).or_insert_with(Vec::new);
+    btree_vec.entry(42).or_insert(vec![]);
 
     let stringy = Some(String::from(""));
     let _ = stringy.unwrap_or_else(|| "".to_owned());
@@ -129,7 +129,7 @@ fn test_or_with_ctors() {
 
     let b = "b".to_string();
     let _ = Some(Bar("a".to_string(), Duration::from_secs(1)))
-        .or_else(|| Some(Bar(b, Duration::from_secs(2))));
+        .or(Some(Bar(b, Duration::from_secs(2))));
 
     let vec = vec!["foo"];
     let _ = opt.ok_or(vec.len());
@@ -155,16 +155,24 @@ fn f() -> Option<()> {
 }
 
 mod issue6675 {
+    unsafe fn ptr_to_ref<'a, T>(p: *const T) -> &'a T {
+        #[allow(unused)]
+        let x = vec![0; 1000]; // future-proofing, make this function expensive.
+        &*p
+    }
+
     unsafe fn foo() {
-        let mut s = "test".to_owned();
-        None.unwrap_or_else(|| s.as_mut_vec());
+        let s = "test".to_owned();
+        let s = &s as *const _;
+        None.unwrap_or_else(|| ptr_to_ref(s));
     }
 
     fn bar() {
-        let mut s = "test".to_owned();
-        None.unwrap_or_else(|| unsafe { s.as_mut_vec() });
+        let s = "test".to_owned();
+        let s = &s as *const _;
+        None.unwrap_or_else(|| unsafe { ptr_to_ref(s) });
         #[rustfmt::skip]
-        None.unwrap_or_else(|| unsafe { s.as_mut_vec() });
+        None.unwrap_or_else(|| unsafe { ptr_to_ref(s) });
     }
 }
 
diff --git a/src/tools/clippy/tests/ui/or_fun_call.rs b/src/tools/clippy/tests/ui/or_fun_call.rs
index afaf92961b0..8eadc6ce3b4 100644
--- a/src/tools/clippy/tests/ui/or_fun_call.rs
+++ b/src/tools/clippy/tests/ui/or_fun_call.rs
@@ -155,16 +155,24 @@ fn f() -> Option<()> {
 }
 
 mod issue6675 {
+    unsafe fn ptr_to_ref<'a, T>(p: *const T) -> &'a T {
+        #[allow(unused)]
+        let x = vec![0; 1000]; // future-proofing, make this function expensive.
+        &*p
+    }
+
     unsafe fn foo() {
-        let mut s = "test".to_owned();
-        None.unwrap_or(s.as_mut_vec());
+        let s = "test".to_owned();
+        let s = &s as *const _;
+        None.unwrap_or(ptr_to_ref(s));
     }
 
     fn bar() {
-        let mut s = "test".to_owned();
-        None.unwrap_or(unsafe { s.as_mut_vec() });
+        let s = "test".to_owned();
+        let s = &s as *const _;
+        None.unwrap_or(unsafe { ptr_to_ref(s) });
         #[rustfmt::skip]
-        None.unwrap_or( unsafe { s.as_mut_vec() }    );
+        None.unwrap_or( unsafe { ptr_to_ref(s) }    );
     }
 }
 
diff --git a/src/tools/clippy/tests/ui/or_fun_call.stderr b/src/tools/clippy/tests/ui/or_fun_call.stderr
index b2bcbd38c2d..9d0c42b10c2 100644
--- a/src/tools/clippy/tests/ui/or_fun_call.stderr
+++ b/src/tools/clippy/tests/ui/or_fun_call.stderr
@@ -1,16 +1,10 @@
 error: use of `unwrap_or` followed by a function call
-  --> $DIR/or_fun_call.rs:46:19
-   |
-LL |     with_const_fn.unwrap_or(Duration::from_secs(5));
-   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Duration::from_secs(5))`
-   |
-   = note: `-D clippy::or-fun-call` implied by `-D warnings`
-
-error: use of `unwrap_or` followed by a function call
   --> $DIR/or_fun_call.rs:49:22
    |
 LL |     with_constructor.unwrap_or(make());
    |                      ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(make)`
+   |
+   = note: `-D clippy::or-fun-call` implied by `-D warnings`
 
 error: use of `unwrap_or` followed by a call to `new`
   --> $DIR/or_fun_call.rs:52:5
@@ -72,30 +66,6 @@ error: use of `unwrap_or` followed by a function call
 LL |     without_default.unwrap_or(Foo::new());
    |                     ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)`
 
-error: use of `or_insert` followed by a function call
-  --> $DIR/or_fun_call.rs:82:19
-   |
-LL |     map.entry(42).or_insert(String::new());
-   |                   ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)`
-
-error: use of `or_insert` followed by a function call
-  --> $DIR/or_fun_call.rs:85:23
-   |
-LL |     map_vec.entry(42).or_insert(vec![]);
-   |                       ^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(Vec::new)`
-
-error: use of `or_insert` followed by a function call
-  --> $DIR/or_fun_call.rs:88:21
-   |
-LL |     btree.entry(42).or_insert(String::new());
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)`
-
-error: use of `or_insert` followed by a function call
-  --> $DIR/or_fun_call.rs:91:25
-   |
-LL |     btree_vec.entry(42).or_insert(vec![]);
-   |                         ^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(Vec::new)`
-
 error: use of `unwrap_or` followed by a function call
   --> $DIR/or_fun_call.rs:94:21
    |
@@ -120,29 +90,23 @@ error: use of `or` followed by a function call
 LL |     let _ = Some("a".to_string()).or(Some("b".to_string()));
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))`
 
-error: use of `or` followed by a function call
-  --> $DIR/or_fun_call.rs:132:10
-   |
-LL |         .or(Some(Bar(b, Duration::from_secs(2))));
-   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))`
-
 error: use of `unwrap_or` followed by a function call
-  --> $DIR/or_fun_call.rs:160:14
+  --> $DIR/or_fun_call.rs:167:14
    |
-LL |         None.unwrap_or(s.as_mut_vec());
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| s.as_mut_vec())`
+LL |         None.unwrap_or(ptr_to_ref(s));
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| ptr_to_ref(s))`
 
 error: use of `unwrap_or` followed by a function call
-  --> $DIR/or_fun_call.rs:165:14
+  --> $DIR/or_fun_call.rs:173:14
    |
-LL |         None.unwrap_or(unsafe { s.as_mut_vec() });
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { s.as_mut_vec() })`
+LL |         None.unwrap_or(unsafe { ptr_to_ref(s) });
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
 
 error: use of `unwrap_or` followed by a function call
-  --> $DIR/or_fun_call.rs:167:14
+  --> $DIR/or_fun_call.rs:175:14
    |
-LL |         None.unwrap_or( unsafe { s.as_mut_vec() }    );
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { s.as_mut_vec() })`
+LL |         None.unwrap_or( unsafe { ptr_to_ref(s) }    );
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
 
-error: aborting due to 24 previous errors
+error: aborting due to 18 previous errors
 
diff --git a/src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.rs b/src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.rs
index 9b4f2f1f579..55a8c26215e 100644
--- a/src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.rs
+++ b/src/tools/clippy/tests/ui/pattern_type_mismatch/mutability.rs
@@ -37,4 +37,13 @@ fn should_not_lint() {
         Some(_) => (),
         _ => (),
     }
+
+    const FOO: &str = "foo";
+
+    fn foo(s: &str) -> i32 {
+        match s {
+            FOO => 1,
+            _ => 0,
+        }
+    }
 }
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_late.rs b/src/tools/clippy/tests/ui/redundant_closure_call_late.rs
index 1f4864b7289..5612827bd39 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_late.rs
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_late.rs
@@ -1,6 +1,7 @@
 // non rustfixable, see redundant_closure_call_fixable.rs
 
 #![warn(clippy::redundant_closure_call)]
+#![allow(clippy::needless_late_init)]
 
 fn main() {
     let mut i = 1;
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_late.stderr b/src/tools/clippy/tests/ui/redundant_closure_call_late.stderr
index 7c8865f1bd3..4eca43a2b59 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_late.stderr
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_late.stderr
@@ -1,5 +1,5 @@
 error: closure called just once immediately after it was declared
-  --> $DIR/redundant_closure_call_late.rs:15:5
+  --> $DIR/redundant_closure_call_late.rs:16:5
    |
 LL |     i = redun_closure();
    |     ^^^^^^^^^^^^^^^^^^^
@@ -7,13 +7,13 @@ LL |     i = redun_closure();
    = note: `-D clippy::redundant-closure-call` implied by `-D warnings`
 
 error: closure called just once immediately after it was declared
-  --> $DIR/redundant_closure_call_late.rs:19:5
+  --> $DIR/redundant_closure_call_late.rs:20:5
    |
 LL |     i = shadowed_closure();
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 error: closure called just once immediately after it was declared
-  --> $DIR/redundant_closure_call_late.rs:21:5
+  --> $DIR/redundant_closure_call_late.rs:22:5
    |
 LL |     i = shadowed_closure();
    |     ^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/redundant_else.rs b/src/tools/clippy/tests/ui/redundant_else.rs
index e8a6e940c01..64f566735cd 100644
--- a/src/tools/clippy/tests/ui/redundant_else.rs
+++ b/src/tools/clippy/tests/ui/redundant_else.rs
@@ -1,5 +1,5 @@
 #![warn(clippy::redundant_else)]
-#![allow(clippy::needless_return, clippy::if_same_then_else)]
+#![allow(clippy::needless_return, clippy::if_same_then_else, clippy::needless_late_init)]
 
 fn main() {
     loop {
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed
index 813e268a60c..cc93859269c 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed
@@ -80,3 +80,9 @@ const fn issue6067() {
 
     None::<()>.is_none();
 }
+
+#[allow(clippy::deref_addrof, dead_code)]
+fn issue7921() {
+    if (&None::<()>).is_none() {}
+    if (&None::<()>).is_none() {}
+}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs
index 82a98468943..280dca40c01 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs
@@ -95,3 +95,9 @@ const fn issue6067() {
         None => true,
     };
 }
+
+#[allow(clippy::deref_addrof, dead_code)]
+fn issue7921() {
+    if let None = *(&None::<()>) {}
+    if let None = *&None::<()> {}
+}
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr
index 3a58e5ad7be..27ff812ba45 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr
@@ -130,5 +130,17 @@ LL | |         None => true,
 LL | |     };
    | |_____^ help: try this: `None::<()>.is_none()`
 
-error: aborting due to 19 previous errors
+error: redundant pattern matching, consider using `is_none()`
+  --> $DIR/redundant_pattern_matching_option.rs:101:12
+   |
+LL |     if let None = *(&None::<()>) {}
+   |     -------^^^^----------------- help: try this: `if (&None::<()>).is_none()`
+
+error: redundant pattern matching, consider using `is_none()`
+  --> $DIR/redundant_pattern_matching_option.rs:102:12
+   |
+LL |     if let None = *&None::<()> {}
+   |     -------^^^^--------------- help: try this: `if (&None::<()>).is_none()`
+
+error: aborting due to 21 previous errors
 
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed
index d7af5d762ae..83c783385ef 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed
@@ -70,8 +70,8 @@ fn issue5504() {
     }
 
     fn try_result_opt() -> Result<i32, i32> {
-        while r#try!(result_opt()).is_some() {}
-        if r#try!(result_opt()).is_some() {}
+        while (r#try!(result_opt())).is_some() {}
+        if (r#try!(result_opt())).is_some() {}
         Ok(42)
     }
 
diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr
index e06f095da20..d674d061e4d 100644
--- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr
+++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr
@@ -88,13 +88,13 @@ error: redundant pattern matching, consider using `is_some()`
   --> $DIR/redundant_pattern_matching_result.rs:85:19
    |
 LL |         while let Some(_) = r#try!(result_opt()) {}
-   |         ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()`
+   |         ----------^^^^^^^----------------------- help: try this: `while (r#try!(result_opt())).is_some()`
 
 error: redundant pattern matching, consider using `is_some()`
   --> $DIR/redundant_pattern_matching_result.rs:86:16
    |
 LL |         if let Some(_) = r#try!(result_opt()) {}
-   |         -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()`
+   |         -------^^^^^^^----------------------- help: try this: `if (r#try!(result_opt())).is_some()`
 
 error: redundant pattern matching, consider using `is_some()`
   --> $DIR/redundant_pattern_matching_result.rs:92:12
diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed
index cc295b509bc..b9425733a8b 100644
--- a/src/tools/clippy/tests/ui/rename.fixed
+++ b/src/tools/clippy/tests/ui/rename.fixed
@@ -6,6 +6,7 @@
 #![allow(clippy::module_name_repetitions)]
 #![allow(clippy::new_without_default)]
 #![allow(clippy::redundant_static_lifetimes)]
+#![allow(clippy::cognitive_complexity)]
 #![allow(clippy::bind_instead_of_map)]
 #![allow(clippy::box_collection)]
 #![allow(clippy::blocks_in_if_conditions)]
@@ -17,6 +18,8 @@
 #![allow(clippy::invisible_characters)]
 #![allow(clippy::single_char_add_str)]
 #![allow(clippy::match_result_ok)]
+#![allow(clippy::disallowed_types)]
+#![allow(clippy::disallowed_methods)]
 // uplifted lints
 #![allow(invalid_value)]
 #![allow(array_into_iter)]
@@ -49,6 +52,8 @@
 #![warn(clippy::invisible_characters)]
 #![warn(clippy::single_char_add_str)]
 #![warn(clippy::match_result_ok)]
+#![warn(clippy::disallowed_types)]
+#![warn(clippy::disallowed_methods)]
 // uplifted lints
 #![warn(invalid_value)]
 #![warn(array_into_iter)]
diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs
index 377075c0246..341c003b9df 100644
--- a/src/tools/clippy/tests/ui/rename.rs
+++ b/src/tools/clippy/tests/ui/rename.rs
@@ -6,6 +6,7 @@
 #![allow(clippy::module_name_repetitions)]
 #![allow(clippy::new_without_default)]
 #![allow(clippy::redundant_static_lifetimes)]
+#![allow(clippy::cognitive_complexity)]
 #![allow(clippy::bind_instead_of_map)]
 #![allow(clippy::box_collection)]
 #![allow(clippy::blocks_in_if_conditions)]
@@ -17,6 +18,8 @@
 #![allow(clippy::invisible_characters)]
 #![allow(clippy::single_char_add_str)]
 #![allow(clippy::match_result_ok)]
+#![allow(clippy::disallowed_types)]
+#![allow(clippy::disallowed_methods)]
 // uplifted lints
 #![allow(invalid_value)]
 #![allow(array_into_iter)]
@@ -49,6 +52,8 @@
 #![warn(clippy::zero_width_space)]
 #![warn(clippy::single_char_push_str)]
 #![warn(clippy::if_let_some_result)]
+#![warn(clippy::disallowed_type)]
+#![warn(clippy::disallowed_method)]
 // uplifted lints
 #![warn(clippy::invalid_ref)]
 #![warn(clippy::into_iter_on_array)]
diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr
index d720f10d117..cdec2808f1d 100644
--- a/src/tools/clippy/tests/ui/rename.stderr
+++ b/src/tools/clippy/tests/ui/rename.stderr
@@ -1,5 +1,5 @@
 error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
-  --> $DIR/rename.rs:31:9
+  --> $DIR/rename.rs:34:9
    |
 LL | #![warn(clippy::stutter)]
    |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
@@ -7,178 +7,190 @@ LL | #![warn(clippy::stutter)]
    = note: `-D renamed-and-removed-lints` implied by `-D warnings`
 
 error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
-  --> $DIR/rename.rs:32:9
+  --> $DIR/rename.rs:35:9
    |
 LL | #![warn(clippy::new_without_default_derive)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
 
 error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
-  --> $DIR/rename.rs:33:9
+  --> $DIR/rename.rs:36:9
    |
 LL | #![warn(clippy::const_static_lifetime)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
 
 error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
-  --> $DIR/rename.rs:34:9
+  --> $DIR/rename.rs:37:9
    |
 LL | #![warn(clippy::cyclomatic_complexity)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
 
 error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
-  --> $DIR/rename.rs:35:9
+  --> $DIR/rename.rs:38:9
    |
 LL | #![warn(clippy::option_and_then_some)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
 
 error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
-  --> $DIR/rename.rs:36:9
+  --> $DIR/rename.rs:39:9
    |
 LL | #![warn(clippy::box_vec)]
    |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
 
 error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions`
-  --> $DIR/rename.rs:37:9
+  --> $DIR/rename.rs:40:9
    |
 LL | #![warn(clippy::block_in_if_condition_expr)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
 
 error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions`
-  --> $DIR/rename.rs:38:9
+  --> $DIR/rename.rs:41:9
    |
 LL | #![warn(clippy::block_in_if_condition_stmt)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
 
 error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or`
-  --> $DIR/rename.rs:39:9
+  --> $DIR/rename.rs:42:9
    |
 LL | #![warn(clippy::option_map_unwrap_or)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
-  --> $DIR/rename.rs:40:9
+  --> $DIR/rename.rs:43:9
    |
 LL | #![warn(clippy::option_map_unwrap_or_else)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
-  --> $DIR/rename.rs:41:9
+  --> $DIR/rename.rs:44:9
    |
 LL | #![warn(clippy::result_map_unwrap_or_else)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used`
-  --> $DIR/rename.rs:42:9
+  --> $DIR/rename.rs:45:9
    |
 LL | #![warn(clippy::option_unwrap_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 
 error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used`
-  --> $DIR/rename.rs:43:9
+  --> $DIR/rename.rs:46:9
    |
 LL | #![warn(clippy::result_unwrap_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 
 error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used`
-  --> $DIR/rename.rs:44:9
+  --> $DIR/rename.rs:47:9
    |
 LL | #![warn(clippy::option_expect_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 
 error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used`
-  --> $DIR/rename.rs:45:9
+  --> $DIR/rename.rs:48:9
    |
 LL | #![warn(clippy::result_expect_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 
 error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles`
-  --> $DIR/rename.rs:46:9
+  --> $DIR/rename.rs:49:9
    |
 LL | #![warn(clippy::for_loop_over_option)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
 
 error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles`
-  --> $DIR/rename.rs:47:9
+  --> $DIR/rename.rs:50:9
    |
 LL | #![warn(clippy::for_loop_over_result)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
 
 error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
-  --> $DIR/rename.rs:48:9
+  --> $DIR/rename.rs:51:9
    |
 LL | #![warn(clippy::identity_conversion)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
 
 error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters`
-  --> $DIR/rename.rs:49:9
+  --> $DIR/rename.rs:52:9
    |
 LL | #![warn(clippy::zero_width_space)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
 
 error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str`
-  --> $DIR/rename.rs:50:9
+  --> $DIR/rename.rs:53:9
    |
 LL | #![warn(clippy::single_char_push_str)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
 
 error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok`
-  --> $DIR/rename.rs:51:9
+  --> $DIR/rename.rs:54:9
    |
 LL | #![warn(clippy::if_let_some_result)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
 
+error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types`
+  --> $DIR/rename.rs:55:9
+   |
+LL | #![warn(clippy::disallowed_type)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types`
+
+error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods`
+  --> $DIR/rename.rs:56:9
+   |
+LL | #![warn(clippy::disallowed_method)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
+
 error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
-  --> $DIR/rename.rs:53:9
+  --> $DIR/rename.rs:58:9
    |
 LL | #![warn(clippy::invalid_ref)]
    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
 
 error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
-  --> $DIR/rename.rs:54:9
+  --> $DIR/rename.rs:59:9
    |
 LL | #![warn(clippy::into_iter_on_array)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
 
 error: lint `clippy::unused_label` has been renamed to `unused_labels`
-  --> $DIR/rename.rs:55:9
+  --> $DIR/rename.rs:60:9
    |
 LL | #![warn(clippy::unused_label)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
 
 error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
-  --> $DIR/rename.rs:56:9
+  --> $DIR/rename.rs:61:9
    |
 LL | #![warn(clippy::drop_bounds)]
    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
 
 error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
-  --> $DIR/rename.rs:57:9
+  --> $DIR/rename.rs:62:9
    |
 LL | #![warn(clippy::temporary_cstring_as_ptr)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
 
 error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
-  --> $DIR/rename.rs:58:9
+  --> $DIR/rename.rs:63:9
    |
 LL | #![warn(clippy::panic_params)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
 
 error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
-  --> $DIR/rename.rs:59:9
+  --> $DIR/rename.rs:64:9
    |
 LL | #![warn(clippy::unknown_clippy_lints)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
 
 error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
-  --> $DIR/rename.rs:60:9
+  --> $DIR/rename.rs:65:9
    |
 LL | #![warn(clippy::invalid_atomic_ordering)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
 
 error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
-  --> $DIR/rename.rs:61:9
+  --> $DIR/rename.rs:66:9
    |
 LL | #![warn(clippy::mem_discriminant_non_enum)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
 
-error: aborting due to 30 previous errors
+error: aborting due to 32 previous errors
 
diff --git a/src/tools/clippy/tests/ui/same_name_method.stderr b/src/tools/clippy/tests/ui/same_name_method.stderr
index 0f9139b41b9..c32c3dd9880 100644
--- a/src/tools/clippy/tests/ui/same_name_method.stderr
+++ b/src/tools/clippy/tests/ui/same_name_method.stderr
@@ -1,4 +1,4 @@
-error: method's name is same to an existing method in a trait
+error: method's name is the same as an existing method in a trait
   --> $DIR/same_name_method.rs:20:13
    |
 LL |             fn foo() {}
@@ -11,7 +11,7 @@ note: existing `foo` defined here
 LL |             fn foo() {}
    |             ^^^^^^^^^^^
 
-error: method's name is same to an existing method in a trait
+error: method's name is the same as an existing method in a trait
   --> $DIR/same_name_method.rs:44:13
    |
 LL |             fn foo() {}
@@ -23,7 +23,7 @@ note: existing `foo` defined here
 LL |             fn foo() {}
    |             ^^^^^^^^^^^
 
-error: method's name is same to an existing method in a trait
+error: method's name is the same as an existing method in a trait
   --> $DIR/same_name_method.rs:58:13
    |
 LL |             fn foo() {}
@@ -35,7 +35,7 @@ note: existing `foo` defined here
 LL |         impl T1 for S {}
    |         ^^^^^^^^^^^^^^^^
 
-error: method's name is same to an existing method in a trait
+error: method's name is the same as an existing method in a trait
   --> $DIR/same_name_method.rs:70:13
    |
 LL |             fn foo() {}
@@ -47,7 +47,7 @@ note: existing `foo` defined here
 LL |         impl T1 for S {}
    |         ^^^^^^^^^^^^^^^^
 
-error: method's name is same to an existing method in a trait
+error: method's name is the same as an existing method in a trait
   --> $DIR/same_name_method.rs:34:13
    |
 LL |             fn clone() {}
diff --git a/src/tools/clippy/tests/ui/search_is_some.rs b/src/tools/clippy/tests/ui/search_is_some.rs
index 72bc6ef35d3..72f335153c1 100644
--- a/src/tools/clippy/tests/ui/search_is_some.rs
+++ b/src/tools/clippy/tests/ui/search_is_some.rs
@@ -36,6 +36,9 @@ fn main() {
     // check that we don't lint if `find()` is called with
     // `Pattern` that is not a string
     let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_some();
+
+    let some_closure = |x: &u32| *x == 0;
+    let _ = (0..1).find(some_closure).is_some();
 }
 
 #[rustfmt::skip]
@@ -70,4 +73,7 @@ fn is_none() {
     // check that we don't lint if `find()` is called with
     // `Pattern` that is not a string
     let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_none();
+
+    let some_closure = |x: &u32| *x == 0;
+    let _ = (0..1).find(some_closure).is_none();
 }
diff --git a/src/tools/clippy/tests/ui/search_is_some.stderr b/src/tools/clippy/tests/ui/search_is_some.stderr
index f3c758e451e..54760545bce 100644
--- a/src/tools/clippy/tests/ui/search_is_some.stderr
+++ b/src/tools/clippy/tests/ui/search_is_some.stderr
@@ -35,8 +35,14 @@ LL | |                    ).is_some();
    |
    = help: this is more succinctly expressed by calling `any()`
 
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some.rs:41:20
+   |
+LL |     let _ = (0..1).find(some_closure).is_some();
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(some_closure)`
+
 error: called `is_none()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some.rs:48:13
+  --> $DIR/search_is_some.rs:51:13
    |
 LL |       let _ = v.iter().find(|&x| {
    |  _____________^
@@ -48,7 +54,7 @@ LL | |                    ).is_none();
    = help: this is more succinctly expressed by calling `any()` with negation
 
 error: called `is_none()` after searching an `Iterator` with `position`
-  --> $DIR/search_is_some.rs:54:13
+  --> $DIR/search_is_some.rs:57:13
    |
 LL |       let _ = v.iter().position(|&x| {
    |  _____________^
@@ -60,7 +66,7 @@ LL | |                    ).is_none();
    = help: this is more succinctly expressed by calling `any()` with negation
 
 error: called `is_none()` after searching an `Iterator` with `rposition`
-  --> $DIR/search_is_some.rs:60:13
+  --> $DIR/search_is_some.rs:63:13
    |
 LL |       let _ = v.iter().rposition(|&x| {
    |  _____________^
@@ -71,5 +77,11 @@ LL | |                    ).is_none();
    |
    = help: this is more succinctly expressed by calling `any()` with negation
 
-error: aborting due to 6 previous errors
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some.rs:78:13
+   |
+LL |     let _ = (0..1).find(some_closure).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(some_closure)`
+
+error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable.fixed
deleted file mode 100644
index 62ff16f67f4..00000000000
--- a/src/tools/clippy/tests/ui/search_is_some_fixable.fixed
+++ /dev/null
@@ -1,68 +0,0 @@
-// run-rustfix
-#![allow(dead_code)]
-#![warn(clippy::search_is_some)]
-
-fn main() {
-    let v = vec![3, 2, 1, 0, -1, -2, -3];
-    let y = &&42;
-
-    // Check `find().is_some()`, single-line case.
-    let _ = v.iter().any(|x| *x < 0);
-    let _ = (0..1).any(|x| **y == x); // one dereference less
-    let _ = (0..1).any(|x| x == 0);
-    let _ = v.iter().any(|x| *x == 0);
-
-    // Check `position().is_some()`, single-line case.
-    let _ = v.iter().any(|&x| x < 0);
-
-    // Check `rposition().is_some()`, single-line case.
-    let _ = v.iter().any(|&x| x < 0);
-
-    let s1 = String::from("hello world");
-    let s2 = String::from("world");
-    // caller of `find()` is a `&`static str`
-    let _ = "hello world".contains("world");
-    let _ = "hello world".contains(&s2);
-    let _ = "hello world".contains(&s2[2..]);
-    // caller of `find()` is a `String`
-    let _ = s1.contains("world");
-    let _ = s1.contains(&s2);
-    let _ = s1.contains(&s2[2..]);
-    // caller of `find()` is slice of `String`
-    let _ = s1[2..].contains("world");
-    let _ = s1[2..].contains(&s2);
-    let _ = s1[2..].contains(&s2[2..]);
-}
-
-fn is_none() {
-    let v = vec![3, 2, 1, 0, -1, -2, -3];
-    let y = &&42;
-
-    // Check `find().is_none()`, single-line case.
-    let _ = !v.iter().any(|x| *x < 0);
-    let _ = !(0..1).any(|x| **y == x); // one dereference less
-    let _ = !(0..1).any(|x| x == 0);
-    let _ = !v.iter().any(|x| *x == 0);
-
-    // Check `position().is_none()`, single-line case.
-    let _ = !v.iter().any(|&x| x < 0);
-
-    // Check `rposition().is_none()`, single-line case.
-    let _ = !v.iter().any(|&x| x < 0);
-
-    let s1 = String::from("hello world");
-    let s2 = String::from("world");
-
-    // caller of `find()` is a `&`static str`
-    let _ = !"hello world".contains("world");
-    let _ = !"hello world".contains(&s2);
-    let _ = !"hello world".contains(&s2[2..]);
-    // caller of `find()` is a `String`
-    let _ = !s1.contains("world");
-    let _ = !s1.contains(&s2);
-    let _ = !s1.contains(&s2[2..]);
-    // caller of `find()` is slice of `String`
-    let _ = !s1[2..].contains("world");
-    let _ = !s1[2..].contains(&s2);
-    let _ = !s1[2..].contains(&s2[2..]);
-}
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable.rs b/src/tools/clippy/tests/ui/search_is_some_fixable.rs
deleted file mode 100644
index 8407f716647..00000000000
--- a/src/tools/clippy/tests/ui/search_is_some_fixable.rs
+++ /dev/null
@@ -1,68 +0,0 @@
-// run-rustfix
-#![allow(dead_code)]
-#![warn(clippy::search_is_some)]
-
-fn main() {
-    let v = vec![3, 2, 1, 0, -1, -2, -3];
-    let y = &&42;
-
-    // Check `find().is_some()`, single-line case.
-    let _ = v.iter().find(|&x| *x < 0).is_some();
-    let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
-    let _ = (0..1).find(|x| *x == 0).is_some();
-    let _ = v.iter().find(|x| **x == 0).is_some();
-
-    // Check `position().is_some()`, single-line case.
-    let _ = v.iter().position(|&x| x < 0).is_some();
-
-    // Check `rposition().is_some()`, single-line case.
-    let _ = v.iter().rposition(|&x| x < 0).is_some();
-
-    let s1 = String::from("hello world");
-    let s2 = String::from("world");
-    // caller of `find()` is a `&`static str`
-    let _ = "hello world".find("world").is_some();
-    let _ = "hello world".find(&s2).is_some();
-    let _ = "hello world".find(&s2[2..]).is_some();
-    // caller of `find()` is a `String`
-    let _ = s1.find("world").is_some();
-    let _ = s1.find(&s2).is_some();
-    let _ = s1.find(&s2[2..]).is_some();
-    // caller of `find()` is slice of `String`
-    let _ = s1[2..].find("world").is_some();
-    let _ = s1[2..].find(&s2).is_some();
-    let _ = s1[2..].find(&s2[2..]).is_some();
-}
-
-fn is_none() {
-    let v = vec![3, 2, 1, 0, -1, -2, -3];
-    let y = &&42;
-
-    // Check `find().is_none()`, single-line case.
-    let _ = v.iter().find(|&x| *x < 0).is_none();
-    let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less
-    let _ = (0..1).find(|x| *x == 0).is_none();
-    let _ = v.iter().find(|x| **x == 0).is_none();
-
-    // Check `position().is_none()`, single-line case.
-    let _ = v.iter().position(|&x| x < 0).is_none();
-
-    // Check `rposition().is_none()`, single-line case.
-    let _ = v.iter().rposition(|&x| x < 0).is_none();
-
-    let s1 = String::from("hello world");
-    let s2 = String::from("world");
-
-    // caller of `find()` is a `&`static str`
-    let _ = "hello world".find("world").is_none();
-    let _ = "hello world".find(&s2).is_none();
-    let _ = "hello world".find(&s2[2..]).is_none();
-    // caller of `find()` is a `String`
-    let _ = s1.find("world").is_none();
-    let _ = s1.find(&s2).is_none();
-    let _ = s1.find(&s2[2..]).is_none();
-    // caller of `find()` is slice of `String`
-    let _ = s1[2..].find("world").is_none();
-    let _ = s1[2..].find(&s2).is_none();
-    let _ = s1[2..].find(&s2[2..]).is_none();
-}
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable.stderr b/src/tools/clippy/tests/ui/search_is_some_fixable.stderr
deleted file mode 100644
index bd1b6955a97..00000000000
--- a/src/tools/clippy/tests/ui/search_is_some_fixable.stderr
+++ /dev/null
@@ -1,184 +0,0 @@
-error: called `is_some()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some_fixable.rs:10:22
-   |
-LL |     let _ = v.iter().find(|&x| *x < 0).is_some();
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x < 0)`
-   |
-   = note: `-D clippy::search-is-some` implied by `-D warnings`
-
-error: called `is_some()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some_fixable.rs:11:20
-   |
-LL |     let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| **y == x)`
-
-error: called `is_some()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some_fixable.rs:12:20
-   |
-LL |     let _ = (0..1).find(|x| *x == 0).is_some();
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0)`
-
-error: called `is_some()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some_fixable.rs:13:22
-   |
-LL |     let _ = v.iter().find(|x| **x == 0).is_some();
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x == 0)`
-
-error: called `is_some()` after searching an `Iterator` with `position`
-  --> $DIR/search_is_some_fixable.rs:16:22
-   |
-LL |     let _ = v.iter().position(|&x| x < 0).is_some();
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)`
-
-error: called `is_some()` after searching an `Iterator` with `rposition`
-  --> $DIR/search_is_some_fixable.rs:19:22
-   |
-LL |     let _ = v.iter().rposition(|&x| x < 0).is_some();
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:24:27
-   |
-LL |     let _ = "hello world".find("world").is_some();
-   |                           ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:25:27
-   |
-LL |     let _ = "hello world".find(&s2).is_some();
-   |                           ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:26:27
-   |
-LL |     let _ = "hello world".find(&s2[2..]).is_some();
-   |                           ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:28:16
-   |
-LL |     let _ = s1.find("world").is_some();
-   |                ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:29:16
-   |
-LL |     let _ = s1.find(&s2).is_some();
-   |                ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:30:16
-   |
-LL |     let _ = s1.find(&s2[2..]).is_some();
-   |                ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:32:21
-   |
-LL |     let _ = s1[2..].find("world").is_some();
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:33:21
-   |
-LL |     let _ = s1[2..].find(&s2).is_some();
-   |                     ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:34:21
-   |
-LL |     let _ = s1[2..].find(&s2[2..]).is_some();
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
-
-error: called `is_none()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some_fixable.rs:42:13
-   |
-LL |     let _ = v.iter().find(|&x| *x < 0).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x < 0)`
-
-error: called `is_none()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some_fixable.rs:43:13
-   |
-LL |     let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| **y == x)`
-
-error: called `is_none()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some_fixable.rs:44:13
-   |
-LL |     let _ = (0..1).find(|x| *x == 0).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| x == 0)`
-
-error: called `is_none()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some_fixable.rs:45:13
-   |
-LL |     let _ = v.iter().find(|x| **x == 0).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x == 0)`
-
-error: called `is_none()` after searching an `Iterator` with `position`
-  --> $DIR/search_is_some_fixable.rs:48:13
-   |
-LL |     let _ = v.iter().position(|&x| x < 0).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)`
-
-error: called `is_none()` after searching an `Iterator` with `rposition`
-  --> $DIR/search_is_some_fixable.rs:51:13
-   |
-LL |     let _ = v.iter().rposition(|&x| x < 0).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:57:13
-   |
-LL |     let _ = "hello world".find("world").is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains("world")`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:58:13
-   |
-LL |     let _ = "hello world".find(&s2).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2)`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:59:13
-   |
-LL |     let _ = "hello world".find(&s2[2..]).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2[2..])`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:61:13
-   |
-LL |     let _ = s1.find("world").is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains("world")`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:62:13
-   |
-LL |     let _ = s1.find(&s2).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2)`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:63:13
-   |
-LL |     let _ = s1.find(&s2[2..]).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2[2..])`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:65:13
-   |
-LL |     let _ = s1[2..].find("world").is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains("world")`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:66:13
-   |
-LL |     let _ = s1[2..].find(&s2).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2)`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:67:13
-   |
-LL |     let _ = s1[2..].find(&s2[2..]).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2[2..])`
-
-error: aborting due to 30 previous errors
-
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed
new file mode 100644
index 00000000000..6831fb2cf59
--- /dev/null
+++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed
@@ -0,0 +1,216 @@
+// run-rustfix
+#![allow(dead_code)]
+#![warn(clippy::search_is_some)]
+
+fn main() {
+    let v = vec![3, 2, 1, 0, -1, -2, -3];
+    let y = &&42;
+
+    // Check `find().is_none()`, single-line case.
+    let _ = !v.iter().any(|x| *x < 0);
+    let _ = !(0..1).any(|x| **y == x); // one dereference less
+    let _ = !(0..1).any(|x| x == 0);
+    let _ = !v.iter().any(|x| *x == 0);
+    let _ = !(4..5).any(|x| x == 1 || x == 3 || x == 5);
+    let _ = !(1..3).any(|x| [1, 2, 3].contains(&x));
+    let _ = !(1..3).any(|x| x == 0 || [1, 2, 3].contains(&x));
+    let _ = !(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0);
+    let _ = !(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1);
+
+    // Check `position().is_none()`, single-line case.
+    let _ = !v.iter().any(|&x| x < 0);
+
+    // Check `rposition().is_none()`, single-line case.
+    let _ = !v.iter().any(|&x| x < 0);
+
+    let s1 = String::from("hello world");
+    let s2 = String::from("world");
+
+    // caller of `find()` is a `&`static str`
+    let _ = !"hello world".contains("world");
+    let _ = !"hello world".contains(&s2);
+    let _ = !"hello world".contains(&s2[2..]);
+    // caller of `find()` is a `String`
+    let _ = !s1.contains("world");
+    let _ = !s1.contains(&s2);
+    let _ = !s1.contains(&s2[2..]);
+    // caller of `find()` is slice of `String`
+    let _ = !s1[2..].contains("world");
+    let _ = !s1[2..].contains(&s2);
+    let _ = !s1[2..].contains(&s2[2..]);
+}
+
+#[allow(clippy::clone_on_copy, clippy::map_clone)]
+mod issue7392 {
+    struct Player {
+        hand: Vec<usize>,
+    }
+    fn filter() {
+        let p = Player {
+            hand: vec![1, 2, 3, 4, 5],
+        };
+        let filter_hand = vec![5];
+        let _ = p
+            .hand
+            .iter()
+            .filter(|c| !filter_hand.iter().any(|cc| c == &cc))
+            .map(|c| c.clone())
+            .collect::<Vec<_>>();
+    }
+
+    struct PlayerTuple {
+        hand: Vec<(usize, char)>,
+    }
+    fn filter_tuple() {
+        let p = PlayerTuple {
+            hand: vec![(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')],
+        };
+        let filter_hand = vec![5];
+        let _ = p
+            .hand
+            .iter()
+            .filter(|(c, _)| !filter_hand.iter().any(|cc| c == cc))
+            .map(|c| c.clone())
+            .collect::<Vec<_>>();
+    }
+
+    fn field_projection() {
+        struct Foo {
+            foo: i32,
+            bar: u32,
+        }
+        let vfoo = vec![Foo { foo: 1, bar: 2 }];
+        let _ = !vfoo.iter().any(|v| v.foo == 1 && v.bar == 2);
+
+        let vfoo = vec![(42, Foo { foo: 1, bar: 2 })];
+        let _ = !vfoo
+            .iter().any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2);
+    }
+
+    fn index_projection() {
+        let vfoo = vec![[0, 1, 2, 3]];
+        let _ = !vfoo.iter().any(|a| a[0] == 42);
+    }
+
+    #[allow(clippy::match_like_matches_macro)]
+    fn slice_projection() {
+        let vfoo = vec![[0, 1, 2, 3, 0, 1, 2, 3]];
+        let _ = !vfoo.iter().any(|sub| sub[1..4].len() == 3);
+    }
+
+    fn please(x: &u32) -> bool {
+        *x == 9
+    }
+
+    fn deref_enough(x: u32) -> bool {
+        x == 78
+    }
+
+    fn arg_no_deref(x: &&u32) -> bool {
+        **x == 78
+    }
+
+    fn more_projections() {
+        let x = 19;
+        let ppx: &u32 = &x;
+        let _ = ![ppx].iter().any(|ppp_x: &&u32| please(ppp_x));
+        let _ = ![String::from("Hey hey")].iter().any(|s| s.len() == 2);
+
+        let v = vec![3, 2, 1, 0];
+        let _ = !v.iter().any(|x| deref_enough(*x));
+        let _ = !v.iter().any(|x: &u32| deref_enough(*x));
+
+        #[allow(clippy::redundant_closure)]
+        let _ = !v.iter().any(|x| arg_no_deref(&x));
+        #[allow(clippy::redundant_closure)]
+        let _ = !v.iter().any(|x: &u32| arg_no_deref(&x));
+    }
+
+    fn field_index_projection() {
+        struct FooDouble {
+            bar: Vec<Vec<i32>>,
+        }
+        struct Foo {
+            bar: Vec<i32>,
+        }
+        struct FooOuter {
+            inner: Foo,
+            inner_double: FooDouble,
+        }
+        let vfoo = vec![FooOuter {
+            inner: Foo { bar: vec![0, 1, 2, 3] },
+            inner_double: FooDouble {
+                bar: vec![vec![0, 1, 2, 3]],
+            },
+        }];
+        let _ = !vfoo
+            .iter().any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2);
+    }
+
+    fn index_field_projection() {
+        struct Foo {
+            bar: i32,
+        }
+        struct FooOuter {
+            inner: Vec<Foo>,
+        }
+        let vfoo = vec![FooOuter {
+            inner: vec![Foo { bar: 0 }],
+        }];
+        let _ = !vfoo.iter().any(|v| v.inner[0].bar == 2);
+    }
+
+    fn double_deref_index_projection() {
+        let vfoo = vec![&&[0, 1, 2, 3]];
+        let _ = !vfoo.iter().any(|x| (**x)[0] == 9);
+    }
+
+    fn method_call_by_ref() {
+        struct Foo {
+            bar: u32,
+        }
+        impl Foo {
+            pub fn by_ref(&self, x: &u32) -> bool {
+                *x == self.bar
+            }
+        }
+        let vfoo = vec![Foo { bar: 1 }];
+        let _ = !vfoo.iter().any(|v| v.by_ref(&v.bar));
+    }
+
+    fn ref_bindings() {
+        let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
+        let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
+    }
+
+    fn test_string_1(s: &str) -> bool {
+        s.is_empty()
+    }
+
+    fn test_u32_1(s: &u32) -> bool {
+        s.is_power_of_two()
+    }
+
+    fn test_u32_2(s: u32) -> bool {
+        s.is_power_of_two()
+    }
+
+    fn projection_in_args_test() {
+        // Index projections
+        let lst = &[String::from("Hello"), String::from("world")];
+        let v: Vec<&[String]> = vec![lst];
+        let _ = !v.iter().any(|s| s[0].is_empty());
+        let _ = !v.iter().any(|s| test_string_1(&s[0]));
+
+        // Field projections
+        struct FieldProjection<'a> {
+            field: &'a u32,
+        }
+        let field = 123456789;
+        let instance = FieldProjection { field: &field };
+        let v = vec![instance];
+        let _ = !v.iter().any(|fp| fp.field.is_power_of_two());
+        let _ = !v.iter().any(|fp| test_u32_1(fp.field));
+        let _ = !v.iter().any(|fp| test_u32_2(*fp.field));
+    }
+}
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs b/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs
new file mode 100644
index 00000000000..778f4f6fa25
--- /dev/null
+++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs
@@ -0,0 +1,222 @@
+// run-rustfix
+#![allow(dead_code)]
+#![warn(clippy::search_is_some)]
+
+fn main() {
+    let v = vec![3, 2, 1, 0, -1, -2, -3];
+    let y = &&42;
+
+    // Check `find().is_none()`, single-line case.
+    let _ = v.iter().find(|&x| *x < 0).is_none();
+    let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less
+    let _ = (0..1).find(|x| *x == 0).is_none();
+    let _ = v.iter().find(|x| **x == 0).is_none();
+    let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_none();
+    let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_none();
+    let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_none();
+    let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_none();
+    let _ = (1..3)
+        .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1)
+        .is_none();
+
+    // Check `position().is_none()`, single-line case.
+    let _ = v.iter().position(|&x| x < 0).is_none();
+
+    // Check `rposition().is_none()`, single-line case.
+    let _ = v.iter().rposition(|&x| x < 0).is_none();
+
+    let s1 = String::from("hello world");
+    let s2 = String::from("world");
+
+    // caller of `find()` is a `&`static str`
+    let _ = "hello world".find("world").is_none();
+    let _ = "hello world".find(&s2).is_none();
+    let _ = "hello world".find(&s2[2..]).is_none();
+    // caller of `find()` is a `String`
+    let _ = s1.find("world").is_none();
+    let _ = s1.find(&s2).is_none();
+    let _ = s1.find(&s2[2..]).is_none();
+    // caller of `find()` is slice of `String`
+    let _ = s1[2..].find("world").is_none();
+    let _ = s1[2..].find(&s2).is_none();
+    let _ = s1[2..].find(&s2[2..]).is_none();
+}
+
+#[allow(clippy::clone_on_copy, clippy::map_clone)]
+mod issue7392 {
+    struct Player {
+        hand: Vec<usize>,
+    }
+    fn filter() {
+        let p = Player {
+            hand: vec![1, 2, 3, 4, 5],
+        };
+        let filter_hand = vec![5];
+        let _ = p
+            .hand
+            .iter()
+            .filter(|c| filter_hand.iter().find(|cc| c == cc).is_none())
+            .map(|c| c.clone())
+            .collect::<Vec<_>>();
+    }
+
+    struct PlayerTuple {
+        hand: Vec<(usize, char)>,
+    }
+    fn filter_tuple() {
+        let p = PlayerTuple {
+            hand: vec![(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')],
+        };
+        let filter_hand = vec![5];
+        let _ = p
+            .hand
+            .iter()
+            .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_none())
+            .map(|c| c.clone())
+            .collect::<Vec<_>>();
+    }
+
+    fn field_projection() {
+        struct Foo {
+            foo: i32,
+            bar: u32,
+        }
+        let vfoo = vec![Foo { foo: 1, bar: 2 }];
+        let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_none();
+
+        let vfoo = vec![(42, Foo { foo: 1, bar: 2 })];
+        let _ = vfoo
+            .iter()
+            .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)
+            .is_none();
+    }
+
+    fn index_projection() {
+        let vfoo = vec![[0, 1, 2, 3]];
+        let _ = vfoo.iter().find(|a| a[0] == 42).is_none();
+    }
+
+    #[allow(clippy::match_like_matches_macro)]
+    fn slice_projection() {
+        let vfoo = vec![[0, 1, 2, 3, 0, 1, 2, 3]];
+        let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_none();
+    }
+
+    fn please(x: &u32) -> bool {
+        *x == 9
+    }
+
+    fn deref_enough(x: u32) -> bool {
+        x == 78
+    }
+
+    fn arg_no_deref(x: &&u32) -> bool {
+        **x == 78
+    }
+
+    fn more_projections() {
+        let x = 19;
+        let ppx: &u32 = &x;
+        let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_none();
+        let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_none();
+
+        let v = vec![3, 2, 1, 0];
+        let _ = v.iter().find(|x| deref_enough(**x)).is_none();
+        let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_none();
+
+        #[allow(clippy::redundant_closure)]
+        let _ = v.iter().find(|x| arg_no_deref(x)).is_none();
+        #[allow(clippy::redundant_closure)]
+        let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_none();
+    }
+
+    fn field_index_projection() {
+        struct FooDouble {
+            bar: Vec<Vec<i32>>,
+        }
+        struct Foo {
+            bar: Vec<i32>,
+        }
+        struct FooOuter {
+            inner: Foo,
+            inner_double: FooDouble,
+        }
+        let vfoo = vec![FooOuter {
+            inner: Foo { bar: vec![0, 1, 2, 3] },
+            inner_double: FooDouble {
+                bar: vec![vec![0, 1, 2, 3]],
+            },
+        }];
+        let _ = vfoo
+            .iter()
+            .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)
+            .is_none();
+    }
+
+    fn index_field_projection() {
+        struct Foo {
+            bar: i32,
+        }
+        struct FooOuter {
+            inner: Vec<Foo>,
+        }
+        let vfoo = vec![FooOuter {
+            inner: vec![Foo { bar: 0 }],
+        }];
+        let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_none();
+    }
+
+    fn double_deref_index_projection() {
+        let vfoo = vec![&&[0, 1, 2, 3]];
+        let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_none();
+    }
+
+    fn method_call_by_ref() {
+        struct Foo {
+            bar: u32,
+        }
+        impl Foo {
+            pub fn by_ref(&self, x: &u32) -> bool {
+                *x == self.bar
+            }
+        }
+        let vfoo = vec![Foo { bar: 1 }];
+        let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_none();
+    }
+
+    fn ref_bindings() {
+        let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none();
+        let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none();
+    }
+
+    fn test_string_1(s: &String) -> bool {
+        s.is_empty()
+    }
+
+    fn test_u32_1(s: &u32) -> bool {
+        s.is_power_of_two()
+    }
+
+    fn test_u32_2(s: u32) -> bool {
+        s.is_power_of_two()
+    }
+
+    fn projection_in_args_test() {
+        // Index projections
+        let lst = &[String::from("Hello"), String::from("world")];
+        let v: Vec<&[String]> = vec![lst];
+        let _ = v.iter().find(|s| s[0].is_empty()).is_none();
+        let _ = v.iter().find(|s| test_string_1(&s[0])).is_none();
+
+        // Field projections
+        struct FieldProjection<'a> {
+            field: &'a u32,
+        }
+        let field = 123456789;
+        let instance = FieldProjection { field: &field };
+        let v = vec![instance];
+        let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_none();
+        let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_none();
+        let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_none();
+    }
+}
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr b/src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr
new file mode 100644
index 00000000000..7c5e5eb589c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none.stderr
@@ -0,0 +1,293 @@
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:10:13
+   |
+LL |     let _ = v.iter().find(|&x| *x < 0).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x < 0)`
+   |
+   = note: `-D clippy::search-is-some` implied by `-D warnings`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:11:13
+   |
+LL |     let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| **y == x)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:12:13
+   |
+LL |     let _ = (0..1).find(|x| *x == 0).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| x == 0)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:13:13
+   |
+LL |     let _ = v.iter().find(|x| **x == 0).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x == 0)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:14:13
+   |
+LL |     let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(4..5).any(|x| x == 1 || x == 3 || x == 5)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:15:13
+   |
+LL |     let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(1..3).any(|x| [1, 2, 3].contains(&x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:16:13
+   |
+LL |     let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(1..3).any(|x| x == 0 || [1, 2, 3].contains(&x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:17:13
+   |
+LL |     let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:18:13
+   |
+LL |       let _ = (1..3)
+   |  _____________^
+LL | |         .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1)
+LL | |         .is_none();
+   | |__________________^ help: use `!_.any()` instead: `!(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)`
+
+error: called `is_none()` after searching an `Iterator` with `position`
+  --> $DIR/search_is_some_fixable_none.rs:23:13
+   |
+LL |     let _ = v.iter().position(|&x| x < 0).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)`
+
+error: called `is_none()` after searching an `Iterator` with `rposition`
+  --> $DIR/search_is_some_fixable_none.rs:26:13
+   |
+LL |     let _ = v.iter().rposition(|&x| x < 0).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:32:13
+   |
+LL |     let _ = "hello world".find("world").is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:33:13
+   |
+LL |     let _ = "hello world".find(&s2).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2)`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:34:13
+   |
+LL |     let _ = "hello world".find(&s2[2..]).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2[2..])`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:36:13
+   |
+LL |     let _ = s1.find("world").is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:37:13
+   |
+LL |     let _ = s1.find(&s2).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2)`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:38:13
+   |
+LL |     let _ = s1.find(&s2[2..]).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2[2..])`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:40:13
+   |
+LL |     let _ = s1[2..].find("world").is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:41:13
+   |
+LL |     let _ = s1[2..].find(&s2).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2)`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:42:13
+   |
+LL |     let _ = s1[2..].find(&s2[2..]).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2[2..])`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:58:25
+   |
+LL |             .filter(|c| filter_hand.iter().find(|cc| c == cc).is_none())
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!filter_hand.iter().any(|cc| c == &cc)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:74:30
+   |
+LL |             .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_none())
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!filter_hand.iter().any(|cc| c == cc)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:85:17
+   |
+LL |         let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|v| v.foo == 1 && v.bar == 2)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:88:17
+   |
+LL |           let _ = vfoo
+   |  _________________^
+LL | |             .iter()
+LL | |             .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)
+LL | |             .is_none();
+   | |______________________^
+   |
+help: use `!_.any()` instead
+   |
+LL ~         let _ = !vfoo
+LL ~             .iter().any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2);
+   |
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:96:17
+   |
+LL |         let _ = vfoo.iter().find(|a| a[0] == 42).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|a| a[0] == 42)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:102:17
+   |
+LL |         let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|sub| sub[1..4].len() == 3)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:120:17
+   |
+LL |         let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![ppx].iter().any(|ppp_x: &&u32| please(ppp_x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:121:17
+   |
+LL |         let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![String::from("Hey hey")].iter().any(|s| s.len() == 2)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:124:17
+   |
+LL |         let _ = v.iter().find(|x| deref_enough(**x)).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| deref_enough(*x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:125:17
+   |
+LL |         let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x: &u32| deref_enough(*x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:128:17
+   |
+LL |         let _ = v.iter().find(|x| arg_no_deref(x)).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| arg_no_deref(&x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:130:17
+   |
+LL |         let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x: &u32| arg_no_deref(&x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:150:17
+   |
+LL |           let _ = vfoo
+   |  _________________^
+LL | |             .iter()
+LL | |             .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)
+LL | |             .is_none();
+   | |______________________^
+   |
+help: use `!_.any()` instead
+   |
+LL ~         let _ = !vfoo
+LL ~             .iter().any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2);
+   |
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:166:17
+   |
+LL |         let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|v| v.inner[0].bar == 2)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:171:17
+   |
+LL |         let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|x| (**x)[0] == 9)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:184:17
+   |
+LL |         let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|v| v.by_ref(&v.bar))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:188:17
+   |
+LL |         let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:189:17
+   |
+LL |         let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)`
+
+error: writing `&String` instead of `&str` involves a new object where a slice will do
+  --> $DIR/search_is_some_fixable_none.rs:192:25
+   |
+LL |     fn test_string_1(s: &String) -> bool {
+   |                         ^^^^^^^ help: change this to: `&str`
+   |
+   = note: `-D clippy::ptr-arg` implied by `-D warnings`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:208:17
+   |
+LL |         let _ = v.iter().find(|s| s[0].is_empty()).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|s| s[0].is_empty())`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:209:17
+   |
+LL |         let _ = v.iter().find(|s| test_string_1(&s[0])).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|s| test_string_1(&s[0]))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:218:17
+   |
+LL |         let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| fp.field.is_power_of_two())`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:219:17
+   |
+LL |         let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| test_u32_1(fp.field))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:220:17
+   |
+LL |         let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| test_u32_2(*fp.field))`
+
+error: aborting due to 44 previous errors
+
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed
new file mode 100644
index 00000000000..7c940a2b069
--- /dev/null
+++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed
@@ -0,0 +1,218 @@
+// run-rustfix
+#![allow(dead_code)]
+#![warn(clippy::search_is_some)]
+
+fn main() {
+    let v = vec![3, 2, 1, 0, -1, -2, -3];
+    let y = &&42;
+
+    // Check `find().is_some()`, single-line case.
+    let _ = v.iter().any(|x| *x < 0);
+    let _ = (0..1).any(|x| **y == x); // one dereference less
+    let _ = (0..1).any(|x| x == 0);
+    let _ = v.iter().any(|x| *x == 0);
+    let _ = (4..5).any(|x| x == 1 || x == 3 || x == 5);
+    let _ = (1..3).any(|x| [1, 2, 3].contains(&x));
+    let _ = (1..3).any(|x| x == 0 || [1, 2, 3].contains(&x));
+    let _ = (1..3).any(|x| [1, 2, 3].contains(&x) || x == 0);
+    let _ = (1..3)
+        .any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1);
+
+    // Check `position().is_some()`, single-line case.
+    let _ = v.iter().any(|&x| x < 0);
+
+    // Check `rposition().is_some()`, single-line case.
+    let _ = v.iter().any(|&x| x < 0);
+
+    let s1 = String::from("hello world");
+    let s2 = String::from("world");
+    // caller of `find()` is a `&`static str`
+    let _ = "hello world".contains("world");
+    let _ = "hello world".contains(&s2);
+    let _ = "hello world".contains(&s2[2..]);
+    // caller of `find()` is a `String`
+    let _ = s1.contains("world");
+    let _ = s1.contains(&s2);
+    let _ = s1.contains(&s2[2..]);
+    // caller of `find()` is slice of `String`
+    let _ = s1[2..].contains("world");
+    let _ = s1[2..].contains(&s2);
+    let _ = s1[2..].contains(&s2[2..]);
+}
+
+#[allow(clippy::clone_on_copy, clippy::map_clone)]
+mod issue7392 {
+    struct Player {
+        hand: Vec<usize>,
+    }
+    fn filter() {
+        let p = Player {
+            hand: vec![1, 2, 3, 4, 5],
+        };
+        let filter_hand = vec![5];
+        let _ = p
+            .hand
+            .iter()
+            .filter(|c| filter_hand.iter().any(|cc| c == &cc))
+            .map(|c| c.clone())
+            .collect::<Vec<_>>();
+    }
+
+    struct PlayerTuple {
+        hand: Vec<(usize, char)>,
+    }
+    fn filter_tuple() {
+        let p = PlayerTuple {
+            hand: vec![(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')],
+        };
+        let filter_hand = vec![5];
+        let _ = p
+            .hand
+            .iter()
+            .filter(|(c, _)| filter_hand.iter().any(|cc| c == cc))
+            .map(|c| c.clone())
+            .collect::<Vec<_>>();
+    }
+
+    fn field_projection() {
+        struct Foo {
+            foo: i32,
+            bar: u32,
+        }
+        let vfoo = vec![Foo { foo: 1, bar: 2 }];
+        let _ = vfoo.iter().any(|v| v.foo == 1 && v.bar == 2);
+
+        let vfoo = vec![(42, Foo { foo: 1, bar: 2 })];
+        let _ = vfoo
+            .iter()
+            .any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2);
+    }
+
+    fn index_projection() {
+        let vfoo = vec![[0, 1, 2, 3]];
+        let _ = vfoo.iter().any(|a| a[0] == 42);
+    }
+
+    #[allow(clippy::match_like_matches_macro)]
+    fn slice_projection() {
+        let vfoo = vec![[0, 1, 2, 3, 0, 1, 2, 3]];
+        let _ = vfoo.iter().any(|sub| sub[1..4].len() == 3);
+    }
+
+    fn please(x: &u32) -> bool {
+        *x == 9
+    }
+
+    fn deref_enough(x: u32) -> bool {
+        x == 78
+    }
+
+    fn arg_no_deref(x: &&u32) -> bool {
+        **x == 78
+    }
+
+    fn more_projections() {
+        let x = 19;
+        let ppx: &u32 = &x;
+        let _ = [ppx].iter().any(|ppp_x: &&u32| please(ppp_x));
+        let _ = [String::from("Hey hey")].iter().any(|s| s.len() == 2);
+
+        let v = vec![3, 2, 1, 0];
+        let _ = v.iter().any(|x| deref_enough(*x));
+        let _ = v.iter().any(|x: &u32| deref_enough(*x));
+
+        #[allow(clippy::redundant_closure)]
+        let _ = v.iter().any(|x| arg_no_deref(&x));
+        #[allow(clippy::redundant_closure)]
+        let _ = v.iter().any(|x: &u32| arg_no_deref(&x));
+    }
+
+    fn field_index_projection() {
+        struct FooDouble {
+            bar: Vec<Vec<i32>>,
+        }
+        struct Foo {
+            bar: Vec<i32>,
+        }
+        struct FooOuter {
+            inner: Foo,
+            inner_double: FooDouble,
+        }
+        let vfoo = vec![FooOuter {
+            inner: Foo { bar: vec![0, 1, 2, 3] },
+            inner_double: FooDouble {
+                bar: vec![vec![0, 1, 2, 3]],
+            },
+        }];
+        let _ = vfoo
+            .iter()
+            .any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2);
+    }
+
+    fn index_field_projection() {
+        struct Foo {
+            bar: i32,
+        }
+        struct FooOuter {
+            inner: Vec<Foo>,
+        }
+        let vfoo = vec![FooOuter {
+            inner: vec![Foo { bar: 0 }],
+        }];
+        let _ = vfoo.iter().any(|v| v.inner[0].bar == 2);
+    }
+
+    fn double_deref_index_projection() {
+        let vfoo = vec![&&[0, 1, 2, 3]];
+        let _ = vfoo.iter().any(|x| (**x)[0] == 9);
+    }
+
+    fn method_call_by_ref() {
+        struct Foo {
+            bar: u32,
+        }
+        impl Foo {
+            pub fn by_ref(&self, x: &u32) -> bool {
+                *x == self.bar
+            }
+        }
+        let vfoo = vec![Foo { bar: 1 }];
+        let _ = vfoo.iter().any(|v| v.by_ref(&v.bar));
+    }
+
+    fn ref_bindings() {
+        let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
+        let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
+    }
+
+    fn test_string_1(s: &str) -> bool {
+        s.is_empty()
+    }
+
+    fn test_u32_1(s: &u32) -> bool {
+        s.is_power_of_two()
+    }
+
+    fn test_u32_2(s: u32) -> bool {
+        s.is_power_of_two()
+    }
+
+    fn projection_in_args_test() {
+        // Index projections
+        let lst = &[String::from("Hello"), String::from("world")];
+        let v: Vec<&[String]> = vec![lst];
+        let _ = v.iter().any(|s| s[0].is_empty());
+        let _ = v.iter().any(|s| test_string_1(&s[0]));
+
+        // Field projections
+        struct FieldProjection<'a> {
+            field: &'a u32,
+        }
+        let field = 123456789;
+        let instance = FieldProjection { field: &field };
+        let v = vec![instance];
+        let _ = v.iter().any(|fp| fp.field.is_power_of_two());
+        let _ = v.iter().any(|fp| test_u32_1(fp.field));
+        let _ = v.iter().any(|fp| test_u32_2(*fp.field));
+    }
+}
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs b/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs
new file mode 100644
index 00000000000..241641fceae
--- /dev/null
+++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs
@@ -0,0 +1,221 @@
+// run-rustfix
+#![allow(dead_code)]
+#![warn(clippy::search_is_some)]
+
+fn main() {
+    let v = vec![3, 2, 1, 0, -1, -2, -3];
+    let y = &&42;
+
+    // Check `find().is_some()`, single-line case.
+    let _ = v.iter().find(|&x| *x < 0).is_some();
+    let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
+    let _ = (0..1).find(|x| *x == 0).is_some();
+    let _ = v.iter().find(|x| **x == 0).is_some();
+    let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_some();
+    let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_some();
+    let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_some();
+    let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_some();
+    let _ = (1..3)
+        .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1)
+        .is_some();
+
+    // Check `position().is_some()`, single-line case.
+    let _ = v.iter().position(|&x| x < 0).is_some();
+
+    // Check `rposition().is_some()`, single-line case.
+    let _ = v.iter().rposition(|&x| x < 0).is_some();
+
+    let s1 = String::from("hello world");
+    let s2 = String::from("world");
+    // caller of `find()` is a `&`static str`
+    let _ = "hello world".find("world").is_some();
+    let _ = "hello world".find(&s2).is_some();
+    let _ = "hello world".find(&s2[2..]).is_some();
+    // caller of `find()` is a `String`
+    let _ = s1.find("world").is_some();
+    let _ = s1.find(&s2).is_some();
+    let _ = s1.find(&s2[2..]).is_some();
+    // caller of `find()` is slice of `String`
+    let _ = s1[2..].find("world").is_some();
+    let _ = s1[2..].find(&s2).is_some();
+    let _ = s1[2..].find(&s2[2..]).is_some();
+}
+
+#[allow(clippy::clone_on_copy, clippy::map_clone)]
+mod issue7392 {
+    struct Player {
+        hand: Vec<usize>,
+    }
+    fn filter() {
+        let p = Player {
+            hand: vec![1, 2, 3, 4, 5],
+        };
+        let filter_hand = vec![5];
+        let _ = p
+            .hand
+            .iter()
+            .filter(|c| filter_hand.iter().find(|cc| c == cc).is_some())
+            .map(|c| c.clone())
+            .collect::<Vec<_>>();
+    }
+
+    struct PlayerTuple {
+        hand: Vec<(usize, char)>,
+    }
+    fn filter_tuple() {
+        let p = PlayerTuple {
+            hand: vec![(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')],
+        };
+        let filter_hand = vec![5];
+        let _ = p
+            .hand
+            .iter()
+            .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_some())
+            .map(|c| c.clone())
+            .collect::<Vec<_>>();
+    }
+
+    fn field_projection() {
+        struct Foo {
+            foo: i32,
+            bar: u32,
+        }
+        let vfoo = vec![Foo { foo: 1, bar: 2 }];
+        let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_some();
+
+        let vfoo = vec![(42, Foo { foo: 1, bar: 2 })];
+        let _ = vfoo
+            .iter()
+            .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)
+            .is_some();
+    }
+
+    fn index_projection() {
+        let vfoo = vec![[0, 1, 2, 3]];
+        let _ = vfoo.iter().find(|a| a[0] == 42).is_some();
+    }
+
+    #[allow(clippy::match_like_matches_macro)]
+    fn slice_projection() {
+        let vfoo = vec![[0, 1, 2, 3, 0, 1, 2, 3]];
+        let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_some();
+    }
+
+    fn please(x: &u32) -> bool {
+        *x == 9
+    }
+
+    fn deref_enough(x: u32) -> bool {
+        x == 78
+    }
+
+    fn arg_no_deref(x: &&u32) -> bool {
+        **x == 78
+    }
+
+    fn more_projections() {
+        let x = 19;
+        let ppx: &u32 = &x;
+        let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_some();
+        let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_some();
+
+        let v = vec![3, 2, 1, 0];
+        let _ = v.iter().find(|x| deref_enough(**x)).is_some();
+        let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_some();
+
+        #[allow(clippy::redundant_closure)]
+        let _ = v.iter().find(|x| arg_no_deref(x)).is_some();
+        #[allow(clippy::redundant_closure)]
+        let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_some();
+    }
+
+    fn field_index_projection() {
+        struct FooDouble {
+            bar: Vec<Vec<i32>>,
+        }
+        struct Foo {
+            bar: Vec<i32>,
+        }
+        struct FooOuter {
+            inner: Foo,
+            inner_double: FooDouble,
+        }
+        let vfoo = vec![FooOuter {
+            inner: Foo { bar: vec![0, 1, 2, 3] },
+            inner_double: FooDouble {
+                bar: vec![vec![0, 1, 2, 3]],
+            },
+        }];
+        let _ = vfoo
+            .iter()
+            .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)
+            .is_some();
+    }
+
+    fn index_field_projection() {
+        struct Foo {
+            bar: i32,
+        }
+        struct FooOuter {
+            inner: Vec<Foo>,
+        }
+        let vfoo = vec![FooOuter {
+            inner: vec![Foo { bar: 0 }],
+        }];
+        let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_some();
+    }
+
+    fn double_deref_index_projection() {
+        let vfoo = vec![&&[0, 1, 2, 3]];
+        let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_some();
+    }
+
+    fn method_call_by_ref() {
+        struct Foo {
+            bar: u32,
+        }
+        impl Foo {
+            pub fn by_ref(&self, x: &u32) -> bool {
+                *x == self.bar
+            }
+        }
+        let vfoo = vec![Foo { bar: 1 }];
+        let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_some();
+    }
+
+    fn ref_bindings() {
+        let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some();
+        let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some();
+    }
+
+    fn test_string_1(s: &String) -> bool {
+        s.is_empty()
+    }
+
+    fn test_u32_1(s: &u32) -> bool {
+        s.is_power_of_two()
+    }
+
+    fn test_u32_2(s: u32) -> bool {
+        s.is_power_of_two()
+    }
+
+    fn projection_in_args_test() {
+        // Index projections
+        let lst = &[String::from("Hello"), String::from("world")];
+        let v: Vec<&[String]> = vec![lst];
+        let _ = v.iter().find(|s| s[0].is_empty()).is_some();
+        let _ = v.iter().find(|s| test_string_1(&s[0])).is_some();
+
+        // Field projections
+        struct FieldProjection<'a> {
+            field: &'a u32,
+        }
+        let field = 123456789;
+        let instance = FieldProjection { field: &field };
+        let v = vec![instance];
+        let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_some();
+        let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_some();
+        let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some();
+    }
+}
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr b/src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr
new file mode 100644
index 00000000000..9212c6e71ff
--- /dev/null
+++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr
@@ -0,0 +1,276 @@
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:10:22
+   |
+LL |     let _ = v.iter().find(|&x| *x < 0).is_some();
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x < 0)`
+   |
+   = note: `-D clippy::search-is-some` implied by `-D warnings`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:11:20
+   |
+LL |     let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| **y == x)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:12:20
+   |
+LL |     let _ = (0..1).find(|x| *x == 0).is_some();
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:13:22
+   |
+LL |     let _ = v.iter().find(|x| **x == 0).is_some();
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x == 0)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:14:20
+   |
+LL |     let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_some();
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 1 || x == 3 || x == 5)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:15:20
+   |
+LL |     let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_some();
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| [1, 2, 3].contains(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:16:20
+   |
+LL |     let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_some();
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0 || [1, 2, 3].contains(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:17:20
+   |
+LL |     let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_some();
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| [1, 2, 3].contains(&x) || x == 0)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:19:10
+   |
+LL |           .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1)
+   |  __________^
+LL | |         .is_some();
+   | |__________________^ help: use `any()` instead: `any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)`
+
+error: called `is_some()` after searching an `Iterator` with `position`
+  --> $DIR/search_is_some_fixable_some.rs:23:22
+   |
+LL |     let _ = v.iter().position(|&x| x < 0).is_some();
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)`
+
+error: called `is_some()` after searching an `Iterator` with `rposition`
+  --> $DIR/search_is_some_fixable_some.rs:26:22
+   |
+LL |     let _ = v.iter().rposition(|&x| x < 0).is_some();
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:31:27
+   |
+LL |     let _ = "hello world".find("world").is_some();
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:32:27
+   |
+LL |     let _ = "hello world".find(&s2).is_some();
+   |                           ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:33:27
+   |
+LL |     let _ = "hello world".find(&s2[2..]).is_some();
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:35:16
+   |
+LL |     let _ = s1.find("world").is_some();
+   |                ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:36:16
+   |
+LL |     let _ = s1.find(&s2).is_some();
+   |                ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:37:16
+   |
+LL |     let _ = s1.find(&s2[2..]).is_some();
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:39:21
+   |
+LL |     let _ = s1[2..].find("world").is_some();
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:40:21
+   |
+LL |     let _ = s1[2..].find(&s2).is_some();
+   |                     ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:41:21
+   |
+LL |     let _ = s1[2..].find(&s2[2..]).is_some();
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:57:44
+   |
+LL |             .filter(|c| filter_hand.iter().find(|cc| c == cc).is_some())
+   |                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|cc| c == &cc)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:73:49
+   |
+LL |             .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_some())
+   |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|cc| c == cc)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:84:29
+   |
+LL |         let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_some();
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|v| v.foo == 1 && v.bar == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:89:14
+   |
+LL |               .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)
+   |  ______________^
+LL | |             .is_some();
+   | |______________________^ help: use `any()` instead: `any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:95:29
+   |
+LL |         let _ = vfoo.iter().find(|a| a[0] == 42).is_some();
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|a| a[0] == 42)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:101:29
+   |
+LL |         let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_some();
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|sub| sub[1..4].len() == 3)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:119:30
+   |
+LL |         let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_some();
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|ppp_x: &&u32| please(ppp_x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:120:50
+   |
+LL |         let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_some();
+   |                                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| s.len() == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:123:26
+   |
+LL |         let _ = v.iter().find(|x| deref_enough(**x)).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| deref_enough(*x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:124:26
+   |
+LL |         let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| deref_enough(*x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:127:26
+   |
+LL |         let _ = v.iter().find(|x| arg_no_deref(x)).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| arg_no_deref(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:129:26
+   |
+LL |         let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| arg_no_deref(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:151:14
+   |
+LL |               .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)
+   |  ______________^
+LL | |             .is_some();
+   | |______________________^ help: use `any()` instead: `any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:165:29
+   |
+LL |         let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_some();
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|v| v.inner[0].bar == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:170:29
+   |
+LL |         let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_some();
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| (**x)[0] == 9)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:183:29
+   |
+LL |         let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_some();
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|v| v.by_ref(&v.bar))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:187:55
+   |
+LL |         let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some();
+   |                                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|(&x, y)| x == *y)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:188:55
+   |
+LL |         let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some();
+   |                                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|(&x, y)| x == *y)`
+
+error: writing `&String` instead of `&str` involves a new object where a slice will do
+  --> $DIR/search_is_some_fixable_some.rs:191:25
+   |
+LL |     fn test_string_1(s: &String) -> bool {
+   |                         ^^^^^^^ help: change this to: `&str`
+   |
+   = note: `-D clippy::ptr-arg` implied by `-D warnings`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:207:26
+   |
+LL |         let _ = v.iter().find(|s| s[0].is_empty()).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| s[0].is_empty())`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:208:26
+   |
+LL |         let _ = v.iter().find(|s| test_string_1(&s[0])).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| test_string_1(&s[0]))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:217:26
+   |
+LL |         let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| fp.field.is_power_of_two())`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:218:26
+   |
+LL |         let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| test_u32_1(fp.field))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:219:26
+   |
+LL |         let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| test_u32_2(*fp.field))`
+
+error: aborting due to 44 previous errors
+
diff --git a/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.rs b/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.rs
index 7a45f1b18d4..91916e7480f 100644
--- a/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.rs
+++ b/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.rs
@@ -1,6 +1,7 @@
 #![warn(clippy::semicolon_if_nothing_returned)]
 #![allow(clippy::redundant_closure)]
 #![feature(label_break_value)]
+#![feature(let_else)]
 
 fn get_unit() {}
 
@@ -110,3 +111,12 @@ fn macro_with_semicolon() {
     }
     repro!();
 }
+
+fn function_returning_option() -> Option<i32> {
+    Some(1)
+}
+
+// No warning
+fn let_else_stmts() {
+    let Some(x) = function_returning_option() else { return; };
+}
diff --git a/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.stderr b/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.stderr
index 78813e7cc1c..41d2c1cfb87 100644
--- a/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.stderr
+++ b/src/tools/clippy/tests/ui/semicolon_if_nothing_returned.stderr
@@ -1,5 +1,5 @@
 error: consider adding a `;` to the last statement for consistent formatting
-  --> $DIR/semicolon_if_nothing_returned.rs:9:5
+  --> $DIR/semicolon_if_nothing_returned.rs:10:5
    |
 LL |     println!("Hello")
    |     ^^^^^^^^^^^^^^^^^ help: add a `;` here: `println!("Hello");`
@@ -7,25 +7,25 @@ LL |     println!("Hello")
    = note: `-D clippy::semicolon-if-nothing-returned` implied by `-D warnings`
 
 error: consider adding a `;` to the last statement for consistent formatting
-  --> $DIR/semicolon_if_nothing_returned.rs:13:5
+  --> $DIR/semicolon_if_nothing_returned.rs:14:5
    |
 LL |     get_unit()
    |     ^^^^^^^^^^ help: add a `;` here: `get_unit();`
 
 error: consider adding a `;` to the last statement for consistent formatting
-  --> $DIR/semicolon_if_nothing_returned.rs:18:5
+  --> $DIR/semicolon_if_nothing_returned.rs:19:5
    |
 LL |     y = x + 1
    |     ^^^^^^^^^ help: add a `;` here: `y = x + 1;`
 
 error: consider adding a `;` to the last statement for consistent formatting
-  --> $DIR/semicolon_if_nothing_returned.rs:24:9
+  --> $DIR/semicolon_if_nothing_returned.rs:25:9
    |
 LL |         hello()
    |         ^^^^^^^ help: add a `;` here: `hello();`
 
 error: consider adding a `;` to the last statement for consistent formatting
-  --> $DIR/semicolon_if_nothing_returned.rs:35:9
+  --> $DIR/semicolon_if_nothing_returned.rs:36:9
    |
 LL |         ptr::drop_in_place(s.as_mut_ptr())
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `ptr::drop_in_place(s.as_mut_ptr());`
diff --git a/src/tools/clippy/tests/ui/shadow.rs b/src/tools/clippy/tests/ui/shadow.rs
index 55caef59f7f..06f6949b66f 100644
--- a/src/tools/clippy/tests/ui/shadow.rs
+++ b/src/tools/clippy/tests/ui/shadow.rs
@@ -79,4 +79,10 @@ fn question_mark() -> Option<()> {
     None
 }
 
+pub async fn foo1(_a: i32) {}
+
+pub async fn foo2(_a: i32, _b: i64) {
+    let _b = _a;
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/shadow.stderr b/src/tools/clippy/tests/ui/shadow.stderr
index feed6e1ba8b..dcc7d4e6b2f 100644
--- a/src/tools/clippy/tests/ui/shadow.stderr
+++ b/src/tools/clippy/tests/ui/shadow.stderr
@@ -241,5 +241,17 @@ note: previous binding is here
 LL |     let _ = |[x]: [u32; 1]| {
    |               ^
 
-error: aborting due to 20 previous errors
+error: `_b` shadows a previous, unrelated binding
+  --> $DIR/shadow.rs:85:9
+   |
+LL |     let _b = _a;
+   |         ^^
+   |
+note: previous binding is here
+  --> $DIR/shadow.rs:84:28
+   |
+LL | pub async fn foo2(_a: i32, _b: i64) {
+   |                            ^^
+
+error: aborting due to 21 previous errors
 
diff --git a/src/tools/clippy/tests/ui/single_char_pattern.fixed b/src/tools/clippy/tests/ui/single_char_pattern.fixed
index 1abd2b7883d..68e26726724 100644
--- a/src/tools/clippy/tests/ui/single_char_pattern.fixed
+++ b/src/tools/clippy/tests/ui/single_char_pattern.fixed
@@ -17,6 +17,7 @@ fn main() {
     x.split('💣');
     // Can't use this lint for unicode code points which don't fit in a char
     x.split("❤️");
+    x.split_inclusive('x');
     x.contains('x');
     x.starts_with('x');
     x.ends_with('x');
@@ -27,6 +28,8 @@ fn main() {
     x.rsplit_terminator('x');
     x.splitn(2, 'x');
     x.rsplitn(2, 'x');
+    x.split_once('x');
+    x.rsplit_once('x');
     x.matches('x');
     x.rmatches('x');
     x.match_indices('x');
@@ -35,6 +38,8 @@ fn main() {
     x.trim_end_matches('x');
     x.strip_prefix('x');
     x.strip_suffix('x');
+    x.replace('x', "y");
+    x.replacen('x', "y", 3);
     // Make sure we escape characters correctly.
     x.split('\n');
     x.split('\'');
@@ -43,7 +48,7 @@ fn main() {
     let h = HashSet::<String>::new();
     h.contains("X"); // should not warn
 
-    x.replace(";", ",").split(','); // issue #2978
+    x.replace(';', ",").split(','); // issue #2978
     x.starts_with('\x03'); // issue #2996
 
     // Issue #3204
@@ -56,4 +61,7 @@ fn main() {
     x.split('a');
     x.split('\'');
     x.split('#');
+    // Must escape backslash in raw strings when converting to char #8060
+    x.split('\\');
+    x.split('\\');
 }
diff --git a/src/tools/clippy/tests/ui/single_char_pattern.rs b/src/tools/clippy/tests/ui/single_char_pattern.rs
index e662bf34be2..186202d78ec 100644
--- a/src/tools/clippy/tests/ui/single_char_pattern.rs
+++ b/src/tools/clippy/tests/ui/single_char_pattern.rs
@@ -17,6 +17,7 @@ fn main() {
     x.split("💣");
     // Can't use this lint for unicode code points which don't fit in a char
     x.split("❤️");
+    x.split_inclusive("x");
     x.contains("x");
     x.starts_with("x");
     x.ends_with("x");
@@ -27,6 +28,8 @@ fn main() {
     x.rsplit_terminator("x");
     x.splitn(2, "x");
     x.rsplitn(2, "x");
+    x.split_once("x");
+    x.rsplit_once("x");
     x.matches("x");
     x.rmatches("x");
     x.match_indices("x");
@@ -35,6 +38,8 @@ fn main() {
     x.trim_end_matches("x");
     x.strip_prefix("x");
     x.strip_suffix("x");
+    x.replace("x", "y");
+    x.replacen("x", "y", 3);
     // Make sure we escape characters correctly.
     x.split("\n");
     x.split("'");
@@ -43,7 +48,7 @@ fn main() {
     let h = HashSet::<String>::new();
     h.contains("X"); // should not warn
 
-    x.replace(";", ",").split(","); // issue #2978
+    x.replace(';', ",").split(","); // issue #2978
     x.starts_with("\x03"); // issue #2996
 
     // Issue #3204
@@ -56,4 +61,7 @@ fn main() {
     x.split(r###"a"###);
     x.split(r###"'"###);
     x.split(r###"#"###);
+    // Must escape backslash in raw strings when converting to char #8060
+    x.split(r#"\"#);
+    x.split(r"\");
 }
diff --git a/src/tools/clippy/tests/ui/single_char_pattern.stderr b/src/tools/clippy/tests/ui/single_char_pattern.stderr
index 22d4b2d460f..5564aac674d 100644
--- a/src/tools/clippy/tests/ui/single_char_pattern.stderr
+++ b/src/tools/clippy/tests/ui/single_char_pattern.stderr
@@ -25,172 +25,214 @@ LL |     x.split("💣");
    |             ^^^^ help: try using a `char` instead: `'💣'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:20:16
+  --> $DIR/single_char_pattern.rs:20:23
+   |
+LL |     x.split_inclusive("x");
+   |                       ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+  --> $DIR/single_char_pattern.rs:21:16
    |
 LL |     x.contains("x");
    |                ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:21:19
+  --> $DIR/single_char_pattern.rs:22:19
    |
 LL |     x.starts_with("x");
    |                   ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:22:17
+  --> $DIR/single_char_pattern.rs:23:17
    |
 LL |     x.ends_with("x");
    |                 ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:23:12
+  --> $DIR/single_char_pattern.rs:24:12
    |
 LL |     x.find("x");
    |            ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:24:13
+  --> $DIR/single_char_pattern.rs:25:13
    |
 LL |     x.rfind("x");
    |             ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:25:14
+  --> $DIR/single_char_pattern.rs:26:14
    |
 LL |     x.rsplit("x");
    |              ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:26:24
+  --> $DIR/single_char_pattern.rs:27:24
    |
 LL |     x.split_terminator("x");
    |                        ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:27:25
+  --> $DIR/single_char_pattern.rs:28:25
    |
 LL |     x.rsplit_terminator("x");
    |                         ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:28:17
+  --> $DIR/single_char_pattern.rs:29:17
    |
 LL |     x.splitn(2, "x");
    |                 ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:29:18
+  --> $DIR/single_char_pattern.rs:30:18
    |
 LL |     x.rsplitn(2, "x");
    |                  ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:30:15
+  --> $DIR/single_char_pattern.rs:31:18
+   |
+LL |     x.split_once("x");
+   |                  ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+  --> $DIR/single_char_pattern.rs:32:19
+   |
+LL |     x.rsplit_once("x");
+   |                   ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+  --> $DIR/single_char_pattern.rs:33:15
    |
 LL |     x.matches("x");
    |               ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:31:16
+  --> $DIR/single_char_pattern.rs:34:16
    |
 LL |     x.rmatches("x");
    |                ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:32:21
+  --> $DIR/single_char_pattern.rs:35:21
    |
 LL |     x.match_indices("x");
    |                     ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:33:22
+  --> $DIR/single_char_pattern.rs:36:22
    |
 LL |     x.rmatch_indices("x");
    |                      ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:34:26
+  --> $DIR/single_char_pattern.rs:37:26
    |
 LL |     x.trim_start_matches("x");
    |                          ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:35:24
+  --> $DIR/single_char_pattern.rs:38:24
    |
 LL |     x.trim_end_matches("x");
    |                        ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:36:20
+  --> $DIR/single_char_pattern.rs:39:20
    |
 LL |     x.strip_prefix("x");
    |                    ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:37:20
+  --> $DIR/single_char_pattern.rs:40:20
    |
 LL |     x.strip_suffix("x");
    |                    ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:39:13
+  --> $DIR/single_char_pattern.rs:41:15
+   |
+LL |     x.replace("x", "y");
+   |               ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+  --> $DIR/single_char_pattern.rs:42:16
+   |
+LL |     x.replacen("x", "y", 3);
+   |                ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+  --> $DIR/single_char_pattern.rs:44:13
    |
 LL |     x.split("/n");
    |             ^^^^ help: try using a `char` instead: `'/n'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:40:13
+  --> $DIR/single_char_pattern.rs:45:13
    |
 LL |     x.split("'");
    |             ^^^ help: try using a `char` instead: `'/''`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:41:13
+  --> $DIR/single_char_pattern.rs:46:13
    |
 LL |     x.split("/'");
    |             ^^^^ help: try using a `char` instead: `'/''`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:46:31
+  --> $DIR/single_char_pattern.rs:51:31
    |
-LL |     x.replace(";", ",").split(","); // issue #2978
+LL |     x.replace(';', ",").split(","); // issue #2978
    |                               ^^^ help: try using a `char` instead: `','`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:47:19
+  --> $DIR/single_char_pattern.rs:52:19
    |
 LL |     x.starts_with("/x03"); // issue #2996
    |                   ^^^^^^ help: try using a `char` instead: `'/x03'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:54:13
+  --> $DIR/single_char_pattern.rs:59:13
    |
 LL |     x.split(r"a");
    |             ^^^^ help: try using a `char` instead: `'a'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:55:13
+  --> $DIR/single_char_pattern.rs:60:13
    |
 LL |     x.split(r#"a"#);
    |             ^^^^^^ help: try using a `char` instead: `'a'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:56:13
+  --> $DIR/single_char_pattern.rs:61:13
    |
 LL |     x.split(r###"a"###);
    |             ^^^^^^^^^^ help: try using a `char` instead: `'a'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:57:13
+  --> $DIR/single_char_pattern.rs:62:13
    |
 LL |     x.split(r###"'"###);
    |             ^^^^^^^^^^ help: try using a `char` instead: `'/''`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:58:13
+  --> $DIR/single_char_pattern.rs:63:13
    |
 LL |     x.split(r###"#"###);
    |             ^^^^^^^^^^ help: try using a `char` instead: `'#'`
 
-error: aborting due to 32 previous errors
+error: single-character string constant used as pattern
+  --> $DIR/single_char_pattern.rs:65:13
+   |
+LL |     x.split(r#"/"#);
+   |             ^^^^^^ help: try using a `char` instead: `'/'`
+
+error: single-character string constant used as pattern
+  --> $DIR/single_char_pattern.rs:66:13
+   |
+LL |     x.split(r"/");
+   |             ^^^^ help: try using a `char` instead: `'/'`
+
+error: aborting due to 39 previous errors
 
diff --git a/src/tools/clippy/tests/ui/strlen_on_c_strings.fixed b/src/tools/clippy/tests/ui/strlen_on_c_strings.fixed
new file mode 100644
index 00000000000..947a59bcc02
--- /dev/null
+++ b/src/tools/clippy/tests/ui/strlen_on_c_strings.fixed
@@ -0,0 +1,34 @@
+// run-rustfix
+
+#![warn(clippy::strlen_on_c_strings)]
+#![allow(dead_code)]
+#![feature(rustc_private)]
+extern crate libc;
+
+#[allow(unused)]
+use libc::strlen;
+use std::ffi::{CStr, CString};
+
+fn main() {
+    // CString
+    let cstring = CString::new("foo").expect("CString::new failed");
+    let _ = cstring.as_bytes().len();
+
+    // CStr
+    let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
+    let _ = cstr.to_bytes().len();
+
+    let _ = cstr.to_bytes().len();
+
+    let pcstr: *const &CStr = &cstr;
+    let _ = unsafe { (*pcstr).to_bytes().len() };
+
+    unsafe fn unsafe_identity<T>(x: T) -> T {
+        x
+    }
+    let _ = unsafe { unsafe_identity(cstr).to_bytes().len() };
+    let _ = unsafe { unsafe_identity(cstr) }.to_bytes().len();
+
+    let f: unsafe fn(_) -> _ = unsafe_identity;
+    let _ = unsafe { f(cstr).to_bytes().len() };
+}
diff --git a/src/tools/clippy/tests/ui/strlen_on_c_strings.rs b/src/tools/clippy/tests/ui/strlen_on_c_strings.rs
index 21902fa8483..1237f1ab03a 100644
--- a/src/tools/clippy/tests/ui/strlen_on_c_strings.rs
+++ b/src/tools/clippy/tests/ui/strlen_on_c_strings.rs
@@ -1,16 +1,34 @@
+// run-rustfix
+
 #![warn(clippy::strlen_on_c_strings)]
 #![allow(dead_code)]
 #![feature(rustc_private)]
 extern crate libc;
 
+#[allow(unused)]
+use libc::strlen;
 use std::ffi::{CStr, CString};
 
 fn main() {
     // CString
     let cstring = CString::new("foo").expect("CString::new failed");
-    let len = unsafe { libc::strlen(cstring.as_ptr()) };
+    let _ = unsafe { libc::strlen(cstring.as_ptr()) };
 
     // CStr
     let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
-    let len = unsafe { libc::strlen(cstr.as_ptr()) };
+    let _ = unsafe { libc::strlen(cstr.as_ptr()) };
+
+    let _ = unsafe { strlen(cstr.as_ptr()) };
+
+    let pcstr: *const &CStr = &cstr;
+    let _ = unsafe { strlen((*pcstr).as_ptr()) };
+
+    unsafe fn unsafe_identity<T>(x: T) -> T {
+        x
+    }
+    let _ = unsafe { strlen(unsafe_identity(cstr).as_ptr()) };
+    let _ = unsafe { strlen(unsafe { unsafe_identity(cstr) }.as_ptr()) };
+
+    let f: unsafe fn(_) -> _ = unsafe_identity;
+    let _ = unsafe { strlen(f(cstr).as_ptr()) };
 }
diff --git a/src/tools/clippy/tests/ui/strlen_on_c_strings.stderr b/src/tools/clippy/tests/ui/strlen_on_c_strings.stderr
index e0ca511557c..296268a5f1d 100644
--- a/src/tools/clippy/tests/ui/strlen_on_c_strings.stderr
+++ b/src/tools/clippy/tests/ui/strlen_on_c_strings.stderr
@@ -1,25 +1,46 @@
 error: using `libc::strlen` on a `CString` or `CStr` value
-  --> $DIR/strlen_on_c_strings.rs:11:24
+  --> $DIR/strlen_on_c_strings.rs:15:13
    |
-LL |     let len = unsafe { libc::strlen(cstring.as_ptr()) };
-   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     let _ = unsafe { libc::strlen(cstring.as_ptr()) };
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `cstring.as_bytes().len()`
    |
    = note: `-D clippy::strlen-on-c-strings` implied by `-D warnings`
-help: try this (you might also need to get rid of `unsafe` block in some cases):
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+  --> $DIR/strlen_on_c_strings.rs:19:13
+   |
+LL |     let _ = unsafe { libc::strlen(cstr.as_ptr()) };
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `cstr.to_bytes().len()`
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+  --> $DIR/strlen_on_c_strings.rs:21:13
+   |
+LL |     let _ = unsafe { strlen(cstr.as_ptr()) };
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `cstr.to_bytes().len()`
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+  --> $DIR/strlen_on_c_strings.rs:24:22
    |
-LL |     let len = unsafe { cstring.as_bytes().len() };
-   |                        ~~~~~~~~~~~~~~~~~~~~~~~~
+LL |     let _ = unsafe { strlen((*pcstr).as_ptr()) };
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(*pcstr).to_bytes().len()`
 
 error: using `libc::strlen` on a `CString` or `CStr` value
-  --> $DIR/strlen_on_c_strings.rs:15:24
+  --> $DIR/strlen_on_c_strings.rs:29:22
    |
-LL |     let len = unsafe { libc::strlen(cstr.as_ptr()) };
-   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     let _ = unsafe { strlen(unsafe_identity(cstr).as_ptr()) };
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unsafe_identity(cstr).to_bytes().len()`
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+  --> $DIR/strlen_on_c_strings.rs:30:13
    |
-help: try this (you might also need to get rid of `unsafe` block in some cases):
+LL |     let _ = unsafe { strlen(unsafe { unsafe_identity(cstr) }.as_ptr()) };
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unsafe { unsafe_identity(cstr) }.to_bytes().len()`
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+  --> $DIR/strlen_on_c_strings.rs:33:22
    |
-LL |     let len = unsafe { cstr.to_bytes().len() };
-   |                        ~~~~~~~~~~~~~~~~~~~~~
+LL |     let _ = unsafe { strlen(f(cstr).as_ptr()) };
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `f(cstr).to_bytes().len()`
 
-error: aborting due to 2 previous errors
+error: aborting due to 7 previous errors
 
diff --git a/src/tools/clippy/tests/ui/suspicious_splitn.rs b/src/tools/clippy/tests/ui/suspicious_splitn.rs
index a21d94cf20b..528f2ddcc86 100644
--- a/src/tools/clippy/tests/ui/suspicious_splitn.rs
+++ b/src/tools/clippy/tests/ui/suspicious_splitn.rs
@@ -1,4 +1,5 @@
 #![warn(clippy::suspicious_splitn)]
+#![allow(clippy::needless_splitn)]
 
 fn main() {
     let _ = "a,b,c".splitn(3, ',');
diff --git a/src/tools/clippy/tests/ui/suspicious_splitn.stderr b/src/tools/clippy/tests/ui/suspicious_splitn.stderr
index b6220ae2393..3bcd681fa49 100644
--- a/src/tools/clippy/tests/ui/suspicious_splitn.stderr
+++ b/src/tools/clippy/tests/ui/suspicious_splitn.stderr
@@ -1,5 +1,5 @@
 error: `splitn` called with `0` splits
-  --> $DIR/suspicious_splitn.rs:9:13
+  --> $DIR/suspicious_splitn.rs:10:13
    |
 LL |     let _ = "a,b".splitn(0, ',');
    |             ^^^^^^^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL |     let _ = "a,b".splitn(0, ',');
    = note: the resulting iterator will always return `None`
 
 error: `rsplitn` called with `0` splits
-  --> $DIR/suspicious_splitn.rs:10:13
+  --> $DIR/suspicious_splitn.rs:11:13
    |
 LL |     let _ = "a,b".rsplitn(0, ',');
    |             ^^^^^^^^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL |     let _ = "a,b".rsplitn(0, ',');
    = note: the resulting iterator will always return `None`
 
 error: `splitn` called with `1` split
-  --> $DIR/suspicious_splitn.rs:11:13
+  --> $DIR/suspicious_splitn.rs:12:13
    |
 LL |     let _ = "a,b".splitn(1, ',');
    |             ^^^^^^^^^^^^^^^^^^^^
@@ -24,7 +24,7 @@ LL |     let _ = "a,b".splitn(1, ',');
    = note: the resulting iterator will always return the entire string followed by `None`
 
 error: `splitn` called with `0` splits
-  --> $DIR/suspicious_splitn.rs:12:13
+  --> $DIR/suspicious_splitn.rs:13:13
    |
 LL |     let _ = [0, 1, 2].splitn(0, |&x| x == 1);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -32,7 +32,7 @@ LL |     let _ = [0, 1, 2].splitn(0, |&x| x == 1);
    = note: the resulting iterator will always return `None`
 
 error: `splitn_mut` called with `0` splits
-  --> $DIR/suspicious_splitn.rs:13:13
+  --> $DIR/suspicious_splitn.rs:14:13
    |
 LL |     let _ = [0, 1, 2].splitn_mut(0, |&x| x == 1);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -40,7 +40,7 @@ LL |     let _ = [0, 1, 2].splitn_mut(0, |&x| x == 1);
    = note: the resulting iterator will always return `None`
 
 error: `splitn` called with `1` split
-  --> $DIR/suspicious_splitn.rs:14:13
+  --> $DIR/suspicious_splitn.rs:15:13
    |
 LL |     let _ = [0, 1, 2].splitn(1, |&x| x == 1);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -48,7 +48,7 @@ LL |     let _ = [0, 1, 2].splitn(1, |&x| x == 1);
    = note: the resulting iterator will always return the entire slice followed by `None`
 
 error: `rsplitn_mut` called with `1` split
-  --> $DIR/suspicious_splitn.rs:15:13
+  --> $DIR/suspicious_splitn.rs:16:13
    |
 LL |     let _ = [0, 1, 2].rsplitn_mut(1, |&x| x == 1);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -56,7 +56,7 @@ LL |     let _ = [0, 1, 2].rsplitn_mut(1, |&x| x == 1);
    = note: the resulting iterator will always return the entire slice followed by `None`
 
 error: `splitn` called with `1` split
-  --> $DIR/suspicious_splitn.rs:18:13
+  --> $DIR/suspicious_splitn.rs:19:13
    |
 LL |     let _ = "a,b".splitn(X + 1, ',');
    |             ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -64,7 +64,7 @@ LL |     let _ = "a,b".splitn(X + 1, ',');
    = note: the resulting iterator will always return the entire string followed by `None`
 
 error: `splitn` called with `0` splits
-  --> $DIR/suspicious_splitn.rs:19:13
+  --> $DIR/suspicious_splitn.rs:20:13
    |
 LL |     let _ = "a,b".splitn(X, ',');
    |             ^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs
index 52577323a58..7e510d89475 100644
--- a/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs
+++ b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs
@@ -284,4 +284,8 @@ fn interference() {
     unsafe {};
 }
 
+pub fn print_binary_tree() {
+    println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr
index 613e9ffca45..ebe589001a1 100644
--- a/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr
+++ b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr
@@ -155,5 +155,17 @@ LL ~     // Safety: ...
 LL ~     unsafe {};
    |
 
-error: aborting due to 13 previous errors
+error: unsafe block missing a safety comment
+  --> $DIR/undocumented_unsafe_blocks.rs:288:20
+   |
+LL |     println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider adding a safety comment
+   |
+LL ~     println!("{}", // Safety: ...
+LL ~     unsafe { String::from_utf8_unchecked(vec![]) });
+   |
+
+error: aborting due to 14 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unicode.rs b/src/tools/clippy/tests/ui/unicode.rs
index 1f596c312fe..e0a4eadce33 100644
--- a/src/tools/clippy/tests/ui/unicode.rs
+++ b/src/tools/clippy/tests/ui/unicode.rs
@@ -20,8 +20,16 @@ fn uni() {
     print!("\u{DC}ben!"); // this is ok
 }
 
+// issue 8013
+#[warn(clippy::non_ascii_literal)]
+fn single_quote() {
+    const _EMPTY_BLOCK: char = '▱';
+    const _FULL_BLOCK: char = '▰';
+}
+
 fn main() {
     zero();
     uni();
     canon();
+    single_quote();
 }
diff --git a/src/tools/clippy/tests/ui/unicode.stderr b/src/tools/clippy/tests/ui/unicode.stderr
index 3fca463c620..3f54e3880e7 100644
--- a/src/tools/clippy/tests/ui/unicode.stderr
+++ b/src/tools/clippy/tests/ui/unicode.stderr
@@ -34,5 +34,17 @@ LL |     print!("Üben!");
    |
    = note: `-D clippy::non-ascii-literal` implied by `-D warnings`
 
-error: aborting due to 5 previous errors
+error: literal non-ASCII character detected
+  --> $DIR/unicode.rs:26:32
+   |
+LL |     const _EMPTY_BLOCK: char = '▱';
+   |                                ^^^ help: consider replacing the string with: `'/u{25b1}'`
+
+error: literal non-ASCII character detected
+  --> $DIR/unicode.rs:27:31
+   |
+LL |     const _FULL_BLOCK: char = '▰';
+   |                               ^^^ help: consider replacing the string with: `'/u{25b0}'`
+
+error: aborting due to 7 previous errors
 
diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml
index b0a13f827d6..80c30393832 100644
--- a/src/tools/clippy/triagebot.toml
+++ b/src/tools/clippy/triagebot.toml
@@ -5,3 +5,8 @@ allow-unauthenticated = [
 ]
 
 [assign]
+
+# Allows shortcuts like `@rustbot ready`
+#
+# See https://github.com/rust-lang/triagebot/wiki/Shortcuts
+[shortcut]
diff --git a/src/tools/clippy/util/gh-pages/index.html b/src/tools/clippy/util/gh-pages/index.html
index 48421150a54..f175700a3f4 100644
--- a/src/tools/clippy/util/gh-pages/index.html
+++ b/src/tools/clippy/util/gh-pages/index.html
@@ -118,6 +118,12 @@ Otherwise, have a great day =^.^=
             background-color: #777777;
             margin: auto 5px;
         }
+
+        .label-version {
+            background-color: #777777;
+            margin: auto 5px;
+            font-family: monospace;
+        }
     </style>
     <style>
         /* Expanding the mdBoom theme*/
@@ -330,7 +336,7 @@ Otherwise, have a great day =^.^=
                     </h2>
                 </header>
 
-                <ul class="list-group lint-docs" ng-class="{collapse: true, in: open[lint.id]}">
+                <div class="list-group lint-docs" ng-class="{collapse: true, in: open[lint.id]}">
                     <div class="list-group-item lint-doc-md" ng-bind-html="lint.docs | markdown"></div>
                     <div class="lint-additional-info-container">
                         <!-- Applicability -->
@@ -339,7 +345,11 @@ Otherwise, have a great day =^.^=
                             <span class="label label-default label-applicability">{{lint.applicability.applicability}}</span>
                             <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/enum.Applicability.html#variants">(?)</a>
                         </div>
-                        <!-- TODO xFrednet 2021-05-19: Somehow collect and show the version See rust-clippy#6492 -->
+                        <!-- Clippy version -->
+                        <div class="lint-additional-info-item">
+                            <span>{{lint.group == "deprecated" ? "Deprecated" : "Added"}} in: </span>
+                            <span class="label label-default label-version">{{lint.version}}</span>
+                        </div>
                         <!-- Open related issues -->
                         <div class="lint-additional-info-item">
                             <a href="https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+{{lint.id}}">Related Issues</a>
@@ -349,7 +359,7 @@ Otherwise, have a great day =^.^=
                             <a href="https://github.com/rust-lang/rust-clippy/blob/{{docVersion}}/clippy_lints/{{lint.id_span.path}}#L{{lint.id_span.line}}">View Source</a>
                         </div>
                     </div>
-                </ul>
+                </div>
             </article>
         </div>
     </div>