]> git.lizzy.rs Git - rust.git/commitdiff
Merge commit 'b52fb5234cd7c11ecfae51897a6f7fa52e8777fc' into clippyup
authorPhilipp Krones <hello@philkrones.com>
Fri, 9 Sep 2022 11:36:26 +0000 (13:36 +0200)
committerPhilipp Krones <hello@philkrones.com>
Fri, 9 Sep 2022 11:36:26 +0000 (13:36 +0200)
685 files changed:
1  2 
src/tools/clippy/CHANGELOG.md
src/tools/clippy/clippy_dev/src/update_lints.rs
src/tools/clippy/clippy_lints/src/async_yields_async.rs
src/tools/clippy/clippy_lints/src/attrs.rs
src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs
src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
src/tools/clippy/clippy_lints/src/dereference.rs
src/tools/clippy/clippy_lints/src/derivable_impls.rs
src/tools/clippy/clippy_lints/src/doc.rs
src/tools/clippy/clippy_lints/src/eta_reduction.rs
src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
src/tools/clippy/clippy_lints/src/functions/must_use.rs
src/tools/clippy/clippy_lints/src/functions/result.rs
src/tools/clippy/clippy_lints/src/implicit_return.rs
src/tools/clippy/clippy_lints/src/infinite_iter.rs
src/tools/clippy/clippy_lints/src/large_enum_variant.rs
src/tools/clippy/clippy_lints/src/len_zero.rs
src/tools/clippy/clippy_lints/src/lib.register_all.rs
src/tools/clippy/clippy_lints/src/lib.register_lints.rs
src/tools/clippy/clippy_lints/src/lib.register_restriction.rs
src/tools/clippy/clippy_lints/src/lib.register_style.rs
src/tools/clippy/clippy_lints/src/lib.rs
src/tools/clippy/clippy_lints/src/lifetimes.rs
src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs
src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs
src/tools/clippy/clippy_lints/src/matches/single_match.rs
src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs
src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs
src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs
src/tools/clippy/clippy_lints/src/methods/filter_map.rs
src/tools/clippy/clippy_lints/src/methods/map_clone.rs
src/tools/clippy/clippy_lints/src/methods/mod.rs
src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs
src/tools/clippy/clippy_lints/src/methods/open_options.rs
src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs
src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs
src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
src/tools/clippy/clippy_lints/src/methods/suspicious_map.rs
src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs
src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs
src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
src/tools/clippy/clippy_lints/src/methods/unwrap_or_else_default.rs
src/tools/clippy/clippy_lints/src/minmax.rs
src/tools/clippy/clippy_lints/src/needless_for_each.rs
src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs
src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
src/tools/clippy/clippy_lints/src/operators/mod.rs
src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
src/tools/clippy/clippy_lints/src/ptr.rs
src/tools/clippy/clippy_lints/src/ranges.rs
src/tools/clippy/clippy_lints/src/returns.rs
src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs
src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs
src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs
src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
src/tools/clippy/clippy_lints/src/unwrap.rs
src/tools/clippy/clippy_lints/src/unwrap_in_result.rs
src/tools/clippy/clippy_lints/src/utils/author.rs
src/tools/clippy/clippy_lints/src/utils/conf.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
src/tools/clippy/clippy_lints/src/write.rs
src/tools/clippy/clippy_utils/src/check_proc_macro.rs
src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
src/tools/clippy/clippy_utils/src/hir_utils.rs
src/tools/clippy/clippy_utils/src/lib.rs
src/tools/clippy/clippy_utils/src/macros.rs
src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
src/tools/clippy/clippy_utils/src/visitors.rs
src/tools/clippy/lintcheck/lintcheck_crates.toml
src/tools/clippy/rust-toolchain
src/tools/clippy/src/docs.rs
src/tools/clippy/src/docs/absurd_extreme_comparisons.txt
src/tools/clippy/src/docs/alloc_instead_of_core.txt
src/tools/clippy/src/docs/allow_attributes_without_reason.txt
src/tools/clippy/src/docs/almost_complete_letter_range.txt
src/tools/clippy/src/docs/almost_swapped.txt
src/tools/clippy/src/docs/approx_constant.txt
src/tools/clippy/src/docs/arithmetic_side_effects.txt
src/tools/clippy/src/docs/as_conversions.txt
src/tools/clippy/src/docs/as_underscore.txt
src/tools/clippy/src/docs/assertions_on_constants.txt
src/tools/clippy/src/docs/assertions_on_result_states.txt
src/tools/clippy/src/docs/assign_op_pattern.txt
src/tools/clippy/src/docs/async_yields_async.txt
src/tools/clippy/src/docs/await_holding_invalid_type.txt
src/tools/clippy/src/docs/await_holding_lock.txt
src/tools/clippy/src/docs/await_holding_refcell_ref.txt
src/tools/clippy/src/docs/bad_bit_mask.txt
src/tools/clippy/src/docs/bind_instead_of_map.txt
src/tools/clippy/src/docs/blanket_clippy_restriction_lints.txt
src/tools/clippy/src/docs/blocks_in_if_conditions.txt
src/tools/clippy/src/docs/bool_assert_comparison.txt
src/tools/clippy/src/docs/bool_comparison.txt
src/tools/clippy/src/docs/bool_to_int_with_if.txt
src/tools/clippy/src/docs/borrow_as_ptr.txt
src/tools/clippy/src/docs/borrow_deref_ref.txt
src/tools/clippy/src/docs/borrow_interior_mutable_const.txt
src/tools/clippy/src/docs/borrowed_box.txt
src/tools/clippy/src/docs/box_collection.txt
src/tools/clippy/src/docs/boxed_local.txt
src/tools/clippy/src/docs/branches_sharing_code.txt
src/tools/clippy/src/docs/builtin_type_shadow.txt
src/tools/clippy/src/docs/bytes_count_to_len.txt
src/tools/clippy/src/docs/bytes_nth.txt
src/tools/clippy/src/docs/cargo_common_metadata.txt
src/tools/clippy/src/docs/case_sensitive_file_extension_comparisons.txt
src/tools/clippy/src/docs/cast_abs_to_unsigned.txt
src/tools/clippy/src/docs/cast_enum_constructor.txt
src/tools/clippy/src/docs/cast_enum_truncation.txt
src/tools/clippy/src/docs/cast_lossless.txt
src/tools/clippy/src/docs/cast_possible_truncation.txt
src/tools/clippy/src/docs/cast_possible_wrap.txt
src/tools/clippy/src/docs/cast_precision_loss.txt
src/tools/clippy/src/docs/cast_ptr_alignment.txt
src/tools/clippy/src/docs/cast_ref_to_mut.txt
src/tools/clippy/src/docs/cast_sign_loss.txt
src/tools/clippy/src/docs/cast_slice_different_sizes.txt
src/tools/clippy/src/docs/cast_slice_from_raw_parts.txt
src/tools/clippy/src/docs/char_lit_as_u8.txt
src/tools/clippy/src/docs/chars_last_cmp.txt
src/tools/clippy/src/docs/chars_next_cmp.txt
src/tools/clippy/src/docs/checked_conversions.txt
src/tools/clippy/src/docs/clone_double_ref.txt
src/tools/clippy/src/docs/clone_on_copy.txt
src/tools/clippy/src/docs/clone_on_ref_ptr.txt
src/tools/clippy/src/docs/cloned_instead_of_copied.txt
src/tools/clippy/src/docs/cmp_nan.txt
src/tools/clippy/src/docs/cmp_null.txt
src/tools/clippy/src/docs/cmp_owned.txt
src/tools/clippy/src/docs/cognitive_complexity.txt
src/tools/clippy/src/docs/collapsible_else_if.txt
src/tools/clippy/src/docs/collapsible_if.txt
src/tools/clippy/src/docs/collapsible_match.txt
src/tools/clippy/src/docs/collapsible_str_replace.txt
src/tools/clippy/src/docs/comparison_chain.txt
src/tools/clippy/src/docs/comparison_to_empty.txt
src/tools/clippy/src/docs/copy_iterator.txt
src/tools/clippy/src/docs/crate_in_macro_def.txt
src/tools/clippy/src/docs/create_dir.txt
src/tools/clippy/src/docs/crosspointer_transmute.txt
src/tools/clippy/src/docs/dbg_macro.txt
src/tools/clippy/src/docs/debug_assert_with_mut_call.txt
src/tools/clippy/src/docs/decimal_literal_representation.txt
src/tools/clippy/src/docs/declare_interior_mutable_const.txt
src/tools/clippy/src/docs/default_instead_of_iter_empty.txt
src/tools/clippy/src/docs/default_numeric_fallback.txt
src/tools/clippy/src/docs/default_trait_access.txt
src/tools/clippy/src/docs/default_union_representation.txt
src/tools/clippy/src/docs/deprecated_cfg_attr.txt
src/tools/clippy/src/docs/deprecated_semver.txt
src/tools/clippy/src/docs/deref_addrof.txt
src/tools/clippy/src/docs/deref_by_slicing.txt
src/tools/clippy/src/docs/derivable_impls.txt
src/tools/clippy/src/docs/derive_hash_xor_eq.txt
src/tools/clippy/src/docs/derive_ord_xor_partial_ord.txt
src/tools/clippy/src/docs/derive_partial_eq_without_eq.txt
src/tools/clippy/src/docs/disallowed_methods.txt
src/tools/clippy/src/docs/disallowed_names.txt
src/tools/clippy/src/docs/disallowed_script_idents.txt
src/tools/clippy/src/docs/disallowed_types.txt
src/tools/clippy/src/docs/diverging_sub_expression.txt
src/tools/clippy/src/docs/doc_link_with_quotes.txt
src/tools/clippy/src/docs/doc_markdown.txt
src/tools/clippy/src/docs/double_comparisons.txt
src/tools/clippy/src/docs/double_must_use.txt
src/tools/clippy/src/docs/double_neg.txt
src/tools/clippy/src/docs/double_parens.txt
src/tools/clippy/src/docs/drop_copy.txt
src/tools/clippy/src/docs/drop_non_drop.txt
src/tools/clippy/src/docs/drop_ref.txt
src/tools/clippy/src/docs/duplicate_mod.txt
src/tools/clippy/src/docs/duplicate_underscore_argument.txt
src/tools/clippy/src/docs/duration_subsec.txt
src/tools/clippy/src/docs/else_if_without_else.txt
src/tools/clippy/src/docs/empty_drop.txt
src/tools/clippy/src/docs/empty_enum.txt
src/tools/clippy/src/docs/empty_line_after_outer_attr.txt
src/tools/clippy/src/docs/empty_loop.txt
src/tools/clippy/src/docs/empty_structs_with_brackets.txt
src/tools/clippy/src/docs/enum_clike_unportable_variant.txt
src/tools/clippy/src/docs/enum_glob_use.txt
src/tools/clippy/src/docs/enum_variant_names.txt
src/tools/clippy/src/docs/eq_op.txt
src/tools/clippy/src/docs/equatable_if_let.txt
src/tools/clippy/src/docs/erasing_op.txt
src/tools/clippy/src/docs/err_expect.txt
src/tools/clippy/src/docs/excessive_precision.txt
src/tools/clippy/src/docs/exhaustive_enums.txt
src/tools/clippy/src/docs/exhaustive_structs.txt
src/tools/clippy/src/docs/exit.txt
src/tools/clippy/src/docs/expect_fun_call.txt
src/tools/clippy/src/docs/expect_used.txt
src/tools/clippy/src/docs/expl_impl_clone_on_copy.txt
src/tools/clippy/src/docs/explicit_auto_deref.txt
src/tools/clippy/src/docs/explicit_counter_loop.txt
src/tools/clippy/src/docs/explicit_deref_methods.txt
src/tools/clippy/src/docs/explicit_into_iter_loop.txt
src/tools/clippy/src/docs/explicit_iter_loop.txt
src/tools/clippy/src/docs/explicit_write.txt
src/tools/clippy/src/docs/extend_with_drain.txt
src/tools/clippy/src/docs/extra_unused_lifetimes.txt
src/tools/clippy/src/docs/fallible_impl_from.txt
src/tools/clippy/src/docs/field_reassign_with_default.txt
src/tools/clippy/src/docs/filetype_is_file.txt
src/tools/clippy/src/docs/filter_map_identity.txt
src/tools/clippy/src/docs/filter_map_next.txt
src/tools/clippy/src/docs/filter_next.txt
src/tools/clippy/src/docs/flat_map_identity.txt
src/tools/clippy/src/docs/flat_map_option.txt
src/tools/clippy/src/docs/float_arithmetic.txt
src/tools/clippy/src/docs/float_cmp.txt
src/tools/clippy/src/docs/float_cmp_const.txt
src/tools/clippy/src/docs/float_equality_without_abs.txt
src/tools/clippy/src/docs/fn_address_comparisons.txt
src/tools/clippy/src/docs/fn_params_excessive_bools.txt
src/tools/clippy/src/docs/fn_to_numeric_cast.txt
src/tools/clippy/src/docs/fn_to_numeric_cast_any.txt
src/tools/clippy/src/docs/fn_to_numeric_cast_with_truncation.txt
src/tools/clippy/src/docs/for_kv_map.txt
src/tools/clippy/src/docs/for_loops_over_fallibles.txt
src/tools/clippy/src/docs/forget_copy.txt
src/tools/clippy/src/docs/forget_non_drop.txt
src/tools/clippy/src/docs/forget_ref.txt
src/tools/clippy/src/docs/format_in_format_args.txt
src/tools/clippy/src/docs/format_push_string.txt
src/tools/clippy/src/docs/from_iter_instead_of_collect.txt
src/tools/clippy/src/docs/from_over_into.txt
src/tools/clippy/src/docs/from_str_radix_10.txt
src/tools/clippy/src/docs/future_not_send.txt
src/tools/clippy/src/docs/get_first.txt
src/tools/clippy/src/docs/get_last_with_len.txt
src/tools/clippy/src/docs/get_unwrap.txt
src/tools/clippy/src/docs/identity_op.txt
src/tools/clippy/src/docs/if_let_mutex.txt
src/tools/clippy/src/docs/if_not_else.txt
src/tools/clippy/src/docs/if_same_then_else.txt
src/tools/clippy/src/docs/if_then_some_else_none.txt
src/tools/clippy/src/docs/ifs_same_cond.txt
src/tools/clippy/src/docs/implicit_clone.txt
src/tools/clippy/src/docs/implicit_hasher.txt
src/tools/clippy/src/docs/implicit_return.txt
src/tools/clippy/src/docs/implicit_saturating_sub.txt
src/tools/clippy/src/docs/imprecise_flops.txt
src/tools/clippy/src/docs/inconsistent_digit_grouping.txt
src/tools/clippy/src/docs/inconsistent_struct_constructor.txt
src/tools/clippy/src/docs/index_refutable_slice.txt
src/tools/clippy/src/docs/indexing_slicing.txt
src/tools/clippy/src/docs/ineffective_bit_mask.txt
src/tools/clippy/src/docs/inefficient_to_string.txt
src/tools/clippy/src/docs/infallible_destructuring_match.txt
src/tools/clippy/src/docs/infinite_iter.txt
src/tools/clippy/src/docs/inherent_to_string.txt
src/tools/clippy/src/docs/inherent_to_string_shadow_display.txt
src/tools/clippy/src/docs/init_numbered_fields.txt
src/tools/clippy/src/docs/inline_always.txt
src/tools/clippy/src/docs/inline_asm_x86_att_syntax.txt
src/tools/clippy/src/docs/inline_asm_x86_intel_syntax.txt
src/tools/clippy/src/docs/inline_fn_without_body.txt
src/tools/clippy/src/docs/inspect_for_each.txt
src/tools/clippy/src/docs/int_plus_one.txt
src/tools/clippy/src/docs/integer_arithmetic.txt
src/tools/clippy/src/docs/integer_division.txt
src/tools/clippy/src/docs/into_iter_on_ref.txt
src/tools/clippy/src/docs/invalid_null_ptr_usage.txt
src/tools/clippy/src/docs/invalid_regex.txt
src/tools/clippy/src/docs/invalid_upcast_comparisons.txt
src/tools/clippy/src/docs/invalid_utf8_in_unchecked.txt
src/tools/clippy/src/docs/invisible_characters.txt
src/tools/clippy/src/docs/is_digit_ascii_radix.txt
src/tools/clippy/src/docs/items_after_statements.txt
src/tools/clippy/src/docs/iter_cloned_collect.txt
src/tools/clippy/src/docs/iter_count.txt
src/tools/clippy/src/docs/iter_next_loop.txt
src/tools/clippy/src/docs/iter_next_slice.txt
src/tools/clippy/src/docs/iter_not_returning_iterator.txt
src/tools/clippy/src/docs/iter_nth.txt
src/tools/clippy/src/docs/iter_nth_zero.txt
src/tools/clippy/src/docs/iter_on_empty_collections.txt
src/tools/clippy/src/docs/iter_on_single_items.txt
src/tools/clippy/src/docs/iter_overeager_cloned.txt
src/tools/clippy/src/docs/iter_skip_next.txt
src/tools/clippy/src/docs/iter_with_drain.txt
src/tools/clippy/src/docs/iterator_step_by_zero.txt
src/tools/clippy/src/docs/just_underscores_and_digits.txt
src/tools/clippy/src/docs/large_const_arrays.txt
src/tools/clippy/src/docs/large_digit_groups.txt
src/tools/clippy/src/docs/large_enum_variant.txt
src/tools/clippy/src/docs/large_include_file.txt
src/tools/clippy/src/docs/large_stack_arrays.txt
src/tools/clippy/src/docs/large_types_passed_by_value.txt
src/tools/clippy/src/docs/len_without_is_empty.txt
src/tools/clippy/src/docs/len_zero.txt
src/tools/clippy/src/docs/let_and_return.txt
src/tools/clippy/src/docs/let_underscore_drop.txt
src/tools/clippy/src/docs/let_underscore_lock.txt
src/tools/clippy/src/docs/let_underscore_must_use.txt
src/tools/clippy/src/docs/let_unit_value.txt
src/tools/clippy/src/docs/linkedlist.txt
src/tools/clippy/src/docs/lossy_float_literal.txt
src/tools/clippy/src/docs/macro_use_imports.txt
src/tools/clippy/src/docs/main_recursion.txt
src/tools/clippy/src/docs/manual_assert.txt
src/tools/clippy/src/docs/manual_async_fn.txt
src/tools/clippy/src/docs/manual_bits.txt
src/tools/clippy/src/docs/manual_filter_map.txt
src/tools/clippy/src/docs/manual_find.txt
src/tools/clippy/src/docs/manual_find_map.txt
src/tools/clippy/src/docs/manual_flatten.txt
src/tools/clippy/src/docs/manual_instant_elapsed.txt
src/tools/clippy/src/docs/manual_map.txt
src/tools/clippy/src/docs/manual_memcpy.txt
src/tools/clippy/src/docs/manual_non_exhaustive.txt
src/tools/clippy/src/docs/manual_ok_or.txt
src/tools/clippy/src/docs/manual_range_contains.txt
src/tools/clippy/src/docs/manual_rem_euclid.txt
src/tools/clippy/src/docs/manual_retain.txt
src/tools/clippy/src/docs/manual_saturating_arithmetic.txt
src/tools/clippy/src/docs/manual_split_once.txt
src/tools/clippy/src/docs/manual_str_repeat.txt
src/tools/clippy/src/docs/manual_string_new.txt
src/tools/clippy/src/docs/manual_strip.txt
src/tools/clippy/src/docs/manual_swap.txt
src/tools/clippy/src/docs/manual_unwrap_or.txt
src/tools/clippy/src/docs/many_single_char_names.txt
src/tools/clippy/src/docs/map_clone.txt
src/tools/clippy/src/docs/map_collect_result_unit.txt
src/tools/clippy/src/docs/map_entry.txt
src/tools/clippy/src/docs/map_err_ignore.txt
src/tools/clippy/src/docs/map_flatten.txt
src/tools/clippy/src/docs/map_identity.txt
src/tools/clippy/src/docs/map_unwrap_or.txt
src/tools/clippy/src/docs/match_as_ref.txt
src/tools/clippy/src/docs/match_bool.txt
src/tools/clippy/src/docs/match_like_matches_macro.txt
src/tools/clippy/src/docs/match_on_vec_items.txt
src/tools/clippy/src/docs/match_overlapping_arm.txt
src/tools/clippy/src/docs/match_ref_pats.txt
src/tools/clippy/src/docs/match_result_ok.txt
src/tools/clippy/src/docs/match_same_arms.txt
src/tools/clippy/src/docs/match_single_binding.txt
src/tools/clippy/src/docs/match_str_case_mismatch.txt
src/tools/clippy/src/docs/match_wild_err_arm.txt
src/tools/clippy/src/docs/match_wildcard_for_single_variants.txt
src/tools/clippy/src/docs/maybe_infinite_iter.txt
src/tools/clippy/src/docs/mem_forget.txt
src/tools/clippy/src/docs/mem_replace_option_with_none.txt
src/tools/clippy/src/docs/mem_replace_with_default.txt
src/tools/clippy/src/docs/mem_replace_with_uninit.txt
src/tools/clippy/src/docs/min_max.txt
src/tools/clippy/src/docs/mismatched_target_os.txt
src/tools/clippy/src/docs/mismatching_type_param_order.txt
src/tools/clippy/src/docs/misrefactored_assign_op.txt
src/tools/clippy/src/docs/missing_const_for_fn.txt
src/tools/clippy/src/docs/missing_docs_in_private_items.txt
src/tools/clippy/src/docs/missing_enforced_import_renames.txt
src/tools/clippy/src/docs/missing_errors_doc.txt
src/tools/clippy/src/docs/missing_inline_in_public_items.txt
src/tools/clippy/src/docs/missing_panics_doc.txt
src/tools/clippy/src/docs/missing_safety_doc.txt
src/tools/clippy/src/docs/missing_spin_loop.txt
src/tools/clippy/src/docs/mistyped_literal_suffixes.txt
src/tools/clippy/src/docs/mixed_case_hex_literals.txt
src/tools/clippy/src/docs/mixed_read_write_in_expression.txt
src/tools/clippy/src/docs/mod_module_files.txt
src/tools/clippy/src/docs/module_inception.txt
src/tools/clippy/src/docs/module_name_repetitions.txt
src/tools/clippy/src/docs/modulo_arithmetic.txt
src/tools/clippy/src/docs/modulo_one.txt
src/tools/clippy/src/docs/multi_assignments.txt
src/tools/clippy/src/docs/multiple_crate_versions.txt
src/tools/clippy/src/docs/multiple_inherent_impl.txt
src/tools/clippy/src/docs/must_use_candidate.txt
src/tools/clippy/src/docs/must_use_unit.txt
src/tools/clippy/src/docs/mut_from_ref.txt
src/tools/clippy/src/docs/mut_mut.txt
src/tools/clippy/src/docs/mut_mutex_lock.txt
src/tools/clippy/src/docs/mut_range_bound.txt
src/tools/clippy/src/docs/mutable_key_type.txt
src/tools/clippy/src/docs/mutex_atomic.txt
src/tools/clippy/src/docs/mutex_integer.txt
src/tools/clippy/src/docs/naive_bytecount.txt
src/tools/clippy/src/docs/needless_arbitrary_self_type.txt
src/tools/clippy/src/docs/needless_bitwise_bool.txt
src/tools/clippy/src/docs/needless_bool.txt
src/tools/clippy/src/docs/needless_borrow.txt
src/tools/clippy/src/docs/needless_borrowed_reference.txt
src/tools/clippy/src/docs/needless_collect.txt
src/tools/clippy/src/docs/needless_continue.txt
src/tools/clippy/src/docs/needless_doctest_main.txt
src/tools/clippy/src/docs/needless_for_each.txt
src/tools/clippy/src/docs/needless_late_init.txt
src/tools/clippy/src/docs/needless_lifetimes.txt
src/tools/clippy/src/docs/needless_match.txt
src/tools/clippy/src/docs/needless_option_as_deref.txt
src/tools/clippy/src/docs/needless_option_take.txt
src/tools/clippy/src/docs/needless_parens_on_range_literals.txt
src/tools/clippy/src/docs/needless_pass_by_value.txt
src/tools/clippy/src/docs/needless_question_mark.txt
src/tools/clippy/src/docs/needless_range_loop.txt
src/tools/clippy/src/docs/needless_return.txt
src/tools/clippy/src/docs/needless_splitn.txt
src/tools/clippy/src/docs/needless_update.txt
src/tools/clippy/src/docs/neg_cmp_op_on_partial_ord.txt
src/tools/clippy/src/docs/neg_multiply.txt
src/tools/clippy/src/docs/negative_feature_names.txt
src/tools/clippy/src/docs/never_loop.txt
src/tools/clippy/src/docs/new_ret_no_self.txt
src/tools/clippy/src/docs/new_without_default.txt
src/tools/clippy/src/docs/no_effect.txt
src/tools/clippy/src/docs/no_effect_replace.txt
src/tools/clippy/src/docs/no_effect_underscore_binding.txt
src/tools/clippy/src/docs/non_ascii_literal.txt
src/tools/clippy/src/docs/non_octal_unix_permissions.txt
src/tools/clippy/src/docs/non_send_fields_in_send_ty.txt
src/tools/clippy/src/docs/nonminimal_bool.txt
src/tools/clippy/src/docs/nonsensical_open_options.txt
src/tools/clippy/src/docs/nonstandard_macro_braces.txt
src/tools/clippy/src/docs/not_unsafe_ptr_arg_deref.txt
src/tools/clippy/src/docs/obfuscated_if_else.txt
src/tools/clippy/src/docs/octal_escapes.txt
src/tools/clippy/src/docs/ok_expect.txt
src/tools/clippy/src/docs/only_used_in_recursion.txt
src/tools/clippy/src/docs/op_ref.txt
src/tools/clippy/src/docs/option_as_ref_deref.txt
src/tools/clippy/src/docs/option_env_unwrap.txt
src/tools/clippy/src/docs/option_filter_map.txt
src/tools/clippy/src/docs/option_if_let_else.txt
src/tools/clippy/src/docs/option_map_or_none.txt
src/tools/clippy/src/docs/option_map_unit_fn.txt
src/tools/clippy/src/docs/option_option.txt
src/tools/clippy/src/docs/or_fun_call.txt
src/tools/clippy/src/docs/or_then_unwrap.txt
src/tools/clippy/src/docs/out_of_bounds_indexing.txt
src/tools/clippy/src/docs/overflow_check_conditional.txt
src/tools/clippy/src/docs/overly_complex_bool_expr.txt
src/tools/clippy/src/docs/panic.txt
src/tools/clippy/src/docs/panic_in_result_fn.txt
src/tools/clippy/src/docs/panicking_unwrap.txt
src/tools/clippy/src/docs/partialeq_ne_impl.txt
src/tools/clippy/src/docs/partialeq_to_none.txt
src/tools/clippy/src/docs/path_buf_push_overwrite.txt
src/tools/clippy/src/docs/pattern_type_mismatch.txt
src/tools/clippy/src/docs/positional_named_format_parameters.txt
src/tools/clippy/src/docs/possible_missing_comma.txt
src/tools/clippy/src/docs/precedence.txt
src/tools/clippy/src/docs/print_in_format_impl.txt
src/tools/clippy/src/docs/print_literal.txt
src/tools/clippy/src/docs/print_stderr.txt
src/tools/clippy/src/docs/print_stdout.txt
src/tools/clippy/src/docs/print_with_newline.txt
src/tools/clippy/src/docs/println_empty_string.txt
src/tools/clippy/src/docs/ptr_arg.txt
src/tools/clippy/src/docs/ptr_as_ptr.txt
src/tools/clippy/src/docs/ptr_eq.txt
src/tools/clippy/src/docs/ptr_offset_with_cast.txt
src/tools/clippy/src/docs/pub_use.txt
src/tools/clippy/src/docs/question_mark.txt
src/tools/clippy/src/docs/range_minus_one.txt
src/tools/clippy/src/docs/range_plus_one.txt
src/tools/clippy/src/docs/range_zip_with_len.txt
src/tools/clippy/src/docs/rc_buffer.txt
src/tools/clippy/src/docs/rc_clone_in_vec_init.txt
src/tools/clippy/src/docs/rc_mutex.txt
src/tools/clippy/src/docs/read_zero_byte_vec.txt
src/tools/clippy/src/docs/recursive_format_impl.txt
src/tools/clippy/src/docs/redundant_allocation.txt
src/tools/clippy/src/docs/redundant_clone.txt
src/tools/clippy/src/docs/redundant_closure.txt
src/tools/clippy/src/docs/redundant_closure_call.txt
src/tools/clippy/src/docs/redundant_closure_for_method_calls.txt
src/tools/clippy/src/docs/redundant_else.txt
src/tools/clippy/src/docs/redundant_feature_names.txt
src/tools/clippy/src/docs/redundant_field_names.txt
src/tools/clippy/src/docs/redundant_pattern.txt
src/tools/clippy/src/docs/redundant_pattern_matching.txt
src/tools/clippy/src/docs/redundant_pub_crate.txt
src/tools/clippy/src/docs/redundant_slicing.txt
src/tools/clippy/src/docs/redundant_static_lifetimes.txt
src/tools/clippy/src/docs/ref_binding_to_reference.txt
src/tools/clippy/src/docs/ref_option_ref.txt
src/tools/clippy/src/docs/repeat_once.txt
src/tools/clippy/src/docs/rest_pat_in_fully_bound_structs.txt
src/tools/clippy/src/docs/result_large_err.txt
src/tools/clippy/src/docs/result_map_or_into_option.txt
src/tools/clippy/src/docs/result_map_unit_fn.txt
src/tools/clippy/src/docs/result_unit_err.txt
src/tools/clippy/src/docs/return_self_not_must_use.txt
src/tools/clippy/src/docs/reversed_empty_ranges.txt
src/tools/clippy/src/docs/same_functions_in_if_condition.txt
src/tools/clippy/src/docs/same_item_push.txt
src/tools/clippy/src/docs/same_name_method.txt
src/tools/clippy/src/docs/search_is_some.txt
src/tools/clippy/src/docs/self_assignment.txt
src/tools/clippy/src/docs/self_named_constructors.txt
src/tools/clippy/src/docs/self_named_module_files.txt
src/tools/clippy/src/docs/semicolon_if_nothing_returned.txt
src/tools/clippy/src/docs/separated_literal_suffix.txt
src/tools/clippy/src/docs/serde_api_misuse.txt
src/tools/clippy/src/docs/shadow_reuse.txt
src/tools/clippy/src/docs/shadow_same.txt
src/tools/clippy/src/docs/shadow_unrelated.txt
src/tools/clippy/src/docs/short_circuit_statement.txt
src/tools/clippy/src/docs/should_implement_trait.txt
src/tools/clippy/src/docs/significant_drop_in_scrutinee.txt
src/tools/clippy/src/docs/similar_names.txt
src/tools/clippy/src/docs/single_char_add_str.txt
src/tools/clippy/src/docs/single_char_lifetime_names.txt
src/tools/clippy/src/docs/single_char_pattern.txt
src/tools/clippy/src/docs/single_component_path_imports.txt
src/tools/clippy/src/docs/single_element_loop.txt
src/tools/clippy/src/docs/single_match.txt
src/tools/clippy/src/docs/single_match_else.txt
src/tools/clippy/src/docs/size_of_in_element_count.txt
src/tools/clippy/src/docs/skip_while_next.txt
src/tools/clippy/src/docs/slow_vector_initialization.txt
src/tools/clippy/src/docs/stable_sort_primitive.txt
src/tools/clippy/src/docs/std_instead_of_alloc.txt
src/tools/clippy/src/docs/std_instead_of_core.txt
src/tools/clippy/src/docs/str_to_string.txt
src/tools/clippy/src/docs/string_add.txt
src/tools/clippy/src/docs/string_add_assign.txt
src/tools/clippy/src/docs/string_extend_chars.txt
src/tools/clippy/src/docs/string_from_utf8_as_bytes.txt
src/tools/clippy/src/docs/string_lit_as_bytes.txt
src/tools/clippy/src/docs/string_slice.txt
src/tools/clippy/src/docs/string_to_string.txt
src/tools/clippy/src/docs/strlen_on_c_strings.txt
src/tools/clippy/src/docs/struct_excessive_bools.txt
src/tools/clippy/src/docs/suboptimal_flops.txt
src/tools/clippy/src/docs/suspicious_arithmetic_impl.txt
src/tools/clippy/src/docs/suspicious_assignment_formatting.txt
src/tools/clippy/src/docs/suspicious_else_formatting.txt
src/tools/clippy/src/docs/suspicious_map.txt
src/tools/clippy/src/docs/suspicious_op_assign_impl.txt
src/tools/clippy/src/docs/suspicious_operation_groupings.txt
src/tools/clippy/src/docs/suspicious_splitn.txt
src/tools/clippy/src/docs/suspicious_to_owned.txt
src/tools/clippy/src/docs/suspicious_unary_op_formatting.txt
src/tools/clippy/src/docs/swap_ptr_to_ref.txt
src/tools/clippy/src/docs/tabs_in_doc_comments.txt
src/tools/clippy/src/docs/temporary_assignment.txt
src/tools/clippy/src/docs/to_digit_is_some.txt
src/tools/clippy/src/docs/to_string_in_format_args.txt
src/tools/clippy/src/docs/todo.txt
src/tools/clippy/src/docs/too_many_arguments.txt
src/tools/clippy/src/docs/too_many_lines.txt
src/tools/clippy/src/docs/toplevel_ref_arg.txt
src/tools/clippy/src/docs/trailing_empty_array.txt
src/tools/clippy/src/docs/trait_duplication_in_bounds.txt
src/tools/clippy/src/docs/transmute_bytes_to_str.txt
src/tools/clippy/src/docs/transmute_float_to_int.txt
src/tools/clippy/src/docs/transmute_int_to_bool.txt
src/tools/clippy/src/docs/transmute_int_to_char.txt
src/tools/clippy/src/docs/transmute_int_to_float.txt
src/tools/clippy/src/docs/transmute_num_to_bytes.txt
src/tools/clippy/src/docs/transmute_ptr_to_ptr.txt
src/tools/clippy/src/docs/transmute_ptr_to_ref.txt
src/tools/clippy/src/docs/transmute_undefined_repr.txt
src/tools/clippy/src/docs/transmutes_expressible_as_ptr_casts.txt
src/tools/clippy/src/docs/transmuting_null.txt
src/tools/clippy/src/docs/trim_split_whitespace.txt
src/tools/clippy/src/docs/trivial_regex.txt
src/tools/clippy/src/docs/trivially_copy_pass_by_ref.txt
src/tools/clippy/src/docs/try_err.txt
src/tools/clippy/src/docs/type_complexity.txt
src/tools/clippy/src/docs/type_repetition_in_bounds.txt
src/tools/clippy/src/docs/undocumented_unsafe_blocks.txt
src/tools/clippy/src/docs/undropped_manually_drops.txt
src/tools/clippy/src/docs/unicode_not_nfc.txt
src/tools/clippy/src/docs/unimplemented.txt
src/tools/clippy/src/docs/uninit_assumed_init.txt
src/tools/clippy/src/docs/uninit_vec.txt
src/tools/clippy/src/docs/unit_arg.txt
src/tools/clippy/src/docs/unit_cmp.txt
src/tools/clippy/src/docs/unit_hash.txt
src/tools/clippy/src/docs/unit_return_expecting_ord.txt
src/tools/clippy/src/docs/unnecessary_cast.txt
src/tools/clippy/src/docs/unnecessary_filter_map.txt
src/tools/clippy/src/docs/unnecessary_find_map.txt
src/tools/clippy/src/docs/unnecessary_fold.txt
src/tools/clippy/src/docs/unnecessary_join.txt
src/tools/clippy/src/docs/unnecessary_lazy_evaluations.txt
src/tools/clippy/src/docs/unnecessary_mut_passed.txt
src/tools/clippy/src/docs/unnecessary_operation.txt
src/tools/clippy/src/docs/unnecessary_owned_empty_strings.txt
src/tools/clippy/src/docs/unnecessary_self_imports.txt
src/tools/clippy/src/docs/unnecessary_sort_by.txt
src/tools/clippy/src/docs/unnecessary_to_owned.txt
src/tools/clippy/src/docs/unnecessary_unwrap.txt
src/tools/clippy/src/docs/unnecessary_wraps.txt
src/tools/clippy/src/docs/unneeded_field_pattern.txt
src/tools/clippy/src/docs/unneeded_wildcard_pattern.txt
src/tools/clippy/src/docs/unnested_or_patterns.txt
src/tools/clippy/src/docs/unreachable.txt
src/tools/clippy/src/docs/unreadable_literal.txt
src/tools/clippy/src/docs/unsafe_derive_deserialize.txt
src/tools/clippy/src/docs/unsafe_removed_from_name.txt
src/tools/clippy/src/docs/unseparated_literal_suffix.txt
src/tools/clippy/src/docs/unsound_collection_transmute.txt
src/tools/clippy/src/docs/unused_async.txt
src/tools/clippy/src/docs/unused_io_amount.txt
src/tools/clippy/src/docs/unused_peekable.txt
src/tools/clippy/src/docs/unused_rounding.txt
src/tools/clippy/src/docs/unused_self.txt
src/tools/clippy/src/docs/unused_unit.txt
src/tools/clippy/src/docs/unusual_byte_groupings.txt
src/tools/clippy/src/docs/unwrap_in_result.txt
src/tools/clippy/src/docs/unwrap_or_else_default.txt
src/tools/clippy/src/docs/unwrap_used.txt
src/tools/clippy/src/docs/upper_case_acronyms.txt
src/tools/clippy/src/docs/use_debug.txt
src/tools/clippy/src/docs/use_self.txt
src/tools/clippy/src/docs/used_underscore_binding.txt
src/tools/clippy/src/docs/useless_asref.txt
src/tools/clippy/src/docs/useless_attribute.txt
src/tools/clippy/src/docs/useless_conversion.txt
src/tools/clippy/src/docs/useless_format.txt
src/tools/clippy/src/docs/useless_let_if_seq.txt
src/tools/clippy/src/docs/useless_transmute.txt
src/tools/clippy/src/docs/useless_vec.txt
src/tools/clippy/src/docs/vec_box.txt
src/tools/clippy/src/docs/vec_init_then_push.txt
src/tools/clippy/src/docs/vec_resize_to_zero.txt
src/tools/clippy/src/docs/verbose_bit_mask.txt
src/tools/clippy/src/docs/verbose_file_reads.txt
src/tools/clippy/src/docs/vtable_address_comparisons.txt
src/tools/clippy/src/docs/while_immutable_condition.txt
src/tools/clippy/src/docs/while_let_loop.txt
src/tools/clippy/src/docs/while_let_on_iterator.txt
src/tools/clippy/src/docs/wildcard_dependencies.txt
src/tools/clippy/src/docs/wildcard_enum_match_arm.txt
src/tools/clippy/src/docs/wildcard_imports.txt
src/tools/clippy/src/docs/wildcard_in_or_patterns.txt
src/tools/clippy/src/docs/write_literal.txt
src/tools/clippy/src/docs/write_with_newline.txt
src/tools/clippy/src/docs/writeln_empty_string.txt
src/tools/clippy/src/docs/wrong_self_convention.txt
src/tools/clippy/src/docs/wrong_transmute.txt
src/tools/clippy/src/docs/zero_divided_by_zero.txt
src/tools/clippy/src/docs/zero_prefixed_literal.txt
src/tools/clippy/src/docs/zero_ptr.txt
src/tools/clippy/src/docs/zero_sized_map_values.txt
src/tools/clippy/src/docs/zst_offset.txt
src/tools/clippy/src/main.rs
src/tools/clippy/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs
src/tools/clippy/tests/ui-toml/arithmetic_side_effects_allowed/clippy.toml
src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
src/tools/clippy/tests/ui/arithmetic_side_effects.rs
src/tools/clippy/tests/ui/arithmetic_side_effects.stderr
src/tools/clippy/tests/ui/author/struct.rs
src/tools/clippy/tests/ui/bool_to_int_with_if.fixed
src/tools/clippy/tests/ui/bool_to_int_with_if.rs
src/tools/clippy/tests/ui/bool_to_int_with_if.stderr
src/tools/clippy/tests/ui/crashes/ice-9405.rs
src/tools/clippy/tests/ui/crashes/ice-9405.stderr
src/tools/clippy/tests/ui/crashes/ice-9414.rs
src/tools/clippy/tests/ui/floating_point_powf.fixed
src/tools/clippy/tests/ui/floating_point_powf.rs
src/tools/clippy/tests/ui/floating_point_powf.stderr
src/tools/clippy/tests/ui/large_enum_variant.rs
src/tools/clippy/tests/ui/large_enum_variant.stderr
src/tools/clippy/tests/ui/match_wild_err_arm.edition2018.stderr
src/tools/clippy/tests/ui/match_wild_err_arm.edition2021.stderr
src/tools/clippy/tests/ui/mut_mutex_lock.fixed
src/tools/clippy/tests/ui/mut_mutex_lock.rs
src/tools/clippy/tests/ui/or_fun_call.fixed
src/tools/clippy/tests/ui/or_fun_call.stderr
src/tools/clippy/tests/ui/range_plus_minus_one.fixed
src/tools/clippy/tests/ui/range_plus_minus_one.rs
src/tools/clippy/tests/ui/range_plus_minus_one.stderr
src/tools/clippy/tests/ui/result_large_err.rs
src/tools/clippy/tests/ui/result_large_err.stderr
src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
src/tools/clippy/tests/ui/unnecessary_to_owned.rs
src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
src/tools/clippy/tests/ui/unwrap_or_else_default.fixed
src/tools/clippy/tests/ui/unwrap_or_else_default.rs
src/tools/clippy/tests/ui/unwrap_or_else_default.stderr
src/tools/clippy/tests/ui/vec_init_then_push.rs
src/tools/clippy/tests/ui/vec_init_then_push.stderr

index c488c142e46fc2847fe41d7c3c3582508a705eac,0000000000000000000000000000000000000000..d847e4c749481fbc06566e2c3bde97064e77f0f0
mode 100644,000000..100644
--- /dev/null
@@@ -1,4204 -1,0 +1,4205 @@@
- [`arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic
 +# Changelog
 +
 +All notable changes to this project will be documented in this file.
 +See [Changelog Update](book/src/development/infrastructure/changelog_update.md) if you want to update this
 +document.
 +
 +## Unreleased / In Rust Nightly
 +
 +[d7b5cbf0...master](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...master)
 +
 +## Rust 1.63
 +
 +Current stable, released 2022-08-11
 +
 +[7c21f91b...d7b5cbf0](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...d7b5cbf0)
 +
 +### New Lints
 +
 +* [`borrow_deref_ref`]
 +  [#7930](https://github.com/rust-lang/rust-clippy/pull/7930)
 +* [`doc_link_with_quotes`]
 +  [#8385](https://github.com/rust-lang/rust-clippy/pull/8385)
 +* [`no_effect_replace`]
 +  [#8754](https://github.com/rust-lang/rust-clippy/pull/8754)
 +* [`rc_clone_in_vec_init`]
 +  [#8769](https://github.com/rust-lang/rust-clippy/pull/8769)
 +* [`derive_partial_eq_without_eq`]
 +  [#8796](https://github.com/rust-lang/rust-clippy/pull/8796)
 +* [`mismatching_type_param_order`]
 +  [#8831](https://github.com/rust-lang/rust-clippy/pull/8831)
 +* [`duplicate_mod`] [#8832](https://github.com/rust-lang/rust-clippy/pull/8832)
 +* [`unused_rounding`]
 +  [#8866](https://github.com/rust-lang/rust-clippy/pull/8866)
 +* [`get_first`] [#8882](https://github.com/rust-lang/rust-clippy/pull/8882)
 +* [`swap_ptr_to_ref`]
 +  [#8916](https://github.com/rust-lang/rust-clippy/pull/8916)
 +* [`almost_complete_letter_range`]
 +  [#8918](https://github.com/rust-lang/rust-clippy/pull/8918)
 +* [`needless_parens_on_range_literals`]
 +  [#8933](https://github.com/rust-lang/rust-clippy/pull/8933)
 +* [`as_underscore`] [#8934](https://github.com/rust-lang/rust-clippy/pull/8934)
 +
 +### Moves and Deprecations
 +
 +* Rename `eval_order_dependence` to [`mixed_read_write_in_expression`], move to
 +  `nursery` [#8621](https://github.com/rust-lang/rust-clippy/pull/8621)
 +
 +### Enhancements
 +
 +* [`undocumented_unsafe_blocks`]: Now also lints on unsafe trait implementations
 +  [#8761](https://github.com/rust-lang/rust-clippy/pull/8761)
 +* [`empty_line_after_outer_attr`]: Now also lints on argumentless macros
 +  [#8790](https://github.com/rust-lang/rust-clippy/pull/8790)
 +* [`expect_used`]: Now can be disabled in tests with the `allow-expect-in-tests`
 +  option [#8802](https://github.com/rust-lang/rust-clippy/pull/8802)
 +* [`unwrap_used`]: Now can be disabled in tests with the `allow-unwrap-in-tests`
 +  option [#8802](https://github.com/rust-lang/rust-clippy/pull/8802)
 +* [`disallowed_methods`]: Now also lints indirect usages
 +  [#8852](https://github.com/rust-lang/rust-clippy/pull/8852)
 +* [`get_last_with_len`]: Now also lints `VecDeque` and any deref to slice
 +  [#8862](https://github.com/rust-lang/rust-clippy/pull/8862)
 +* [`manual_range_contains`]: Now also lints on chains of `&&` and `||`
 +  [#8884](https://github.com/rust-lang/rust-clippy/pull/8884)
 +* [`rc_clone_in_vec_init`]: Now also lints on `Weak`
 +  [#8885](https://github.com/rust-lang/rust-clippy/pull/8885)
 +* [`dbg_macro`]: Introduce `allow-dbg-in-tests` config option
 +  [#8897](https://github.com/rust-lang/rust-clippy/pull/8897)
 +* [`use_self`]: Now also lints on `TupleStruct` and `Struct` patterns
 +  [#8899](https://github.com/rust-lang/rust-clippy/pull/8899)
 +* [`manual_find_map`] and [`manual_filter_map`]: Now also lints on more complex
 +  method chains inside `map`
 +  [#8930](https://github.com/rust-lang/rust-clippy/pull/8930)
 +* [`needless_return`]: Now also lints on macro expressions in return statements
 +  [#8932](https://github.com/rust-lang/rust-clippy/pull/8932)
 +* [`doc_markdown`]: Users can now indicate, that the `doc-valid-idents` config
 +  should extend the default and not replace it
 +  [#8944](https://github.com/rust-lang/rust-clippy/pull/8944)
 +* [`disallowed_names`]: Users can now indicate, that the `disallowed-names`
 +  config should extend the default and not replace it
 +  [#8944](https://github.com/rust-lang/rust-clippy/pull/8944)
 +* [`never_loop`]: Now checks for `continue` in struct expression
 +  [#9002](https://github.com/rust-lang/rust-clippy/pull/9002)
 +
 +### False Positive Fixes
 +
 +* [`useless_transmute`]: No longer lints on types with erased regions
 +  [#8564](https://github.com/rust-lang/rust-clippy/pull/8564)
 +* [`vec_init_then_push`]: No longer lints when further extended
 +  [#8699](https://github.com/rust-lang/rust-clippy/pull/8699)
 +* [`cmp_owned`]: No longer lints on `From::from` for `Copy` types
 +  [#8807](https://github.com/rust-lang/rust-clippy/pull/8807)
 +* [`redundant_allocation`]: No longer lints on fat pointers that would become
 +  thin pointers [#8813](https://github.com/rust-lang/rust-clippy/pull/8813)
 +* [`derive_partial_eq_without_eq`]:
 +    * Handle differing predicates applied by `#[derive(PartialEq)]` and
 +      `#[derive(Eq)]`
 +      [#8869](https://github.com/rust-lang/rust-clippy/pull/8869)
 +    * No longer lints on non-public types and better handles generics
 +      [#8950](https://github.com/rust-lang/rust-clippy/pull/8950)
 +* [`empty_line_after_outer_attr`]: No longer lints empty lines in inner
 +  string values [#8892](https://github.com/rust-lang/rust-clippy/pull/8892)
 +* [`branches_sharing_code`]: No longer lints when using different binding names
 +  [#8901](https://github.com/rust-lang/rust-clippy/pull/8901)
 +* [`significant_drop_in_scrutinee`]: No longer lints on Try `?` and `await`
 +  desugared expressions [#8902](https://github.com/rust-lang/rust-clippy/pull/8902)
 +* [`checked_conversions`]: No longer lints in `const` contexts
 +  [#8907](https://github.com/rust-lang/rust-clippy/pull/8907)
 +* [`iter_overeager_cloned`]: No longer lints on `.cloned().flatten()` when
 +  `T::Item` doesn't implement `IntoIterator`
 +  [#8960](https://github.com/rust-lang/rust-clippy/pull/8960)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`vec_init_then_push`]: Suggest to remove `mut` binding when possible
 +  [#8699](https://github.com/rust-lang/rust-clippy/pull/8699)
 +* [`manual_range_contains`]: Fix suggestion for integers with different signs
 +  [#8763](https://github.com/rust-lang/rust-clippy/pull/8763)
 +* [`identity_op`]: Add parenthesis to suggestions where required
 +  [#8786](https://github.com/rust-lang/rust-clippy/pull/8786)
 +* [`cast_lossless`]: No longer gives wrong suggestion on `usize`/`isize`->`f64`
 +  [#8778](https://github.com/rust-lang/rust-clippy/pull/8778)
 +* [`rc_clone_in_vec_init`]: Add suggestion
 +  [#8814](https://github.com/rust-lang/rust-clippy/pull/8814)
 +* The "unknown field" error messages for config files now wraps the field names
 +  [#8823](https://github.com/rust-lang/rust-clippy/pull/8823)
 +* [`cast_abs_to_unsigned`]: Do not remove cast if it's required
 +  [#8876](https://github.com/rust-lang/rust-clippy/pull/8876)
 +* [`significant_drop_in_scrutinee`]: Improve lint message for types that are not
 +  references and not trivially clone-able
 +  [#8902](https://github.com/rust-lang/rust-clippy/pull/8902)
 +* [`for_loops_over_fallibles`]: Now suggests the correct variant of `iter()`,
 +  `iter_mut()` or `into_iter()`
 +  [#8941](https://github.com/rust-lang/rust-clippy/pull/8941)
 +
 +### ICE Fixes
 +
 +* Fix ICE in [`let_unit_value`] when calling a `static`/`const` callable type
 +  [#8835](https://github.com/rust-lang/rust-clippy/pull/8835)
 +* Fix ICEs on callable `static`/`const`s
 +  [#8896](https://github.com/rust-lang/rust-clippy/pull/8896)
 +* [`needless_late_init`]
 +  [#8912](https://github.com/rust-lang/rust-clippy/pull/8912)
 +* Fix ICE in shadow lints
 +  [#8913](https://github.com/rust-lang/rust-clippy/pull/8913)
 +
 +### Documentation Improvements
 +
 +* Clippy has a [Book](https://doc.rust-lang.org/nightly/clippy/) now!
 +  [#7359](https://github.com/rust-lang/rust-clippy/pull/7359)
 +* Add a *copy lint name*-button to Clippy's lint list
 +  [#8839](https://github.com/rust-lang/rust-clippy/pull/8839)
 +* Display past names of renamed lints on Clippy's lint list
 +  [#8843](https://github.com/rust-lang/rust-clippy/pull/8843)
 +* Add the ability to show the lint output in the lint list
 +  [#8947](https://github.com/rust-lang/rust-clippy/pull/8947)
 +
 +## Rust 1.62
 +
 +Released 2022-06-30
 +
 +[d0cf3481...7c21f91b](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...7c21f91b)
 +
 +### New Lints
 +
 +* [`large_include_file`]
 +  [#8727](https://github.com/rust-lang/rust-clippy/pull/8727)
 +* [`cast_abs_to_unsigned`]
 +  [#8635](https://github.com/rust-lang/rust-clippy/pull/8635)
 +* [`err_expect`]
 +  [#8606](https://github.com/rust-lang/rust-clippy/pull/8606)
 +* [`unnecessary_owned_empty_strings`]
 +  [#8660](https://github.com/rust-lang/rust-clippy/pull/8660)
 +* [`empty_structs_with_brackets`]
 +  [#8594](https://github.com/rust-lang/rust-clippy/pull/8594)
 +* [`crate_in_macro_def`]
 +  [#8576](https://github.com/rust-lang/rust-clippy/pull/8576)
 +* [`needless_option_take`]
 +  [#8665](https://github.com/rust-lang/rust-clippy/pull/8665)
 +* [`bytes_count_to_len`]
 +  [#8711](https://github.com/rust-lang/rust-clippy/pull/8711)
 +* [`is_digit_ascii_radix`]
 +  [#8624](https://github.com/rust-lang/rust-clippy/pull/8624)
 +* [`await_holding_invalid_type`]
 +  [#8707](https://github.com/rust-lang/rust-clippy/pull/8707)
 +* [`trim_split_whitespace`]
 +  [#8575](https://github.com/rust-lang/rust-clippy/pull/8575)
 +* [`pub_use`]
 +  [#8670](https://github.com/rust-lang/rust-clippy/pull/8670)
 +* [`format_push_string`]
 +  [#8626](https://github.com/rust-lang/rust-clippy/pull/8626)
 +* [`empty_drop`]
 +  [#8571](https://github.com/rust-lang/rust-clippy/pull/8571)
 +* [`drop_non_drop`]
 +  [#8630](https://github.com/rust-lang/rust-clippy/pull/8630)
 +* [`forget_non_drop`]
 +  [#8630](https://github.com/rust-lang/rust-clippy/pull/8630)
 +
 +### Moves and Deprecations
 +
 +* Move [`only_used_in_recursion`] to `nursery` (now allow-by-default)
 +  [#8783](https://github.com/rust-lang/rust-clippy/pull/8783)
 +* Move [`stable_sort_primitive`] to `pedantic` (now allow-by-default)
 +  [#8716](https://github.com/rust-lang/rust-clippy/pull/8716)
 +
 +### Enhancements
 +
 +* Remove overlap between [`manual_split_once`] and [`needless_splitn`]
 +  [#8631](https://github.com/rust-lang/rust-clippy/pull/8631)
 +* [`map_identity`]: Now checks for needless `map_err`
 +  [#8487](https://github.com/rust-lang/rust-clippy/pull/8487)
 +* [`extra_unused_lifetimes`]: Now checks for impl lifetimes
 +  [#8737](https://github.com/rust-lang/rust-clippy/pull/8737)
 +* [`cast_possible_truncation`]: Now catches more cases with larger shift or divide operations
 +  [#8687](https://github.com/rust-lang/rust-clippy/pull/8687)
 +* [`identity_op`]: Now checks for modulo expressions
 +  [#8519](https://github.com/rust-lang/rust-clippy/pull/8519)
 +* [`panic`]: No longer lint in constant context
 +  [#8592](https://github.com/rust-lang/rust-clippy/pull/8592)
 +* [`manual_split_once`]: Now lints manual iteration of `splitn`
 +  [#8717](https://github.com/rust-lang/rust-clippy/pull/8717)
 +* [`self_named_module_files`], [`mod_module_files`]: Now handle relative module paths
 +  [#8611](https://github.com/rust-lang/rust-clippy/pull/8611)
 +* [`unsound_collection_transmute`]: Now has better size and alignment checks
 +  [#8648](https://github.com/rust-lang/rust-clippy/pull/8648)
 +* [`unnested_or_patterns`]: Ignore cases, where the suggestion would be longer
 +  [#8619](https://github.com/rust-lang/rust-clippy/pull/8619)
 +
 +### False Positive Fixes
 +
 +* [`rest_pat_in_fully_bound_structs`]: Now ignores structs marked with `#[non_exhaustive]`
 +  [#8690](https://github.com/rust-lang/rust-clippy/pull/8690)
 +* [`needless_late_init`]: No longer lints `if let` statements, `let mut` bindings or instances that
 +  changes the drop order significantly
 +  [#8617](https://github.com/rust-lang/rust-clippy/pull/8617)
 +* [`unnecessary_cast`]: No longer lints to casts to aliased or non-primitive types
 +  [#8596](https://github.com/rust-lang/rust-clippy/pull/8596)
 +* [`init_numbered_fields`]: No longer lints type aliases
 +  [#8780](https://github.com/rust-lang/rust-clippy/pull/8780)
 +* [`needless_option_as_deref`]: No longer lints for `as_deref_mut` on `Option` values that can't be moved
 +  [#8646](https://github.com/rust-lang/rust-clippy/pull/8646)
 +* [`mistyped_literal_suffixes`]: Now ignores float literals without an exponent
 +  [#8742](https://github.com/rust-lang/rust-clippy/pull/8742)
 +* [`undocumented_unsafe_blocks`]: Now ignores unsafe blocks from proc-macros and works better for sub-expressions
 +  [#8450](https://github.com/rust-lang/rust-clippy/pull/8450)
 +* [`same_functions_in_if_condition`]: Now allows different constants, even if they have the same value
 +  [#8673](https://github.com/rust-lang/rust-clippy/pull/8673)
 +* [`needless_match`]: Now checks for more complex types and ignores type coercion
 +  [#8549](https://github.com/rust-lang/rust-clippy/pull/8549)
 +* [`assertions_on_constants`]: Now ignores constants from `cfg!` macros
 +  [#8614](https://github.com/rust-lang/rust-clippy/pull/8614)
 +* [`indexing_slicing`]: Fix false positives with constant indices in
 +  [#8588](https://github.com/rust-lang/rust-clippy/pull/8588)
 +* [`iter_with_drain`]: Now ignores iterator references
 +  [#8668](https://github.com/rust-lang/rust-clippy/pull/8668)
 +* [`useless_attribute`]: Now allows [`redundant_pub_crate`] on `use` items
 +  [#8743](https://github.com/rust-lang/rust-clippy/pull/8743)
 +* [`cast_ptr_alignment`]: Now ignores expressions, when used for unaligned reads and writes
 +  [#8632](https://github.com/rust-lang/rust-clippy/pull/8632)
 +* [`wrong_self_convention`]: Now allows `&mut self` and no self as arguments for `is_*` methods
 +  [#8738](https://github.com/rust-lang/rust-clippy/pull/8738)
 +* [`mut_from_ref`]: Only lint in unsafe code
 +  [#8647](https://github.com/rust-lang/rust-clippy/pull/8647)
 +* [`redundant_pub_crate`]: Now allows macro exports
 +  [#8736](https://github.com/rust-lang/rust-clippy/pull/8736)
 +* [`needless_match`]: Ignores cases where the else block expression is different
 +  [#8700](https://github.com/rust-lang/rust-clippy/pull/8700)
 +* [`transmute_int_to_char`]: Now allows transmutations in `const` code
 +  [#8610](https://github.com/rust-lang/rust-clippy/pull/8610)
 +* [`manual_non_exhaustive`]: Ignores cases, where the enum value is used
 +  [#8645](https://github.com/rust-lang/rust-clippy/pull/8645)
 +* [`redundant_closure`]: Now ignores coerced closure
 +  [#8431](https://github.com/rust-lang/rust-clippy/pull/8431)
 +* [`identity_op`]: Is now ignored in cases where extra brackets would be needed
 +  [#8730](https://github.com/rust-lang/rust-clippy/pull/8730)
 +* [`let_unit_value`]: Now ignores cases which are used for type inference
 +  [#8563](https://github.com/rust-lang/rust-clippy/pull/8563)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`manual_split_once`]: Fixed incorrect suggestions for single result accesses
 +  [#8631](https://github.com/rust-lang/rust-clippy/pull/8631)
 +* [`bytes_nth`]: Fix typos in the diagnostic message
 +  [#8403](https://github.com/rust-lang/rust-clippy/pull/8403)
 +* [`mistyped_literal_suffixes`]: Now suggests the correct integer types
 +  [#8742](https://github.com/rust-lang/rust-clippy/pull/8742)
 +* [`unnecessary_to_owned`]: Fixed suggestion based on the configured msrv
 +  [#8692](https://github.com/rust-lang/rust-clippy/pull/8692)
 +* [`single_element_loop`]: Improve lint for Edition 2021 arrays
 +  [#8616](https://github.com/rust-lang/rust-clippy/pull/8616)
 +* [`manual_bits`]: Now includes a cast for proper type conversion, when needed
 +  [#8677](https://github.com/rust-lang/rust-clippy/pull/8677)
 +* [`option_map_unit_fn`], [`result_map_unit_fn`]: Fix some incorrect suggestions
 +  [#8584](https://github.com/rust-lang/rust-clippy/pull/8584)
 +* [`collapsible_else_if`]: Add whitespace in suggestion
 +  [#8729](https://github.com/rust-lang/rust-clippy/pull/8729)
 +* [`transmute_bytes_to_str`]: Now suggest `from_utf8_unchecked` in `const` context
 +  [#8612](https://github.com/rust-lang/rust-clippy/pull/8612)
 +* [`map_clone`]: Improve message and suggestion based on the msrv
 +  [#8688](https://github.com/rust-lang/rust-clippy/pull/8688)
 +* [`needless_late_init`]: Now shows the `let` statement where it was first initialized
 +  [#8779](https://github.com/rust-lang/rust-clippy/pull/8779)
 +
 +### ICE Fixes
 +
 +* [`only_used_in_recursion`]
 +  [#8691](https://github.com/rust-lang/rust-clippy/pull/8691)
 +* [`cast_slice_different_sizes`]
 +  [#8720](https://github.com/rust-lang/rust-clippy/pull/8720)
 +* [`iter_overeager_cloned`]
 +  [#8602](https://github.com/rust-lang/rust-clippy/pull/8602)
 +* [`undocumented_unsafe_blocks`]
 +  [#8686](https://github.com/rust-lang/rust-clippy/pull/8686)
 +
 +## Rust 1.61
 +
 +Released 2022-05-19
 +
 +[57b3c4b...d0cf3481](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...d0cf3481)
 +
 +### New Lints
 +
 +* [`only_used_in_recursion`]
 +  [#8422](https://github.com/rust-lang/rust-clippy/pull/8422)
 +* [`cast_enum_truncation`]
 +  [#8381](https://github.com/rust-lang/rust-clippy/pull/8381)
 +* [`missing_spin_loop`]
 +  [#8174](https://github.com/rust-lang/rust-clippy/pull/8174)
 +* [`deref_by_slicing`]
 +  [#8218](https://github.com/rust-lang/rust-clippy/pull/8218)
 +* [`needless_match`]
 +  [#8471](https://github.com/rust-lang/rust-clippy/pull/8471)
 +* [`allow_attributes_without_reason`] (Requires `#![feature(lint_reasons)]`)
 +  [#8504](https://github.com/rust-lang/rust-clippy/pull/8504)
 +* [`print_in_format_impl`]
 +  [#8253](https://github.com/rust-lang/rust-clippy/pull/8253)
 +* [`unnecessary_find_map`]
 +  [#8489](https://github.com/rust-lang/rust-clippy/pull/8489)
 +* [`or_then_unwrap`]
 +  [#8561](https://github.com/rust-lang/rust-clippy/pull/8561)
 +* [`unnecessary_join`]
 +  [#8579](https://github.com/rust-lang/rust-clippy/pull/8579)
 +* [`iter_with_drain`]
 +  [#8483](https://github.com/rust-lang/rust-clippy/pull/8483)
 +* [`cast_enum_constructor`]
 +  [#8562](https://github.com/rust-lang/rust-clippy/pull/8562)
 +* [`cast_slice_different_sizes`]
 +  [#8445](https://github.com/rust-lang/rust-clippy/pull/8445)
 +
 +### Moves and Deprecations
 +
 +* Moved [`transmute_undefined_repr`] to `nursery` (now allow-by-default)
 +  [#8432](https://github.com/rust-lang/rust-clippy/pull/8432)
 +* Moved [`try_err`] to `restriction`
 +  [#8544](https://github.com/rust-lang/rust-clippy/pull/8544)
 +* Move [`iter_with_drain`] to `nursery`
 +  [#8541](https://github.com/rust-lang/rust-clippy/pull/8541)
 +* Renamed `to_string_in_display` to [`recursive_format_impl`]
 +  [#8188](https://github.com/rust-lang/rust-clippy/pull/8188)
 +
 +### Enhancements
 +
 +* [`dbg_macro`]: The lint level can now be set with crate attributes and works inside macros
 +  [#8411](https://github.com/rust-lang/rust-clippy/pull/8411)
 +* [`ptr_as_ptr`]: Now works inside macros
 +  [#8442](https://github.com/rust-lang/rust-clippy/pull/8442)
 +* [`use_self`]: Now works for variants in match expressions
 +  [#8456](https://github.com/rust-lang/rust-clippy/pull/8456)
 +* [`await_holding_lock`]: Now lints for `parking_lot::{Mutex, RwLock}`
 +  [#8419](https://github.com/rust-lang/rust-clippy/pull/8419)
 +* [`recursive_format_impl`]: Now checks for format calls on `self`
 +  [#8188](https://github.com/rust-lang/rust-clippy/pull/8188)
 +
 +### False Positive Fixes
 +
 +* [`new_without_default`]: No longer lints for `new()` methods with `#[doc(hidden)]`
 +  [#8472](https://github.com/rust-lang/rust-clippy/pull/8472)
 +* [`transmute_undefined_repr`]: No longer lints for single field structs with `#[repr(C)]`,
 +  generic parameters, wide pointers, unions, tuples and allow several forms of type erasure
 +  [#8425](https://github.com/rust-lang/rust-clippy/pull/8425)
 +  [#8553](https://github.com/rust-lang/rust-clippy/pull/8553)
 +  [#8440](https://github.com/rust-lang/rust-clippy/pull/8440)
 +  [#8547](https://github.com/rust-lang/rust-clippy/pull/8547)
 +* [`match_single_binding`], [`match_same_arms`], [`match_as_ref`], [`match_bool`]: No longer
 +  lint `match` expressions with `cfg`ed arms
 +  [#8443](https://github.com/rust-lang/rust-clippy/pull/8443)
 +* [`single_component_path_imports`]: No longer lint on macros
 +  [#8537](https://github.com/rust-lang/rust-clippy/pull/8537)
 +* [`ptr_arg`]: Allow `&mut` arguments for `Cow<_>`
 +  [#8552](https://github.com/rust-lang/rust-clippy/pull/8552)
 +* [`needless_borrow`]: No longer lints for method calls
 +  [#8441](https://github.com/rust-lang/rust-clippy/pull/8441)
 +* [`match_same_arms`]: Now ensures that interposing arm patterns don't overlap
 +  [#8232](https://github.com/rust-lang/rust-clippy/pull/8232)
 +* [`default_trait_access`]: Now allows `Default::default` in update expressions
 +  [#8433](https://github.com/rust-lang/rust-clippy/pull/8433)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`redundant_slicing`]: Fixed suggestion for a method calls
 +  [#8218](https://github.com/rust-lang/rust-clippy/pull/8218)
 +* [`map_flatten`]: Long suggestions will now be split up into two help messages
 +  [#8520](https://github.com/rust-lang/rust-clippy/pull/8520)
 +* [`unnecessary_lazy_evaluations`]: Now shows suggestions for longer code snippets
 +  [#8543](https://github.com/rust-lang/rust-clippy/pull/8543)
 +* [`unnecessary_sort_by`]: Now suggests `Reverse` including the path
 +  [#8462](https://github.com/rust-lang/rust-clippy/pull/8462)
 +* [`search_is_some`]: More suggestions are now `MachineApplicable`
 +  [#8536](https://github.com/rust-lang/rust-clippy/pull/8536)
 +
 +### Documentation Improvements
 +
 +* [`new_without_default`]: Document `pub` requirement for the struct and fields
 +  [#8429](https://github.com/rust-lang/rust-clippy/pull/8429)
 +
 +## Rust 1.60
 +
 +Released 2022-04-07
 +
 +[0eff589...57b3c4b](https://github.com/rust-lang/rust-clippy/compare/0eff589...57b3c4b)
 +
 +### New Lints
 +
 +* [`single_char_lifetime_names`]
 +  [#8236](https://github.com/rust-lang/rust-clippy/pull/8236)
 +* [`iter_overeager_cloned`]
 +  [#8203](https://github.com/rust-lang/rust-clippy/pull/8203)
 +* [`transmute_undefined_repr`]
 +  [#8398](https://github.com/rust-lang/rust-clippy/pull/8398)
 +* [`default_union_representation`]
 +  [#8289](https://github.com/rust-lang/rust-clippy/pull/8289)
 +* [`manual_bits`]
 +  [#8213](https://github.com/rust-lang/rust-clippy/pull/8213)
 +* [`borrow_as_ptr`]
 +  [#8210](https://github.com/rust-lang/rust-clippy/pull/8210)
 +
 +### Moves and Deprecations
 +
 +* Moved [`disallowed_methods`] and [`disallowed_types`] to `style` (now warn-by-default)
 +  [#8261](https://github.com/rust-lang/rust-clippy/pull/8261)
 +* Rename `ref_in_deref` to [`needless_borrow`]
 +  [#8217](https://github.com/rust-lang/rust-clippy/pull/8217)
 +* Moved [`mutex_atomic`] to `nursery` (now allow-by-default)
 +  [#8260](https://github.com/rust-lang/rust-clippy/pull/8260)
 +
 +### Enhancements
 +
 +* [`ptr_arg`]: Now takes the argument usage into account and lints for mutable references
 +  [#8271](https://github.com/rust-lang/rust-clippy/pull/8271)
 +* [`unused_io_amount`]: Now supports async read and write traits
 +  [#8179](https://github.com/rust-lang/rust-clippy/pull/8179)
 +* [`while_let_on_iterator`]: Improved detection to catch more cases
 +  [#8221](https://github.com/rust-lang/rust-clippy/pull/8221)
 +* [`trait_duplication_in_bounds`]: Now covers trait functions with `Self` bounds
 +  [#8252](https://github.com/rust-lang/rust-clippy/pull/8252)
 +* [`unwrap_used`]: Now works for `.get(i).unwrap()` and `.get_mut(i).unwrap()`
 +  [#8372](https://github.com/rust-lang/rust-clippy/pull/8372)
 +* [`map_clone`]: The suggestion takes `msrv` into account
 +  [#8280](https://github.com/rust-lang/rust-clippy/pull/8280)
 +* [`manual_bits`] and [`borrow_as_ptr`]: Now track the `clippy::msrv` attribute
 +  [#8280](https://github.com/rust-lang/rust-clippy/pull/8280)
 +* [`disallowed_methods`]: Now works for methods on primitive types
 +  [#8112](https://github.com/rust-lang/rust-clippy/pull/8112)
 +* [`not_unsafe_ptr_arg_deref`]: Now works for type aliases
 +  [#8273](https://github.com/rust-lang/rust-clippy/pull/8273)
 +* [`needless_question_mark`]: Now works for async functions
 +  [#8311](https://github.com/rust-lang/rust-clippy/pull/8311)
 +* [`iter_not_returning_iterator`]: Now handles type projections
 +  [#8228](https://github.com/rust-lang/rust-clippy/pull/8228)
 +* [`wrong_self_convention`]: Now detects wrong `self` references in more cases
 +  [#8208](https://github.com/rust-lang/rust-clippy/pull/8208)
 +* [`single_match`]: Now works for `match` statements with tuples
 +  [#8322](https://github.com/rust-lang/rust-clippy/pull/8322)
 +
 +### False Positive Fixes
 +
 +* [`erasing_op`]: No longer triggers if the output type changes
 +  [#8204](https://github.com/rust-lang/rust-clippy/pull/8204)
 +* [`if_same_then_else`]: No longer triggers for `if let` statements
 +  [#8297](https://github.com/rust-lang/rust-clippy/pull/8297)
 +* [`manual_memcpy`]: No longer lints on `VecDeque`
 +  [#8226](https://github.com/rust-lang/rust-clippy/pull/8226)
 +* [`trait_duplication_in_bounds`]: Now takes path segments into account
 +  [#8315](https://github.com/rust-lang/rust-clippy/pull/8315)
 +* [`deref_addrof`]: No longer lints when the dereference or borrow occurs in different a context
 +  [#8268](https://github.com/rust-lang/rust-clippy/pull/8268)
 +* [`type_repetition_in_bounds`]: Now checks for full equality to prevent false positives
 +  [#8224](https://github.com/rust-lang/rust-clippy/pull/8224)
 +* [`ptr_arg`]: No longer lint for mutable references in traits
 +  [#8369](https://github.com/rust-lang/rust-clippy/pull/8369)
 +* [`implicit_clone`]: No longer lints for double references
 +  [#8231](https://github.com/rust-lang/rust-clippy/pull/8231)
 +* [`needless_lifetimes`]: No longer lints lifetimes for explicit `self` types
 +  [#8278](https://github.com/rust-lang/rust-clippy/pull/8278)
 +* [`op_ref`]: No longer lints in `BinOp` impl if that can cause recursion
 +  [#8298](https://github.com/rust-lang/rust-clippy/pull/8298)
 +* [`enum_variant_names`]: No longer triggers for empty variant names
 +  [#8329](https://github.com/rust-lang/rust-clippy/pull/8329)
 +* [`redundant_closure`]: No longer lints for `Arc<T>` or `Rc<T>`
 +  [#8193](https://github.com/rust-lang/rust-clippy/pull/8193)
 +* [`iter_not_returning_iterator`]: No longer lints on trait implementations but therefore on trait definitions
 +  [#8228](https://github.com/rust-lang/rust-clippy/pull/8228)
 +* [`single_match`]: No longer lints on exhaustive enum patterns without a wildcard
 +  [#8322](https://github.com/rust-lang/rust-clippy/pull/8322)
 +* [`manual_swap`]: No longer lints on cases that involve automatic dereferences
 +  [#8220](https://github.com/rust-lang/rust-clippy/pull/8220)
 +* [`useless_format`]: Now works for implicit named arguments
 +  [#8295](https://github.com/rust-lang/rust-clippy/pull/8295)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`needless_borrow`]: Prevent mutable borrows being moved and suggest removing the borrow on method calls
 +  [#8217](https://github.com/rust-lang/rust-clippy/pull/8217)
 +* [`chars_next_cmp`]: Correctly escapes the suggestion
 +  [#8376](https://github.com/rust-lang/rust-clippy/pull/8376)
 +* [`explicit_write`]: Add suggestions for `write!`s with format arguments
 +  [#8365](https://github.com/rust-lang/rust-clippy/pull/8365)
 +* [`manual_memcpy`]: Suggests `copy_from_slice` when applicable
 +  [#8226](https://github.com/rust-lang/rust-clippy/pull/8226)
 +* [`or_fun_call`]: Improved suggestion display for long arguments
 +  [#8292](https://github.com/rust-lang/rust-clippy/pull/8292)
 +* [`unnecessary_cast`]: Now correctly includes the sign
 +  [#8350](https://github.com/rust-lang/rust-clippy/pull/8350)
 +* [`cmp_owned`]: No longer flips the comparison order
 +  [#8299](https://github.com/rust-lang/rust-clippy/pull/8299)
 +* [`explicit_counter_loop`]: Now correctly suggests `iter()` on references
 +  [#8382](https://github.com/rust-lang/rust-clippy/pull/8382)
 +
 +### ICE Fixes
 +
 +* [`manual_split_once`]
 +  [#8250](https://github.com/rust-lang/rust-clippy/pull/8250)
 +
 +### Documentation Improvements
 +
 +* [`map_flatten`]: Add documentation for the `Option` type
 +  [#8354](https://github.com/rust-lang/rust-clippy/pull/8354)
 +* Document that Clippy's driver might use a different code generation than rustc
 +  [#8037](https://github.com/rust-lang/rust-clippy/pull/8037)
 +* Clippy's lint list will now automatically focus the search box
 +  [#8343](https://github.com/rust-lang/rust-clippy/pull/8343)
 +
 +### Others
 +
 +* Clippy now warns if we find multiple Clippy config files exist
 +  [#8326](https://github.com/rust-lang/rust-clippy/pull/8326)
 +
 +## Rust 1.59
 +
 +Released 2022-02-24
 +
 +[e181011...0eff589](https://github.com/rust-lang/rust-clippy/compare/e181011...0eff589)
 +
 +### New Lints
 +
 +* [`index_refutable_slice`]
 +  [#7643](https://github.com/rust-lang/rust-clippy/pull/7643)
 +* [`needless_splitn`]
 +  [#7896](https://github.com/rust-lang/rust-clippy/pull/7896)
 +* [`unnecessary_to_owned`]
 +  [#7978](https://github.com/rust-lang/rust-clippy/pull/7978)
 +* [`needless_late_init`]
 +  [#7995](https://github.com/rust-lang/rust-clippy/pull/7995)
 +* [`octal_escapes`] [#8007](https://github.com/rust-lang/rust-clippy/pull/8007)
 +* [`return_self_not_must_use`]
 +  [#8071](https://github.com/rust-lang/rust-clippy/pull/8071)
 +* [`init_numbered_fields`]
 +  [#8170](https://github.com/rust-lang/rust-clippy/pull/8170)
 +
 +### Moves and Deprecations
 +
 +* Move `if_then_panic` to `pedantic` and rename to [`manual_assert`] (now
 +  allow-by-default) [#7810](https://github.com/rust-lang/rust-clippy/pull/7810)
 +* Rename `disallow_type` to [`disallowed_types`] and `disallowed_method` to
 +  [`disallowed_methods`]
 +  [#7984](https://github.com/rust-lang/rust-clippy/pull/7984)
 +* Move [`map_flatten`] to `complexity` (now warn-by-default)
 +  [#8054](https://github.com/rust-lang/rust-clippy/pull/8054)
 +
 +### Enhancements
 +
 +* [`match_overlapping_arm`]: Fix false negative where after included ranges,
 +  overlapping ranges weren't linted anymore
 +  [#7909](https://github.com/rust-lang/rust-clippy/pull/7909)
 +* [`deprecated_cfg_attr`]: Now takes the specified MSRV into account
 +  [#7944](https://github.com/rust-lang/rust-clippy/pull/7944)
 +* [`cast_lossless`]: Now also lints for `bool` to integer casts
 +  [#7948](https://github.com/rust-lang/rust-clippy/pull/7948)
 +* [`let_underscore_lock`]: Also emit lints for the `parking_lot` crate
 +  [#7957](https://github.com/rust-lang/rust-clippy/pull/7957)
 +* [`needless_borrow`]
 +  [#7977](https://github.com/rust-lang/rust-clippy/pull/7977)
 +    * Lint when a borrow is auto-dereffed more than once
 +    * Lint in the trailing expression of a block for a match arm
 +* [`strlen_on_c_strings`]
 +  [8001](https://github.com/rust-lang/rust-clippy/pull/8001)
 +    * Lint when used without a fully-qualified path
 +    * Suggest removing the surrounding unsafe block when possible
 +* [`non_ascii_literal`]: Now also lints on `char`s, not just `string`s
 +  [#8034](https://github.com/rust-lang/rust-clippy/pull/8034)
 +* [`single_char_pattern`]: Now also lints on `split_inclusive`, `split_once`,
 +  `rsplit_once`, `replace`, and `replacen`
 +  [#8077](https://github.com/rust-lang/rust-clippy/pull/8077)
 +* [`unwrap_or_else_default`]: Now also lints on `std` constructors like
 +  `Vec::new`, `HashSet::new`, and `HashMap::new`
 +  [#8163](https://github.com/rust-lang/rust-clippy/pull/8163)
 +* [`shadow_reuse`]: Now also lints on shadowed `if let` bindings, instead of
 +  [`shadow_unrelated`]
 +  [#8165](https://github.com/rust-lang/rust-clippy/pull/8165)
 +
 +### False Positive Fixes
 +
 +* [`or_fun_call`], [`unnecessary_lazy_evaluations`]: Improve heuristics, so that
 +  cheap functions (e.g. calling `.len()` on a `Vec`) won't get linted anymore
 +  [#7639](https://github.com/rust-lang/rust-clippy/pull/7639)
 +* [`manual_split_once`]: No longer suggests code changing the original behavior
 +  [#7896](https://github.com/rust-lang/rust-clippy/pull/7896)
 +* Don't show [`no_effect`] or [`unnecessary_operation`] warning for unit struct
 +  implementing `FnOnce`
 +  [#7898](https://github.com/rust-lang/rust-clippy/pull/7898)
 +* [`semicolon_if_nothing_returned`]: Fixed a bug, where the lint wrongly
 +  triggered on `let-else` statements
 +  [#7955](https://github.com/rust-lang/rust-clippy/pull/7955)
 +* [`if_then_some_else_none`]: No longer lints if there is an early return
 +  [#7980](https://github.com/rust-lang/rust-clippy/pull/7980)
 +* [`needless_collect`]: No longer suggests removal of `collect` when removal
 +  would create code requiring mutably borrowing a value multiple times
 +  [#7982](https://github.com/rust-lang/rust-clippy/pull/7982)
 +* [`shadow_same`]: Fix false positive for `async` function's params
 +  [#7997](https://github.com/rust-lang/rust-clippy/pull/7997)
 +* [`suboptimal_flops`]: No longer triggers in constant functions
 +  [#8009](https://github.com/rust-lang/rust-clippy/pull/8009)
 +* [`type_complexity`]: No longer lints on associated types in traits
 +  [#8030](https://github.com/rust-lang/rust-clippy/pull/8030)
 +* [`question_mark`]: No longer lints if returned object is not local
 +  [#8080](https://github.com/rust-lang/rust-clippy/pull/8080)
 +* [`option_if_let_else`]: No longer lint on complex sub-patterns
 +  [#8086](https://github.com/rust-lang/rust-clippy/pull/8086)
 +* [`blocks_in_if_conditions`]: No longer lints on empty closures
 +  [#8100](https://github.com/rust-lang/rust-clippy/pull/8100)
 +* [`enum_variant_names`]: No longer lint when first prefix is only a substring
 +  of a camel-case word
 +  [#8127](https://github.com/rust-lang/rust-clippy/pull/8127)
 +* [`identity_op`]: Only lint on integral operands
 +  [#8183](https://github.com/rust-lang/rust-clippy/pull/8183)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`search_is_some`]: Fix suggestion for `any()` not taking item by reference
 +  [#7463](https://github.com/rust-lang/rust-clippy/pull/7463)
 +* [`almost_swapped`]: Now detects if there is a `no_std` or `no_core` attribute
 +  and adapts the suggestion accordingly
 +  [#7877](https://github.com/rust-lang/rust-clippy/pull/7877)
 +* [`redundant_pattern_matching`]: Fix suggestion for deref expressions
 +  [#7949](https://github.com/rust-lang/rust-clippy/pull/7949)
 +* [`explicit_counter_loop`]: Now also produces a suggestion for non-`usize`
 +  types [#7950](https://github.com/rust-lang/rust-clippy/pull/7950)
 +* [`manual_map`]: Fix suggestion when used with unsafe functions and blocks
 +  [#7968](https://github.com/rust-lang/rust-clippy/pull/7968)
 +* [`option_map_or_none`]: Suggest `map` over `and_then` when possible
 +  [#7971](https://github.com/rust-lang/rust-clippy/pull/7971)
 +* [`option_if_let_else`]: No longer expands macros in the suggestion
 +  [#7974](https://github.com/rust-lang/rust-clippy/pull/7974)
 +* [`iter_cloned_collect`]: Suggest `copied` over `cloned` when possible
 +  [#8006](https://github.com/rust-lang/rust-clippy/pull/8006)
 +* [`doc_markdown`]: No longer uses inline hints to improve readability of
 +  suggestion [#8011](https://github.com/rust-lang/rust-clippy/pull/8011)
 +* [`needless_question_mark`]: Now better explains the suggestion
 +  [#8028](https://github.com/rust-lang/rust-clippy/pull/8028)
 +* [`single_char_pattern`]: Escape backslash `\` in suggestion
 +  [#8067](https://github.com/rust-lang/rust-clippy/pull/8067)
 +* [`needless_bool`]: Suggest `a != b` over `!(a == b)`
 +  [#8117](https://github.com/rust-lang/rust-clippy/pull/8117)
 +* [`iter_skip_next`]: Suggest to add a `mut` if it is necessary in order to
 +  apply this lints suggestion
 +  [#8133](https://github.com/rust-lang/rust-clippy/pull/8133)
 +* [`neg_multiply`]: Now produces a suggestion
 +  [#8144](https://github.com/rust-lang/rust-clippy/pull/8144)
 +* [`needless_return`]: Now suggests the unit type `()` over an empty block `{}`
 +  in match arms [#8185](https://github.com/rust-lang/rust-clippy/pull/8185)
 +* [`suboptimal_flops`]: Now gives a syntactically correct suggestion for
 +  `to_radians` and `to_degrees`
 +  [#8187](https://github.com/rust-lang/rust-clippy/pull/8187)
 +
 +### ICE Fixes
 +
 +* [`undocumented_unsafe_blocks`]
 +  [#7945](https://github.com/rust-lang/rust-clippy/pull/7945)
 +  [#7988](https://github.com/rust-lang/rust-clippy/pull/7988)
 +* [`unnecessary_cast`]
 +  [#8167](https://github.com/rust-lang/rust-clippy/pull/8167)
 +
 +### Documentation Improvements
 +
 +* [`print_stdout`], [`print_stderr`], [`dbg_macro`]: Document how the lint level
 +  can be changed crate-wide
 +  [#8040](https://github.com/rust-lang/rust-clippy/pull/8040)
 +* Added a note to the `README` that config changes don't apply to already
 +  compiled code [#8175](https://github.com/rust-lang/rust-clippy/pull/8175)
 +
 +### Others
 +
 +* [Clippy's lint
 +  list](https://rust-lang.github.io/rust-clippy/master/index.html) now displays
 +  the version a lint was added. :tada:
 +  [#7813](https://github.com/rust-lang/rust-clippy/pull/7813)
 +* New and improved issue templates
 +  [#8032](https://github.com/rust-lang/rust-clippy/pull/8032)
 +* _Dev:_ Add `cargo dev lint` command, to run your modified Clippy version on a
 +  file [#7917](https://github.com/rust-lang/rust-clippy/pull/7917)
 +
 +## Rust 1.58
 +
 +Released 2022-01-13
 +
 +[00e31fa...e181011](https://github.com/rust-lang/rust-clippy/compare/00e31fa...e181011)
 +
 +### Rust 1.58.1
 +
 +* Move [`non_send_fields_in_send_ty`] to `nursery` (now allow-by-default)
 +  [#8075](https://github.com/rust-lang/rust-clippy/pull/8075)
 +* [`useless_format`]: Handle implicit named arguments
 +  [#8295](https://github.com/rust-lang/rust-clippy/pull/8295)
 +
 +### New lints
 +
 +* [`transmute_num_to_bytes`]
 +  [#7805](https://github.com/rust-lang/rust-clippy/pull/7805)
 +* [`match_str_case_mismatch`]
 +  [#7806](https://github.com/rust-lang/rust-clippy/pull/7806)
 +* [`format_in_format_args`], [`to_string_in_format_args`]
 +  [#7743](https://github.com/rust-lang/rust-clippy/pull/7743)
 +* [`uninit_vec`]
 +  [#7682](https://github.com/rust-lang/rust-clippy/pull/7682)
 +* [`fn_to_numeric_cast_any`]
 +  [#7705](https://github.com/rust-lang/rust-clippy/pull/7705)
 +* [`undocumented_unsafe_blocks`]
 +  [#7748](https://github.com/rust-lang/rust-clippy/pull/7748)
 +* [`trailing_empty_array`]
 +  [#7838](https://github.com/rust-lang/rust-clippy/pull/7838)
 +* [`string_slice`]
 +  [#7878](https://github.com/rust-lang/rust-clippy/pull/7878)
 +
 +### Moves or deprecations of lints
 +
 +* Move [`non_send_fields_in_send_ty`] to `suspicious`
 +  [#7874](https://github.com/rust-lang/rust-clippy/pull/7874)
 +* Move [`non_ascii_literal`] to `restriction`
 +  [#7907](https://github.com/rust-lang/rust-clippy/pull/7907)
 +
 +### Changes that expand what code existing lints cover
 +
 +* [`question_mark`] now covers `Result`
 +  [#7840](https://github.com/rust-lang/rust-clippy/pull/7840)
 +* Make [`useless_format`] recognize bare `format!("")`
 +  [#7801](https://github.com/rust-lang/rust-clippy/pull/7801)
 +* Lint on underscored variables with no side effects in [`no_effect`]
 +  [#7775](https://github.com/rust-lang/rust-clippy/pull/7775)
 +* Expand [`match_ref_pats`] to check for multiple reference patterns
 +  [#7800](https://github.com/rust-lang/rust-clippy/pull/7800)
 +
 +### False positive fixes
 +
 +* Fix false positive of [`implicit_saturating_sub`] with `else` clause
 +  [#7832](https://github.com/rust-lang/rust-clippy/pull/7832)
 +* Fix [`question_mark`] when there is call in conditional predicate
 +  [#7860](https://github.com/rust-lang/rust-clippy/pull/7860)
 +* [`mut_mut`] no longer lints when type is defined in external macros
 +  [#7795](https://github.com/rust-lang/rust-clippy/pull/7795)
 +* Avoid [`eq_op`] in test functions
 +  [#7811](https://github.com/rust-lang/rust-clippy/pull/7811)
 +* [`cast_possible_truncation`] no longer lints when cast is coming from `signum`
 +  method call [#7850](https://github.com/rust-lang/rust-clippy/pull/7850)
 +* [`match_str_case_mismatch`] no longer lints on uncased characters
 +  [#7865](https://github.com/rust-lang/rust-clippy/pull/7865)
 +* [`ptr_arg`] no longer lints references to type aliases
 +  [#7890](https://github.com/rust-lang/rust-clippy/pull/7890)
 +* [`missing_safety_doc`] now also accepts "implementation safety" headers
 +  [#7856](https://github.com/rust-lang/rust-clippy/pull/7856)
 +* [`missing_safety_doc`] no longer lints if any parent has `#[doc(hidden)]`
 +  attribute [#7849](https://github.com/rust-lang/rust-clippy/pull/7849)
 +* [`if_not_else`] now ignores else-if statements
 +  [#7895](https://github.com/rust-lang/rust-clippy/pull/7895)
 +* Avoid linting [`cast_possible_truncation`] on bit-reducing operations
 +  [#7819](https://github.com/rust-lang/rust-clippy/pull/7819)
 +* Avoid linting [`field_reassign_with_default`] when `Drop` and `Copy` are
 +  involved [#7794](https://github.com/rust-lang/rust-clippy/pull/7794)
 +* [`unnecessary_sort_by`] now checks if argument implements `Ord` trait
 +  [#7824](https://github.com/rust-lang/rust-clippy/pull/7824)
 +* Fix false positive in [`match_overlapping_arm`]
 +  [#7847](https://github.com/rust-lang/rust-clippy/pull/7847)
 +* Prevent [`needless_lifetimes`] false positive in `async` function definition
 +  [#7901](https://github.com/rust-lang/rust-clippy/pull/7901)
 +
 +### Suggestion fixes/improvements
 +
 +* Keep an initial `::` when [`doc_markdown`] suggests to use ticks
 +  [#7916](https://github.com/rust-lang/rust-clippy/pull/7916)
 +* Add a machine applicable suggestion for the [`doc_markdown`] missing backticks
 +  lint [#7904](https://github.com/rust-lang/rust-clippy/pull/7904)
 +* [`equatable_if_let`] no longer expands macros in the suggestion
 +  [#7788](https://github.com/rust-lang/rust-clippy/pull/7788)
 +* Make [`shadow_reuse`] suggestion less verbose
 +  [#7782](https://github.com/rust-lang/rust-clippy/pull/7782)
 +
 +### ICE fixes
 +
 +* Fix ICE in [`enum_variant_names`]
 +  [#7873](https://github.com/rust-lang/rust-clippy/pull/7873)
 +* Fix ICE in [`undocumented_unsafe_blocks`]
 +  [#7891](https://github.com/rust-lang/rust-clippy/pull/7891)
 +
 +### Documentation improvements
 +
 +* Fixed naive doc formatting for `#[must_use]` lints ([`must_use_unit`],
 +  [`double_must_use`], [`must_use_candidate`], [`let_underscore_must_use`])
 +  [#7827](https://github.com/rust-lang/rust-clippy/pull/7827)
 +* Fix typo in example for [`match_result_ok`]
 +  [#7815](https://github.com/rust-lang/rust-clippy/pull/7815)
 +
 +### Others
 +
 +* Allow giving reasons for [`disallowed_types`]
 +  [#7791](https://github.com/rust-lang/rust-clippy/pull/7791)
 +* Fix [`manual_assert`] and [`match_wild_err_arm`] for `#![no_std]` and Rust
 +  2021. [#7851](https://github.com/rust-lang/rust-clippy/pull/7851)
 +* Fix regression in [`semicolon_if_nothing_returned`] on macros containing while
 +  loops [#7789](https://github.com/rust-lang/rust-clippy/pull/7789)
 +* Added a new configuration `literal-suffix-style` to enforce a certain style
 +  writing [`unseparated_literal_suffix`]
 +  [#7726](https://github.com/rust-lang/rust-clippy/pull/7726)
 +
 +## Rust 1.57
 +
 +Released 2021-12-02
 +
 +[7bfc26e...00e31fa](https://github.com/rust-lang/rust-clippy/compare/7bfc26e...00e31fa)
 +
 +### New Lints
 +
 +* [`negative_feature_names`]
 +  [#7539](https://github.com/rust-lang/rust-clippy/pull/7539)
 +* [`redundant_feature_names`]
 +  [#7539](https://github.com/rust-lang/rust-clippy/pull/7539)
 +* [`mod_module_files`]
 +  [#7543](https://github.com/rust-lang/rust-clippy/pull/7543)
 +* [`self_named_module_files`]
 +  [#7543](https://github.com/rust-lang/rust-clippy/pull/7543)
 +* [`manual_split_once`]
 +  [#7565](https://github.com/rust-lang/rust-clippy/pull/7565)
 +* [`derivable_impls`]
 +  [#7570](https://github.com/rust-lang/rust-clippy/pull/7570)
 +* [`needless_option_as_deref`]
 +  [#7596](https://github.com/rust-lang/rust-clippy/pull/7596)
 +* [`iter_not_returning_iterator`]
 +  [#7610](https://github.com/rust-lang/rust-clippy/pull/7610)
 +* [`same_name_method`]
 +  [#7653](https://github.com/rust-lang/rust-clippy/pull/7653)
 +* [`manual_assert`] [#7669](https://github.com/rust-lang/rust-clippy/pull/7669)
 +* [`non_send_fields_in_send_ty`]
 +  [#7709](https://github.com/rust-lang/rust-clippy/pull/7709)
 +* [`equatable_if_let`]
 +  [#7762](https://github.com/rust-lang/rust-clippy/pull/7762)
 +
 +### Moves and Deprecations
 +
 +* Move [`shadow_unrelated`] to `restriction`
 +  [#7338](https://github.com/rust-lang/rust-clippy/pull/7338)
 +* Move [`option_if_let_else`] to `nursery`
 +  [#7568](https://github.com/rust-lang/rust-clippy/pull/7568)
 +* Move [`branches_sharing_code`] to `nursery`
 +  [#7595](https://github.com/rust-lang/rust-clippy/pull/7595)
 +* Rename `if_let_some_result` to [`match_result_ok`] which now also handles
 +  `while let` cases [#7608](https://github.com/rust-lang/rust-clippy/pull/7608)
 +* Move [`many_single_char_names`] to `pedantic`
 +  [#7671](https://github.com/rust-lang/rust-clippy/pull/7671)
 +* Move [`float_cmp`] to `pedantic`
 +  [#7692](https://github.com/rust-lang/rust-clippy/pull/7692)
 +* Rename `box_vec` to [`box_collection`] and lint on more general cases
 +  [#7693](https://github.com/rust-lang/rust-clippy/pull/7693)
 +* Uplift `invalid_atomic_ordering` to rustc
 +  [rust-lang/rust#84039](https://github.com/rust-lang/rust/pull/84039)
 +
 +### Enhancements
 +
 +* Rewrite the `shadow*` lints, so that they find a lot more shadows and are not
 +  limited to certain patterns
 +  [#7338](https://github.com/rust-lang/rust-clippy/pull/7338)
 +* The `avoid-breaking-exported-api` configuration now also works for
 +  [`box_collection`], [`redundant_allocation`], [`rc_buffer`], [`vec_box`],
 +  [`option_option`], [`linkedlist`], [`rc_mutex`]
 +  [#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_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`
 +  [#7629](https://github.com/rust-lang/rust-clippy/pull/7629)
 +* [`approx_constant`]: Add `TAU`
 +  [#7642](https://github.com/rust-lang/rust-clippy/pull/7642)
 +* [`needless_borrow`]: Now also lints on needless mutable borrows
 +  [#7657](https://github.com/rust-lang/rust-clippy/pull/7657)
 +* [`missing_safety_doc`]: Now also lints on unsafe traits
 +  [#7734](https://github.com/rust-lang/rust-clippy/pull/7734)
 +
 +### False Positive Fixes
 +
 +* [`manual_map`]: No longer lints when the option is borrowed in the match and
 +  also consumed in the arm
 +  [#7531](https://github.com/rust-lang/rust-clippy/pull/7531)
 +* [`filter_next`]: No longer lints if `filter` method is not the
 +  `Iterator::filter` method
 +  [#7562](https://github.com/rust-lang/rust-clippy/pull/7562)
 +* [`manual_flatten`]: No longer lints if expression is used after `if let`
 +  [#7566](https://github.com/rust-lang/rust-clippy/pull/7566)
 +* [`option_if_let_else`]: Multiple fixes
 +  [#7573](https://github.com/rust-lang/rust-clippy/pull/7573)
 +    * `break` and `continue` statements local to the would-be closure are
 +      allowed
 +    * Don't lint in const contexts
 +    * Don't lint when yield expressions are used
 +    * Don't lint when the captures made by the would-be closure conflict with
 +      the other branch
 +    * Don't lint when a field of a local is used when the type could be
 +      potentially moved from
 +    * In some cases, don't lint when scrutinee expression conflicts with the
 +      captures of the would-be closure
 +* [`redundant_allocation`]: No longer lints on `Box<Box<dyn T>>` which replaces
 +  wide pointers with thin pointers
 +  [#7592](https://github.com/rust-lang/rust-clippy/pull/7592)
 +* [`bool_assert_comparison`]: No longer lints on types that do not implement the
 +  `Not` trait with `Output = bool`
 +  [#7605](https://github.com/rust-lang/rust-clippy/pull/7605)
 +* [`mut_range_bound`]: No longer lints on range bound mutations, that are
 +  immediately followed by a `break;`
 +  [#7607](https://github.com/rust-lang/rust-clippy/pull/7607)
 +* [`mutable_key_type`]: Improve accuracy and document remaining false positives
 +  and false negatives
 +  [#7640](https://github.com/rust-lang/rust-clippy/pull/7640)
 +* [`redundant_closure`]: Rewrite the lint to fix various false positives and
 +  false negatives [#7661](https://github.com/rust-lang/rust-clippy/pull/7661)
 +* [`large_enum_variant`]: No longer wrongly identifies the second largest
 +  variant [#7677](https://github.com/rust-lang/rust-clippy/pull/7677)
 +* [`needless_return`]: No longer lints on let-else expressions
 +  [#7685](https://github.com/rust-lang/rust-clippy/pull/7685)
 +* [`suspicious_else_formatting`]: No longer lints in proc-macros
 +  [#7707](https://github.com/rust-lang/rust-clippy/pull/7707)
 +* [`excessive_precision`]: No longer lints when in some cases the float was
 +  already written in the shortest form
 +  [#7722](https://github.com/rust-lang/rust-clippy/pull/7722)
 +* [`doc_markdown`]: No longer lints on intra-doc links
 +  [#7772](https://github.com/rust-lang/rust-clippy/pull/7772)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`unnecessary_operation`]: Recommend using an `assert!` instead of using a
 +  function call in an indexing operation
 +  [#7453](https://github.com/rust-lang/rust-clippy/pull/7453)
 +* [`manual_split_once`]: Produce semantically equivalent suggestion when
 +  `rsplitn` is used [#7663](https://github.com/rust-lang/rust-clippy/pull/7663)
 +* [`while_let_on_iterator`]: Produce correct suggestion when using `&mut`
 +  [#7690](https://github.com/rust-lang/rust-clippy/pull/7690)
 +* [`manual_assert`]: No better handles complex conditions
 +  [#7741](https://github.com/rust-lang/rust-clippy/pull/7741)
 +* Correctly handle signs in exponents in numeric literals lints
 +  [#7747](https://github.com/rust-lang/rust-clippy/pull/7747)
 +* [`suspicious_map`]: Now also suggests to use `inspect` as an alternative
 +  [#7770](https://github.com/rust-lang/rust-clippy/pull/7770)
 +* Drop exponent from suggestion if it is 0 in numeric literals lints
 +  [#7774](https://github.com/rust-lang/rust-clippy/pull/7774)
 +
 +### ICE Fixes
 +
 +* [`implicit_hasher`]
 +  [#7761](https://github.com/rust-lang/rust-clippy/pull/7761)
 +
 +### Others
 +
 +* Clippy now uses the 2021
 +  [Edition!](https://www.youtube.com/watch?v=q0aNduqb2Ro)
 +  [#7664](https://github.com/rust-lang/rust-clippy/pull/7664)
 +
 +## Rust 1.56
 +
 +Released 2021-10-21
 +
 +[74d1561...7bfc26e](https://github.com/rust-lang/rust-clippy/compare/74d1561...7bfc26e)
 +
 +### New Lints
 +
 +* [`unwrap_or_else_default`]
 +  [#7516](https://github.com/rust-lang/rust-clippy/pull/7516)
 +
 +### Enhancements
 +
 +* [`needless_continue`]: Now also lints in `loop { continue; }` case
 +  [#7477](https://github.com/rust-lang/rust-clippy/pull/7477)
 +* [`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)
 +* [`map_flatten`]: Now also lints on the `Result` type
 +  [#7522](https://github.com/rust-lang/rust-clippy/pull/7522)
 +* [`no_effect`]: Now also lints on inclusive ranges
 +  [#7556](https://github.com/rust-lang/rust-clippy/pull/7556)
 +
 +### False Positive Fixes
 +
 +* [`nonstandard_macro_braces`]: No longer lints on similar named nested macros
 +  [#7478](https://github.com/rust-lang/rust-clippy/pull/7478)
 +* [`too_many_lines`]: No longer lints in closures to avoid duplicated diagnostics
 +  [#7534](https://github.com/rust-lang/rust-clippy/pull/7534)
 +* [`similar_names`]: No longer complains about `iter` and `item` being too
 +  similar [#7546](https://github.com/rust-lang/rust-clippy/pull/7546)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`similar_names`]: No longer suggests to insert or add an underscore as a fix
 +  [#7221](https://github.com/rust-lang/rust-clippy/pull/7221)
 +* [`new_without_default`]: No longer shows the full qualified type path when
 +  suggesting adding a `Default` implementation
 +  [#7493](https://github.com/rust-lang/rust-clippy/pull/7493)
 +* [`while_let_on_iterator`]: Now suggests re-borrowing mutable references
 +  [#7520](https://github.com/rust-lang/rust-clippy/pull/7520)
 +* [`extend_with_drain`]: Improve code suggestion for mutable and immutable
 +  references [#7533](https://github.com/rust-lang/rust-clippy/pull/7533)
 +* [`trivially_copy_pass_by_ref`]: Now properly handles `Self` type
 +  [#7535](https://github.com/rust-lang/rust-clippy/pull/7535)
 +* [`never_loop`]: Now suggests using `if let` instead of a `for` loop when
 +  applicable [#7541](https://github.com/rust-lang/rust-clippy/pull/7541)
 +
 +### Documentation Improvements
 +
 +* Clippy now uses a lint to generate its lint documentation. [Lints all the way
 +  down](https://en.wikipedia.org/wiki/Turtles_all_the_way_down).
 +  [#7502](https://github.com/rust-lang/rust-clippy/pull/7502)
 +* Reworked Clippy's website:
 +  [#7172](https://github.com/rust-lang/rust-clippy/issues/7172)
 +  [#7279](https://github.com/rust-lang/rust-clippy/pull/7279)
 +  * Added applicability information about lints
 +  * Added a link to jump into the implementation
 +  * Improved loading times
 +  * Adapted some styling
 +* `cargo clippy --help` now also explains the `--fix` and `--no-deps` flag
 +  [#7492](https://github.com/rust-lang/rust-clippy/pull/7492)
 +* [`unnested_or_patterns`]: Removed `or_patterns` feature gate in the code
 +  example [#7507](https://github.com/rust-lang/rust-clippy/pull/7507)
 +
 +## Rust 1.55
 +
 +Released 2021-09-09
 +
 +[3ae8faf...74d1561](https://github.com/rust-lang/rust-clippy/compare/3ae8faf...74d1561)
 +
 +### Important Changes
 +
 +* Stabilized `cargo clippy --fix` :tada:
 +  [#7405](https://github.com/rust-lang/rust-clippy/pull/7405)
 +
 +### New Lints
 +
 +* [`rc_mutex`]
 +  [#7316](https://github.com/rust-lang/rust-clippy/pull/7316)
 +* [`nonstandard_macro_braces`]
 +  [#7299](https://github.com/rust-lang/rust-clippy/pull/7299)
 +* [`strlen_on_c_strings`]
 +  [#7243](https://github.com/rust-lang/rust-clippy/pull/7243)
 +* [`self_named_constructors`]
 +  [#7403](https://github.com/rust-lang/rust-clippy/pull/7403)
 +* [`disallowed_script_idents`]
 +  [#7400](https://github.com/rust-lang/rust-clippy/pull/7400)
 +* [`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)
 +* [`extend_with_drain`]
 +  [#7270](https://github.com/rust-lang/rust-clippy/pull/7270)
 +
 +### Moves and Deprecations
 +
 +* Moved [`from_iter_instead_of_collect`] to `pedantic`
 +  [#7375](https://github.com/rust-lang/rust-clippy/pull/7375)
 +* Added `suspicious` as a new lint group for *code that is most likely wrong or useless*
 +  [#7350](https://github.com/rust-lang/rust-clippy/pull/7350)
 +  * Moved [`blanket_clippy_restriction_lints`] to `suspicious`
 +  * Moved [`empty_loop`] to `suspicious`
 +  * Moved [`eval_order_dependence`] to `suspicious`
 +  * Moved [`float_equality_without_abs`] to `suspicious`
 +  * Moved [`for_loops_over_fallibles`] to `suspicious`
 +  * Moved [`misrefactored_assign_op`] to `suspicious`
 +  * Moved [`mut_range_bound`] to `suspicious`
 +  * Moved [`mutable_key_type`] to `suspicious`
 +  * Moved [`suspicious_arithmetic_impl`] to `suspicious`
 +  * Moved [`suspicious_assignment_formatting`] to `suspicious`
 +  * Moved [`suspicious_else_formatting`] to `suspicious`
 +  * Moved [`suspicious_map`] to `suspicious`
 +  * Moved [`suspicious_op_assign_impl`] to `suspicious`
 +  * Moved [`suspicious_unary_op_formatting`] to `suspicious`
 +
 +### Enhancements
 +
 +* [`while_let_on_iterator`]: Now suggests `&mut iter` inside closures
 +  [#7262](https://github.com/rust-lang/rust-clippy/pull/7262)
 +* [`doc_markdown`]:
 +  * Now detects unbalanced ticks
 +    [#7357](https://github.com/rust-lang/rust-clippy/pull/7357)
 +  * Add `FreeBSD` to the default configuration as an allowed identifier
 +    [#7334](https://github.com/rust-lang/rust-clippy/pull/7334)
 +* [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]: Now allows wildcards for enums with unstable
 +  or hidden variants
 +  [#7407](https://github.com/rust-lang/rust-clippy/pull/7407)
 +* [`redundant_allocation`]: Now additionally supports the `Arc<>` type
 +  [#7308](https://github.com/rust-lang/rust-clippy/pull/7308)
 +* [`disallowed_names`]: Now allows disallowed names in test code
 +  [#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_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
 +  [#7396](https://github.com/rust-lang/rust-clippy/pull/7396)
 +
 +### False Positive Fixes
 +
 +* [`default_numeric_fallback`]: No longer lints on float literals as function arguments
 +  [#7446](https://github.com/rust-lang/rust-clippy/pull/7446)
 +* [`use_self`]: No longer lints on type parameters
 +  [#7288](https://github.com/rust-lang/rust-clippy/pull/7288)
 +* [`unimplemented`]: Now ignores the `assert` and `debug_assert` macros
 +  [#7439](https://github.com/rust-lang/rust-clippy/pull/7439)
 +* [`branches_sharing_code`]: Now always checks for block expressions
 +  [#7462](https://github.com/rust-lang/rust-clippy/pull/7462)
 +* [`field_reassign_with_default`]: No longer triggers in macros
 +  [#7160](https://github.com/rust-lang/rust-clippy/pull/7160)
 +* [`redundant_clone`]: No longer lints on required clones for borrowed data
 +  [#7346](https://github.com/rust-lang/rust-clippy/pull/7346)
 +* [`default_numeric_fallback`]: No longer triggers in external macros
 +  [#7325](https://github.com/rust-lang/rust-clippy/pull/7325)
 +* [`needless_bool`]: No longer lints in macros
 +  [#7442](https://github.com/rust-lang/rust-clippy/pull/7442)
 +* [`useless_format`]: No longer triggers when additional text is being appended
 +  [#7442](https://github.com/rust-lang/rust-clippy/pull/7442)
 +* [`assertions_on_constants`]: `cfg!(...)` is no longer considered to be a constant
 +  [#7319](https://github.com/rust-lang/rust-clippy/pull/7319)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`needless_collect`]: Now show correct lint messages for shadowed values
 +  [#7289](https://github.com/rust-lang/rust-clippy/pull/7289)
 +* [`wrong_pub_self_convention`]: The deprecated message now suggest the correct configuration value
 +  [#7382](https://github.com/rust-lang/rust-clippy/pull/7382)
 +* [`semicolon_if_nothing_returned`]: Allow missing semicolon in blocks with only one expression
 +  [#7326](https://github.com/rust-lang/rust-clippy/pull/7326)
 +
 +### ICE Fixes
 +
 +* [`zero_sized_map_values`]
 +  [#7470](https://github.com/rust-lang/rust-clippy/pull/7470)
 +* [`redundant_pattern_matching`]
 +  [#7471](https://github.com/rust-lang/rust-clippy/pull/7471)
 +* [`modulo_one`]
 +  [#7473](https://github.com/rust-lang/rust-clippy/pull/7473)
 +* [`use_self`]
 +  [#7428](https://github.com/rust-lang/rust-clippy/pull/7428)
 +
 +## Rust 1.54
 +
 +Released 2021-07-29
 +
 +[7c7683c...3ae8faf](https://github.com/rust-lang/rust-clippy/compare/7c7683c...3ae8faf)
 +
 +### New Lints
 +
 +- [`ref_binding_to_reference`]
 +  [#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
 +- [`needless_bitwise_bool`]
 +  [#7133](https://github.com/rust-lang/rust-clippy/pull/7133)
 +- [`unused_async`] [#7225](https://github.com/rust-lang/rust-clippy/pull/7225)
 +- [`manual_str_repeat`]
 +  [#7265](https://github.com/rust-lang/rust-clippy/pull/7265)
 +- [`suspicious_splitn`]
 +  [#7292](https://github.com/rust-lang/rust-clippy/pull/7292)
 +
 +### Moves and Deprecations
 +
 +- Deprecate `pub_enum_variant_names` and `wrong_pub_self_convention` in favor of
 +  the new `avoid-breaking-exported-api` config option (see
 +  [Enhancements](#1-54-enhancements))
 +  [#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
 +- Move [`inconsistent_struct_constructor`] to `pedantic`
 +  [#7193](https://github.com/rust-lang/rust-clippy/pull/7193)
 +- Move [`needless_borrow`] to `style` (now warn-by-default)
 +  [#7254](https://github.com/rust-lang/rust-clippy/pull/7254)
 +- Move [`suspicious_operation_groupings`] to `nursery`
 +  [#7266](https://github.com/rust-lang/rust-clippy/pull/7266)
 +- Move [`semicolon_if_nothing_returned`] to `pedantic`
 +  [#7268](https://github.com/rust-lang/rust-clippy/pull/7268)
 +
 +### Enhancements <a name="1-54-enhancements"></a>
 +
 +- [`while_let_on_iterator`]: Now also lints in nested loops
 +  [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
 +- [`single_char_pattern`]: Now also lints on `strip_prefix` and `strip_suffix`
 +  [#7156](https://github.com/rust-lang/rust-clippy/pull/7156)
 +- [`needless_collect`]: Now also lints on assignments with type annotations
 +  [#7163](https://github.com/rust-lang/rust-clippy/pull/7163)
 +- [`if_then_some_else_none`]: Now works with the MSRV config
 +  [#7177](https://github.com/rust-lang/rust-clippy/pull/7177)
 +- Add `avoid-breaking-exported-api` config option for the lints
 +  [`enum_variant_names`], [`large_types_passed_by_value`],
 +  [`trivially_copy_pass_by_ref`], [`unnecessary_wraps`],
 +  [`upper_case_acronyms`], and [`wrong_self_convention`]. We recommend to set
 +  this configuration option to `false` before a major release (1.0/2.0/...) to
 +  clean up the API [#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
 +- [`needless_collect`]: Now lints on even more data structures
 +  [#7188](https://github.com/rust-lang/rust-clippy/pull/7188)
 +- [`missing_docs_in_private_items`]: No longer sees `#[<name> = "<value>"]` like
 +  attributes as sufficient documentation
 +  [#7281](https://github.com/rust-lang/rust-clippy/pull/7281)
 +- [`needless_collect`], [`short_circuit_statement`], [`unnecessary_operation`]:
 +  Now work as expected when used with `allow`
 +  [#7282](https://github.com/rust-lang/rust-clippy/pull/7282)
 +
 +### False Positive Fixes
 +
 +- [`implicit_return`]: Now takes all diverging functions in account to avoid
 +  false positives [#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
 +- [`while_let_on_iterator`]: No longer lints when the iterator is a struct field
 +  and the struct is used in the loop
 +  [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
 +- [`multiple_inherent_impl`]: No longer lints with generic arguments
 +  [#7089](https://github.com/rust-lang/rust-clippy/pull/7089)
 +- [`comparison_chain`]: No longer lints in a `const` context
 +  [#7118](https://github.com/rust-lang/rust-clippy/pull/7118)
 +- [`while_immutable_condition`]: Fix false positive where mutation in the loop
 +  variable wasn't picked up
 +  [#7144](https://github.com/rust-lang/rust-clippy/pull/7144)
 +- [`default_trait_access`]: No longer lints in macros
 +  [#7150](https://github.com/rust-lang/rust-clippy/pull/7150)
 +- [`needless_question_mark`]: No longer lints when the inner value is implicitly
 +  dereferenced [#7165](https://github.com/rust-lang/rust-clippy/pull/7165)
 +- [`unused_unit`]: No longer lints when multiple macro contexts are involved
 +  [#7167](https://github.com/rust-lang/rust-clippy/pull/7167)
 +- [`eval_order_dependence`]: Fix false positive in async context
 +  [#7174](https://github.com/rust-lang/rust-clippy/pull/7174)
 +- [`unnecessary_filter_map`]: No longer lints if the `filter_map` changes the
 +  type [#7175](https://github.com/rust-lang/rust-clippy/pull/7175)
 +- [`wrong_self_convention`]: No longer lints in trait implementations of
 +  non-`Copy` types [#7182](https://github.com/rust-lang/rust-clippy/pull/7182)
 +- [`suboptimal_flops`]: No longer lints on `powi(2)`
 +  [#7201](https://github.com/rust-lang/rust-clippy/pull/7201)
 +- [`wrong_self_convention`]: No longer lints if there is no implicit `self`
 +  [#7215](https://github.com/rust-lang/rust-clippy/pull/7215)
 +- [`option_if_let_else`]: No longer lints on `else if let` pattern
 +  [#7216](https://github.com/rust-lang/rust-clippy/pull/7216)
 +- [`use_self`], [`useless_conversion`]: Fix false positives when generic
 +  arguments are involved
 +  [#7223](https://github.com/rust-lang/rust-clippy/pull/7223)
 +- [`manual_unwrap_or`]: Fix false positive with deref coercion
 +  [#7233](https://github.com/rust-lang/rust-clippy/pull/7233)
 +- [`similar_names`]: No longer lints on `wparam`/`lparam`
 +  [#7255](https://github.com/rust-lang/rust-clippy/pull/7255)
 +- [`redundant_closure`]: No longer lints on using the `vec![]` macro in a
 +  closure [#7263](https://github.com/rust-lang/rust-clippy/pull/7263)
 +
 +### Suggestion Fixes/Improvements
 +
 +- [`implicit_return`]
 +  [#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
 +    - Fix suggestion for async functions
 +    - Improve suggestion with macros
 +    - Suggest to change `break` to `return` when appropriate
 +- [`while_let_on_iterator`]: Now suggests `&mut iter` when necessary
 +  [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
 +- [`match_single_binding`]: Improve suggestion when match scrutinee has side
 +  effects [#7095](https://github.com/rust-lang/rust-clippy/pull/7095)
 +- [`needless_borrow`]: Now suggests to also change usage sites as needed
 +  [#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
 +- [`write_with_newline`]: Improve suggestion when only `\n` is written to the
 +  buffer [#7183](https://github.com/rust-lang/rust-clippy/pull/7183)
 +- [`from_iter_instead_of_collect`]: The suggestion is now auto applicable also
 +  when a `<_ as Trait>::_` is involved
 +  [#7264](https://github.com/rust-lang/rust-clippy/pull/7264)
 +- [`not_unsafe_ptr_arg_deref`]: Improved error message
 +  [#7294](https://github.com/rust-lang/rust-clippy/pull/7294)
 +
 +### ICE Fixes
 +
 +- Fix ICE when running Clippy on `libstd`
 +  [#7140](https://github.com/rust-lang/rust-clippy/pull/7140)
 +- [`implicit_return`]
 +  [#7242](https://github.com/rust-lang/rust-clippy/pull/7242)
 +
 +## Rust 1.53
 +
 +Released 2021-06-17
 +
 +[6ed6f1e...7c7683c](https://github.com/rust-lang/rust-clippy/compare/6ed6f1e...7c7683c)
 +
 +### New Lints
 +
 +* [`option_filter_map`]
 +  [#6342](https://github.com/rust-lang/rust-clippy/pull/6342)
 +* [`branches_sharing_code`]
 +  [#6463](https://github.com/rust-lang/rust-clippy/pull/6463)
 +* [`needless_for_each`]
 +  [#6706](https://github.com/rust-lang/rust-clippy/pull/6706)
 +* [`if_then_some_else_none`]
 +  [#6859](https://github.com/rust-lang/rust-clippy/pull/6859)
 +* [`non_octal_unix_permissions`]
 +  [#7001](https://github.com/rust-lang/rust-clippy/pull/7001)
 +* [`unnecessary_self_imports`]
 +  [#7072](https://github.com/rust-lang/rust-clippy/pull/7072)
 +* [`bool_assert_comparison`]
 +  [#7083](https://github.com/rust-lang/rust-clippy/pull/7083)
 +* [`cloned_instead_of_copied`]
 +  [#7098](https://github.com/rust-lang/rust-clippy/pull/7098)
 +* [`flat_map_option`]
 +  [#7101](https://github.com/rust-lang/rust-clippy/pull/7101)
 +
 +### Moves and Deprecations
 +
 +* Deprecate [`filter_map`] lint
 +  [#7059](https://github.com/rust-lang/rust-clippy/pull/7059)
 +* Move [`transmute_ptr_to_ptr`] to `pedantic`
 +  [#7102](https://github.com/rust-lang/rust-clippy/pull/7102)
 +
 +### Enhancements
 +
 +* [`mem_replace_with_default`]: Also lint on common std constructors
 +  [#6820](https://github.com/rust-lang/rust-clippy/pull/6820)
 +* [`wrong_self_convention`]: Also lint on `to_*_mut` methods
 +  [#6828](https://github.com/rust-lang/rust-clippy/pull/6828)
 +* [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]:
 +  [#6863](https://github.com/rust-lang/rust-clippy/pull/6863)
 +    * Attempt to find a common path prefix in suggestion
 +    * Don't lint on `Option` and `Result`
 +    * Consider `Self` prefix
 +* [`explicit_deref_methods`]: Also lint on chained `deref` calls
 +  [#6865](https://github.com/rust-lang/rust-clippy/pull/6865)
 +* [`or_fun_call`]: Also lint on `unsafe` blocks
 +  [#6928](https://github.com/rust-lang/rust-clippy/pull/6928)
 +* [`vec_box`], [`linkedlist`], [`option_option`]: Also lint in `const` and
 +  `static` items [#6938](https://github.com/rust-lang/rust-clippy/pull/6938)
 +* [`search_is_some`]: Also check for `is_none`
 +  [#6942](https://github.com/rust-lang/rust-clippy/pull/6942)
 +* [`string_lit_as_bytes`]: Also lint on `into_bytes`
 +  [#6959](https://github.com/rust-lang/rust-clippy/pull/6959)
 +* [`len_without_is_empty`]: Also lint if function signatures of `len` and
 +  `is_empty` don't match
 +  [#6980](https://github.com/rust-lang/rust-clippy/pull/6980)
 +* [`redundant_pattern_matching`]: Also lint if the pattern is a `&` pattern
 +  [#6991](https://github.com/rust-lang/rust-clippy/pull/6991)
 +* [`clone_on_copy`]: Also lint on chained method calls taking `self` by value
 +  [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
 +* [`missing_panics_doc`]: Also lint on `assert_eq!` and `assert_ne!`
 +  [#7029](https://github.com/rust-lang/rust-clippy/pull/7029)
 +* [`needless_return`]: Also lint in `async` functions
 +  [#7067](https://github.com/rust-lang/rust-clippy/pull/7067)
 +* [`unused_io_amount`]: Also lint on expressions like `_.read().ok()?`
 +  [#7100](https://github.com/rust-lang/rust-clippy/pull/7100)
 +* [`iter_cloned_collect`]: Also lint on large arrays, since const-generics are
 +  now stable [#7138](https://github.com/rust-lang/rust-clippy/pull/7138)
 +
 +### False Positive Fixes
 +
 +* [`upper_case_acronyms`]: No longer lints on public items
 +  [#6805](https://github.com/rust-lang/rust-clippy/pull/6805)
 +* [`suspicious_map`]: No longer lints when side effects may occur inside the
 +  `map` call [#6831](https://github.com/rust-lang/rust-clippy/pull/6831)
 +* [`manual_map`], [`manual_unwrap_or`]: No longer lints in `const` functions
 +  [#6917](https://github.com/rust-lang/rust-clippy/pull/6917)
 +* [`wrong_self_convention`]: Now respects `Copy` types
 +  [#6924](https://github.com/rust-lang/rust-clippy/pull/6924)
 +* [`needless_question_mark`]: No longer lints if the `?` and the `Some(..)` come
 +  from different macro contexts [#6935](https://github.com/rust-lang/rust-clippy/pull/6935)
 +* [`map_entry`]: Better detect if the entry API can be used
 +  [#6937](https://github.com/rust-lang/rust-clippy/pull/6937)
 +* [`or_fun_call`]: No longer lints on some `len` function calls
 +  [#6950](https://github.com/rust-lang/rust-clippy/pull/6950)
 +* [`new_ret_no_self`]: No longer lints when `Self` is returned with different
 +  generic arguments [#6952](https://github.com/rust-lang/rust-clippy/pull/6952)
 +* [`upper_case_acronyms`]: No longer lints on public items
 +  [#6981](https://github.com/rust-lang/rust-clippy/pull/6981)
 +* [`explicit_into_iter_loop`]: Only lint when `into_iter` is an implementation
 +  of `IntoIterator` [#6982](https://github.com/rust-lang/rust-clippy/pull/6982)
 +* [`expl_impl_clone_on_copy`]: Take generic constraints into account before
 +  suggesting to use `derive` instead
 +  [#6993](https://github.com/rust-lang/rust-clippy/pull/6993)
 +* [`missing_panics_doc`]: No longer lints when only debug-assertions are used
 +  [#6996](https://github.com/rust-lang/rust-clippy/pull/6996)
 +* [`clone_on_copy`]: Only lint when using the `Clone` trait
 +  [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
 +* [`wrong_self_convention`]: No longer lints inside a trait implementation
 +  [#7002](https://github.com/rust-lang/rust-clippy/pull/7002)
 +* [`redundant_clone`]: No longer lints when the cloned value is modified while
 +  the clone is in use
 +  [#7011](https://github.com/rust-lang/rust-clippy/pull/7011)
 +* [`same_item_push`]: No longer lints if the `Vec` is used in the loop body
 +  [#7018](https://github.com/rust-lang/rust-clippy/pull/7018)
 +* [`cargo_common_metadata`]: Remove author requirement
 +  [#7026](https://github.com/rust-lang/rust-clippy/pull/7026)
 +* [`panic_in_result_fn`]: No longer lints on `debug_assert` family
 +  [#7060](https://github.com/rust-lang/rust-clippy/pull/7060)
 +* [`panic`]: No longer wrongfully lints on `debug_assert` with message
 +  [#7063](https://github.com/rust-lang/rust-clippy/pull/7063)
 +* [`wrong_self_convention`]: No longer lints in trait implementations where no
 +  `self` is involved [#7064](https://github.com/rust-lang/rust-clippy/pull/7064)
 +* [`missing_const_for_fn`]: No longer lints when unstable `const` function is
 +  involved [#7076](https://github.com/rust-lang/rust-clippy/pull/7076)
 +* [`suspicious_else_formatting`]: Allow Allman style braces
 +  [#7087](https://github.com/rust-lang/rust-clippy/pull/7087)
 +* [`inconsistent_struct_constructor`]: No longer lints in macros
 +  [#7097](https://github.com/rust-lang/rust-clippy/pull/7097)
 +* [`single_component_path_imports`]: No longer lints on macro re-exports
 +  [#7120](https://github.com/rust-lang/rust-clippy/pull/7120)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`redundant_pattern_matching`]: Add a note when applying this lint would
 +  change the drop order
 +  [#6568](https://github.com/rust-lang/rust-clippy/pull/6568)
 +* [`write_literal`], [`print_literal`]: Add auto-applicable suggestion
 +  [#6821](https://github.com/rust-lang/rust-clippy/pull/6821)
 +* [`manual_map`]: Fix suggestion for complex `if let ... else` chains
 +  [#6856](https://github.com/rust-lang/rust-clippy/pull/6856)
 +* [`inconsistent_struct_constructor`]: Make lint description and message clearer
 +  [#6892](https://github.com/rust-lang/rust-clippy/pull/6892)
 +* [`map_entry`]: Now suggests `or_insert`, `insert_with` or `match _.entry(_)`
 +  as appropriate [#6937](https://github.com/rust-lang/rust-clippy/pull/6937)
 +* [`manual_flatten`]: Suggest to insert `copied` if necessary
 +  [#6962](https://github.com/rust-lang/rust-clippy/pull/6962)
 +* [`redundant_slicing`]: Fix suggestion when a re-borrow might be required or
 +  when the value is from a macro call
 +  [#6975](https://github.com/rust-lang/rust-clippy/pull/6975)
 +* [`match_wildcard_for_single_variants`]: Fix suggestion for hidden variant
 +  [#6988](https://github.com/rust-lang/rust-clippy/pull/6988)
 +* [`clone_on_copy`]: Correct suggestion when the cloned value is a macro call
 +  [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
 +* [`manual_map`]: Fix suggestion at the end of an if chain
 +  [#7004](https://github.com/rust-lang/rust-clippy/pull/7004)
 +* Fix needless parenthesis output in multiple lint suggestions
 +  [#7013](https://github.com/rust-lang/rust-clippy/pull/7013)
 +* [`needless_collect`]: Better explanation in the lint message
 +  [#7020](https://github.com/rust-lang/rust-clippy/pull/7020)
 +* [`useless_vec`]: Now considers mutability
 +  [#7036](https://github.com/rust-lang/rust-clippy/pull/7036)
 +* [`useless_format`]: Wrap the content in braces if necessary
 +  [#7092](https://github.com/rust-lang/rust-clippy/pull/7092)
 +* [`single_match`]: Don't suggest an equality check for types which don't
 +  implement `PartialEq`
 +  [#7093](https://github.com/rust-lang/rust-clippy/pull/7093)
 +* [`from_over_into`]: Mention type in help message
 +  [#7099](https://github.com/rust-lang/rust-clippy/pull/7099)
 +* [`manual_unwrap_or`]: Fix invalid code suggestion due to a macro call
 +  [#7136](https://github.com/rust-lang/rust-clippy/pull/7136)
 +
 +### ICE Fixes
 +
 +* [`macro_use_imports`]
 +  [#7022](https://github.com/rust-lang/rust-clippy/pull/7022)
 +* [`missing_panics_doc`]
 +  [#7034](https://github.com/rust-lang/rust-clippy/pull/7034)
 +* [`tabs_in_doc_comments`]
 +  [#7039](https://github.com/rust-lang/rust-clippy/pull/7039)
 +* [`missing_const_for_fn`]
 +  [#7128](https://github.com/rust-lang/rust-clippy/pull/7128)
 +
 +### Others
 +
 +* [Clippy's lint
 +  list](https://rust-lang.github.io/rust-clippy/master/index.html) now supports
 +  themes [#7030](https://github.com/rust-lang/rust-clippy/pull/7030)
 +* Lints that were uplifted to `rustc` now mention the new `rustc` name in the
 +  deprecation warning
 +  [#7056](https://github.com/rust-lang/rust-clippy/pull/7056)
 +
 +## Rust 1.52
 +
 +Released 2021-05-06
 +
 +[3e41797...6ed6f1e](https://github.com/rust-lang/rust-clippy/compare/3e41797...6ed6f1e)
 +
 +### New Lints
 +
 +* [`from_str_radix_10`]
 +  [#6717](https://github.com/rust-lang/rust-clippy/pull/6717)
 +* [`implicit_clone`]
 +  [#6730](https://github.com/rust-lang/rust-clippy/pull/6730)
 +* [`semicolon_if_nothing_returned`]
 +  [#6681](https://github.com/rust-lang/rust-clippy/pull/6681)
 +* [`manual_flatten`]
 +  [#6646](https://github.com/rust-lang/rust-clippy/pull/6646)
 +* [`inconsistent_struct_constructor`]
 +  [#6769](https://github.com/rust-lang/rust-clippy/pull/6769)
 +* [`iter_count`]
 +  [#6791](https://github.com/rust-lang/rust-clippy/pull/6791)
 +* [`default_numeric_fallback`]
 +  [#6662](https://github.com/rust-lang/rust-clippy/pull/6662)
 +* [`bytes_nth`]
 +  [#6695](https://github.com/rust-lang/rust-clippy/pull/6695)
 +* [`filter_map_identity`]
 +  [#6685](https://github.com/rust-lang/rust-clippy/pull/6685)
 +* [`manual_map`]
 +  [#6573](https://github.com/rust-lang/rust-clippy/pull/6573)
 +
 +### Moves and Deprecations
 +
 +* Moved [`upper_case_acronyms`] to `pedantic`
 +  [#6775](https://github.com/rust-lang/rust-clippy/pull/6775)
 +* Moved [`manual_map`] to `nursery`
 +  [#6796](https://github.com/rust-lang/rust-clippy/pull/6796)
 +* Moved [`unnecessary_wraps`] to `pedantic`
 +  [#6765](https://github.com/rust-lang/rust-clippy/pull/6765)
 +* Moved [`trivial_regex`] to `nursery`
 +  [#6696](https://github.com/rust-lang/rust-clippy/pull/6696)
 +* Moved [`naive_bytecount`] to `pedantic`
 +  [#6825](https://github.com/rust-lang/rust-clippy/pull/6825)
 +* Moved [`upper_case_acronyms`] to `style`
 +  [#6788](https://github.com/rust-lang/rust-clippy/pull/6788)
 +* Moved [`manual_map`] to `style`
 +  [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
 +
 +### Enhancements
 +
 +* [`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
 +  [#6788](https://github.com/rust-lang/rust-clippy/pull/6788)
 +* [`collapsible_match`]: Now supports block comparison with different value names
 +  [#6754](https://github.com/rust-lang/rust-clippy/pull/6754)
 +* [`unnecessary_wraps`]: Will now suggest removing unnecessary wrapped return unit type, like `Option<()>`
 +  [#6665](https://github.com/rust-lang/rust-clippy/pull/6665)
 +* Improved value usage detection in closures
 +  [#6698](https://github.com/rust-lang/rust-clippy/pull/6698)
 +
 +### False Positive Fixes
 +
 +* [`use_self`]: No longer lints in macros
 +  [#6833](https://github.com/rust-lang/rust-clippy/pull/6833)
 +* [`use_self`]: Fixed multiple false positives for: generics, associated types and derive implementations
 +  [#6179](https://github.com/rust-lang/rust-clippy/pull/6179)
 +* [`missing_inline_in_public_items`]: No longer lints for procedural macros
 +  [#6814](https://github.com/rust-lang/rust-clippy/pull/6814)
 +* [`inherent_to_string`]: No longer lints on functions with function generics
 +  [#6771](https://github.com/rust-lang/rust-clippy/pull/6771)
 +* [`doc_markdown`]: Add `OpenDNS` to the default configuration as an allowed identifier
 +  [#6783](https://github.com/rust-lang/rust-clippy/pull/6783)
 +* [`missing_panics_doc`]: No longer lints on [`unreachable!`](https://doc.rust-lang.org/std/macro.unreachable.html)
 +  [#6700](https://github.com/rust-lang/rust-clippy/pull/6700)
 +* [`collapsible_if`]: No longer lints on if statements with attributes
 +  [#6701](https://github.com/rust-lang/rust-clippy/pull/6701)
 +* [`match_same_arms`]: Only considers empty blocks as equal if the tokens contained are the same
 +  [#6843](https://github.com/rust-lang/rust-clippy/pull/6843)
 +* [`redundant_closure`]: Now ignores macros
 +  [#6871](https://github.com/rust-lang/rust-clippy/pull/6871)
 +* [`manual_map`]: Fixed false positives when control flow statements like `return`, `break` etc. are used
 +  [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
 +* [`vec_init_then_push`]: Fixed false positives for loops and if statements
 +  [#6697](https://github.com/rust-lang/rust-clippy/pull/6697)
 +* [`len_without_is_empty`]: Will now consider multiple impl blocks and `#[allow]` on
 +  the `len` method as well as the type definition.
 +  [#6853](https://github.com/rust-lang/rust-clippy/pull/6853)
 +* [`let_underscore_drop`]: Only lints on types which implement `Drop`
 +  [#6682](https://github.com/rust-lang/rust-clippy/pull/6682)
 +* [`unit_arg`]: No longer lints on unit arguments when they come from a path expression.
 +  [#6601](https://github.com/rust-lang/rust-clippy/pull/6601)
 +* [`cargo_common_metadata`]: No longer lints if
 +  [`publish = false`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field)
 +  is defined in the manifest
 +  [#6650](https://github.com/rust-lang/rust-clippy/pull/6650)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`collapsible_match`]: Fixed lint message capitalization
 +  [#6766](https://github.com/rust-lang/rust-clippy/pull/6766)
 +* [`or_fun_call`]: Improved suggestions for `or_insert(vec![])`
 +  [#6790](https://github.com/rust-lang/rust-clippy/pull/6790)
 +* [`manual_map`]: No longer expands macros in the suggestions
 +  [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
 +* Aligned Clippy's lint messages with the rustc dev guide
 +  [#6787](https://github.com/rust-lang/rust-clippy/pull/6787)
 +
 +### ICE Fixes
 +
 +* [`zero_sized_map_values`]
 +  [#6866](https://github.com/rust-lang/rust-clippy/pull/6866)
 +
 +### Documentation Improvements
 +
 +* [`useless_format`]: Improved the documentation example
 +  [#6854](https://github.com/rust-lang/rust-clippy/pull/6854)
 +* Clippy's [`README.md`]: Includes a new subsection on running Clippy as a rustc wrapper
 +  [#6782](https://github.com/rust-lang/rust-clippy/pull/6782)
 +
 +### Others
 +* Running `cargo clippy` after `cargo check` now works as expected
 +  (`cargo clippy` and `cargo check` no longer shares the same build cache)
 +  [#6687](https://github.com/rust-lang/rust-clippy/pull/6687)
 +* Cargo now re-runs Clippy if arguments after `--` provided to `cargo clippy` are changed.
 +  [#6834](https://github.com/rust-lang/rust-clippy/pull/6834)
 +* Extracted Clippy's `utils` module into the new `clippy_utils` crate
 +  [#6756](https://github.com/rust-lang/rust-clippy/pull/6756)
 +* Clippy lintcheck tool improvements
 +  [#6800](https://github.com/rust-lang/rust-clippy/pull/6800)
 +  [#6735](https://github.com/rust-lang/rust-clippy/pull/6735)
 +  [#6764](https://github.com/rust-lang/rust-clippy/pull/6764)
 +  [#6708](https://github.com/rust-lang/rust-clippy/pull/6708)
 +  [#6780](https://github.com/rust-lang/rust-clippy/pull/6780)
 +  [#6686](https://github.com/rust-lang/rust-clippy/pull/6686)
 +
 +## Rust 1.51
 +
 +Released 2021-03-25
 +
 +[4911ab1...3e41797](https://github.com/rust-lang/rust-clippy/compare/4911ab1...3e41797)
 +
 +### New Lints
 +
 +* [`upper_case_acronyms`]
 +  [#6475](https://github.com/rust-lang/rust-clippy/pull/6475)
 +* [`from_over_into`] [#6476](https://github.com/rust-lang/rust-clippy/pull/6476)
 +* [`case_sensitive_file_extension_comparisons`]
 +  [#6500](https://github.com/rust-lang/rust-clippy/pull/6500)
 +* [`needless_question_mark`]
 +  [#6507](https://github.com/rust-lang/rust-clippy/pull/6507)
 +* [`missing_panics_doc`]
 +  [#6523](https://github.com/rust-lang/rust-clippy/pull/6523)
 +* [`redundant_slicing`]
 +  [#6528](https://github.com/rust-lang/rust-clippy/pull/6528)
 +* [`vec_init_then_push`]
 +  [#6538](https://github.com/rust-lang/rust-clippy/pull/6538)
 +* [`ptr_as_ptr`] [#6542](https://github.com/rust-lang/rust-clippy/pull/6542)
 +* [`collapsible_else_if`] (split out from `collapsible_if`)
 +  [#6544](https://github.com/rust-lang/rust-clippy/pull/6544)
 +* [`inspect_for_each`] [#6577](https://github.com/rust-lang/rust-clippy/pull/6577)
 +* [`manual_filter_map`]
 +  [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
 +* [`exhaustive_enums`]
 +  [#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
 +* [`exhaustive_structs`]
 +  [#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
 +
 +### Moves and Deprecations
 +
 +* Replace [`find_map`] with [`manual_find_map`]
 +  [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
 +* `unknown_clippy_lints` Now integrated in the `unknown_lints` rustc lint
 +  [#6653](https://github.com/rust-lang/rust-clippy/pull/6653)
 +
 +### Enhancements
 +
 +* [`ptr_arg`] Now also suggests to use `&Path` instead of `&PathBuf`
 +  [#6506](https://github.com/rust-lang/rust-clippy/pull/6506)
 +* [`cast_ptr_alignment`] Also lint when the `pointer::cast` method is used
 +  [#6557](https://github.com/rust-lang/rust-clippy/pull/6557)
 +* [`collapsible_match`] Now also deals with `&` and `*` operators in the `match`
 +  scrutinee [#6619](https://github.com/rust-lang/rust-clippy/pull/6619)
 +
 +### False Positive Fixes
 +
 +* [`similar_names`] Ignore underscore prefixed names
 +  [#6403](https://github.com/rust-lang/rust-clippy/pull/6403)
 +* [`print_literal`] and [`write_literal`] No longer lint numeric literals
 +  [#6408](https://github.com/rust-lang/rust-clippy/pull/6408)
 +* [`large_enum_variant`] No longer lints in external macros
 +  [#6485](https://github.com/rust-lang/rust-clippy/pull/6485)
 +* [`empty_enum`] Only lint if `never_type` feature is enabled
 +  [#6513](https://github.com/rust-lang/rust-clippy/pull/6513)
 +* [`field_reassign_with_default`] No longer lints in macros
 +  [#6553](https://github.com/rust-lang/rust-clippy/pull/6553)
 +* [`size_of_in_element_count`] No longer lints when dividing by element size
 +  [#6578](https://github.com/rust-lang/rust-clippy/pull/6578)
 +* [`needless_return`] No longer lints in macros
 +  [#6586](https://github.com/rust-lang/rust-clippy/pull/6586)
 +* [`match_overlapping_arm`] No longer lint when first arm is completely included
 +  in second arm [#6603](https://github.com/rust-lang/rust-clippy/pull/6603)
 +* [`doc_markdown`] Add `WebGL` to the default configuration as an allowed
 +  identifier [#6605](https://github.com/rust-lang/rust-clippy/pull/6605)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`field_reassign_with_default`] Don't expand macro in lint suggestion
 +  [#6531](https://github.com/rust-lang/rust-clippy/pull/6531)
 +* [`match_like_matches_macro`] Strip references in suggestion
 +  [#6532](https://github.com/rust-lang/rust-clippy/pull/6532)
 +* [`single_match`] Suggest `if` over `if let` when possible
 +  [#6574](https://github.com/rust-lang/rust-clippy/pull/6574)
 +* `ref_in_deref` Use parentheses correctly in suggestion
 +  [#6609](https://github.com/rust-lang/rust-clippy/pull/6609)
 +* [`stable_sort_primitive`] Clarify error message
 +  [#6611](https://github.com/rust-lang/rust-clippy/pull/6611)
 +
 +### ICE Fixes
 +
 +* [`zero_sized_map_values`]
 +  [#6582](https://github.com/rust-lang/rust-clippy/pull/6582)
 +
 +### Documentation Improvements
 +
 +* Improve search performance on the Clippy website and make it possible to
 +  directly search for lints on the GitHub issue tracker
 +  [#6483](https://github.com/rust-lang/rust-clippy/pull/6483)
 +* Clean up `README.md` by removing outdated paragraph
 +  [#6488](https://github.com/rust-lang/rust-clippy/pull/6488)
 +* [`await_holding_refcell_ref`] and [`await_holding_lock`]
 +  [#6585](https://github.com/rust-lang/rust-clippy/pull/6585)
 +* [`as_conversions`] [#6608](https://github.com/rust-lang/rust-clippy/pull/6608)
 +
 +### Others
 +
 +* Clippy now has a [Roadmap] for 2021. If you like to get involved in a bigger
 +  project, take a look at the [Roadmap project page]. All issues listed there
 +  are actively mentored
 +  [#6462](https://github.com/rust-lang/rust-clippy/pull/6462)
 +* The Clippy version number now corresponds to the Rust version number
 +  [#6526](https://github.com/rust-lang/rust-clippy/pull/6526)
 +* Fix oversight which caused Clippy to lint deps in some environments, where
 +  `CLIPPY_TESTS=true` was set somewhere
 +  [#6575](https://github.com/rust-lang/rust-clippy/pull/6575)
 +* Add `cargo dev-lintcheck` tool to the Clippy Dev Tool
 +  [#6469](https://github.com/rust-lang/rust-clippy/pull/6469)
 +
 +[Roadmap]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/proposals/roadmap-2021.md
 +[Roadmap project page]: https://github.com/rust-lang/rust-clippy/projects/3
 +
 +## Rust 1.50
 +
 +Released 2021-02-11
 +
 +[b20d4c1...4bd77a1](https://github.com/rust-lang/rust-clippy/compare/b20d4c1...4bd77a1)
 +
 +### New Lints
 +
 +* [`suspicious_operation_groupings`] [#6086](https://github.com/rust-lang/rust-clippy/pull/6086)
 +* [`size_of_in_element_count`] [#6394](https://github.com/rust-lang/rust-clippy/pull/6394)
 +* [`unnecessary_wraps`] [#6070](https://github.com/rust-lang/rust-clippy/pull/6070)
 +* [`let_underscore_drop`] [#6305](https://github.com/rust-lang/rust-clippy/pull/6305)
 +* [`collapsible_match`] [#6402](https://github.com/rust-lang/rust-clippy/pull/6402)
 +* [`redundant_else`] [#6330](https://github.com/rust-lang/rust-clippy/pull/6330)
 +* [`zero_sized_map_values`] [#6218](https://github.com/rust-lang/rust-clippy/pull/6218)
 +* [`print_stderr`] [#6367](https://github.com/rust-lang/rust-clippy/pull/6367)
 +* [`string_from_utf8_as_bytes`] [#6134](https://github.com/rust-lang/rust-clippy/pull/6134)
 +
 +### Moves and Deprecations
 +
 +* Previously deprecated [`str_to_string`] and [`string_to_string`] have been un-deprecated
 +  as `restriction` lints [#6333](https://github.com/rust-lang/rust-clippy/pull/6333)
 +* Deprecate `panic_params` lint. This is now available in rustc as `non_fmt_panics`
 +  [#6351](https://github.com/rust-lang/rust-clippy/pull/6351)
 +* Move [`map_err_ignore`] to `restriction`
 +  [#6416](https://github.com/rust-lang/rust-clippy/pull/6416)
 +* Move [`await_holding_refcell_ref`] to `pedantic`
 +  [#6354](https://github.com/rust-lang/rust-clippy/pull/6354)
 +* Move [`await_holding_lock`] to `pedantic`
 +  [#6354](https://github.com/rust-lang/rust-clippy/pull/6354)
 +
 +### Enhancements
 +
 +* Add the `unreadable-literal-lint-fractions` configuration to disable
 +  the `unreadable_literal` lint for fractions
 +  [#6421](https://github.com/rust-lang/rust-clippy/pull/6421)
 +* [`clone_on_copy`]: Now shows the type in the lint message
 +  [#6443](https://github.com/rust-lang/rust-clippy/pull/6443)
 +* [`redundant_pattern_matching`]: Now also lints on `std::task::Poll`
 +  [#6339](https://github.com/rust-lang/rust-clippy/pull/6339)
 +* [`redundant_pattern_matching`]: Additionally also lints on `std::net::IpAddr`
 +  [#6377](https://github.com/rust-lang/rust-clippy/pull/6377)
 +* [`search_is_some`]: Now suggests `contains` instead of `find(foo).is_some()`
 +  [#6119](https://github.com/rust-lang/rust-clippy/pull/6119)
 +* [`clone_double_ref`]: Now prints the reference type in the lint message
 +  [#6442](https://github.com/rust-lang/rust-clippy/pull/6442)
 +* [`modulo_one`]: Now also lints on -1.
 +  [#6360](https://github.com/rust-lang/rust-clippy/pull/6360)
 +* [`empty_loop`]: Now lints no_std crates, too
 +  [#6205](https://github.com/rust-lang/rust-clippy/pull/6205)
 +* [`or_fun_call`]: Now also lints when indexing `HashMap` or `BTreeMap`
 +  [#6267](https://github.com/rust-lang/rust-clippy/pull/6267)
 +* [`wrong_self_convention`]: Now also lints in trait definitions
 +  [#6316](https://github.com/rust-lang/rust-clippy/pull/6316)
 +* [`needless_borrow`]: Print the type in the lint message
 +  [#6449](https://github.com/rust-lang/rust-clippy/pull/6449)
 +
 +[msrv_readme]: https://github.com/rust-lang/rust-clippy#specifying-the-minimum-supported-rust-version
 +
 +### False Positive Fixes
 +
 +* [`manual_range_contains`]: No longer lints in `const fn`
 +  [#6382](https://github.com/rust-lang/rust-clippy/pull/6382)
 +* [`unnecessary_lazy_evaluations`]: No longer lints if closure argument is used
 +  [#6370](https://github.com/rust-lang/rust-clippy/pull/6370)
 +* [`match_single_binding`]: Now ignores cases with `#[cfg()]` macros
 +  [#6435](https://github.com/rust-lang/rust-clippy/pull/6435)
 +* [`match_like_matches_macro`]: No longer lints on arms with attributes
 +  [#6290](https://github.com/rust-lang/rust-clippy/pull/6290)
 +* [`map_clone`]: No longer lints with deref and clone
 +  [#6269](https://github.com/rust-lang/rust-clippy/pull/6269)
 +* [`map_clone`]: No longer lints in the case of &mut
 +  [#6301](https://github.com/rust-lang/rust-clippy/pull/6301)
 +* [`needless_update`]: Now ignores `non_exhaustive` structs
 +  [#6464](https://github.com/rust-lang/rust-clippy/pull/6464)
 +* [`needless_collect`]: No longer lints when a collect is needed multiple times
 +  [#6313](https://github.com/rust-lang/rust-clippy/pull/6313)
 +* [`unnecessary_cast`] No longer lints cfg-dependent types
 +  [#6369](https://github.com/rust-lang/rust-clippy/pull/6369)
 +* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]:
 +  Both now ignore enums with frozen variants
 +  [#6110](https://github.com/rust-lang/rust-clippy/pull/6110)
 +* [`field_reassign_with_default`] No longer lint for private fields
 +  [#6537](https://github.com/rust-lang/rust-clippy/pull/6537)
 +
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`vec_box`]: Provide correct type scope suggestion
 +  [#6271](https://github.com/rust-lang/rust-clippy/pull/6271)
 +* [`manual_range_contains`]: Give correct suggestion when using floats
 +  [#6320](https://github.com/rust-lang/rust-clippy/pull/6320)
 +* [`unnecessary_lazy_evaluations`]: Don't always mark suggestion as MachineApplicable
 +  [#6272](https://github.com/rust-lang/rust-clippy/pull/6272)
 +* [`manual_async_fn`]: Improve suggestion formatting
 +  [#6294](https://github.com/rust-lang/rust-clippy/pull/6294)
 +* [`unnecessary_cast`]: Fix incorrectly formatted float literal suggestion
 +  [#6362](https://github.com/rust-lang/rust-clippy/pull/6362)
 +
 +### ICE Fixes
 +
 +* Fix a crash in [`from_iter_instead_of_collect`]
 +  [#6304](https://github.com/rust-lang/rust-clippy/pull/6304)
 +* Fix a silent crash when parsing doc comments in [`needless_doctest_main`]
 +  [#6458](https://github.com/rust-lang/rust-clippy/pull/6458)
 +
 +### Documentation Improvements
 +
 +* The lint website search has been improved ([#6477](https://github.com/rust-lang/rust-clippy/pull/6477)):
 +  * Searching for lints with dashes and spaces is possible now. For example
 +    `missing-errors-doc` and `missing errors doc` are now valid aliases for lint names
 +  * Improved fuzzy search in lint descriptions
 +* Various README improvements
 +  [#6287](https://github.com/rust-lang/rust-clippy/pull/6287)
 +* Add known problems to [`comparison_chain`] documentation
 +  [#6390](https://github.com/rust-lang/rust-clippy/pull/6390)
 +* Fix example used in [`cargo_common_metadata`]
 +  [#6293](https://github.com/rust-lang/rust-clippy/pull/6293)
 +* Improve [`map_clone`] documentation
 +  [#6340](https://github.com/rust-lang/rust-clippy/pull/6340)
 +
 +### Others
 +
 +* You can now tell Clippy about the MSRV your project supports. Please refer to
 +  the specific README section to learn more about MSRV support [here][msrv_readme]
 +  [#6201](https://github.com/rust-lang/rust-clippy/pull/6201)
 +* Add `--no-deps` option to avoid running on path dependencies in workspaces
 +  [#6188](https://github.com/rust-lang/rust-clippy/pull/6188)
 +
 +## Rust 1.49
 +
 +Released 2020-12-31
 +
 +[e636b88...b20d4c1](https://github.com/rust-lang/rust-clippy/compare/e636b88...b20d4c1)
 +
 +### New Lints
 +
 +* [`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_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)
 +* [`mut_mutex_lock`] [#6103](https://github.com/rust-lang/rust-clippy/pull/6103)
 +* [`single_element_loop`] [#6109](https://github.com/rust-lang/rust-clippy/pull/6109)
 +* [`manual_unwrap_or`] [#6123](https://github.com/rust-lang/rust-clippy/pull/6123)
 +* [`large_types_passed_by_value`] [#6135](https://github.com/rust-lang/rust-clippy/pull/6135)
 +* [`result_unit_err`] [#6157](https://github.com/rust-lang/rust-clippy/pull/6157)
 +* [`ref_option_ref`] [#6165](https://github.com/rust-lang/rust-clippy/pull/6165)
 +* [`manual_range_contains`] [#6177](https://github.com/rust-lang/rust-clippy/pull/6177)
 +* [`unusual_byte_groupings`] [#6183](https://github.com/rust-lang/rust-clippy/pull/6183)
 +* [`comparison_to_empty`] [#6226](https://github.com/rust-lang/rust-clippy/pull/6226)
 +* [`map_collect_result_unit`] [#6227](https://github.com/rust-lang/rust-clippy/pull/6227)
 +* [`manual_ok_or`] [#6233](https://github.com/rust-lang/rust-clippy/pull/6233)
 +
 +### Moves and Deprecations
 +
 +* Rename `single_char_push_str` to [`single_char_add_str`]
 +  [#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
 +* Rename `zero_width_space` to [`invisible_characters`]
 +  [#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
 +* Deprecate `drop_bounds` (uplifted)
 +  [#6111](https://github.com/rust-lang/rust-clippy/pull/6111)
 +* Move [`string_lit_as_bytes`] to `nursery`
 +  [#6117](https://github.com/rust-lang/rust-clippy/pull/6117)
 +* Move [`rc_buffer`] to `restriction`
 +  [#6128](https://github.com/rust-lang/rust-clippy/pull/6128)
 +
 +### Enhancements
 +
 +* [`manual_memcpy`]: Also lint when there are loop counters (and produce a
 +  reliable suggestion)
 +  [#5727](https://github.com/rust-lang/rust-clippy/pull/5727)
 +* [`single_char_add_str`]: Also lint on `String::insert_str`
 +  [#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
 +* [`invisible_characters`]: Also lint the characters `\u{AD}` and `\u{2060}`
 +  [#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
 +* [`eq_op`]: Also lint on the `assert_*!` macro family
 +  [#6167](https://github.com/rust-lang/rust-clippy/pull/6167)
 +* [`items_after_statements`]: Also lint in local macro expansions
 +  [#6176](https://github.com/rust-lang/rust-clippy/pull/6176)
 +* [`unnecessary_cast`]: Also lint casts on integer and float literals
 +  [#6187](https://github.com/rust-lang/rust-clippy/pull/6187)
 +* [`manual_unwrap_or`]: Also lint `Result::unwrap_or`
 +  [#6190](https://github.com/rust-lang/rust-clippy/pull/6190)
 +* [`match_like_matches_macro`]: Also lint when `match` has more than two arms
 +  [#6216](https://github.com/rust-lang/rust-clippy/pull/6216)
 +* [`integer_arithmetic`]: Better handle `/` an `%` operators
 +  [#6229](https://github.com/rust-lang/rust-clippy/pull/6229)
 +
 +### False Positive Fixes
 +
 +* [`needless_lifetimes`]: Bail out if the function has a `where` clause with the
 +  lifetime [#5978](https://github.com/rust-lang/rust-clippy/pull/5978)
 +* [`explicit_counter_loop`]: No longer lints, when loop counter is used after it
 +  is incremented [#6076](https://github.com/rust-lang/rust-clippy/pull/6076)
 +* [`or_fun_call`]: Revert changes addressing the handling of `const fn`
 +  [#6077](https://github.com/rust-lang/rust-clippy/pull/6077)
 +* [`needless_range_loop`]: No longer lints, when the iterable is used in the
 +  range [#6102](https://github.com/rust-lang/rust-clippy/pull/6102)
 +* [`inconsistent_digit_grouping`]: Fix bug when using floating point exponent
 +  [#6104](https://github.com/rust-lang/rust-clippy/pull/6104)
 +* [`mistyped_literal_suffixes`]: No longer lints on the fractional part of a
 +  float (e.g. `713.32_64`)
 +  [#6114](https://github.com/rust-lang/rust-clippy/pull/6114)
 +* [`invalid_regex`]: No longer lint on unicode characters within `bytes::Regex`
 +  [#6132](https://github.com/rust-lang/rust-clippy/pull/6132)
 +* [`boxed_local`]: No longer lints on `extern fn` arguments
 +  [#6133](https://github.com/rust-lang/rust-clippy/pull/6133)
 +* [`needless_lifetimes`]: Fix regression, where lifetime is used in `where`
 +  clause [#6198](https://github.com/rust-lang/rust-clippy/pull/6198)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`unnecessary_sort_by`]: Avoid dereferencing the suggested closure parameter
 +  [#6078](https://github.com/rust-lang/rust-clippy/pull/6078)
 +* [`needless_arbitrary_self_type`]: Correctly handle expanded code
 +  [#6093](https://github.com/rust-lang/rust-clippy/pull/6093)
 +* [`useless_format`]: Preserve raw strings in suggestion
 +  [#6151](https://github.com/rust-lang/rust-clippy/pull/6151)
 +* [`empty_loop`]: Suggest alternatives
 +  [#6162](https://github.com/rust-lang/rust-clippy/pull/6162)
 +* [`borrowed_box`]: Correctly add parentheses in suggestion
 +  [#6200](https://github.com/rust-lang/rust-clippy/pull/6200)
 +* [`unused_unit`]: Improve suggestion formatting
 +  [#6247](https://github.com/rust-lang/rust-clippy/pull/6247)
 +
 +### Documentation Improvements
 +
 +* Some doc improvements:
 +    * [`rc_buffer`] [#6090](https://github.com/rust-lang/rust-clippy/pull/6090)
 +    * [`empty_loop`] [#6162](https://github.com/rust-lang/rust-clippy/pull/6162)
 +* [`doc_markdown`]: Document problematic link text style
 +  [#6107](https://github.com/rust-lang/rust-clippy/pull/6107)
 +
 +## Rust 1.48
 +
 +Released 2020-11-19
 +
 +[09bd400...e636b88](https://github.com/rust-lang/rust-clippy/compare/09bd400...e636b88)
 +
 +### New lints
 +
 +* [`self_assignment`] [#5894](https://github.com/rust-lang/rust-clippy/pull/5894)
 +* [`unnecessary_lazy_evaluations`] [#5720](https://github.com/rust-lang/rust-clippy/pull/5720)
 +* [`manual_strip`] [#6038](https://github.com/rust-lang/rust-clippy/pull/6038)
 +* [`map_err_ignore`] [#5998](https://github.com/rust-lang/rust-clippy/pull/5998)
 +* [`rc_buffer`] [#6044](https://github.com/rust-lang/rust-clippy/pull/6044)
 +* `to_string_in_display` [#5831](https://github.com/rust-lang/rust-clippy/pull/5831)
 +* `single_char_push_str` [#5881](https://github.com/rust-lang/rust-clippy/pull/5881)
 +
 +### Moves and Deprecations
 +
 +* Downgrade [`verbose_bit_mask`] to pedantic
 +  [#6036](https://github.com/rust-lang/rust-clippy/pull/6036)
 +
 +### Enhancements
 +
 +* Extend [`precedence`] to handle chains of methods combined with unary negation
 +  [#5928](https://github.com/rust-lang/rust-clippy/pull/5928)
 +* [`useless_vec`]: add a configuration value for the maximum allowed size on the stack
 +  [#5907](https://github.com/rust-lang/rust-clippy/pull/5907)
 +* [`suspicious_arithmetic_impl`]: extend to implementations of `BitAnd`, `BitOr`, `BitXor`, `Rem`, `Shl`, and `Shr`
 +  [#5884](https://github.com/rust-lang/rust-clippy/pull/5884)
 +* `invalid_atomic_ordering`: detect misuse of `compare_exchange`, `compare_exchange_weak`, and `fetch_update`
 +  [#6025](https://github.com/rust-lang/rust-clippy/pull/6025)
 +* Avoid [`redundant_pattern_matching`] triggering in macros
 +  [#6069](https://github.com/rust-lang/rust-clippy/pull/6069)
 +* [`option_if_let_else`]: distinguish pure from impure `else` expressions
 +  [#5937](https://github.com/rust-lang/rust-clippy/pull/5937)
 +* [`needless_doctest_main`]: parse doctests instead of using textual search
 +  [#5912](https://github.com/rust-lang/rust-clippy/pull/5912)
 +* [`wildcard_imports`]: allow `prelude` to appear in any segment of an import
 +  [#5929](https://github.com/rust-lang/rust-clippy/pull/5929)
 +* Re-enable [`len_zero`] for ranges now that `range_is_empty` is stable
 +  [#5961](https://github.com/rust-lang/rust-clippy/pull/5961)
 +* [`option_as_ref_deref`]: catch fully-qualified calls to `Deref::deref` and `DerefMut::deref_mut`
 +  [#5933](https://github.com/rust-lang/rust-clippy/pull/5933)
 +
 +### False Positive Fixes
 +
 +* [`useless_attribute`]: permit allowing [`wildcard_imports`] and [`enum_glob_use`]
 +  [#5994](https://github.com/rust-lang/rust-clippy/pull/5994)
 +* [`transmute_ptr_to_ptr`]: avoid suggesting dereferencing raw pointers in const contexts
 +  [#5999](https://github.com/rust-lang/rust-clippy/pull/5999)
 +* [`redundant_closure_call`]: take into account usages of the closure in nested functions and closures
 +  [#5920](https://github.com/rust-lang/rust-clippy/pull/5920)
 +* Fix false positive in [`borrow_interior_mutable_const`] when referencing a field behind a pointer
 +  [#5949](https://github.com/rust-lang/rust-clippy/pull/5949)
 +* [`doc_markdown`]: allow using "GraphQL" without backticks
 +  [#5996](https://github.com/rust-lang/rust-clippy/pull/5996)
 +* `to_string_in_display`: avoid linting when calling `to_string()` on anything that is not `self`
 +  [#5971](https://github.com/rust-lang/rust-clippy/pull/5971)
 +* [`indexing_slicing`] and [`out_of_bounds_indexing`] treat references to arrays as arrays
 +  [#6034](https://github.com/rust-lang/rust-clippy/pull/6034)
 +* [`should_implement_trait`]: ignore methods with lifetime parameters
 +  [#5725](https://github.com/rust-lang/rust-clippy/pull/5725)
 +* [`needless_return`]: avoid linting if a temporary borrows a local variable
 +  [#5903](https://github.com/rust-lang/rust-clippy/pull/5903)
 +* Restrict [`unnecessary_sort_by`] to non-reference, Copy types
 +  [#6006](https://github.com/rust-lang/rust-clippy/pull/6006)
 +* Avoid suggesting `from_bits`/`to_bits` in const contexts in [`transmute_int_to_float`]
 +  [#5919](https://github.com/rust-lang/rust-clippy/pull/5919)
 +* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]: improve detection of interior mutable types
 +  [#6046](https://github.com/rust-lang/rust-clippy/pull/6046)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`let_and_return`]: add a cast to the suggestion when the return expression has adjustments
 +  [#5946](https://github.com/rust-lang/rust-clippy/pull/5946)
 +* [`useless_conversion`]: show the type in the error message
 +  [#6035](https://github.com/rust-lang/rust-clippy/pull/6035)
 +* [`unnecessary_mut_passed`]: discriminate between functions and methods in the error message
 +  [#5892](https://github.com/rust-lang/rust-clippy/pull/5892)
 +* [`float_cmp`] and [`float_cmp_const`]: change wording to make margin of error less ambiguous
 +  [#6043](https://github.com/rust-lang/rust-clippy/pull/6043)
 +* [`default_trait_access`]: do not use unnecessary type parameters in the suggestion
 +  [#5993](https://github.com/rust-lang/rust-clippy/pull/5993)
 +* [`collapsible_if`]: don't use expanded code in the suggestion
 +  [#5992](https://github.com/rust-lang/rust-clippy/pull/5992)
 +* Do not suggest empty format strings in [`print_with_newline`] and [`write_with_newline`]
 +  [#6042](https://github.com/rust-lang/rust-clippy/pull/6042)
 +* [`unit_arg`]: improve the readability of the suggestion
 +  [#5931](https://github.com/rust-lang/rust-clippy/pull/5931)
 +* [`stable_sort_primitive`]: print the type that is being sorted in the lint message
 +  [#5935](https://github.com/rust-lang/rust-clippy/pull/5935)
 +* Show line count and max lines in [`too_many_lines`] lint message
 +  [#6009](https://github.com/rust-lang/rust-clippy/pull/6009)
 +* Keep parentheses in the suggestion of [`useless_conversion`] where applicable
 +  [#5900](https://github.com/rust-lang/rust-clippy/pull/5900)
 +* [`option_map_unit_fn`] and [`result_map_unit_fn`]: print the unit type `()` explicitly
 +  [#6024](https://github.com/rust-lang/rust-clippy/pull/6024)
 +* [`redundant_allocation`]: suggest replacing `Rc<Box<T>>` with `Rc<T>`
 +  [#5899](https://github.com/rust-lang/rust-clippy/pull/5899)
 +* Make lint messages adhere to rustc dev guide conventions
 +  [#5893](https://github.com/rust-lang/rust-clippy/pull/5893)
 +
 +### ICE Fixes
 +
 +* Fix ICE in [`repeat_once`]
 +  [#5948](https://github.com/rust-lang/rust-clippy/pull/5948)
 +
 +### Documentation Improvements
 +
 +* [`mutable_key_type`]: explain potential for false positives when the interior mutable type is not accessed in the `Hash` implementation
 +  [#6019](https://github.com/rust-lang/rust-clippy/pull/6019)
 +* [`unnecessary_mut_passed`]: fix typo
 +  [#5913](https://github.com/rust-lang/rust-clippy/pull/5913)
 +* Add example of false positive to [`ptr_arg`] docs.
 +  [#5885](https://github.com/rust-lang/rust-clippy/pull/5885)
 +* [`box_vec`](https://rust-lang.github.io/rust-clippy/master/index.html#box_collection), [`vec_box`] and [`borrowed_box`]: add link to the documentation of `Box`
 +  [#6023](https://github.com/rust-lang/rust-clippy/pull/6023)
 +
 +## Rust 1.47
 +
 +Released 2020-10-08
 +
 +[c2c07fa...09bd400](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...09bd400)
 +
 +### New lints
 +
 +* [`derive_ord_xor_partial_ord`] [#5848](https://github.com/rust-lang/rust-clippy/pull/5848)
 +* [`trait_duplication_in_bounds`] [#5852](https://github.com/rust-lang/rust-clippy/pull/5852)
 +* [`map_identity`] [#5694](https://github.com/rust-lang/rust-clippy/pull/5694)
 +* [`unit_return_expecting_ord`] [#5737](https://github.com/rust-lang/rust-clippy/pull/5737)
 +* [`pattern_type_mismatch`] [#4841](https://github.com/rust-lang/rust-clippy/pull/4841)
 +* [`repeat_once`] [#5773](https://github.com/rust-lang/rust-clippy/pull/5773)
 +* [`same_item_push`] [#5825](https://github.com/rust-lang/rust-clippy/pull/5825)
 +* [`needless_arbitrary_self_type`] [#5869](https://github.com/rust-lang/rust-clippy/pull/5869)
 +* [`match_like_matches_macro`] [#5769](https://github.com/rust-lang/rust-clippy/pull/5769)
 +* [`stable_sort_primitive`] [#5809](https://github.com/rust-lang/rust-clippy/pull/5809)
 +* [`blanket_clippy_restriction_lints`] [#5750](https://github.com/rust-lang/rust-clippy/pull/5750)
 +* [`option_if_let_else`] [#5301](https://github.com/rust-lang/rust-clippy/pull/5301)
 +
 +### Moves and Deprecations
 +
 +* Deprecate [`regex_macro`] lint
 +  [#5760](https://github.com/rust-lang/rust-clippy/pull/5760)
 +* Move [`range_minus_one`] to `pedantic`
 +  [#5752](https://github.com/rust-lang/rust-clippy/pull/5752)
 +
 +### Enhancements
 +
 +* Improve [`needless_collect`] by catching `collect` calls followed by `iter` or `into_iter` calls
 +  [#5837](https://github.com/rust-lang/rust-clippy/pull/5837)
 +* [`panic`], [`todo`], [`unimplemented`] and [`unreachable`] now detect calls with formatting
 +  [#5811](https://github.com/rust-lang/rust-clippy/pull/5811)
 +* Detect more cases of [`suboptimal_flops`] and [`imprecise_flops`]
 +  [#5443](https://github.com/rust-lang/rust-clippy/pull/5443)
 +* Handle asymmetrical implementations of `PartialEq` in [`cmp_owned`]
 +  [#5701](https://github.com/rust-lang/rust-clippy/pull/5701)
 +* Make it possible to allow [`unsafe_derive_deserialize`]
 +  [#5870](https://github.com/rust-lang/rust-clippy/pull/5870)
 +* Catch `ord.min(a).max(b)` where a < b in [`min_max`]
 +  [#5871](https://github.com/rust-lang/rust-clippy/pull/5871)
 +* Make [`clone_on_copy`] suggestion machine applicable
 +  [#5745](https://github.com/rust-lang/rust-clippy/pull/5745)
 +* Enable [`len_zero`] on ranges now that `is_empty` is stable on them
 +  [#5961](https://github.com/rust-lang/rust-clippy/pull/5961)
 +
 +### False Positive Fixes
 +
 +* Avoid triggering [`or_fun_call`] with const fns that take no arguments
 +  [#5889](https://github.com/rust-lang/rust-clippy/pull/5889)
 +* Fix [`redundant_closure_call`] false positive for closures that have multiple calls
 +  [#5800](https://github.com/rust-lang/rust-clippy/pull/5800)
 +* Don't lint cases involving `ManuallyDrop` in [`redundant_clone`]
 +  [#5824](https://github.com/rust-lang/rust-clippy/pull/5824)
 +* Treat a single expression the same as a single statement in the 2nd arm of a match in [`single_match_else`]
 +  [#5771](https://github.com/rust-lang/rust-clippy/pull/5771)
 +* Don't trigger [`unnested_or_patterns`] if the feature `or_patterns` is not enabled
 +  [#5758](https://github.com/rust-lang/rust-clippy/pull/5758)
 +* Avoid linting if key borrows in [`unnecessary_sort_by`]
 +  [#5756](https://github.com/rust-lang/rust-clippy/pull/5756)
 +* Consider `Try` impl for `Poll` when generating suggestions in [`try_err`]
 +  [#5857](https://github.com/rust-lang/rust-clippy/pull/5857)
 +* Take input lifetimes into account in `manual_async_fn`
 +  [#5859](https://github.com/rust-lang/rust-clippy/pull/5859)
 +* Fix multiple false positives in [`type_repetition_in_bounds`] and add a configuration option
 +  [#5761](https://github.com/rust-lang/rust-clippy/pull/5761)
 +* Limit the [`suspicious_arithmetic_impl`] lint to one binary operation
 +  [#5820](https://github.com/rust-lang/rust-clippy/pull/5820)
 +
 +### Suggestion Fixes/Improvements
 +
 +* Improve readability of [`shadow_unrelated`] suggestion by truncating the RHS snippet
 +  [#5788](https://github.com/rust-lang/rust-clippy/pull/5788)
 +* Suggest `filter_map` instead of `flat_map` when mapping to `Option` in [`map_flatten`]
 +  [#5846](https://github.com/rust-lang/rust-clippy/pull/5846)
 +* Ensure suggestion is shown correctly for long method call chains in [`iter_nth_zero`]
 +  [#5793](https://github.com/rust-lang/rust-clippy/pull/5793)
 +* Drop borrow operator in suggestions of [`redundant_pattern_matching`]
 +  [#5815](https://github.com/rust-lang/rust-clippy/pull/5815)
 +* Add suggestion for [`iter_skip_next`]
 +  [#5843](https://github.com/rust-lang/rust-clippy/pull/5843)
 +* Improve [`collapsible_if`] fix suggestion
 +  [#5732](https://github.com/rust-lang/rust-clippy/pull/5732)
 +
 +### ICE Fixes
 +
 +* Fix ICE caused by [`needless_collect`]
 +  [#5877](https://github.com/rust-lang/rust-clippy/pull/5877)
 +* Fix ICE caused by [`unnested_or_patterns`]
 +  [#5784](https://github.com/rust-lang/rust-clippy/pull/5784)
 +
 +### Documentation Improvements
 +
 +* Fix grammar of [`await_holding_lock`] documentation
 +  [#5748](https://github.com/rust-lang/rust-clippy/pull/5748)
 +
 +### Others
 +
 +* Make lints adhere to the rustc dev guide
 +  [#5888](https://github.com/rust-lang/rust-clippy/pull/5888)
 +
 +## Rust 1.46
 +
 +Released 2020-08-27
 +
 +[7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...c2c07fa)
 +
 +### New lints
 +
 +* [`unnested_or_patterns`] [#5378](https://github.com/rust-lang/rust-clippy/pull/5378)
 +* [`iter_next_slice`] [#5597](https://github.com/rust-lang/rust-clippy/pull/5597)
 +* [`unnecessary_sort_by`] [#5623](https://github.com/rust-lang/rust-clippy/pull/5623)
 +* [`vec_resize_to_zero`] [#5637](https://github.com/rust-lang/rust-clippy/pull/5637)
 +
 +### Moves and Deprecations
 +
 +* Move [`cast_ptr_alignment`] to pedantic [#5667](https://github.com/rust-lang/rust-clippy/pull/5667)
 +
 +### Enhancements
 +
 +* Improve [`mem_replace_with_uninit`] lint [#5695](https://github.com/rust-lang/rust-clippy/pull/5695)
 +
 +### False Positive Fixes
 +
 +* [`len_zero`]: Avoid linting ranges when the `range_is_empty` feature is not enabled
 +  [#5656](https://github.com/rust-lang/rust-clippy/pull/5656)
 +* [`let_and_return`]: Don't lint if a temporary borrow is involved
 +  [#5680](https://github.com/rust-lang/rust-clippy/pull/5680)
 +* [`reversed_empty_ranges`]: Avoid linting `N..N` in for loop arguments in
 +  [#5692](https://github.com/rust-lang/rust-clippy/pull/5692)
 +* [`if_same_then_else`]: Don't assume multiplication is always commutative
 +  [#5702](https://github.com/rust-lang/rust-clippy/pull/5702)
 +* [`disallowed_names`]: Remove `bar` from the default configuration
 +  [#5712](https://github.com/rust-lang/rust-clippy/pull/5712)
 +* [`redundant_pattern_matching`]: Avoid suggesting non-`const fn` calls in const contexts
 +  [#5724](https://github.com/rust-lang/rust-clippy/pull/5724)
 +
 +### Suggestion Fixes/Improvements
 +
 +* Fix suggestion of [`unit_arg`] lint, so that it suggest semantic equivalent code
 +  [#4455](https://github.com/rust-lang/rust-clippy/pull/4455)
 +* Add auto applicable suggestion to [`macro_use_imports`]
 +  [#5279](https://github.com/rust-lang/rust-clippy/pull/5279)
 +
 +### ICE Fixes
 +
 +* Fix ICE in the `consts` module of Clippy [#5709](https://github.com/rust-lang/rust-clippy/pull/5709)
 +
 +### Documentation Improvements
 +
 +* Improve code examples across multiple lints [#5664](https://github.com/rust-lang/rust-clippy/pull/5664)
 +
 +### Others
 +
 +* Introduce a `--rustc` flag to `clippy-driver`, which turns `clippy-driver`
 +  into `rustc` and passes all the given arguments to `rustc`. This is especially
 +  useful for tools that need the `rustc` version Clippy was compiled with,
 +  instead of the Clippy version. E.g. `clippy-driver --rustc --version` will
 +  print the output of `rustc --version`.
 +  [#5178](https://github.com/rust-lang/rust-clippy/pull/5178)
 +* New issue templates now make it easier to complain if Clippy is too annoying
 +  or not annoying enough! [#5735](https://github.com/rust-lang/rust-clippy/pull/5735)
 +
 +## Rust 1.45
 +
 +Released 2020-07-16
 +
 +[891e1a8...7ea7cd1](https://github.com/rust-lang/rust-clippy/compare/891e1a8...7ea7cd1)
 +
 +### New lints
 +
 +* [`match_wildcard_for_single_variants`] [#5582](https://github.com/rust-lang/rust-clippy/pull/5582)
 +* [`unsafe_derive_deserialize`] [#5493](https://github.com/rust-lang/rust-clippy/pull/5493)
 +* [`if_let_mutex`] [#5332](https://github.com/rust-lang/rust-clippy/pull/5332)
 +* [`mismatched_target_os`] [#5506](https://github.com/rust-lang/rust-clippy/pull/5506)
 +* [`await_holding_lock`] [#5439](https://github.com/rust-lang/rust-clippy/pull/5439)
 +* [`match_on_vec_items`] [#5522](https://github.com/rust-lang/rust-clippy/pull/5522)
 +* [`manual_async_fn`] [#5576](https://github.com/rust-lang/rust-clippy/pull/5576)
 +* [`reversed_empty_ranges`] [#5583](https://github.com/rust-lang/rust-clippy/pull/5583)
 +* [`manual_non_exhaustive`] [#5550](https://github.com/rust-lang/rust-clippy/pull/5550)
 +
 +### Moves and Deprecations
 +
 +* Downgrade [`match_bool`] to pedantic [#5408](https://github.com/rust-lang/rust-clippy/pull/5408)
 +* Downgrade [`match_wild_err_arm`] to pedantic and update help messages. [#5622](https://github.com/rust-lang/rust-clippy/pull/5622)
 +* Downgrade [`useless_let_if_seq`] to nursery. [#5599](https://github.com/rust-lang/rust-clippy/pull/5599)
 +* Generalize `option_and_then_some` and rename to [`bind_instead_of_map`]. [#5529](https://github.com/rust-lang/rust-clippy/pull/5529)
 +* Rename `identity_conversion` to [`useless_conversion`]. [#5568](https://github.com/rust-lang/rust-clippy/pull/5568)
 +* Merge `block_in_if_condition_expr` and `block_in_if_condition_stmt` into [`blocks_in_if_conditions`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `option_map_unwrap_or`, `option_map_unwrap_or_else` and `result_map_unwrap_or_else` into [`map_unwrap_or`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `option_unwrap_used` and `result_unwrap_used` into [`unwrap_used`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `option_expect_used` and `result_expect_used` into [`expect_used`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `for_loop_over_option` and `for_loop_over_result` into [`for_loops_over_fallibles`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +
 +### Enhancements
 +
 +* Avoid running cargo lints when not enabled to improve performance. [#5505](https://github.com/rust-lang/rust-clippy/pull/5505)
 +* Extend [`useless_conversion`] with `TryFrom` and `TryInto`. [#5631](https://github.com/rust-lang/rust-clippy/pull/5631)
 +* Lint also in type parameters and where clauses in [`unused_unit`]. [#5592](https://github.com/rust-lang/rust-clippy/pull/5592)
 +* Do not suggest deriving `Default` in [`new_without_default`]. [#5616](https://github.com/rust-lang/rust-clippy/pull/5616)
 +
 +### False Positive Fixes
 +
 +* [`while_let_on_iterator`] [#5525](https://github.com/rust-lang/rust-clippy/pull/5525)
 +* [`empty_line_after_outer_attr`] [#5609](https://github.com/rust-lang/rust-clippy/pull/5609)
 +* [`unnecessary_unwrap`] [#5558](https://github.com/rust-lang/rust-clippy/pull/5558)
 +* [`comparison_chain`] [#5596](https://github.com/rust-lang/rust-clippy/pull/5596)
 +* Don't trigger [`used_underscore_binding`] in await desugaring. [#5535](https://github.com/rust-lang/rust-clippy/pull/5535)
 +* Don't trigger [`borrowed_box`] on mutable references. [#5491](https://github.com/rust-lang/rust-clippy/pull/5491)
 +* Allow `1 << 0` in [`identity_op`]. [#5602](https://github.com/rust-lang/rust-clippy/pull/5602)
 +* Allow `use super::*;` glob imports in [`wildcard_imports`]. [#5564](https://github.com/rust-lang/rust-clippy/pull/5564)
 +* Whitelist more words in [`doc_markdown`]. [#5611](https://github.com/rust-lang/rust-clippy/pull/5611)
 +* Skip dev and build deps in [`multiple_crate_versions`]. [#5636](https://github.com/rust-lang/rust-clippy/pull/5636)
 +* Honor `allow` attribute on arguments in [`ptr_arg`]. [#5647](https://github.com/rust-lang/rust-clippy/pull/5647)
 +* Honor lint level attributes for [`redundant_field_names`], [`just_underscores_and_digits`], [`many_single_char_names`]
 +and [`similar_names`]. [#5651](https://github.com/rust-lang/rust-clippy/pull/5651)
 +* Ignore calls to `len` in [`or_fun_call`]. [#4429](https://github.com/rust-lang/rust-clippy/pull/4429)
 +
 +### Suggestion Improvements
 +
 +* Simplify suggestions in [`manual_memcpy`]. [#5536](https://github.com/rust-lang/rust-clippy/pull/5536)
 +* Fix suggestion in [`redundant_pattern_matching`] for macros. [#5511](https://github.com/rust-lang/rust-clippy/pull/5511)
 +* Avoid suggesting `copied()` for mutable references in [`map_clone`]. [#5530](https://github.com/rust-lang/rust-clippy/pull/5530)
 +* Improve help message for [`clone_double_ref`]. [#5547](https://github.com/rust-lang/rust-clippy/pull/5547)
 +
 +### ICE Fixes
 +
 +* Fix ICE caused in unwrap module. [#5590](https://github.com/rust-lang/rust-clippy/pull/5590)
 +* Fix ICE on rustc test issue-69020-assoc-const-arith-overflow.rs [#5499](https://github.com/rust-lang/rust-clippy/pull/5499)
 +
 +### Documentation
 +
 +* Clarify the documentation of [`unnecessary_mut_passed`]. [#5639](https://github.com/rust-lang/rust-clippy/pull/5639)
 +* Extend example for [`unneeded_field_pattern`]. [#5541](https://github.com/rust-lang/rust-clippy/pull/5541)
 +
 +## Rust 1.44
 +
 +Released 2020-06-04
 +
 +[204bb9b...891e1a8](https://github.com/rust-lang/rust-clippy/compare/204bb9b...891e1a8)
 +
 +### New lints
 +
 +* [`explicit_deref_methods`] [#5226](https://github.com/rust-lang/rust-clippy/pull/5226)
 +* [`implicit_saturating_sub`] [#5427](https://github.com/rust-lang/rust-clippy/pull/5427)
 +* [`macro_use_imports`] [#5230](https://github.com/rust-lang/rust-clippy/pull/5230)
 +* [`verbose_file_reads`] [#5272](https://github.com/rust-lang/rust-clippy/pull/5272)
 +* [`future_not_send`] [#5423](https://github.com/rust-lang/rust-clippy/pull/5423)
 +* [`redundant_pub_crate`] [#5319](https://github.com/rust-lang/rust-clippy/pull/5319)
 +* [`large_const_arrays`] [#5248](https://github.com/rust-lang/rust-clippy/pull/5248)
 +* [`result_map_or_into_option`] [#5415](https://github.com/rust-lang/rust-clippy/pull/5415)
 +* [`redundant_allocation`] [#5349](https://github.com/rust-lang/rust-clippy/pull/5349)
 +* [`fn_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
 +* [`vtable_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
 +
 +
 +### Moves and Deprecations
 +
 +* Deprecate [`replace_consts`] lint [#5380](https://github.com/rust-lang/rust-clippy/pull/5380)
 +* Move [`cognitive_complexity`] to nursery [#5428](https://github.com/rust-lang/rust-clippy/pull/5428)
 +* Move [`useless_transmute`] to nursery [#5364](https://github.com/rust-lang/rust-clippy/pull/5364)
 +* Downgrade [`inefficient_to_string`] to pedantic [#5412](https://github.com/rust-lang/rust-clippy/pull/5412)
 +* Downgrade [`option_option`] to pedantic [#5401](https://github.com/rust-lang/rust-clippy/pull/5401)
 +* Downgrade [`unreadable_literal`] to pedantic [#5419](https://github.com/rust-lang/rust-clippy/pull/5419)
 +* Downgrade [`let_unit_value`] to pedantic [#5409](https://github.com/rust-lang/rust-clippy/pull/5409)
 +* Downgrade [`trivially_copy_pass_by_ref`] to pedantic [#5410](https://github.com/rust-lang/rust-clippy/pull/5410)
 +* Downgrade [`implicit_hasher`] to pedantic [#5411](https://github.com/rust-lang/rust-clippy/pull/5411)
 +
 +### Enhancements
 +
 +* On _nightly_ you can now use `cargo clippy --fix -Z unstable-options` to
 +  auto-fix lints that support this [#5363](https://github.com/rust-lang/rust-clippy/pull/5363)
 +* Make [`redundant_clone`] also trigger on cases where the cloned value is not
 +  consumed. [#5304](https://github.com/rust-lang/rust-clippy/pull/5304)
 +* Expand [`integer_arithmetic`] to also disallow bit-shifting [#5430](https://github.com/rust-lang/rust-clippy/pull/5430)
 +* [`option_as_ref_deref`] now detects more deref cases [#5425](https://github.com/rust-lang/rust-clippy/pull/5425)
 +* [`large_enum_variant`] now report the sizes of the largest and second-largest variants [#5466](https://github.com/rust-lang/rust-clippy/pull/5466)
 +* [`bool_comparison`] now also checks for inequality comparisons that can be
 +  written more concisely [#5365](https://github.com/rust-lang/rust-clippy/pull/5365)
 +* Expand [`clone_on_copy`] to work in method call arguments as well [#5441](https://github.com/rust-lang/rust-clippy/pull/5441)
 +* [`redundant_pattern_matching`] now also handles `while let` [#5483](https://github.com/rust-lang/rust-clippy/pull/5483)
 +* [`integer_arithmetic`] now also lints references of integers [#5329](https://github.com/rust-lang/rust-clippy/pull/5329)
 +* Expand [`float_cmp_const`] to also work on arrays [#5345](https://github.com/rust-lang/rust-clippy/pull/5345)
 +* Trigger [`map_flatten`] when map is called on an `Option` [#5473](https://github.com/rust-lang/rust-clippy/pull/5473)
 +
 +### False Positive Fixes
 +
 +* [`many_single_char_names`] [#5468](https://github.com/rust-lang/rust-clippy/pull/5468)
 +* [`should_implement_trait`] [#5437](https://github.com/rust-lang/rust-clippy/pull/5437)
 +* [`unused_self`] [#5387](https://github.com/rust-lang/rust-clippy/pull/5387)
 +* [`redundant_clone`] [#5453](https://github.com/rust-lang/rust-clippy/pull/5453)
 +* [`precedence`] [#5445](https://github.com/rust-lang/rust-clippy/pull/5445)
 +* [`suspicious_op_assign_impl`] [#5424](https://github.com/rust-lang/rust-clippy/pull/5424)
 +* [`needless_lifetimes`] [#5293](https://github.com/rust-lang/rust-clippy/pull/5293)
 +* [`redundant_pattern`] [#5287](https://github.com/rust-lang/rust-clippy/pull/5287)
 +* [`inconsistent_digit_grouping`] [#5451](https://github.com/rust-lang/rust-clippy/pull/5451)
 +
 +
 +### Suggestion Improvements
 +
 +* Improved [`question_mark`] lint suggestion so that it doesn't add redundant `as_ref()` [#5481](https://github.com/rust-lang/rust-clippy/pull/5481)
 +* Improve the suggested placeholder in [`option_map_unit_fn`] [#5292](https://github.com/rust-lang/rust-clippy/pull/5292)
 +* Improve suggestion for [`match_single_binding`] when triggered inside a closure [#5350](https://github.com/rust-lang/rust-clippy/pull/5350)
 +
 +### ICE Fixes
 +
 +* Handle the unstable `trivial_bounds` feature [#5296](https://github.com/rust-lang/rust-clippy/pull/5296)
 +* `shadow_*` lints [#5297](https://github.com/rust-lang/rust-clippy/pull/5297)
 +
 +### Documentation
 +
 +* Fix documentation generation for configurable lints [#5353](https://github.com/rust-lang/rust-clippy/pull/5353)
 +* Update documentation for [`new_ret_no_self`] [#5448](https://github.com/rust-lang/rust-clippy/pull/5448)
 +* The documentation for [`option_option`] now suggest using a tri-state enum [#5403](https://github.com/rust-lang/rust-clippy/pull/5403)
 +* Fix bit mask example in [`verbose_bit_mask`] documentation [#5454](https://github.com/rust-lang/rust-clippy/pull/5454)
 +* [`wildcard_imports`] documentation now mentions that `use ...::prelude::*` is
 +  not linted [#5312](https://github.com/rust-lang/rust-clippy/pull/5312)
 +
 +## Rust 1.43
 +
 +Released 2020-04-23
 +
 +[4ee1206...204bb9b](https://github.com/rust-lang/rust-clippy/compare/4ee1206...204bb9b)
 +
 +### New lints
 +
 +* [`imprecise_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
 +* [`suboptimal_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
 +* [`wildcard_imports`] [#5029](https://github.com/rust-lang/rust-clippy/pull/5029)
 +* [`single_component_path_imports`] [#5058](https://github.com/rust-lang/rust-clippy/pull/5058)
 +* [`match_single_binding`] [#5061](https://github.com/rust-lang/rust-clippy/pull/5061)
 +* [`let_underscore_lock`] [#5101](https://github.com/rust-lang/rust-clippy/pull/5101)
 +* [`struct_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
 +* [`fn_params_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
 +* [`option_env_unwrap`] [#5148](https://github.com/rust-lang/rust-clippy/pull/5148)
 +* [`lossy_float_literal`] [#5202](https://github.com/rust-lang/rust-clippy/pull/5202)
 +* [`rest_pat_in_fully_bound_structs`] [#5258](https://github.com/rust-lang/rust-clippy/pull/5258)
 +
 +### Moves and Deprecations
 +
 +* Move [`unneeded_field_pattern`] to pedantic group [#5200](https://github.com/rust-lang/rust-clippy/pull/5200)
 +
 +### Enhancements
 +
 +* Make [`missing_errors_doc`] lint also trigger on `async` functions
 +  [#5181](https://github.com/rust-lang/rust-clippy/pull/5181)
 +* Add more constants to [`approx_constant`] [#5193](https://github.com/rust-lang/rust-clippy/pull/5193)
 +* Extend [`question_mark`] lint [#5266](https://github.com/rust-lang/rust-clippy/pull/5266)
 +
 +### False Positive Fixes
 +
 +* [`use_debug`] [#5047](https://github.com/rust-lang/rust-clippy/pull/5047)
 +* [`unnecessary_unwrap`] [#5132](https://github.com/rust-lang/rust-clippy/pull/5132)
 +* [`zero_prefixed_literal`] [#5170](https://github.com/rust-lang/rust-clippy/pull/5170)
 +* [`missing_const_for_fn`] [#5216](https://github.com/rust-lang/rust-clippy/pull/5216)
 +
 +### Suggestion Improvements
 +
 +* Improve suggestion when blocks of code are suggested [#5134](https://github.com/rust-lang/rust-clippy/pull/5134)
 +
 +### ICE Fixes
 +
 +* `misc_early` lints [#5129](https://github.com/rust-lang/rust-clippy/pull/5129)
 +* [`missing_errors_doc`] [#5213](https://github.com/rust-lang/rust-clippy/pull/5213)
 +* Fix ICE when evaluating `usize`s [#5256](https://github.com/rust-lang/rust-clippy/pull/5256)
 +
 +### Documentation
 +
 +* Improve documentation of [`iter_nth_zero`]
 +* Add documentation pages for stable releases [#5171](https://github.com/rust-lang/rust-clippy/pull/5171)
 +
 +### Others
 +
 +* Clippy now completely runs on GitHub Actions [#5190](https://github.com/rust-lang/rust-clippy/pull/5190)
 +
 +
 +## Rust 1.42
 +
 +Released 2020-03-12
 +
 +[69f99e7...4ee1206](https://github.com/rust-lang/rust-clippy/compare/69f99e7...4ee1206)
 +
 +### New lints
 +
 +* [`filetype_is_file`] [#4543](https://github.com/rust-lang/rust-clippy/pull/4543)
 +* [`let_underscore_must_use`] [#4823](https://github.com/rust-lang/rust-clippy/pull/4823)
 +* [`modulo_arithmetic`] [#4867](https://github.com/rust-lang/rust-clippy/pull/4867)
 +* [`mem_replace_with_default`] [#4881](https://github.com/rust-lang/rust-clippy/pull/4881)
 +* [`mutable_key_type`] [#4885](https://github.com/rust-lang/rust-clippy/pull/4885)
 +* [`option_as_ref_deref`] [#4945](https://github.com/rust-lang/rust-clippy/pull/4945)
 +* [`wildcard_in_or_patterns`] [#4960](https://github.com/rust-lang/rust-clippy/pull/4960)
 +* [`iter_nth_zero`] [#4966](https://github.com/rust-lang/rust-clippy/pull/4966)
 +* `invalid_atomic_ordering` [#4999](https://github.com/rust-lang/rust-clippy/pull/4999)
 +* [`skip_while_next`] [#5067](https://github.com/rust-lang/rust-clippy/pull/5067)
 +
 +### Moves and Deprecations
 +
 +* Move [`transmute_float_to_int`] from nursery to complexity group
 +  [#5015](https://github.com/rust-lang/rust-clippy/pull/5015)
 +* Move [`range_plus_one`] to pedantic group [#5057](https://github.com/rust-lang/rust-clippy/pull/5057)
 +* Move [`debug_assert_with_mut_call`] to nursery group [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
 +* Deprecate `unused_label` [#4930](https://github.com/rust-lang/rust-clippy/pull/4930)
 +
 +### Enhancements
 +
 +* Lint vectored IO in [`unused_io_amount`] [#5027](https://github.com/rust-lang/rust-clippy/pull/5027)
 +* Make [`vec_box`] configurable by adding a size threshold [#5081](https://github.com/rust-lang/rust-clippy/pull/5081)
 +* Also lint constants in [`cmp_nan`] [#4910](https://github.com/rust-lang/rust-clippy/pull/4910)
 +* Fix false negative in [`expect_fun_call`] [#4915](https://github.com/rust-lang/rust-clippy/pull/4915)
 +* Fix false negative in [`redundant_clone`] [#5017](https://github.com/rust-lang/rust-clippy/pull/5017)
 +
 +### False Positive Fixes
 +
 +* [`map_clone`] [#4937](https://github.com/rust-lang/rust-clippy/pull/4937)
 +* [`replace_consts`] [#4977](https://github.com/rust-lang/rust-clippy/pull/4977)
 +* [`let_and_return`] [#5008](https://github.com/rust-lang/rust-clippy/pull/5008)
 +* [`eq_op`] [#5079](https://github.com/rust-lang/rust-clippy/pull/5079)
 +* [`possible_missing_comma`] [#5083](https://github.com/rust-lang/rust-clippy/pull/5083)
 +* [`debug_assert_with_mut_call`] [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
 +* Don't trigger [`let_underscore_must_use`] in external macros
 +  [#5082](https://github.com/rust-lang/rust-clippy/pull/5082)
 +* Don't trigger [`empty_loop`] in `no_std` crates [#5086](https://github.com/rust-lang/rust-clippy/pull/5086)
 +
 +### Suggestion Improvements
 +
 +* `option_map_unwrap_or` [#4634](https://github.com/rust-lang/rust-clippy/pull/4634)
 +* [`wildcard_enum_match_arm`] [#4934](https://github.com/rust-lang/rust-clippy/pull/4934)
 +* [`cognitive_complexity`] [#4935](https://github.com/rust-lang/rust-clippy/pull/4935)
 +* [`decimal_literal_representation`] [#4956](https://github.com/rust-lang/rust-clippy/pull/4956)
 +* `unknown_clippy_lints` [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
 +* [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978)
 +* [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022)
 +* `if_let_some_result` [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
 +
 +### ICE fixes
 +
 +* [`unsound_collection_transmute`] [#4975](https://github.com/rust-lang/rust-clippy/pull/4975)
 +
 +### Documentation
 +
 +* Improve documentation of [`empty_enum`], [`replace_consts`], [`redundant_clone`], and [`iterator_step_by_zero`]
 +
 +
 +## Rust 1.41
 +
 +Released 2020-01-30
 +
 +[c8e3cfb...69f99e7](https://github.com/rust-lang/rust-clippy/compare/c8e3cfb...69f99e7)
 +
 +* New Lints:
 +  * [`exit`] [#4697](https://github.com/rust-lang/rust-clippy/pull/4697)
 +  * [`to_digit_is_some`] [#4801](https://github.com/rust-lang/rust-clippy/pull/4801)
 +  * [`tabs_in_doc_comments`] [#4806](https://github.com/rust-lang/rust-clippy/pull/4806)
 +  * [`large_stack_arrays`] [#4807](https://github.com/rust-lang/rust-clippy/pull/4807)
 +  * [`same_functions_in_if_condition`] [#4814](https://github.com/rust-lang/rust-clippy/pull/4814)
 +  * [`zst_offset`] [#4816](https://github.com/rust-lang/rust-clippy/pull/4816)
 +  * [`as_conversions`] [#4821](https://github.com/rust-lang/rust-clippy/pull/4821)
 +  * [`missing_errors_doc`] [#4884](https://github.com/rust-lang/rust-clippy/pull/4884)
 +  * [`transmute_float_to_int`] [#4889](https://github.com/rust-lang/rust-clippy/pull/4889)
 +* Remove plugin interface, see
 +  [Inside Rust Blog](https://blog.rust-lang.org/inside-rust/2019/11/04/Clippy-removes-plugin-interface.html) for
 +  details [#4714](https://github.com/rust-lang/rust-clippy/pull/4714)
 +* Move [`use_self`] to nursery group [#4863](https://github.com/rust-lang/rust-clippy/pull/4863)
 +* Deprecate `into_iter_on_array` [#4788](https://github.com/rust-lang/rust-clippy/pull/4788)
 +* Expand [`string_lit_as_bytes`] to also trigger when literal has escapes
 +  [#4808](https://github.com/rust-lang/rust-clippy/pull/4808)
 +* Fix false positive in `comparison_chain` [#4842](https://github.com/rust-lang/rust-clippy/pull/4842)
 +* Fix false positive in `while_immutable_condition` [#4730](https://github.com/rust-lang/rust-clippy/pull/4730)
 +* Fix false positive in `explicit_counter_loop` [#4803](https://github.com/rust-lang/rust-clippy/pull/4803)
 +* Fix false positive in `must_use_candidate` [#4794](https://github.com/rust-lang/rust-clippy/pull/4794)
 +* Fix false positive in `print_with_newline` and `write_with_newline`
 +  [#4769](https://github.com/rust-lang/rust-clippy/pull/4769)
 +* Fix false positive in `derive_hash_xor_eq` [#4766](https://github.com/rust-lang/rust-clippy/pull/4766)
 +* Fix false positive in `missing_inline_in_public_items` [#4870](https://github.com/rust-lang/rust-clippy/pull/4870)
 +* Fix false positive in `string_add` [#4880](https://github.com/rust-lang/rust-clippy/pull/4880)
 +* Fix false positive in `float_arithmetic` [#4851](https://github.com/rust-lang/rust-clippy/pull/4851)
 +* Fix false positive in `cast_sign_loss` [#4883](https://github.com/rust-lang/rust-clippy/pull/4883)
 +* Fix false positive in `manual_swap` [#4877](https://github.com/rust-lang/rust-clippy/pull/4877)
 +* Fix ICEs occurring while checking some block expressions [#4772](https://github.com/rust-lang/rust-clippy/pull/4772)
 +* Fix ICE in `use_self` [#4776](https://github.com/rust-lang/rust-clippy/pull/4776)
 +* Fix ICEs related to `const_generics` [#4780](https://github.com/rust-lang/rust-clippy/pull/4780)
 +* Display help when running `clippy-driver` without arguments, instead of ICEing
 +  [#4810](https://github.com/rust-lang/rust-clippy/pull/4810)
 +* Clippy has its own ICE message now [#4588](https://github.com/rust-lang/rust-clippy/pull/4588)
 +* Show deprecated lints in the documentation again [#4757](https://github.com/rust-lang/rust-clippy/pull/4757)
 +* Improve Documentation by adding positive examples to some lints
 +  [#4832](https://github.com/rust-lang/rust-clippy/pull/4832)
 +
 +## Rust 1.40
 +
 +Released 2019-12-19
 +
 +[4e7e71b...c8e3cfb](https://github.com/rust-lang/rust-clippy/compare/4e7e71b...c8e3cfb)
 +
 +* New Lints:
 +  * [`unneeded_wildcard_pattern`] [#4537](https://github.com/rust-lang/rust-clippy/pull/4537)
 +  * [`needless_doctest_main`] [#4603](https://github.com/rust-lang/rust-clippy/pull/4603)
 +  * [`suspicious_unary_op_formatting`] [#4615](https://github.com/rust-lang/rust-clippy/pull/4615)
 +  * [`debug_assert_with_mut_call`] [#4680](https://github.com/rust-lang/rust-clippy/pull/4680)
 +  * [`unused_self`] [#4619](https://github.com/rust-lang/rust-clippy/pull/4619)
 +  * [`inefficient_to_string`] [#4683](https://github.com/rust-lang/rust-clippy/pull/4683)
 +  * [`must_use_unit`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`must_use_candidate`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`double_must_use`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`comparison_chain`] [#4569](https://github.com/rust-lang/rust-clippy/pull/4569)
 +  * [`unsound_collection_transmute`] [#4592](https://github.com/rust-lang/rust-clippy/pull/4592)
 +  * [`panic`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * [`unreachable`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * [`todo`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * `option_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * `result_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +* Move `redundant_clone` to perf group [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
 +* Move `manual_mul_add` to nursery group [#4736](https://github.com/rust-lang/rust-clippy/pull/4736)
 +* Expand `unit_cmp` to also work with `assert_eq!`, `debug_assert_eq!`, `assert_ne!` and `debug_assert_ne!` [#4613](https://github.com/rust-lang/rust-clippy/pull/4613)
 +* Expand `integer_arithmetic` to also detect mutating arithmetic like `+=` [#4585](https://github.com/rust-lang/rust-clippy/pull/4585)
 +* Fix false positive in `nonminimal_bool` [#4568](https://github.com/rust-lang/rust-clippy/pull/4568)
 +* Fix false positive in `missing_safety_doc` [#4611](https://github.com/rust-lang/rust-clippy/pull/4611)
 +* Fix false positive in `cast_sign_loss` [#4614](https://github.com/rust-lang/rust-clippy/pull/4614)
 +* Fix false positive in `redundant_clone` [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
 +* Fix false positive in `try_err` [#4721](https://github.com/rust-lang/rust-clippy/pull/4721)
 +* Fix false positive in `toplevel_ref_arg` [#4570](https://github.com/rust-lang/rust-clippy/pull/4570)
 +* Fix false positive in `multiple_inherent_impl` [#4593](https://github.com/rust-lang/rust-clippy/pull/4593)
 +* Improve more suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4575](https://github.com/rust-lang/rust-clippy/pull/4575)
 +* Improve suggestion for `zero_ptr` [#4599](https://github.com/rust-lang/rust-clippy/pull/4599)
 +* Improve suggestion for `explicit_counter_loop` [#4691](https://github.com/rust-lang/rust-clippy/pull/4691)
 +* Improve suggestion for `mul_add` [#4602](https://github.com/rust-lang/rust-clippy/pull/4602)
 +* Improve suggestion for `assertions_on_constants` [#4635](https://github.com/rust-lang/rust-clippy/pull/4635)
 +* Fix ICE in `use_self` [#4671](https://github.com/rust-lang/rust-clippy/pull/4671)
 +* Fix ICE when encountering const casts [#4590](https://github.com/rust-lang/rust-clippy/pull/4590)
 +
 +## Rust 1.39
 +
 +Released 2019-11-07
 +
 +[3aea860...4e7e71b](https://github.com/rust-lang/rust-clippy/compare/3aea860...4e7e71b)
 +
 +* New Lints:
 +  * [`uninit_assumed_init`] [#4479](https://github.com/rust-lang/rust-clippy/pull/4479)
 +  * [`flat_map_identity`] [#4231](https://github.com/rust-lang/rust-clippy/pull/4231)
 +  * [`missing_safety_doc`] [#4535](https://github.com/rust-lang/rust-clippy/pull/4535)
 +  * [`mem_replace_with_uninit`] [#4511](https://github.com/rust-lang/rust-clippy/pull/4511)
 +  * [`suspicious_map`] [#4394](https://github.com/rust-lang/rust-clippy/pull/4394)
 +  * `option_and_then_some` [#4386](https://github.com/rust-lang/rust-clippy/pull/4386)
 +  * [`manual_saturating_arithmetic`] [#4498](https://github.com/rust-lang/rust-clippy/pull/4498)
 +* Deprecate `unused_collect` lint. This is fully covered by rustc's `#[must_use]` on `collect` [#4348](https://github.com/rust-lang/rust-clippy/pull/4348)
 +* Move `type_repetition_in_bounds` to pedantic group [#4403](https://github.com/rust-lang/rust-clippy/pull/4403)
 +* Move `cast_lossless` to pedantic group [#4539](https://github.com/rust-lang/rust-clippy/pull/4539)
 +* `temporary_cstring_as_ptr` now catches more cases [#4425](https://github.com/rust-lang/rust-clippy/pull/4425)
 +* `use_self` now works in constructors, too [#4525](https://github.com/rust-lang/rust-clippy/pull/4525)
 +* `cargo_common_metadata` now checks for license files [#4518](https://github.com/rust-lang/rust-clippy/pull/4518)
 +* `cognitive_complexity` now includes the measured complexity in the warning message [#4469](https://github.com/rust-lang/rust-clippy/pull/4469)
 +* Fix false positives in `block_in_if_*` lints [#4458](https://github.com/rust-lang/rust-clippy/pull/4458)
 +* Fix false positive in `cast_lossless` [#4473](https://github.com/rust-lang/rust-clippy/pull/4473)
 +* Fix false positive in `clone_on_copy` [#4411](https://github.com/rust-lang/rust-clippy/pull/4411)
 +* Fix false positive in `deref_addrof` [#4487](https://github.com/rust-lang/rust-clippy/pull/4487)
 +* Fix false positive in `too_many_lines` [#4490](https://github.com/rust-lang/rust-clippy/pull/4490)
 +* Fix false positive in `new_ret_no_self` [#4365](https://github.com/rust-lang/rust-clippy/pull/4365)
 +* Fix false positive in `manual_swap` [#4478](https://github.com/rust-lang/rust-clippy/pull/4478)
 +* Fix false positive in `missing_const_for_fn` [#4450](https://github.com/rust-lang/rust-clippy/pull/4450)
 +* Fix false positive in `extra_unused_lifetimes` [#4477](https://github.com/rust-lang/rust-clippy/pull/4477)
 +* Fix false positive in `inherent_to_string` [#4460](https://github.com/rust-lang/rust-clippy/pull/4460)
 +* Fix false positive in `map_entry` [#4495](https://github.com/rust-lang/rust-clippy/pull/4495)
 +* Fix false positive in `unused_unit` [#4445](https://github.com/rust-lang/rust-clippy/pull/4445)
 +* Fix false positive in `redundant_pattern` [#4489](https://github.com/rust-lang/rust-clippy/pull/4489)
 +* Fix false positive in `wrong_self_convention` [#4369](https://github.com/rust-lang/rust-clippy/pull/4369)
 +* Improve various suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4558](https://github.com/rust-lang/rust-clippy/pull/4558)
 +* Improve suggestions for `redundant_pattern_matching` [#4352](https://github.com/rust-lang/rust-clippy/pull/4352)
 +* Improve suggestions for `explicit_write` [#4544](https://github.com/rust-lang/rust-clippy/pull/4544)
 +* Improve suggestion for `or_fun_call` [#4522](https://github.com/rust-lang/rust-clippy/pull/4522)
 +* Improve suggestion for `match_as_ref` [#4446](https://github.com/rust-lang/rust-clippy/pull/4446)
 +* Improve suggestion for `unnecessary_fold_span` [#4382](https://github.com/rust-lang/rust-clippy/pull/4382)
 +* Add suggestions for `unseparated_literal_suffix` [#4401](https://github.com/rust-lang/rust-clippy/pull/4401)
 +* Add suggestions for `char_lit_as_u8` [#4418](https://github.com/rust-lang/rust-clippy/pull/4418)
 +
 +## Rust 1.38
 +
 +Released 2019-09-26
 +
 +[e3cb40e...3aea860](https://github.com/rust-lang/rust-clippy/compare/e3cb40e...3aea860)
 +
 +* New Lints:
 +  * [`main_recursion`] [#4203](https://github.com/rust-lang/rust-clippy/pull/4203)
 +  * [`inherent_to_string`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
 +  * [`inherent_to_string_shadow_display`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
 +  * [`type_repetition_in_bounds`] [#3766](https://github.com/rust-lang/rust-clippy/pull/3766)
 +  * [`try_err`] [#4222](https://github.com/rust-lang/rust-clippy/pull/4222)
 +* Move `{unnecessary,panicking}_unwrap` out of nursery [#4307](https://github.com/rust-lang/rust-clippy/pull/4307)
 +* Extend the `use_self` lint to suggest uses of `Self::Variant` [#4308](https://github.com/rust-lang/rust-clippy/pull/4308)
 +* Improve suggestion for needless return [#4262](https://github.com/rust-lang/rust-clippy/pull/4262)
 +* Add auto-fixable suggestion for `let_unit` [#4337](https://github.com/rust-lang/rust-clippy/pull/4337)
 +* Fix false positive in `pub_enum_variant_names` and `enum_variant_names` [#4345](https://github.com/rust-lang/rust-clippy/pull/4345)
 +* Fix false positive in `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
 +* Fix false positive in `string_lit_as_bytes` [#4233](https://github.com/rust-lang/rust-clippy/pull/4233)
 +* Fix false positive in `needless_lifetimes` [#4266](https://github.com/rust-lang/rust-clippy/pull/4266)
 +* Fix false positive in `float_cmp` [#4275](https://github.com/rust-lang/rust-clippy/pull/4275)
 +* Fix false positives in `needless_return` [#4274](https://github.com/rust-lang/rust-clippy/pull/4274)
 +* Fix false negative in `match_same_arms` [#4246](https://github.com/rust-lang/rust-clippy/pull/4246)
 +* Fix incorrect suggestion for `needless_bool` [#4335](https://github.com/rust-lang/rust-clippy/pull/4335)
 +* Improve suggestion for `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
 +* Improve suggestion for `single_char_literal` [#4361](https://github.com/rust-lang/rust-clippy/pull/4361)
 +* Improve suggestion for `len_zero` [#4314](https://github.com/rust-lang/rust-clippy/pull/4314)
 +* Fix ICE in `implicit_hasher` [#4268](https://github.com/rust-lang/rust-clippy/pull/4268)
 +* Fix allow bug in `trivially_copy_pass_by_ref` [#4250](https://github.com/rust-lang/rust-clippy/pull/4250)
 +
 +## Rust 1.37
 +
 +Released 2019-08-15
 +
 +[082cfa7...e3cb40e](https://github.com/rust-lang/rust-clippy/compare/082cfa7...e3cb40e)
 +
 +* New Lints:
 +  * [`checked_conversions`] [#4088](https://github.com/rust-lang/rust-clippy/pull/4088)
 +  * [`get_last_with_len`] [#3832](https://github.com/rust-lang/rust-clippy/pull/3832)
 +  * [`integer_division`] [#4195](https://github.com/rust-lang/rust-clippy/pull/4195)
 +* Renamed Lint: `const_static_lifetime` is now called [`redundant_static_lifetimes`].
 +  The lint now covers statics in addition to consts [#4162](https://github.com/rust-lang/rust-clippy/pull/4162)
 +* [`match_same_arms`] now warns for all identical arms, instead of only the first one [#4102](https://github.com/rust-lang/rust-clippy/pull/4102)
 +* [`needless_return`] now works with void functions [#4220](https://github.com/rust-lang/rust-clippy/pull/4220)
 +* Fix false positive in [`redundant_closure`] [#4190](https://github.com/rust-lang/rust-clippy/pull/4190)
 +* Fix false positive in [`useless_attribute`] [#4107](https://github.com/rust-lang/rust-clippy/pull/4107)
 +* Fix incorrect suggestion for [`float_cmp`] [#4214](https://github.com/rust-lang/rust-clippy/pull/4214)
 +* Add suggestions for [`print_with_newline`] and [`write_with_newline`] [#4136](https://github.com/rust-lang/rust-clippy/pull/4136)
 +* Improve suggestions for `option_map_unwrap_or_else` and `result_map_unwrap_or_else` [#4164](https://github.com/rust-lang/rust-clippy/pull/4164)
 +* Improve suggestions for [`non_ascii_literal`] [#4119](https://github.com/rust-lang/rust-clippy/pull/4119)
 +* Improve diagnostics for [`let_and_return`] [#4137](https://github.com/rust-lang/rust-clippy/pull/4137)
 +* Improve diagnostics for [`trivially_copy_pass_by_ref`] [#4071](https://github.com/rust-lang/rust-clippy/pull/4071)
 +* Add macro check for [`unreadable_literal`] [#4099](https://github.com/rust-lang/rust-clippy/pull/4099)
 +
 +## Rust 1.36
 +
 +Released 2019-07-04
 +
 +[eb9f9b1...082cfa7](https://github.com/rust-lang/rust-clippy/compare/eb9f9b1...082cfa7)
 +
 +* New lints: [`find_map`], [`filter_map_next`] [#4039](https://github.com/rust-lang/rust-clippy/pull/4039)
 +* New lint: [`path_buf_push_overwrite`] [#3954](https://github.com/rust-lang/rust-clippy/pull/3954)
 +* Move `path_buf_push_overwrite` to the nursery [#4013](https://github.com/rust-lang/rust-clippy/pull/4013)
 +* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
 +* Allow allowing of [`toplevel_ref_arg`] lint [#4007](https://github.com/rust-lang/rust-clippy/pull/4007)
 +* Fix false negative in [`or_fun_call`] pertaining to nested constructors [#4084](https://github.com/rust-lang/rust-clippy/pull/4084)
 +* Fix false positive in [`or_fun_call`] pertaining to enum variant constructors [#4018](https://github.com/rust-lang/rust-clippy/pull/4018)
 +* Fix false positive in [`useless_let_if_seq`] pertaining to interior mutability [#4035](https://github.com/rust-lang/rust-clippy/pull/4035)
 +* Fix false positive in [`redundant_closure`] pertaining to non-function types [#4008](https://github.com/rust-lang/rust-clippy/pull/4008)
 +* Fix false positive in [`let_and_return`] pertaining to attributes on `let`s [#4024](https://github.com/rust-lang/rust-clippy/pull/4024)
 +* Fix false positive in [`module_name_repetitions`] lint pertaining to attributes [#4006](https://github.com/rust-lang/rust-clippy/pull/4006)
 +* Fix false positive on [`assertions_on_constants`] pertaining to `debug_assert!` [#3989](https://github.com/rust-lang/rust-clippy/pull/3989)
 +* Improve suggestion in [`map_clone`] to suggest `.copied()` where applicable  [#3970](https://github.com/rust-lang/rust-clippy/pull/3970) [#4043](https://github.com/rust-lang/rust-clippy/pull/4043)
 +* Improve suggestion for [`search_is_some`] [#4049](https://github.com/rust-lang/rust-clippy/pull/4049)
 +* Improve suggestion applicability for [`naive_bytecount`] [#3984](https://github.com/rust-lang/rust-clippy/pull/3984)
 +* Improve suggestion applicability for [`while_let_loop`] [#3975](https://github.com/rust-lang/rust-clippy/pull/3975)
 +* Improve diagnostics for [`too_many_arguments`] [#4053](https://github.com/rust-lang/rust-clippy/pull/4053)
 +* Improve diagnostics for [`cast_lossless`] [#4021](https://github.com/rust-lang/rust-clippy/pull/4021)
 +* Deal with macro checks in desugarings better [#4082](https://github.com/rust-lang/rust-clippy/pull/4082)
 +* Add macro check for [`unnecessary_cast`]  [#4026](https://github.com/rust-lang/rust-clippy/pull/4026)
 +* Remove [`approx_constant`]'s documentation's "Known problems" section. [#4027](https://github.com/rust-lang/rust-clippy/pull/4027)
 +* Fix ICE in [`suspicious_else_formatting`] [#3960](https://github.com/rust-lang/rust-clippy/pull/3960)
 +* Fix ICE in [`decimal_literal_representation`] [#3931](https://github.com/rust-lang/rust-clippy/pull/3931)
 +
 +
 +## Rust 1.35
 +
 +Released 2019-05-20
 +
 +[1fac380..37f5c1e](https://github.com/rust-lang/rust-clippy/compare/1fac380...37f5c1e)
 +
 +* New lint: `drop_bounds` to detect `T: Drop` bounds
 +* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
 +* Rename `cyclomatic_complexity` to [`cognitive_complexity`], start work on making lint more practical for Rust code
 +* Move [`get_unwrap`] to the restriction category
 +* Improve suggestions for [`iter_cloned_collect`]
 +* Improve suggestions for [`cast_lossless`] to suggest suffixed literals
 +* Fix false positives in [`print_with_newline`] and [`write_with_newline`] pertaining to raw strings
 +* Fix false positive in [`needless_range_loop`] pertaining to structs without a `.iter()`
 +* Fix false positive in [`bool_comparison`] pertaining to non-bool types
 +* Fix false positive in [`redundant_closure`] pertaining to differences in borrows
 +* Fix false positive in `option_map_unwrap_or` on non-copy types
 +* Fix false positives in [`missing_const_for_fn`] pertaining to macros and trait method impls
 +* Fix false positive in [`needless_pass_by_value`] pertaining to procedural macros
 +* Fix false positive in [`needless_continue`] pertaining to loop labels
 +* Fix false positive for [`boxed_local`] pertaining to arguments moved into closures
 +* Fix false positive for [`use_self`] in nested functions
 +* Fix suggestion for [`expect_fun_call`] (https://github.com/rust-lang/rust-clippy/pull/3846)
 +* Fix suggestion for [`explicit_counter_loop`] to deal with parenthesizing range variables
 +* Fix suggestion for [`single_char_pattern`] to correctly escape single quotes
 +* Avoid triggering [`redundant_closure`] in macros
 +* ICE fixes: [#3805](https://github.com/rust-lang/rust-clippy/pull/3805), [#3772](https://github.com/rust-lang/rust-clippy/pull/3772), [#3741](https://github.com/rust-lang/rust-clippy/pull/3741)
 +
 +## Rust 1.34
 +
 +Released 2019-04-10
 +
 +[1b89724...1fac380](https://github.com/rust-lang/rust-clippy/compare/1b89724...1fac380)
 +
 +* New lint: [`assertions_on_constants`] to detect for example `assert!(true)`
 +* New lint: [`dbg_macro`] to detect uses of the `dbg!` macro
 +* New lint: [`missing_const_for_fn`] that can suggest functions to be made `const`
 +* New lint: [`too_many_lines`] to detect functions with excessive LOC. It can be
 +  configured using the `too-many-lines-threshold` configuration.
 +* New lint: [`wildcard_enum_match_arm`] to check for wildcard enum matches using `_`
 +* Expand `redundant_closure` to also work for methods (not only functions)
 +* Fix ICEs in `vec_box`, `needless_pass_by_value` and `implicit_hasher`
 +* Fix false positive in `cast_sign_loss`
 +* Fix false positive in `integer_arithmetic`
 +* Fix false positive in `unit_arg`
 +* Fix false positives in `implicit_return`
 +* Add suggestion to `explicit_write`
 +* Improve suggestions for `question_mark` lint
 +* Fix incorrect suggestion for `cast_lossless`
 +* Fix incorrect suggestion for `expect_fun_call`
 +* Fix incorrect suggestion for `needless_bool`
 +* Fix incorrect suggestion for `needless_range_loop`
 +* Fix incorrect suggestion for `use_self`
 +* Fix incorrect suggestion for `while_let_on_iterator`
 +* Clippy is now slightly easier to invoke in non-cargo contexts. See
 +  [#3665][pull3665] for more details.
 +* We now have [improved documentation][adding_lints] on how to add new lints
 +
 +## Rust 1.33
 +
 +Released 2019-02-26
 +
 +[b2601be...1b89724](https://github.com/rust-lang/rust-clippy/compare/b2601be...1b89724)
 +
 +* New lints: [`implicit_return`], [`vec_box`], [`cast_ref_to_mut`]
 +* The `rust-clippy` repository is now part of the `rust-lang` org.
 +* Rename `stutter` to `module_name_repetitions`
 +* Merge `new_without_default_derive` into `new_without_default` lint
 +* Move `large_digit_groups` from `style` group to `pedantic`
 +* Expand `bool_comparison` to check for `<`, `<=`, `>`, `>=`, and `!=`
 +  comparisons against booleans
 +* Expand `no_effect` to detect writes to constants such as `A_CONST.field = 2`
 +* Expand `redundant_clone` to work on struct fields
 +* Expand `suspicious_else_formatting` to detect `if .. {..} {..}`
 +* Expand `use_self` to work on tuple structs and also in local macros
 +* Fix ICE in `result_map_unit_fn` and `option_map_unit_fn`
 +* Fix false positives in `implicit_return`
 +* Fix false positives in `use_self`
 +* Fix false negative in `clone_on_copy`
 +* Fix false positive in `doc_markdown`
 +* Fix false positive in `empty_loop`
 +* Fix false positive in `if_same_then_else`
 +* Fix false positive in `infinite_iter`
 +* Fix false positive in `question_mark`
 +* Fix false positive in `useless_asref`
 +* Fix false positive in `wildcard_dependencies`
 +* Fix false positive in `write_with_newline`
 +* Add suggestion to `explicit_write`
 +* Improve suggestions for `question_mark` lint
 +* Fix incorrect suggestion for `get_unwrap`
 +
 +## Rust 1.32
 +
 +Released 2019-01-17
 +
 +[2e26fdc2...b2601be](https://github.com/rust-lang/rust-clippy/compare/2e26fdc2...b2601be)
 +
 +* New lints: [`slow_vector_initialization`], `mem_discriminant_non_enum`,
 +  [`redundant_clone`], [`wildcard_dependencies`],
 +  [`into_iter_on_ref`], `into_iter_on_array`, [`deprecated_cfg_attr`],
 +  [`cargo_common_metadata`]
 +* Add support for `u128` and `i128` to integer related lints
 +* Add float support to `mistyped_literal_suffixes`
 +* Fix false positives in `use_self`
 +* Fix false positives in `missing_comma`
 +* Fix false positives in `new_ret_no_self`
 +* Fix false positives in `possible_missing_comma`
 +* Fix false positive in `integer_arithmetic` in constant items
 +* Fix false positive in `needless_borrow`
 +* Fix false positive in `out_of_bounds_indexing`
 +* Fix false positive in `new_without_default_derive`
 +* Fix false positive in `string_lit_as_bytes`
 +* Fix false negative in `out_of_bounds_indexing`
 +* Fix false negative in `use_self`. It will now also check existential types
 +* Fix incorrect suggestion for `redundant_closure_call`
 +* Fix various suggestions that contained expanded macros
 +* Fix `bool_comparison` triggering 3 times on on on the same code
 +* Expand `trivially_copy_pass_by_ref` to work on trait methods
 +* Improve suggestion for `needless_range_loop`
 +* Move `needless_pass_by_value` from `pedantic` group to `style`
 +
 +## Rust 1.31
 +
 +Released 2018-12-06
 +
 +[125907ad..2e26fdc2](https://github.com/rust-lang/rust-clippy/compare/125907ad..2e26fdc2)
 +
 +* Clippy has been relicensed under a dual MIT / Apache license.
 +  See [#3093](https://github.com/rust-lang/rust-clippy/issues/3093) for more
 +  information.
 +* With Rust 1.31, Clippy is no longer available via crates.io. The recommended
 +  installation method is via `rustup component add clippy`.
 +* New lints: [`redundant_pattern_matching`], [`unnecessary_filter_map`],
 +  [`unused_unit`], [`map_flatten`], [`mem_replace_option_with_none`]
 +* Fix ICE in `if_let_redundant_pattern_matching`
 +* Fix ICE in `needless_pass_by_value` when encountering a generic function
 +  argument with a lifetime parameter
 +* Fix ICE in `needless_range_loop`
 +* Fix ICE in `single_char_pattern` when encountering a constant value
 +* Fix false positive in `assign_op_pattern`
 +* Fix false positive in `boxed_local` on trait implementations
 +* Fix false positive in `cmp_owned`
 +* Fix false positive in `collapsible_if` when conditionals have comments
 +* Fix false positive in `double_parens`
 +* Fix false positive in `excessive_precision`
 +* Fix false positive in `explicit_counter_loop`
 +* Fix false positive in `fn_to_numeric_cast_with_truncation`
 +* Fix false positive in `map_clone`
 +* Fix false positive in `new_ret_no_self`
 +* Fix false positive in `new_without_default` when `new` is unsafe
 +* Fix false positive in `type_complexity` when using extern types
 +* Fix false positive in `useless_format`
 +* Fix false positive in `wrong_self_convention`
 +* Fix incorrect suggestion for `excessive_precision`
 +* Fix incorrect suggestion for `expect_fun_call`
 +* Fix incorrect suggestion for `get_unwrap`
 +* Fix incorrect suggestion for `useless_format`
 +* `fn_to_numeric_cast_with_truncation` lint can be disabled again
 +* Improve suggestions for `manual_memcpy`
 +* Improve help message for `needless_lifetimes`
 +
 +## Rust 1.30
 +
 +Released 2018-10-25
 +
 +[14207503...125907ad](https://github.com/rust-lang/rust-clippy/compare/14207503...125907ad)
 +
 +* Deprecate `assign_ops` lint
 +* New lints: [`mistyped_literal_suffixes`], [`ptr_offset_with_cast`],
 +  [`needless_collect`], [`copy_iterator`]
 +* `cargo clippy -V` now includes the Clippy commit hash of the Rust
 +  Clippy component
 +* Fix ICE in `implicit_hasher`
 +* Fix ICE when encountering `println!("{}" a);`
 +* Fix ICE when encountering a macro call in match statements
 +* Fix false positive in `default_trait_access`
 +* Fix false positive in `trivially_copy_pass_by_ref`
 +* Fix false positive in `similar_names`
 +* Fix false positive in `redundant_field_name`
 +* Fix false positive in `expect_fun_call`
 +* Fix false negative in `identity_conversion`
 +* Fix false negative in `explicit_counter_loop`
 +* Fix `range_plus_one` suggestion and false negative
 +* `print_with_newline` / `write_with_newline`: don't warn about string with several `\n`s in them
 +* Fix `useless_attribute` to also whitelist `unused_extern_crates`
 +* Fix incorrect suggestion for `single_char_pattern`
 +* Improve suggestion for `identity_conversion` lint
 +* Move `explicit_iter_loop` and `explicit_into_iter_loop` from `style` group to `pedantic`
 +* Move `range_plus_one` and `range_minus_one` from `nursery` group to `complexity`
 +* Move `shadow_unrelated` from `restriction` group to `pedantic`
 +* Move `indexing_slicing` from `pedantic` group to `restriction`
 +
 +## Rust 1.29
 +
 +Released 2018-09-13
 +
 +[v0.0.212...14207503](https://github.com/rust-lang/rust-clippy/compare/v0.0.212...14207503)
 +
 +* :tada: :tada: **Rust 1.29 is the first stable Rust that includes a bundled Clippy** :tada:
 +  :tada:
 +  You can now run `rustup component add clippy-preview` and then `cargo
 +  clippy` to run Clippy. This should put an end to the continuous nightly
 +  upgrades for Clippy users.
 +* Clippy now follows the Rust versioning scheme instead of its own
 +* Fix ICE when encountering a `while let (..) = x.iter()` construct
 +* Fix false positives in `use_self`
 +* Fix false positive in `trivially_copy_pass_by_ref`
 +* Fix false positive in `useless_attribute` lint
 +* Fix false positive in `print_literal`
 +* Fix `use_self` regressions
 +* Improve lint message for `neg_cmp_op_on_partial_ord`
 +* Improve suggestion highlight for `single_char_pattern`
 +* Improve suggestions for various print/write macro lints
 +* Improve website header
 +
 +## 0.0.212 (2018-07-10)
 +* Rustup to *rustc 1.29.0-nightly (e06c87544 2018-07-06)*
 +
 +## 0.0.211
 +* Rustup to *rustc 1.28.0-nightly (e3bf634e0 2018-06-28)*
 +
 +## 0.0.210
 +* Rustup to *rustc 1.28.0-nightly (01cc982e9 2018-06-24)*
 +
 +## 0.0.209
 +* Rustup to *rustc 1.28.0-nightly (523097979 2018-06-18)*
 +
 +## 0.0.208
 +* Rustup to *rustc 1.28.0-nightly (86a8f1a63 2018-06-17)*
 +
 +## 0.0.207
 +* Rustup to *rustc 1.28.0-nightly (2a0062974 2018-06-09)*
 +
 +## 0.0.206
 +* Rustup to *rustc 1.28.0-nightly (5bf68db6e 2018-05-28)*
 +
 +## 0.0.205
 +* Rustup to *rustc 1.28.0-nightly (990d8aa74 2018-05-25)*
 +* Rename `unused_lifetimes` to `extra_unused_lifetimes` because of naming conflict with new rustc lint
 +
 +## 0.0.204
 +* Rustup to *rustc 1.28.0-nightly (71e87be38 2018-05-22)*
 +
 +## 0.0.203
 +* Rustup to *rustc 1.28.0-nightly (a3085756e 2018-05-19)*
 +* Clippy attributes are now of the form `clippy::cyclomatic_complexity` instead of `clippy(cyclomatic_complexity)`
 +
 +## 0.0.202
 +* Rustup to *rustc 1.28.0-nightly (952f344cd 2018-05-18)*
 +
 +## 0.0.201
 +* Rustup to *rustc 1.27.0-nightly (2f2a11dfc 2018-05-16)*
 +
 +## 0.0.200
 +* Rustup to *rustc 1.27.0-nightly (9fae15374 2018-05-13)*
 +
 +## 0.0.199
 +* Rustup to *rustc 1.27.0-nightly (ff2ac35db 2018-05-12)*
 +
 +## 0.0.198
 +* Rustup to *rustc 1.27.0-nightly (acd3871ba 2018-05-10)*
 +
 +## 0.0.197
 +* Rustup to *rustc 1.27.0-nightly (428ea5f6b 2018-05-06)*
 +
 +## 0.0.196
 +* Rustup to *rustc 1.27.0-nightly (e82261dfb 2018-05-03)*
 +
 +## 0.0.195
 +* Rustup to *rustc 1.27.0-nightly (ac3c2288f 2018-04-18)*
 +
 +## 0.0.194
 +* Rustup to *rustc 1.27.0-nightly (bd40cbbe1 2018-04-14)*
 +* New lints: [`cast_ptr_alignment`], [`transmute_ptr_to_ptr`], [`write_literal`], [`write_with_newline`], [`writeln_empty_string`]
 +
 +## 0.0.193
 +* Rustup to *rustc 1.27.0-nightly (eeea94c11 2018-04-06)*
 +
 +## 0.0.192
 +* Rustup to *rustc 1.27.0-nightly (fb44b4c0e 2018-04-04)*
 +* New lint: [`print_literal`]
 +
 +## 0.0.191
 +* Rustup to *rustc 1.26.0-nightly (ae544ee1c 2018-03-29)*
 +* Lint audit; categorize lints as style, correctness, complexity, pedantic, nursery, restriction.
 +
 +## 0.0.190
 +* Fix a bunch of intermittent cargo bugs
 +
 +## 0.0.189
 +* Rustup to *rustc 1.26.0-nightly (5508b2714 2018-03-18)*
 +
 +## 0.0.188
 +* Rustup to *rustc 1.26.0-nightly (392645394 2018-03-15)*
 +* New lint: [`while_immutable_condition`]
 +
 +## 0.0.187
 +* Rustup to *rustc 1.26.0-nightly (322d7f7b9 2018-02-25)*
 +* New lints: [`redundant_field_names`], [`suspicious_arithmetic_impl`], [`suspicious_op_assign_impl`]
 +
 +## 0.0.186
 +* Rustup to *rustc 1.25.0-nightly (0c6091fbd 2018-02-04)*
 +* Various false positive fixes
 +
 +## 0.0.185
 +* Rustup to *rustc 1.25.0-nightly (56733bc9f 2018-02-01)*
 +* New lint: [`question_mark`]
 +
 +## 0.0.184
 +* Rustup to *rustc 1.25.0-nightly (90eb44a58 2018-01-29)*
 +* New lints: [`double_comparisons`], [`empty_line_after_outer_attr`]
 +
 +## 0.0.183
 +* Rustup to *rustc 1.25.0-nightly (21882aad7 2018-01-28)*
 +* New lint: [`misaligned_transmute`]
 +
 +## 0.0.182
 +* Rustup to *rustc 1.25.0-nightly (a0dcecff9 2018-01-24)*
 +* New lint: [`decimal_literal_representation`]
 +
 +## 0.0.181
 +* Rustup to *rustc 1.25.0-nightly (97520ccb1 2018-01-21)*
 +* New lints: [`else_if_without_else`], [`option_option`], [`unit_arg`], [`unnecessary_fold`]
 +* Removed `unit_expr`
 +* Various false positive fixes for [`needless_pass_by_value`]
 +
 +## 0.0.180
 +* Rustup to *rustc 1.25.0-nightly (3f92e8d89 2018-01-14)*
 +
 +## 0.0.179
 +* Rustup to *rustc 1.25.0-nightly (61452e506 2018-01-09)*
 +
 +## 0.0.178
 +* Rustup to *rustc 1.25.0-nightly (ee220daca 2018-01-07)*
 +
 +## 0.0.177
 +* Rustup to *rustc 1.24.0-nightly (250b49205 2017-12-21)*
 +* New lint: [`match_as_ref`]
 +
 +## 0.0.176
 +* Rustup to *rustc 1.24.0-nightly (0077d128d 2017-12-14)*
 +
 +## 0.0.175
 +* Rustup to *rustc 1.24.0-nightly (bb42071f6 2017-12-01)*
 +
 +## 0.0.174
 +* Rustup to *rustc 1.23.0-nightly (63739ab7b 2017-11-21)*
 +
 +## 0.0.173
 +* Rustup to *rustc 1.23.0-nightly (33374fa9d 2017-11-20)*
 +
 +## 0.0.172
 +* Rustup to *rustc 1.23.0-nightly (d0f8e2913 2017-11-16)*
 +
 +## 0.0.171
 +* Rustup to *rustc 1.23.0-nightly (ff0f5de3b 2017-11-14)*
 +
 +## 0.0.170
 +* Rustup to *rustc 1.23.0-nightly (d6b06c63a 2017-11-09)*
 +
 +## 0.0.169
 +* Rustup to *rustc 1.23.0-nightly (3b82e4c74 2017-11-05)*
 +* New lints: [`just_underscores_and_digits`], `result_map_unwrap_or_else`, [`transmute_bytes_to_str`]
 +
 +## 0.0.168
 +* Rustup to *rustc 1.23.0-nightly (f0fe716db 2017-10-30)*
 +
 +## 0.0.167
 +* Rustup to *rustc 1.23.0-nightly (90ef3372e 2017-10-29)*
 +* New lints: `const_static_lifetime`, [`erasing_op`], [`fallible_impl_from`], [`println_empty_string`], [`useless_asref`]
 +
 +## 0.0.166
 +* Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)*
 +* New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], `invalid_ref`, [`option_map_or_none`],
 +  [`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`],
 +  [`transmute_int_to_float`]
 +
 +## 0.0.165
 +* Rust upgrade to rustc 1.22.0-nightly (0e6f4cf51 2017-09-27)
 +* New lint: [`mut_range_bound`]
 +
 +## 0.0.164
 +* Update to *rustc 1.22.0-nightly (6c476ce46 2017-09-25)*
 +* New lint: [`int_plus_one`]
 +
 +## 0.0.163
 +* Update to *rustc 1.22.0-nightly (14039a42a 2017-09-22)*
 +
 +## 0.0.162
 +* Update to *rustc 1.22.0-nightly (0701b37d9 2017-09-18)*
 +* New lint: [`chars_last_cmp`]
 +* Improved suggestions for [`needless_borrow`], [`ptr_arg`],
 +
 +## 0.0.161
 +* Update to *rustc 1.22.0-nightly (539f2083d 2017-09-13)*
 +
 +## 0.0.160
 +* Update to *rustc 1.22.0-nightly (dd08c3070 2017-09-12)*
 +
 +## 0.0.159
 +* Update to *rustc 1.22.0-nightly (eba374fb2 2017-09-11)*
 +* New lint: [`clone_on_ref_ptr`]
 +
 +## 0.0.158
 +* New lint: [`manual_memcpy`]
 +* [`cast_lossless`] no longer has redundant parentheses in its suggestions
 +* Update to *rustc 1.22.0-nightly (dead08cb3 2017-09-08)*
 +
 +## 0.0.157 - 2017-09-04
 +* Update to *rustc 1.22.0-nightly (981ce7d8d 2017-09-03)*
 +* New lint: `unit_expr`
 +
 +## 0.0.156 - 2017-09-03
 +* Update to *rustc 1.22.0-nightly (744dd6c1d 2017-09-02)*
 +
 +## 0.0.155
 +* Update to *rustc 1.21.0-nightly (c11f689d2 2017-08-29)*
 +* New lint: [`infinite_iter`], [`maybe_infinite_iter`], [`cast_lossless`]
 +
 +## 0.0.154
 +* Update to *rustc 1.21.0-nightly (2c0558f63 2017-08-24)*
 +* Fix [`use_self`] triggering inside derives
 +* Add support for linting an entire workspace with `cargo clippy --all`
 +* New lint: [`naive_bytecount`]
 +
 +## 0.0.153
 +* Update to *rustc 1.21.0-nightly (8c303ed87 2017-08-20)*
 +* New lint: [`use_self`]
 +
 +## 0.0.152
 +* Update to *rustc 1.21.0-nightly (df511d554 2017-08-14)*
 +
 +## 0.0.151
 +* Update to *rustc 1.21.0-nightly (13d94d5fa 2017-08-10)*
 +
 +## 0.0.150
 +* Update to *rustc 1.21.0-nightly (215e0b10e 2017-08-08)*
 +
 +## 0.0.148
 +* Update to *rustc 1.21.0-nightly (37c7d0ebb 2017-07-31)*
 +* New lints: [`unreadable_literal`], [`inconsistent_digit_grouping`], [`large_digit_groups`]
 +
 +## 0.0.147
 +* Update to *rustc 1.21.0-nightly (aac223f4f 2017-07-30)*
 +
 +## 0.0.146
 +* Update to *rustc 1.21.0-nightly (52a330969 2017-07-27)*
 +* Fixes false positives in `inline_always`
 +* Fixes false negatives in `panic_params`
 +
 +## 0.0.145
 +* Update to *rustc 1.20.0-nightly (afe145d22 2017-07-23)*
 +
 +## 0.0.144
 +* Update to *rustc 1.20.0-nightly (086eaa78e 2017-07-15)*
 +
 +## 0.0.143
 +* Update to *rustc 1.20.0-nightly (d84693b93 2017-07-09)*
 +* Fix `cargo clippy` crashing on `dylib` projects
 +* Fix false positives around `nested_while_let` and `never_loop`
 +
 +## 0.0.142
 +* Update to *rustc 1.20.0-nightly (067971139 2017-07-02)*
 +
 +## 0.0.141
 +* Rewrite of the `doc_markdown` lint.
 +* Deprecated [`range_step_by_zero`]
 +* New lint: [`iterator_step_by_zero`]
 +* New lint: [`needless_borrowed_reference`]
 +* Update to *rustc 1.20.0-nightly (69c65d296 2017-06-28)*
 +
 +## 0.0.140 - 2017-06-16
 +* Update to *rustc 1.19.0-nightly (258ae6dd9 2017-06-15)*
 +
 +## 0.0.139 — 2017-06-10
 +* Update to *rustc 1.19.0-nightly (4bf5c99af 2017-06-10)*
 +* Fix bugs with for loop desugaring
 +* Check for [`AsRef`]/[`AsMut`] arguments in [`wrong_self_convention`]
 +
 +## 0.0.138 — 2017-06-05
 +* Update to *rustc 1.19.0-nightly (0418fa9d3 2017-06-04)*
 +
 +## 0.0.137 — 2017-06-05
 +* Update to *rustc 1.19.0-nightly (6684d176c 2017-06-03)*
 +
 +## 0.0.136 — 2017—05—26
 +* Update to *rustc 1.19.0-nightly (557967766 2017-05-26)*
 +
 +## 0.0.135 — 2017—05—24
 +* Update to *rustc 1.19.0-nightly (5b13bff52 2017-05-23)*
 +
 +## 0.0.134 — 2017—05—19
 +* Update to *rustc 1.19.0-nightly (0ed1ec9f9 2017-05-18)*
 +
 +## 0.0.133 — 2017—05—14
 +* Update to *rustc 1.19.0-nightly (826d8f385 2017-05-13)*
 +
 +## 0.0.132 — 2017—05—05
 +* Fix various bugs and some ices
 +
 +## 0.0.131 — 2017—05—04
 +* Update to *rustc 1.19.0-nightly (2d4ed8e0c 2017-05-03)*
 +
 +## 0.0.130 — 2017—05—03
 +* Update to *rustc 1.19.0-nightly (6a5fc9eec 2017-05-02)*
 +
 +## 0.0.129 — 2017-05-01
 +* Update to *rustc 1.19.0-nightly (06fb4d256 2017-04-30)*
 +
 +## 0.0.128 — 2017-04-28
 +* Update to *rustc 1.18.0-nightly (94e884b63 2017-04-27)*
 +
 +## 0.0.127 — 2017-04-27
 +* Update to *rustc 1.18.0-nightly (036983201 2017-04-26)*
 +* New lint: [`needless_continue`]
 +
 +## 0.0.126 — 2017-04-24
 +* Update to *rustc 1.18.0-nightly (2bd4b5c6d 2017-04-23)*
 +
 +## 0.0.125 — 2017-04-19
 +* Update to *rustc 1.18.0-nightly (9f2abadca 2017-04-18)*
 +
 +## 0.0.124 — 2017-04-16
 +* Update to *rustc 1.18.0-nightly (d5cf1cb64 2017-04-15)*
 +
 +## 0.0.123 — 2017-04-07
 +* Fix various false positives
 +
 +## 0.0.122 — 2017-04-07
 +* Rustup to *rustc 1.18.0-nightly (91ae22a01 2017-04-05)*
 +* New lint: [`op_ref`]
 +
 +## 0.0.121 — 2017-03-21
 +* Rustup to *rustc 1.17.0-nightly (134c4a0f0 2017-03-20)*
 +
 +## 0.0.120 — 2017-03-17
 +* Rustup to *rustc 1.17.0-nightly (0aeb9c129 2017-03-15)*
 +
 +## 0.0.119 — 2017-03-13
 +* Rustup to *rustc 1.17.0-nightly (824c9ebbd 2017-03-12)*
 +
 +## 0.0.118 — 2017-03-05
 +* Rustup to *rustc 1.17.0-nightly (b1e31766d 2017-03-03)*
 +
 +## 0.0.117 — 2017-03-01
 +* Rustup to *rustc 1.17.0-nightly (be760566c 2017-02-28)*
 +
 +## 0.0.116 — 2017-02-28
 +* Fix `cargo clippy` on 64 bit windows systems
 +
 +## 0.0.115 — 2017-02-27
 +* Rustup to *rustc 1.17.0-nightly (60a0edc6c 2017-02-26)*
 +* New lints: [`zero_ptr`], [`never_loop`], [`mut_from_ref`]
 +
 +## 0.0.114 — 2017-02-08
 +* Rustup to *rustc 1.17.0-nightly (c49d10207 2017-02-07)*
 +* Tests are now ui tests (testing the exact output of rustc)
 +
 +## 0.0.113 — 2017-02-04
 +* Rustup to *rustc 1.16.0-nightly (eedaa94e3 2017-02-02)*
 +* New lint: [`large_enum_variant`]
 +* `explicit_into_iter_loop` provides suggestions
 +
 +## 0.0.112 — 2017-01-27
 +* Rustup to *rustc 1.16.0-nightly (df8debf6d 2017-01-25)*
 +
 +## 0.0.111 — 2017-01-21
 +* Rustup to *rustc 1.16.0-nightly (a52da95ce 2017-01-20)*
 +
 +## 0.0.110 — 2017-01-20
 +* Add badges and categories to `Cargo.toml`
 +
 +## 0.0.109 — 2017-01-19
 +* Update to *rustc 1.16.0-nightly (c07a6ae77 2017-01-17)*
 +
 +## 0.0.108 — 2017-01-12
 +* Update to *rustc 1.16.0-nightly (2782e8f8f 2017-01-12)*
 +
 +## 0.0.107 — 2017-01-11
 +* Update regex dependency
 +* Fix FP when matching `&&mut` by `&ref`
 +* Reintroduce `for (_, x) in &mut hash_map` -> `for x in hash_map.values_mut()`
 +* New lints: [`unused_io_amount`], [`forget_ref`], [`short_circuit_statement`]
 +
 +## 0.0.106 — 2017-01-04
 +* Fix FP introduced by rustup in [`wrong_self_convention`]
 +
 +## 0.0.105 — 2017-01-04
 +* Update to *rustc 1.16.0-nightly (468227129 2017-01-03)*
 +* New lints: [`deref_addrof`], [`double_parens`], [`pub_enum_variant_names`]
 +* Fix suggestion in [`new_without_default`]
 +* FP fix in [`absurd_extreme_comparisons`]
 +
 +## 0.0.104 — 2016-12-15
 +* Update to *rustc 1.15.0-nightly (8f02c429a 2016-12-15)*
 +
 +## 0.0.103 — 2016-11-25
 +* Update to *rustc 1.15.0-nightly (d5814b03e 2016-11-23)*
 +
 +## 0.0.102 — 2016-11-24
 +* Update to *rustc 1.15.0-nightly (3bf2be9ce 2016-11-22)*
 +
 +## 0.0.101 — 2016-11-23
 +* Update to *rustc 1.15.0-nightly (7b3eeea22 2016-11-21)*
 +* New lint: [`string_extend_chars`]
 +
 +## 0.0.100 — 2016-11-20
 +* Update to *rustc 1.15.0-nightly (ac635aa95 2016-11-18)*
 +
 +## 0.0.99 — 2016-11-18
 +* Update to rustc 1.15.0-nightly (0ed951993 2016-11-14)
 +* New lint: [`get_unwrap`]
 +
 +## 0.0.98 — 2016-11-08
 +* Fixes an issue due to a change in how cargo handles `--sysroot`, which broke `cargo clippy`
 +
 +## 0.0.97 — 2016-11-03
 +* For convenience, `cargo clippy` defines a `cargo-clippy` feature. This was
 +  previously added for a short time under the name `clippy` but removed for
 +  compatibility.
 +* `cargo clippy --help` is more helping (and less helpful :smile:)
 +* Rustup to *rustc 1.14.0-nightly (5665bdf3e 2016-11-02)*
 +* New lints: [`if_let_redundant_pattern_matching`], [`partialeq_ne_impl`]
 +
 +## 0.0.96 — 2016-10-22
 +* Rustup to *rustc 1.14.0-nightly (f09420685 2016-10-20)*
 +* New lint: [`iter_skip_next`]
 +
 +## 0.0.95 — 2016-10-06
 +* Rustup to *rustc 1.14.0-nightly (3210fd5c2 2016-10-05)*
 +
 +## 0.0.94 — 2016-10-04
 +* Fixes bustage on Windows due to forbidden directory name
 +
 +## 0.0.93 — 2016-10-03
 +* Rustup to *rustc 1.14.0-nightly (144af3e97 2016-10-02)*
 +* `option_map_unwrap_or` and `option_map_unwrap_or_else` are now
 +  allowed by default.
 +* New lint: [`explicit_into_iter_loop`]
 +
 +## 0.0.92 — 2016-09-30
 +* Rustup to *rustc 1.14.0-nightly (289f3a4ca 2016-09-29)*
 +
 +## 0.0.91 — 2016-09-28
 +* Rustup to *rustc 1.13.0-nightly (d0623cf7b 2016-09-26)*
 +
 +## 0.0.90 — 2016-09-09
 +* Rustup to *rustc 1.13.0-nightly (f1f40f850 2016-09-09)*
 +
 +## 0.0.89 — 2016-09-06
 +* Rustup to *rustc 1.13.0-nightly (cbe4de78e 2016-09-05)*
 +
 +## 0.0.88 — 2016-09-04
 +* Rustup to *rustc 1.13.0-nightly (70598e04f 2016-09-03)*
 +* The following lints are not new but were only usable through the `clippy`
 +  lint groups: [`filter_next`], `for_loop_over_option`,
 +  `for_loop_over_result` and [`match_overlapping_arm`]. You should now be
 +  able to `#[allow/deny]` them individually and they are available directly
 +  through `cargo clippy`.
 +
 +## 0.0.87 — 2016-08-31
 +* Rustup to *rustc 1.13.0-nightly (eac41469d 2016-08-30)*
 +* New lints: [`builtin_type_shadow`]
 +* Fix FP in [`zero_prefixed_literal`] and `0b`/`0o`
 +
 +## 0.0.86 — 2016-08-28
 +* Rustup to *rustc 1.13.0-nightly (a23064af5 2016-08-27)*
 +* New lints: [`missing_docs_in_private_items`], [`zero_prefixed_literal`]
 +
 +## 0.0.85 — 2016-08-19
 +* Fix ICE with [`useless_attribute`]
 +* [`useless_attribute`] ignores `unused_imports` on `use` statements
 +
 +## 0.0.84 — 2016-08-18
 +* Rustup to *rustc 1.13.0-nightly (aef6971ca 2016-08-17)*
 +
 +## 0.0.83 — 2016-08-17
 +* Rustup to *rustc 1.12.0-nightly (1bf5fa326 2016-08-16)*
 +* New lints: [`print_with_newline`], [`useless_attribute`]
 +
 +## 0.0.82 — 2016-08-17
 +* Rustup to *rustc 1.12.0-nightly (197be89f3 2016-08-15)*
 +* New lint: [`module_inception`]
 +
 +## 0.0.81 — 2016-08-14
 +* Rustup to *rustc 1.12.0-nightly (1deb02ea6 2016-08-12)*
 +* New lints: [`eval_order_dependence`], [`mixed_case_hex_literals`], [`unseparated_literal_suffix`]
 +* False positive fix in [`too_many_arguments`]
 +* Addition of functionality to [`needless_borrow`]
 +* Suggestions for [`clone_on_copy`]
 +* Bug fix in [`wrong_self_convention`]
 +* Doc improvements
 +
 +## 0.0.80 — 2016-07-31
 +* Rustup to *rustc 1.12.0-nightly (1225e122f 2016-07-30)*
 +* New lints: [`misrefactored_assign_op`], [`serde_api_misuse`]
 +
 +## 0.0.79 — 2016-07-10
 +* Rustup to *rustc 1.12.0-nightly (f93aaf84c 2016-07-09)*
 +* Major suggestions refactoring
 +
 +## 0.0.78 — 2016-07-02
 +* Rustup to *rustc 1.11.0-nightly (01411937f 2016-07-01)*
 +* New lints: [`wrong_transmute`], [`double_neg`], [`filter_map`]
 +* For compatibility, `cargo clippy` does not defines the `clippy` feature
 +  introduced in 0.0.76 anymore
 +* [`collapsible_if`] now considers `if let`
 +
 +## 0.0.77 — 2016-06-21
 +* Rustup to *rustc 1.11.0-nightly (5522e678b 2016-06-20)*
 +* New lints: `stutter` and [`iter_nth`]
 +
 +## 0.0.76 — 2016-06-10
 +* Rustup to *rustc 1.11.0-nightly (7d2f75a95 2016-06-09)*
 +* `cargo clippy` now automatically defines the `clippy` feature
 +* New lint: [`not_unsafe_ptr_arg_deref`]
 +
 +## 0.0.75 — 2016-06-08
 +* Rustup to *rustc 1.11.0-nightly (763f9234b 2016-06-06)*
 +
 +## 0.0.74 — 2016-06-07
 +* Fix bug with `cargo-clippy` JSON parsing
 +* Add the `CLIPPY_DISABLE_DOCS_LINKS` environment variable to deactivate the
 +  “for further information visit *lint-link*” message.
 +
 +## 0.0.73 — 2016-06-05
 +* Fix false positives in [`useless_let_if_seq`]
 +
 +## 0.0.72 — 2016-06-04
 +* Fix false positives in [`useless_let_if_seq`]
 +
 +## 0.0.71 — 2016-05-31
 +* Rustup to *rustc 1.11.0-nightly (a967611d8 2016-05-30)*
 +* New lint: [`useless_let_if_seq`]
 +
 +## 0.0.70 — 2016-05-28
 +* Rustup to *rustc 1.10.0-nightly (7bddce693 2016-05-27)*
 +* [`invalid_regex`] and [`trivial_regex`] can now warn on `RegexSet::new`,
 +  `RegexBuilder::new` and byte regexes
 +
 +## 0.0.69 — 2016-05-20
 +* Rustup to *rustc 1.10.0-nightly (476fe6eef 2016-05-21)*
 +* [`used_underscore_binding`] has been made `Allow` temporarily
 +
 +## 0.0.68 — 2016-05-17
 +* Rustup to *rustc 1.10.0-nightly (cd6a40017 2016-05-16)*
 +* New lint: [`unnecessary_operation`]
 +
 +## 0.0.67 — 2016-05-12
 +* Rustup to *rustc 1.10.0-nightly (22ac88f1a 2016-05-11)*
 +
 +## 0.0.66 — 2016-05-11
 +* New `cargo clippy` subcommand
 +* New lints: [`assign_op_pattern`], [`assign_ops`], [`needless_borrow`]
 +
 +## 0.0.65 — 2016-05-08
 +* Rustup to *rustc 1.10.0-nightly (62e2b2fb7 2016-05-06)*
 +* New lints: [`float_arithmetic`], [`integer_arithmetic`]
 +
 +## 0.0.64 — 2016-04-26
 +* Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)*
 +* New lints: `temporary_cstring_as_ptr`, [`unsafe_removed_from_name`], and [`mem_forget`]
 +
 +## 0.0.63 — 2016-04-08
 +* Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)*
 +
 +## 0.0.62 — 2016-04-07
 +* Rustup to *rustc 1.9.0-nightly (bf5da36f1 2016-04-06)*
 +
 +## 0.0.61 — 2016-04-03
 +* Rustup to *rustc 1.9.0-nightly (5ab11d72c 2016-04-02)*
 +* New lint: [`invalid_upcast_comparisons`]
 +
 +## 0.0.60 — 2016-04-01
 +* Rustup to *rustc 1.9.0-nightly (e1195c24b 2016-03-31)*
 +
 +## 0.0.59 — 2016-03-31
 +* Rustup to *rustc 1.9.0-nightly (30a3849f2 2016-03-30)*
 +* New lints: [`logic_bug`], [`nonminimal_bool`]
 +* Fixed: [`match_same_arms`] now ignores arms with guards
 +* Improved: [`useless_vec`] now warns on `for … in vec![…]`
 +
 +## 0.0.58 — 2016-03-27
 +* Rustup to *rustc 1.9.0-nightly (d5a91e695 2016-03-26)*
 +* New lint: [`doc_markdown`]
 +
 +## 0.0.57 — 2016-03-27
 +* Update to *rustc 1.9.0-nightly (a1e29daf1 2016-03-25)*
 +* Deprecated lints: [`str_to_string`], [`string_to_string`], [`unstable_as_slice`], [`unstable_as_mut_slice`]
 +* New lint: [`crosspointer_transmute`]
 +
 +## 0.0.56 — 2016-03-23
 +* Update to *rustc 1.9.0-nightly (0dcc413e4 2016-03-22)*
 +* New lints: [`many_single_char_names`] and [`similar_names`]
 +
 +## 0.0.55 — 2016-03-21
 +* Update to *rustc 1.9.0-nightly (02310fd31 2016-03-19)*
 +
 +## 0.0.54 — 2016-03-16
 +* Update to *rustc 1.9.0-nightly (c66d2380a 2016-03-15)*
 +
 +## 0.0.53 — 2016-03-15
 +* Add a [configuration file]
 +
 +## ~~0.0.52~~
 +
 +## 0.0.51 — 2016-03-13
 +* Add `str` to types considered by [`len_zero`]
 +* New lints: [`indexing_slicing`]
 +
 +## 0.0.50 — 2016-03-11
 +* Update to *rustc 1.9.0-nightly (c9629d61c 2016-03-10)*
 +
 +## 0.0.49 — 2016-03-09
 +* Update to *rustc 1.9.0-nightly (eabfc160f 2016-03-08)*
 +* New lints: [`overflow_check_conditional`], `unused_label`, [`new_without_default`]
 +
 +## 0.0.48 — 2016-03-07
 +* Fixed: ICE in [`needless_range_loop`] with globals
 +
 +## 0.0.47 — 2016-03-07
 +* Update to *rustc 1.9.0-nightly (998a6720b 2016-03-07)*
 +* New lint: [`redundant_closure_call`]
 +
 +[`AsMut`]: https://doc.rust-lang.org/std/convert/trait.AsMut.html
 +[`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html
 +[configuration file]: ./rust-clippy#configuration
 +[pull3665]: https://github.com/rust-lang/rust-clippy/pull/3665
 +[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/adding_lints.md
 +[`README.md`]: https://github.com/rust-lang/rust-clippy/blob/master/README.md
 +
 +<!-- lint disable no-unused-definitions -->
 +<!-- begin autogenerated links to lint list -->
 +[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
 +[`alloc_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#alloc_instead_of_core
 +[`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason
 +[`almost_complete_letter_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_letter_range
 +[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
 +[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
++[`arithmetic_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects
 +[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
 +[`as_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_underscore
 +[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
 +[`assertions_on_result_states`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states
 +[`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern
 +[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
 +[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async
 +[`await_holding_invalid_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type
 +[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
 +[`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref
 +[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
 +[`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map
 +[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
 +[`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints
 +[`block_in_if_condition_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_expr
 +[`block_in_if_condition_stmt`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_stmt
 +[`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions
 +[`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
 +[`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
++[`bool_to_int_with_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_to_int_with_if
 +[`borrow_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr
 +[`borrow_deref_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_deref_ref
 +[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
 +[`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
 +[`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection
 +[`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec
 +[`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local
 +[`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code
 +[`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow
 +[`bytes_count_to_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_count_to_len
 +[`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth
 +[`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
 +[`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons
 +[`cast_abs_to_unsigned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned
 +[`cast_enum_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_constructor
 +[`cast_enum_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_truncation
 +[`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless
 +[`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation
 +[`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap
 +[`cast_precision_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_precision_loss
 +[`cast_ptr_alignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ptr_alignment
 +[`cast_ref_to_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ref_to_mut
 +[`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss
 +[`cast_slice_different_sizes`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_different_sizes
 +[`cast_slice_from_raw_parts`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_from_raw_parts
 +[`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8
 +[`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
 +[`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
 +[`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions
 +[`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref
 +[`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy
 +[`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr
 +[`cloned_instead_of_copied`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied
 +[`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan
 +[`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null
 +[`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned
 +[`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity
 +[`collapsible_else_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if
 +[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
 +[`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
 +[`collapsible_str_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace
 +[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
 +[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
 +[`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime
 +[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
 +[`crate_in_macro_def`]: https://rust-lang.github.io/rust-clippy/master/index.html#crate_in_macro_def
 +[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir
 +[`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute
 +[`cyclomatic_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cyclomatic_complexity
 +[`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro
 +[`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
 +[`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
 +[`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const
 +[`default_instead_of_iter_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_instead_of_iter_empty
 +[`default_numeric_fallback`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_numeric_fallback
 +[`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access
 +[`default_union_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_union_representation
 +[`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr
 +[`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver
 +[`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof
 +[`deref_by_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_by_slicing
 +[`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
 +[`derive_partial_eq_without_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq
 +[`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_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names
 +[`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_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes
 +[`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
 +[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
 +[`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg
 +[`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens
 +[`drop_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_bounds
 +[`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
 +[`drop_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_non_drop
 +[`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref
 +[`duplicate_mod`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_mod
 +[`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
 +[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
 +[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
 +[`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop
 +[`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
 +[`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
 +[`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop
 +[`empty_structs_with_brackets`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_structs_with_brackets
 +[`enum_clike_unportable_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_clike_unportable_variant
 +[`enum_glob_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_glob_use
 +[`enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names
 +[`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op
 +[`equatable_if_let`]: https://rust-lang.github.io/rust-clippy/master/index.html#equatable_if_let
 +[`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op
 +[`err_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#err_expect
 +[`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence
 +[`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision
 +[`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums
 +[`exhaustive_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_structs
 +[`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit
 +[`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call
 +[`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used
 +[`expl_impl_clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#expl_impl_clone_on_copy
 +[`explicit_auto_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_auto_deref
 +[`explicit_counter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_counter_loop
 +[`explicit_deref_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_deref_methods
 +[`explicit_into_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_into_iter_loop
 +[`explicit_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop
 +[`explicit_write`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_write
 +[`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice
 +[`extend_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_with_drain
 +[`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes
 +[`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from
 +[`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default
 +[`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file
 +[`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map
 +[`filter_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_identity
 +[`filter_map_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next
 +[`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next
 +[`find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#find_map
 +[`flat_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_identity
 +[`flat_map_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_option
 +[`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic
 +[`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp
 +[`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const
 +[`float_equality_without_abs`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_equality_without_abs
 +[`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons
 +[`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools
 +[`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast
 +[`fn_to_numeric_cast_any`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_any
 +[`fn_to_numeric_cast_with_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_with_truncation
 +[`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map
 +[`for_loop_over_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_option
 +[`for_loop_over_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_result
 +[`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles
 +[`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy
 +[`forget_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_non_drop
 +[`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
 +[`format_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_in_format_args
 +[`format_push_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string
 +[`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect
 +[`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into
 +[`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10
 +[`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send
 +[`get_first`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_first
 +[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
 +[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
 +[`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion
 +[`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op
 +[`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex
 +[`if_let_redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_redundant_pattern_matching
 +[`if_let_some_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_some_result
 +[`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else
 +[`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else
 +[`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none
 +[`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond
 +[`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone
 +[`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher
 +[`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return
 +[`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub
 +[`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
 +[`infallible_destructuring_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#infallible_destructuring_match
 +[`infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_iter
 +[`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string
 +[`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display
 +[`init_numbered_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#init_numbered_fields
 +[`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always
 +[`inline_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax
 +[`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax
 +[`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body
 +[`inspect_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#inspect_for_each
 +[`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
 +[`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic
 +[`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division
 +[`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array
 +[`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
 +[`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering
 +[`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage
 +[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref
 +[`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
 +[`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
 +[`invalid_utf8_in_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_utf8_in_unchecked
 +[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
 +[`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix
 +[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
 +[`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect
 +[`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count
 +[`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop
 +[`iter_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice
 +[`iter_not_returning_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_not_returning_iterator
 +[`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth
 +[`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
 +[`iter_on_empty_collections`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_empty_collections
 +[`iter_on_single_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_single_items
 +[`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned
 +[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
 +[`iter_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_with_drain
 +[`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero
 +[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits
 +[`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays
 +[`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups
 +[`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant
 +[`large_include_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file
 +[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
 +[`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value
 +[`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
 +[`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero
 +[`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return
 +[`let_underscore_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_drop
 +[`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock
 +[`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use
 +[`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
 +[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
 +[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
 +[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
 +[`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports
 +[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
 +[`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert
 +[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
 +[`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits
 +[`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
 +[`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find
 +[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
 +[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
 +[`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed
 +[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
 +[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
 +[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
 +[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
 +[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
 +[`manual_rem_euclid`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid
 +[`manual_retain`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain
 +[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
 +[`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once
 +[`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat
 +[`manual_string_new`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_string_new
 +[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
 +[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
 +[`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or
 +[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
 +[`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone
 +[`map_collect_result_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_collect_result_unit
 +[`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry
 +[`map_err_ignore`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_err_ignore
 +[`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten
 +[`map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_identity
 +[`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or
 +[`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref
 +[`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool
 +[`match_like_matches_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro
 +[`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items
 +[`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm
 +[`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats
 +[`match_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_result_ok
 +[`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms
 +[`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding
 +[`match_str_case_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_str_case_mismatch
 +[`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm
 +[`match_wildcard_for_single_variants`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants
 +[`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter
 +[`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum
 +[`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget
 +[`mem_replace_option_with_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_none
 +[`mem_replace_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default
 +[`mem_replace_with_uninit`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_uninit
 +[`min_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_max
 +[`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute
 +[`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os
 +[`mismatching_type_param_order`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatching_type_param_order
 +[`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
 +[`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
 +[`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
 +[`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames
 +[`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc
 +[`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items
 +[`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc
 +[`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
 +[`missing_spin_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_spin_loop
 +[`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes
 +[`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals
 +[`mixed_read_write_in_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_read_write_in_expression
 +[`mod_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#mod_module_files
 +[`module_inception`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_inception
 +[`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions
 +[`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic
 +[`modulo_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_one
 +[`multi_assignments`]: https://rust-lang.github.io/rust-clippy/master/index.html#multi_assignments
 +[`multiple_crate_versions`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions
 +[`multiple_inherent_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl
 +[`must_use_candidate`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate
 +[`must_use_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_unit
 +[`mut_from_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_from_ref
 +[`mut_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mut
 +[`mut_mutex_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mutex_lock
 +[`mut_range_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_range_bound
 +[`mutable_key_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type
 +[`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic
 +[`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer
 +[`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount
 +[`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type
 +[`needless_bitwise_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bitwise_bool
 +[`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool
 +[`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
 +[`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
 +[`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect
 +[`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_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match
 +[`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref
 +[`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take
 +[`needless_parens_on_range_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_parens_on_range_literals
 +[`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
 +[`negative_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#negative_feature_names
 +[`never_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#never_loop
 +[`new_ret_no_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self
 +[`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default
 +[`new_without_default_derive`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default_derive
 +[`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect
 +[`no_effect_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_replace
 +[`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding
 +[`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal
 +[`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions
 +[`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty
 +[`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool
 +[`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
 +[`obfuscated_if_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#obfuscated_if_else
 +[`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
 +[`only_used_in_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#only_used_in_recursion
 +[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
 +[`option_and_then_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_and_then_some
 +[`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
 +[`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap
 +[`option_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_expect_used
 +[`option_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_filter_map
 +[`option_if_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
 +[`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none
 +[`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn
 +[`option_map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or
 +[`option_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or_else
 +[`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option
 +[`option_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_unwrap_used
 +[`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call
 +[`or_then_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_then_unwrap
 +[`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing
 +[`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional
 +[`overly_complex_bool_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#overly_complex_bool_expr
 +[`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
 +[`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn
 +[`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params
 +[`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
 +[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
 +[`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none
 +[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
 +[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
 +[`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
 +[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
 +[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
 +[`print_in_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_in_format_impl
 +[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
 +[`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr
 +[`print_stdout`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout
 +[`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline
 +[`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string
 +[`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg
 +[`ptr_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr
 +[`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq
 +[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
 +[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
 +[`pub_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_use
 +[`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark
 +[`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one
 +[`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
 +[`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero
 +[`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len
 +[`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer
 +[`rc_clone_in_vec_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_clone_in_vec_init
 +[`rc_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex
 +[`read_zero_byte_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#read_zero_byte_vec
 +[`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl
 +[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
 +[`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
 +[`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
 +[`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
 +[`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls
 +[`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else
 +[`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names
 +[`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names
 +[`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern
 +[`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching
 +[`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate
 +[`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing
 +[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
 +[`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference
 +[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
 +[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
 +[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
 +[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
 +[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
 +[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
 +[`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used
 +[`result_large_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err
 +[`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option
 +[`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn
 +[`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else
 +[`result_unit_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err
 +[`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used
 +[`return_self_not_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#return_self_not_must_use
 +[`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges
 +[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
 +[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push
 +[`same_name_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_name_method
 +[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
 +[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment
 +[`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors
 +[`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files
 +[`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned
 +[`separated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#separated_literal_suffix
 +[`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse
 +[`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse
 +[`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same
 +[`shadow_unrelated`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
 +[`short_circuit_statement`]: https://rust-lang.github.io/rust-clippy/master/index.html#short_circuit_statement
 +[`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq
 +[`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait
 +[`significant_drop_in_scrutinee`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_in_scrutinee
 +[`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
 +[`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str
 +[`single_char_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_lifetime_names
 +[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
 +[`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str
 +[`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports
 +[`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop
 +[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
 +[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
 +[`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count
 +[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
 +[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
 +[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
 +[`std_instead_of_alloc`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_alloc
 +[`std_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_core
 +[`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string
 +[`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add
 +[`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign
 +[`string_extend_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_extend_chars
 +[`string_from_utf8_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_from_utf8_as_bytes
 +[`string_lit_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_lit_as_bytes
 +[`string_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_slice
 +[`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string
 +[`strlen_on_c_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#strlen_on_c_strings
 +[`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools
 +[`stutter`]: https://rust-lang.github.io/rust-clippy/master/index.html#stutter
 +[`suboptimal_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#suboptimal_flops
 +[`suspicious_arithmetic_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl
 +[`suspicious_assignment_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_assignment_formatting
 +[`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting
 +[`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map
 +[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
 +[`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings
 +[`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn
 +[`suspicious_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_to_owned
 +[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
 +[`swap_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_ptr_to_ref
 +[`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
 +[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
 +[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
 +[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
 +[`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display
 +[`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args
 +[`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
 +[`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
 +[`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines
 +[`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg
 +[`trailing_empty_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#trailing_empty_array
 +[`trait_duplication_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds
 +[`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str
 +[`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int
 +[`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool
 +[`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char
 +[`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float
 +[`transmute_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes
 +[`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr
 +[`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref
 +[`transmute_undefined_repr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_undefined_repr
 +[`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts
 +[`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null
 +[`trim_split_whitespace`]: https://rust-lang.github.io/rust-clippy/master/index.html#trim_split_whitespace
 +[`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex
 +[`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
 +[`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err
 +[`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity
 +[`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds
 +[`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks
 +[`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops
 +[`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc
 +[`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented
 +[`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init
 +[`uninit_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_vec
 +[`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg
 +[`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp
 +[`unit_hash`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_hash
 +[`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord
 +[`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints
 +[`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
 +[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
 +[`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map
 +[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
 +[`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join
 +[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
 +[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
 +[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
 +[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings
 +[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
 +[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
 +[`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned
 +[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
 +[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
 +[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern
 +[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
 +[`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns
 +[`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable
 +[`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal
 +[`unsafe_derive_deserialize`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_derive_deserialize
 +[`unsafe_removed_from_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_removed_from_name
 +[`unsafe_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_vector_initialization
 +[`unseparated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#unseparated_literal_suffix
 +[`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute
 +[`unstable_as_mut_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_mut_slice
 +[`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice
 +[`unused_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async
 +[`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
 +[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
 +[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
 +[`unused_peekable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_peekable
 +[`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding
 +[`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
 +[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
 +[`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings
 +[`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result
 +[`unwrap_or_else_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_or_else_default
 +[`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used
 +[`upper_case_acronyms`]: https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms
 +[`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug
 +[`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self
 +[`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding
 +[`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref
 +[`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute
 +[`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion
 +[`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format
 +[`useless_let_if_seq`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq
 +[`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute
 +[`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec
 +[`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box
 +[`vec_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_init_then_push
 +[`vec_resize_to_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_resize_to_zero
 +[`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask
 +[`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads
 +[`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons
 +[`while_immutable_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_immutable_condition
 +[`while_let_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_loop
 +[`while_let_on_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_on_iterator
 +[`wildcard_dependencies`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_dependencies
 +[`wildcard_enum_match_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_enum_match_arm
 +[`wildcard_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports
 +[`wildcard_in_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_in_or_patterns
 +[`write_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_literal
 +[`write_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_with_newline
 +[`writeln_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#writeln_empty_string
 +[`wrong_pub_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_pub_self_convention
 +[`wrong_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention
 +[`wrong_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_transmute
 +[`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero
 +[`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal
 +[`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr
 +[`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values
 +[`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space
 +[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
 +<!-- end autogenerated links to lint list -->
index c503142e5e4552b1cdc28877dca9ab3734ba2083,0000000000000000000000000000000000000000..b95061bf81a25e3b5290a95571cf50d87f0e235f
mode 100644,000000..100644
--- /dev/null
@@@ -1,1277 -1,0 +1,1413 @@@
- use std::collections::{HashMap, HashSet};
 +use crate::clippy_project_root;
 +use aho_corasick::AhoCorasickBuilder;
 +use indoc::writedoc;
 +use itertools::Itertools;
 +use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
-     fn new(name: &str, group: &str, desc: &str, module: &str, declaration_range: Range<usize>) -> Self {
++use std::collections::{BTreeSet, HashMap, HashSet};
 +use std::ffi::OsStr;
 +use std::fmt::Write;
 +use std::fs::{self, OpenOptions};
 +use std::io::{self, Read, Seek, SeekFrom, Write as _};
 +use std::ops::Range;
 +use std::path::{Path, PathBuf};
 +use walkdir::{DirEntry, WalkDir};
 +
 +const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\
 +     // Use that command to update this file and do not edit by hand.\n\
 +     // Manual edits will be overwritten.\n\n";
 +
 +const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
 +
 +#[derive(Clone, Copy, PartialEq, Eq)]
 +pub enum UpdateMode {
 +    Check,
 +    Change,
 +}
 +
 +/// Runs the `update_lints` command.
 +///
 +/// This updates various generated values from the lint source code.
 +///
 +/// `update_mode` indicates if the files should be updated or if updates should be checked for.
 +///
 +/// # Panics
 +///
 +/// Panics if a file path could not read from or then written to
 +pub fn update(update_mode: UpdateMode) {
 +    let (lints, deprecated_lints, renamed_lints) = gather_all();
 +    generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints);
 +}
 +
 +fn generate_lint_files(
 +    update_mode: UpdateMode,
 +    lints: &[Lint],
 +    deprecated_lints: &[DeprecatedLint],
 +    renamed_lints: &[RenamedLint],
 +) {
 +    let internal_lints = Lint::internal_lints(lints);
 +    let usable_lints = Lint::usable_lints(lints);
 +    let mut sorted_usable_lints = usable_lints.clone();
 +    sorted_usable_lints.sort_by_key(|lint| lint.name.clone());
 +
 +    replace_region_in_file(
 +        update_mode,
 +        Path::new("README.md"),
 +        "[There are over ",
 +        " lints included in this crate!]",
 +        |res| {
 +            write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap();
 +        },
 +    );
 +
 +    replace_region_in_file(
 +        update_mode,
 +        Path::new("book/src/README.md"),
 +        "[There are over ",
 +        " lints included in this crate!]",
 +        |res| {
 +            write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap();
 +        },
 +    );
 +
 +    replace_region_in_file(
 +        update_mode,
 +        Path::new("CHANGELOG.md"),
 +        "<!-- begin autogenerated links to lint list -->\n",
 +        "<!-- end autogenerated links to lint list -->",
 +        |res| {
 +            for lint in usable_lints
 +                .iter()
 +                .map(|l| &*l.name)
 +                .chain(deprecated_lints.iter().map(|l| &*l.name))
 +                .chain(
 +                    renamed_lints
 +                        .iter()
 +                        .map(|l| l.old_name.strip_prefix("clippy::").unwrap_or(&l.old_name)),
 +                )
 +                .sorted()
 +            {
 +                writeln!(res, "[`{}`]: {}#{}", lint, DOCS_LINK, lint).unwrap();
 +            }
 +        },
 +    );
 +
 +    // This has to be in lib.rs, otherwise rustfmt doesn't work
 +    replace_region_in_file(
 +        update_mode,
 +        Path::new("clippy_lints/src/lib.rs"),
 +        "// begin lints modules, do not remove this comment, it’s used in `update_lints`\n",
 +        "// end lints modules, do not remove this comment, it’s used in `update_lints`",
 +        |res| {
 +            for lint_mod in usable_lints.iter().map(|l| &l.module).unique().sorted() {
 +                writeln!(res, "mod {};", lint_mod).unwrap();
 +            }
 +        },
 +    );
 +
 +    process_file(
 +        "clippy_lints/src/lib.register_lints.rs",
 +        update_mode,
 +        &gen_register_lint_list(internal_lints.iter(), usable_lints.iter()),
 +    );
 +    process_file(
 +        "clippy_lints/src/lib.deprecated.rs",
 +        update_mode,
 +        &gen_deprecated(deprecated_lints),
 +    );
 +
 +    let all_group_lints = usable_lints.iter().filter(|l| {
 +        matches!(
 +            &*l.group,
 +            "correctness" | "suspicious" | "style" | "complexity" | "perf"
 +        )
 +    });
 +    let content = gen_lint_group_list("all", all_group_lints);
 +    process_file("clippy_lints/src/lib.register_all.rs", update_mode, &content);
 +
++    update_docs(update_mode, &usable_lints);
++
 +    for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) {
 +        let content = gen_lint_group_list(&lint_group, lints.iter());
 +        process_file(
 +            &format!("clippy_lints/src/lib.register_{}.rs", lint_group),
 +            update_mode,
 +            &content,
 +        );
 +    }
 +
 +    let content = gen_deprecated_lints_test(deprecated_lints);
 +    process_file("tests/ui/deprecated.rs", update_mode, &content);
 +
 +    let content = gen_renamed_lints_test(renamed_lints);
 +    process_file("tests/ui/rename.rs", update_mode, &content);
 +}
 +
++fn update_docs(update_mode: UpdateMode, usable_lints: &[Lint]) {
++    replace_region_in_file(update_mode, Path::new("src/docs.rs"), "docs! {\n", "\n}\n", |res| {
++        for name in usable_lints.iter().map(|lint| lint.name.clone()).sorted() {
++            writeln!(res, r#"    "{name}","#).unwrap();
++        }
++    });
++
++    if update_mode == UpdateMode::Check {
++        let mut extra = BTreeSet::new();
++        let mut lint_names = usable_lints
++            .iter()
++            .map(|lint| lint.name.clone())
++            .collect::<BTreeSet<_>>();
++        for file in std::fs::read_dir("src/docs").unwrap() {
++            let filename = file.unwrap().file_name().into_string().unwrap();
++            if let Some(name) = filename.strip_suffix(".txt") {
++                if !lint_names.remove(name) {
++                    extra.insert(name.to_string());
++                }
++            }
++        }
++
++        let failed = print_lint_names("extra lint docs:", &extra) | print_lint_names("missing lint docs:", &lint_names);
++
++        if failed {
++            exit_with_failure();
++        }
++    } else {
++        if std::fs::remove_dir_all("src/docs").is_err() {
++            eprintln!("could not remove src/docs directory");
++        }
++        if std::fs::create_dir("src/docs").is_err() {
++            eprintln!("could not recreate src/docs directory");
++        }
++    }
++    for lint in usable_lints {
++        process_file(
++            Path::new("src/docs").join(lint.name.clone() + ".txt"),
++            update_mode,
++            &lint.documentation,
++        );
++    }
++}
++
++fn print_lint_names(header: &str, lints: &BTreeSet<String>) -> bool {
++    if lints.is_empty() {
++        return false;
++    }
++    println!("{}", header);
++    for lint in lints.iter().sorted() {
++        println!("    {}", lint);
++    }
++    println!();
++    true
++}
++
 +pub fn print_lints() {
 +    let (lint_list, _, _) = gather_all();
 +    let usable_lints = Lint::usable_lints(&lint_list);
 +    let usable_lint_count = usable_lints.len();
 +    let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter());
 +
 +    for (lint_group, mut lints) in grouped_by_lint_group {
 +        println!("\n## {}", lint_group);
 +
 +        lints.sort_by_key(|l| l.name.clone());
 +
 +        for lint in lints {
 +            println!("* [{}]({}#{}) ({})", lint.name, DOCS_LINK, lint.name, lint.desc);
 +        }
 +    }
 +
 +    println!("there are {} lints", usable_lint_count);
 +}
 +
 +/// Runs the `rename_lint` command.
 +///
 +/// This does the following:
 +/// * Adds an entry to `renamed_lints.rs`.
 +/// * Renames all lint attributes to the new name (e.g. `#[allow(clippy::lint_name)]`).
 +/// * Renames the lint struct to the new name.
 +/// * Renames the module containing the lint struct to the new name if it shares a name with the
 +///   lint.
 +///
 +/// # Panics
 +/// Panics for the following conditions:
 +/// * If a file path could not read from or then written to
 +/// * If either lint name has a prefix
 +/// * If `old_name` doesn't name an existing lint.
 +/// * If `old_name` names a deprecated or renamed lint.
 +#[allow(clippy::too_many_lines)]
 +pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
 +    if let Some((prefix, _)) = old_name.split_once("::") {
 +        panic!("`{}` should not contain the `{}` prefix", old_name, prefix);
 +    }
 +    if let Some((prefix, _)) = new_name.split_once("::") {
 +        panic!("`{}` should not contain the `{}` prefix", new_name, prefix);
 +    }
 +
 +    let (mut lints, deprecated_lints, mut renamed_lints) = gather_all();
 +    let mut old_lint_index = None;
 +    let mut found_new_name = false;
 +    for (i, lint) in lints.iter().enumerate() {
 +        if lint.name == old_name {
 +            old_lint_index = Some(i);
 +        } else if lint.name == new_name {
 +            found_new_name = true;
 +        }
 +    }
 +    let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{}`", old_name));
 +
 +    let lint = RenamedLint {
 +        old_name: format!("clippy::{}", old_name),
 +        new_name: if uplift {
 +            new_name.into()
 +        } else {
 +            format!("clippy::{}", new_name)
 +        },
 +    };
 +
 +    // Renamed lints and deprecated lints shouldn't have been found in the lint list, but check just in
 +    // case.
 +    assert!(
 +        !renamed_lints.iter().any(|l| lint.old_name == l.old_name),
 +        "`{}` has already been renamed",
 +        old_name
 +    );
 +    assert!(
 +        !deprecated_lints.iter().any(|l| lint.old_name == l.name),
 +        "`{}` has already been deprecated",
 +        old_name
 +    );
 +
 +    // Update all lint level attributes. (`clippy::lint_name`)
 +    for file in WalkDir::new(clippy_project_root())
 +        .into_iter()
 +        .map(Result::unwrap)
 +        .filter(|f| {
 +            let name = f.path().file_name();
 +            let ext = f.path().extension();
 +            (ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed")))
 +                && name != Some(OsStr::new("rename.rs"))
 +                && name != Some(OsStr::new("renamed_lints.rs"))
 +        })
 +    {
 +        rewrite_file(file.path(), |s| {
 +            replace_ident_like(s, &[(&lint.old_name, &lint.new_name)])
 +        });
 +    }
 +
 +    renamed_lints.push(lint);
 +    renamed_lints.sort_by(|lhs, rhs| {
 +        lhs.new_name
 +            .starts_with("clippy::")
 +            .cmp(&rhs.new_name.starts_with("clippy::"))
 +            .reverse()
 +            .then_with(|| lhs.old_name.cmp(&rhs.old_name))
 +    });
 +
 +    write_file(
 +        Path::new("clippy_lints/src/renamed_lints.rs"),
 +        &gen_renamed_lints_list(&renamed_lints),
 +    );
 +
 +    if uplift {
 +        write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints));
 +        println!(
 +            "`{}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually.",
 +            old_name
 +        );
 +    } else if found_new_name {
 +        write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints));
 +        println!(
 +            "`{}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually.",
 +            new_name
 +        );
 +    } else {
 +        // Rename the lint struct and source files sharing a name with the lint.
 +        let lint = &mut lints[old_lint_index];
 +        let old_name_upper = old_name.to_uppercase();
 +        let new_name_upper = new_name.to_uppercase();
 +        lint.name = new_name.into();
 +
 +        // Rename test files. only rename `.stderr` and `.fixed` files if the new test name doesn't exist.
 +        if try_rename_file(
 +            Path::new(&format!("tests/ui/{}.rs", old_name)),
 +            Path::new(&format!("tests/ui/{}.rs", new_name)),
 +        ) {
 +            try_rename_file(
 +                Path::new(&format!("tests/ui/{}.stderr", old_name)),
 +                Path::new(&format!("tests/ui/{}.stderr", new_name)),
 +            );
 +            try_rename_file(
 +                Path::new(&format!("tests/ui/{}.fixed", old_name)),
 +                Path::new(&format!("tests/ui/{}.fixed", new_name)),
 +            );
 +        }
 +
 +        // Try to rename the file containing the lint if the file name matches the lint's name.
 +        let replacements;
 +        let replacements = if lint.module == old_name
 +            && try_rename_file(
 +                Path::new(&format!("clippy_lints/src/{}.rs", old_name)),
 +                Path::new(&format!("clippy_lints/src/{}.rs", new_name)),
 +            ) {
 +            // Edit the module name in the lint list. Note there could be multiple lints.
 +            for lint in lints.iter_mut().filter(|l| l.module == old_name) {
 +                lint.module = new_name.into();
 +            }
 +            replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)];
 +            replacements.as_slice()
 +        } else if !lint.module.contains("::")
 +            // Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs`
 +            && try_rename_file(
 +                Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, old_name)),
 +                Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, new_name)),
 +            )
 +        {
 +            // Edit the module name in the lint list. Note there could be multiple lints, or none.
 +            let renamed_mod = format!("{}::{}", lint.module, old_name);
 +            for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) {
 +                lint.module = format!("{}::{}", lint.module, new_name);
 +            }
 +            replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)];
 +            replacements.as_slice()
 +        } else {
 +            replacements = [(&*old_name_upper, &*new_name_upper), ("", "")];
 +            &replacements[0..1]
 +        };
 +
 +        // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being
 +        // renamed.
 +        for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("renamed_lints.rs")) {
 +            rewrite_file(file.path(), |s| replace_ident_like(s, replacements));
 +        }
 +
 +        generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
 +        println!("{} has been successfully renamed", old_name);
 +    }
 +
 +    println!("note: `cargo uitest` still needs to be run to update the test results");
 +}
 +
 +const DEFAULT_DEPRECATION_REASON: &str = "default deprecation note";
 +/// Runs the `deprecate` command
 +///
 +/// This does the following:
 +/// * Adds an entry to `deprecated_lints.rs`.
 +/// * Removes the lint declaration (and the entire file if applicable)
 +///
 +/// # Panics
 +///
 +/// If a file path could not read from or written to
 +pub fn deprecate(name: &str, reason: Option<&String>) {
 +    fn finish(
 +        (lints, mut deprecated_lints, renamed_lints): (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>),
 +        name: &str,
 +        reason: &str,
 +    ) {
 +        deprecated_lints.push(DeprecatedLint {
 +            name: name.to_string(),
 +            reason: reason.to_string(),
 +            declaration_range: Range::default(),
 +        });
 +
 +        generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
 +        println!("info: `{}` has successfully been deprecated", name);
 +
 +        if reason == DEFAULT_DEPRECATION_REASON {
 +            println!("note: the deprecation reason must be updated in `clippy_lints/src/deprecated_lints.rs`");
 +        }
 +        println!("note: you must run `cargo uitest` to update the test results");
 +    }
 +
 +    let reason = reason.map_or(DEFAULT_DEPRECATION_REASON, String::as_str);
 +    let name_lower = name.to_lowercase();
 +    let name_upper = name.to_uppercase();
 +
 +    let (mut lints, deprecated_lints, renamed_lints) = gather_all();
 +    let Some(lint) = lints.iter().find(|l| l.name == name_lower) else { eprintln!("error: failed to find lint `{}`", name); return; };
 +
 +    let mod_path = {
 +        let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module));
 +        if mod_path.is_dir() {
 +            mod_path = mod_path.join("mod");
 +        }
 +
 +        mod_path.set_extension("rs");
 +        mod_path
 +    };
 +
 +    let deprecated_lints_path = &*clippy_project_root().join("clippy_lints/src/deprecated_lints.rs");
 +
 +    if remove_lint_declaration(&name_lower, &mod_path, &mut lints).unwrap_or(false) {
 +        declare_deprecated(&name_upper, deprecated_lints_path, reason).unwrap();
 +        finish((lints, deprecated_lints, renamed_lints), name, reason);
 +        return;
 +    }
 +
 +    eprintln!("error: lint not found");
 +}
 +
 +fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io::Result<bool> {
 +    fn remove_lint(name: &str, lints: &mut Vec<Lint>) {
 +        lints.iter().position(|l| l.name == name).map(|pos| lints.remove(pos));
 +    }
 +
 +    fn remove_test_assets(name: &str) {
 +        let test_file_stem = format!("tests/ui/{}", name);
 +        let path = Path::new(&test_file_stem);
 +
 +        // Some lints have their own directories, delete them
 +        if path.is_dir() {
 +            fs::remove_dir_all(path).ok();
 +            return;
 +        }
 +
 +        // Remove all related test files
 +        fs::remove_file(path.with_extension("rs")).ok();
 +        fs::remove_file(path.with_extension("stderr")).ok();
 +        fs::remove_file(path.with_extension("fixed")).ok();
 +    }
 +
 +    fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) {
 +        let impl_lint_pass_start = content.find("impl_lint_pass!").unwrap_or_else(|| {
 +            content
 +                .find("declare_lint_pass!")
 +                .unwrap_or_else(|| panic!("failed to find `impl_lint_pass`"))
 +        });
 +        let mut impl_lint_pass_end = content[impl_lint_pass_start..]
 +            .find(']')
 +            .expect("failed to find `impl_lint_pass` terminator");
 +
 +        impl_lint_pass_end += impl_lint_pass_start;
 +        if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(lint_name_upper) {
 +            let mut lint_name_end = impl_lint_pass_start + (lint_name_pos + lint_name_upper.len());
 +            for c in content[lint_name_end..impl_lint_pass_end].chars() {
 +                // Remove trailing whitespace
 +                if c == ',' || c.is_whitespace() {
 +                    lint_name_end += 1;
 +                } else {
 +                    break;
 +                }
 +            }
 +
 +            content.replace_range(impl_lint_pass_start + lint_name_pos..lint_name_end, "");
 +        }
 +    }
 +
 +    if path.exists() {
 +        if let Some(lint) = lints.iter().find(|l| l.name == name) {
 +            if lint.module == name {
 +                // The lint name is the same as the file, we can just delete the entire file
 +                fs::remove_file(path)?;
 +            } else {
 +                // We can't delete the entire file, just remove the declaration
 +
 +                if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) {
 +                    // Remove clippy_lints/src/some_mod/some_lint.rs
 +                    let mut lint_mod_path = path.to_path_buf();
 +                    lint_mod_path.set_file_name(name);
 +                    lint_mod_path.set_extension("rs");
 +
 +                    fs::remove_file(lint_mod_path).ok();
 +                }
 +
 +                let mut content =
 +                    fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));
 +
 +                eprintln!(
 +                    "warn: you will have to manually remove any code related to `{}` from `{}`",
 +                    name,
 +                    path.display()
 +                );
 +
 +                assert!(
 +                    content[lint.declaration_range.clone()].contains(&name.to_uppercase()),
 +                    "error: `{}` does not contain lint `{}`'s declaration",
 +                    path.display(),
 +                    lint.name
 +                );
 +
 +                // Remove lint declaration (declare_clippy_lint!)
 +                content.replace_range(lint.declaration_range.clone(), "");
 +
 +                // Remove the module declaration (mod xyz;)
 +                let mod_decl = format!("\nmod {};", name);
 +                content = content.replacen(&mod_decl, "", 1);
 +
 +                remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content);
 +                fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy()));
 +            }
 +
 +            remove_test_assets(name);
 +            remove_lint(name, lints);
 +            return Ok(true);
 +        }
 +    }
 +
 +    Ok(false)
 +}
 +
 +fn declare_deprecated(name: &str, path: &Path, reason: &str) -> io::Result<()> {
 +    let mut file = OpenOptions::new().write(true).open(path)?;
 +
 +    file.seek(SeekFrom::End(0))?;
 +
 +    let version = crate::new_lint::get_stabilization_version();
 +    let deprecation_reason = if reason == DEFAULT_DEPRECATION_REASON {
 +        "TODO"
 +    } else {
 +        reason
 +    };
 +
 +    writedoc!(
 +        file,
 +        "
 +
 +        declare_deprecated_lint! {{
 +            /// ### What it does
 +            /// Nothing. This lint has been deprecated.
 +            ///
 +            /// ### Deprecation reason
 +            /// {}
 +            #[clippy::version = \"{}\"]
 +            pub {},
 +            \"{}\"
 +        }}
 +
 +        ",
 +        deprecation_reason,
 +        version,
 +        name,
 +        reason,
 +    )
 +}
 +
 +/// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there
 +/// were no replacements.
 +fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option<String> {
 +    fn is_ident_char(c: u8) -> bool {
 +        matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_')
 +    }
 +
 +    let searcher = AhoCorasickBuilder::new()
 +        .dfa(true)
 +        .match_kind(aho_corasick::MatchKind::LeftmostLongest)
 +        .build_with_size::<u16, _, _>(replacements.iter().map(|&(x, _)| x.as_bytes()))
 +        .unwrap();
 +
 +    let mut result = String::with_capacity(contents.len() + 1024);
 +    let mut pos = 0;
 +    let mut edited = false;
 +    for m in searcher.find_iter(contents) {
 +        let (old, new) = replacements[m.pattern()];
 +        result.push_str(&contents[pos..m.start()]);
 +        result.push_str(
 +            if !is_ident_char(contents.as_bytes().get(m.start().wrapping_sub(1)).copied().unwrap_or(0))
 +                && !is_ident_char(contents.as_bytes().get(m.end()).copied().unwrap_or(0))
 +            {
 +                edited = true;
 +                new
 +            } else {
 +                old
 +            },
 +        );
 +        pos = m.end();
 +    }
 +    result.push_str(&contents[pos..]);
 +    edited.then_some(result)
 +}
 +
 +fn round_to_fifty(count: usize) -> usize {
 +    count / 50 * 50
 +}
 +
 +fn process_file(path: impl AsRef<Path>, update_mode: UpdateMode, content: &str) {
 +    if update_mode == UpdateMode::Check {
 +        let old_content =
 +            fs::read_to_string(&path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", path.as_ref().display(), e));
 +        if content != old_content {
 +            exit_with_failure();
 +        }
 +    } else {
 +        fs::write(&path, content.as_bytes())
 +            .unwrap_or_else(|e| panic!("Cannot write to {}: {}", path.as_ref().display(), e));
 +    }
 +}
 +
 +fn exit_with_failure() {
 +    println!(
 +        "Not all lints defined properly. \
 +                 Please run `cargo dev update_lints` to make sure all lints are defined properly."
 +    );
 +    std::process::exit(1);
 +}
 +
 +/// Lint data parsed from the Clippy source code.
 +#[derive(Clone, PartialEq, Eq, Debug)]
 +struct Lint {
 +    name: String,
 +    group: String,
 +    desc: String,
 +    module: String,
 +    declaration_range: Range<usize>,
++    documentation: String,
 +}
 +
 +impl Lint {
 +    #[must_use]
-         let mut iter = iter
-             .by_ref()
-             .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
++    fn new(
++        name: &str,
++        group: &str,
++        desc: &str,
++        module: &str,
++        declaration_range: Range<usize>,
++        documentation: String,
++    ) -> Self {
 +        Self {
 +            name: name.to_lowercase(),
 +            group: group.into(),
 +            desc: remove_line_splices(desc),
 +            module: module.into(),
 +            declaration_range,
++            documentation,
 +        }
 +    }
 +
 +    /// Returns all non-deprecated lints and non-internal lints
 +    #[must_use]
 +    fn usable_lints(lints: &[Self]) -> Vec<Self> {
 +        lints
 +            .iter()
 +            .filter(|l| !l.group.starts_with("internal"))
 +            .cloned()
 +            .collect()
 +    }
 +
 +    /// Returns all internal lints (not `internal_warn` lints)
 +    #[must_use]
 +    fn internal_lints(lints: &[Self]) -> Vec<Self> {
 +        lints.iter().filter(|l| l.group == "internal").cloned().collect()
 +    }
 +
 +    /// Returns the lints in a `HashMap`, grouped by the different lint groups
 +    #[must_use]
 +    fn by_lint_group(lints: impl Iterator<Item = Self>) -> HashMap<String, Vec<Self>> {
 +        lints.map(|lint| (lint.group.to_string(), lint)).into_group_map()
 +    }
 +}
 +
 +#[derive(Clone, PartialEq, Eq, Debug)]
 +struct DeprecatedLint {
 +    name: String,
 +    reason: String,
 +    declaration_range: Range<usize>,
 +}
 +impl DeprecatedLint {
 +    fn new(name: &str, reason: &str, declaration_range: Range<usize>) -> Self {
 +        Self {
 +            name: name.to_lowercase(),
 +            reason: remove_line_splices(reason),
 +            declaration_range,
 +        }
 +    }
 +}
 +
 +struct RenamedLint {
 +    old_name: String,
 +    new_name: String,
 +}
 +impl RenamedLint {
 +    fn new(old_name: &str, new_name: &str) -> Self {
 +        Self {
 +            old_name: remove_line_splices(old_name),
 +            new_name: remove_line_splices(new_name),
 +        }
 +    }
 +}
 +
 +/// Generates the code for registering a group
 +fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator<Item = &'a Lint>) -> String {
 +    let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect();
 +    details.sort_unstable();
 +
 +    let mut output = GENERATED_FILE_COMMENT.to_string();
 +
 +    let _ = writeln!(
 +        output,
 +        "store.register_group(true, \"clippy::{0}\", Some(\"clippy_{0}\"), vec![",
 +        group_name
 +    );
 +    for (module, name) in details {
 +        let _ = writeln!(output, "    LintId::of({}::{}),", module, name);
 +    }
 +    output.push_str("])\n");
 +
 +    output
 +}
 +
 +/// Generates the `register_removed` code
 +#[must_use]
 +fn gen_deprecated(lints: &[DeprecatedLint]) -> String {
 +    let mut output = GENERATED_FILE_COMMENT.to_string();
 +    output.push_str("{\n");
 +    for lint in lints {
 +        let _ = write!(
 +            output,
 +            concat!(
 +                "    store.register_removed(\n",
 +                "        \"clippy::{}\",\n",
 +                "        \"{}\",\n",
 +                "    );\n"
 +            ),
 +            lint.name, lint.reason,
 +        );
 +    }
 +    output.push_str("}\n");
 +
 +    output
 +}
 +
 +/// Generates the code for registering lints
 +#[must_use]
 +fn gen_register_lint_list<'a>(
 +    internal_lints: impl Iterator<Item = &'a Lint>,
 +    usable_lints: impl Iterator<Item = &'a Lint>,
 +) -> String {
 +    let mut details: Vec<_> = internal_lints
 +        .map(|l| (false, &l.module, l.name.to_uppercase()))
 +        .chain(usable_lints.map(|l| (true, &l.module, l.name.to_uppercase())))
 +        .collect();
 +    details.sort_unstable();
 +
 +    let mut output = GENERATED_FILE_COMMENT.to_string();
 +    output.push_str("store.register_lints(&[\n");
 +
 +    for (is_public, module_name, lint_name) in details {
 +        if !is_public {
 +            output.push_str("    #[cfg(feature = \"internal\")]\n");
 +        }
 +        let _ = writeln!(output, "    {}::{},", module_name, lint_name);
 +    }
 +    output.push_str("])\n");
 +
 +    output
 +}
 +
 +fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String {
 +    let mut res: String = GENERATED_FILE_COMMENT.into();
 +    for lint in lints {
 +        writeln!(res, "#![warn(clippy::{})]", lint.name).unwrap();
 +    }
 +    res.push_str("\nfn main() {}\n");
 +    res
 +}
 +
 +fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String {
 +    let mut seen_lints = HashSet::new();
 +    let mut res: String = GENERATED_FILE_COMMENT.into();
 +    res.push_str("// run-rustfix\n\n");
 +    for lint in lints {
 +        if seen_lints.insert(&lint.new_name) {
 +            writeln!(res, "#![allow({})]", lint.new_name).unwrap();
 +        }
 +    }
 +    seen_lints.clear();
 +    for lint in lints {
 +        if seen_lints.insert(&lint.old_name) {
 +            writeln!(res, "#![warn({})]", lint.old_name).unwrap();
 +        }
 +    }
 +    res.push_str("\nfn main() {}\n");
 +    res
 +}
 +
 +fn gen_renamed_lints_list(lints: &[RenamedLint]) -> String {
 +    const HEADER: &str = "\
 +        // This file is managed by `cargo dev rename_lint`. Prefer using that when possible.\n\n\
 +        #[rustfmt::skip]\n\
 +        pub static RENAMED_LINTS: &[(&str, &str)] = &[\n";
 +
 +    let mut res = String::from(HEADER);
 +    for lint in lints {
 +        writeln!(res, "    (\"{}\", \"{}\"),", lint.old_name, lint.new_name).unwrap();
 +    }
 +    res.push_str("];\n");
 +    res
 +}
 +
 +/// Gathers all lints defined in `clippy_lints/src`
 +fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>) {
 +    let mut lints = Vec::with_capacity(1000);
 +    let mut deprecated_lints = Vec::with_capacity(50);
 +    let mut renamed_lints = Vec::with_capacity(50);
 +
 +    for (rel_path, file) in clippy_lints_src_files() {
 +        let path = file.path();
 +        let contents =
 +            fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e));
 +        let module = rel_path
 +            .components()
 +            .map(|c| c.as_os_str().to_str().unwrap())
 +            .collect::<Vec<_>>()
 +            .join("::");
 +
 +        // If the lints are stored in mod.rs, we get the module name from
 +        // the containing directory:
 +        let module = if let Some(module) = module.strip_suffix("::mod.rs") {
 +            module
 +        } else {
 +            module.strip_suffix(".rs").unwrap_or(&module)
 +        };
 +
 +        match module {
 +            "deprecated_lints" => parse_deprecated_contents(&contents, &mut deprecated_lints),
 +            "renamed_lints" => parse_renamed_contents(&contents, &mut renamed_lints),
 +            _ => parse_contents(&contents, module, &mut lints),
 +        }
 +    }
 +    (lints, deprecated_lints, renamed_lints)
 +}
 +
 +fn clippy_lints_src_files() -> impl Iterator<Item = (PathBuf, DirEntry)> {
 +    let root_path = clippy_project_root().join("clippy_lints/src");
 +    let iter = WalkDir::new(&root_path).into_iter();
 +    iter.map(Result::unwrap)
 +        .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
 +        .map(move |f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f))
 +}
 +
 +macro_rules! match_tokens {
 +    ($iter:ident, $($token:ident $({$($fields:tt)*})? $(($capture:ident))?)*) => {
 +         {
 +            $($(let $capture =)? if let Some(LintDeclSearchResult {
 +                    token_kind: TokenKind::$token $({$($fields)*})?,
 +                    content: _x,
 +                    ..
 +            }) = $iter.next() {
 +                _x
 +            } else {
 +                continue;
 +            };)*
 +            #[allow(clippy::unused_unit)]
 +            { ($($($capture,)?)*) }
 +        }
 +    }
 +}
 +
 +pub(crate) use match_tokens;
 +
 +pub(crate) struct LintDeclSearchResult<'a> {
 +    pub token_kind: TokenKind,
 +    pub content: &'a str,
 +    pub range: Range<usize>,
 +}
 +
 +/// Parse a source file looking for `declare_clippy_lint` macro invocations.
 +fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
 +    let mut offset = 0usize;
 +    let mut iter = tokenize(contents).map(|t| {
 +        let range = offset..offset + t.len as usize;
 +        offset = range.end;
 +
 +        LintDeclSearchResult {
 +            token_kind: t.kind,
 +            content: &contents[range.clone()],
 +            range,
 +        }
 +    });
 +
 +    while let Some(LintDeclSearchResult { range, .. }) = iter.find(
 +        |LintDeclSearchResult {
 +             token_kind, content, ..
 +         }| token_kind == &TokenKind::Ident && *content == "declare_clippy_lint",
 +    ) {
 +        let start = range.start;
-         match iter.next() {
-             // #[clippy::version = "version"] pub
-             Some(LintDeclSearchResult {
-                 token_kind: TokenKind::Pound,
-                 ..
-             }) => {
-                 match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident);
-             },
-             // pub
-             Some(LintDeclSearchResult {
-                 token_kind: TokenKind::Ident,
-                 ..
-             }) => (),
-             _ => continue,
++        let mut docs = String::with_capacity(128);
++        let mut iter = iter.by_ref().filter(|t| !matches!(t.token_kind, TokenKind::Whitespace));
 +        // matches `!{`
 +        match_tokens!(iter, Bang OpenBrace);
-             lints.push(Lint::new(name, group, desc, module, start..range.end));
++        let mut in_code = false;
++        while let Some(t) = iter.next() {
++            match t.token_kind {
++                TokenKind::LineComment { .. } => {
++                    if let Some(line) = t.content.strip_prefix("/// ").or_else(|| t.content.strip_prefix("///")) {
++                        if line.starts_with("```") {
++                            docs += "```\n";
++                            in_code = !in_code;
++                        } else if !(in_code && line.starts_with("# ")) {
++                            docs += line;
++                            docs.push('\n');
++                        }
++                    }
++                },
++                TokenKind::Pound => {
++                    match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident);
++                    break;
++                },
++                TokenKind::Ident => {
++                    break;
++                },
++                _ => {},
++            }
 +        }
++        docs.pop(); // remove final newline
 +
 +        let (name, group, desc) = match_tokens!(
 +            iter,
 +            // LINT_NAME
 +            Ident(name) Comma
 +            // group,
 +            Ident(group) Comma
 +            // "description"
 +            Literal{..}(desc)
 +        );
 +
 +        if let Some(LintDeclSearchResult {
 +            token_kind: TokenKind::CloseBrace,
 +            range,
 +            ..
 +        }) = iter.next()
 +        {
-     unescape::unescape_literal(s, unescape::Mode::Str, &mut |range, _| res.push_str(&s[range]));
++            lints.push(Lint::new(name, group, desc, module, start..range.end, docs));
 +        }
 +    }
 +}
 +
 +/// Parse a source file looking for `declare_deprecated_lint` macro invocations.
 +fn parse_deprecated_contents(contents: &str, lints: &mut Vec<DeprecatedLint>) {
 +    let mut offset = 0usize;
 +    let mut iter = tokenize(contents).map(|t| {
 +        let range = offset..offset + t.len as usize;
 +        offset = range.end;
 +
 +        LintDeclSearchResult {
 +            token_kind: t.kind,
 +            content: &contents[range.clone()],
 +            range,
 +        }
 +    });
 +
 +    while let Some(LintDeclSearchResult { range, .. }) = iter.find(
 +        |LintDeclSearchResult {
 +             token_kind, content, ..
 +         }| token_kind == &TokenKind::Ident && *content == "declare_deprecated_lint",
 +    ) {
 +        let start = range.start;
 +
 +        let mut iter = iter.by_ref().filter(|LintDeclSearchResult { ref token_kind, .. }| {
 +            !matches!(token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. })
 +        });
 +        let (name, reason) = match_tokens!(
 +            iter,
 +            // !{
 +            Bang OpenBrace
 +            // #[clippy::version = "version"]
 +            Pound OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket
 +            // pub LINT_NAME,
 +            Ident Ident(name) Comma
 +            // "description"
 +            Literal{kind: LiteralKind::Str{..},..}(reason)
 +        );
 +
 +        if let Some(LintDeclSearchResult {
 +            token_kind: TokenKind::CloseBrace,
 +            range,
 +            ..
 +        }) = iter.next()
 +        {
 +            lints.push(DeprecatedLint::new(name, reason, start..range.end));
 +        }
 +    }
 +}
 +
 +fn parse_renamed_contents(contents: &str, lints: &mut Vec<RenamedLint>) {
 +    for line in contents.lines() {
 +        let mut offset = 0usize;
 +        let mut iter = tokenize(line).map(|t| {
 +            let range = offset..offset + t.len as usize;
 +            offset = range.end;
 +
 +            LintDeclSearchResult {
 +                token_kind: t.kind,
 +                content: &line[range.clone()],
 +                range,
 +            }
 +        });
 +
 +        let (old_name, new_name) = match_tokens!(
 +            iter,
 +            // ("old_name",
 +            Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(old_name) Comma
 +            // "new_name"),
 +            Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma
 +        );
 +        lints.push(RenamedLint::new(old_name, new_name));
 +    }
 +}
 +
 +/// Removes the line splices and surrounding quotes from a string literal
 +fn remove_line_splices(s: &str) -> String {
 +    let s = s
 +        .strip_prefix('r')
 +        .unwrap_or(s)
 +        .trim_matches('#')
 +        .strip_prefix('"')
 +        .and_then(|s| s.strip_suffix('"'))
 +        .unwrap_or_else(|| panic!("expected quoted string, found `{}`", s));
 +    let mut res = String::with_capacity(s.len());
-             Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
++    unescape::unescape_literal(s, unescape::Mode::Str, &mut |range, ch| {
++        if ch.is_ok() {
++            res.push_str(&s[range]);
++        }
++    });
 +    res
 +}
 +
 +/// Replaces a region in a file delimited by two lines matching regexes.
 +///
 +/// `path` is the relative path to the file on which you want to perform the replacement.
 +///
 +/// See `replace_region_in_text` for documentation of the other options.
 +///
 +/// # Panics
 +///
 +/// Panics if the path could not read or then written
 +fn replace_region_in_file(
 +    update_mode: UpdateMode,
 +    path: &Path,
 +    start: &str,
 +    end: &str,
 +    write_replacement: impl FnMut(&mut String),
 +) {
 +    let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e));
 +    let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) {
 +        Ok(x) => x,
 +        Err(delim) => panic!("Couldn't find `{}` in file `{}`", delim, path.display()),
 +    };
 +
 +    match update_mode {
 +        UpdateMode::Check if contents != new_contents => exit_with_failure(),
 +        UpdateMode::Check => (),
 +        UpdateMode::Change => {
 +            if let Err(e) = fs::write(path, new_contents.as_bytes()) {
 +                panic!("Cannot write to `{}`: {}", path.display(), e);
 +            }
 +        },
 +    }
 +}
 +
 +/// Replaces a region in a text delimited by two strings. Returns the new text if both delimiters
 +/// were found, or the missing delimiter if not.
 +fn replace_region_in_text<'a>(
 +    text: &str,
 +    start: &'a str,
 +    end: &'a str,
 +    mut write_replacement: impl FnMut(&mut String),
 +) -> Result<String, &'a str> {
 +    let (text_start, rest) = text.split_once(start).ok_or(start)?;
 +    let (_, text_end) = rest.split_once(end).ok_or(end)?;
 +
 +    let mut res = String::with_capacity(text.len() + 4096);
 +    res.push_str(text_start);
 +    res.push_str(start);
 +    write_replacement(&mut res);
 +    res.push_str(end);
 +    res.push_str(text_end);
 +
 +    Ok(res)
 +}
 +
 +fn try_rename_file(old_name: &Path, new_name: &Path) -> bool {
 +    match fs::OpenOptions::new().create_new(true).write(true).open(new_name) {
 +        Ok(file) => drop(file),
 +        Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false,
 +        Err(e) => panic_file(e, new_name, "create"),
 +    };
 +    match fs::rename(old_name, new_name) {
 +        Ok(()) => true,
 +        Err(e) => {
 +            drop(fs::remove_file(new_name));
 +            if e.kind() == io::ErrorKind::NotFound {
 +                false
 +            } else {
 +                panic_file(e, old_name, "rename");
 +            }
 +        },
 +    }
 +}
 +
 +#[allow(clippy::needless_pass_by_value)]
 +fn panic_file(error: io::Error, name: &Path, action: &str) -> ! {
 +    panic!("failed to {} file `{}`: {}", action, name.display(), error)
 +}
 +
 +fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option<String>) {
 +    let mut file = fs::OpenOptions::new()
 +        .write(true)
 +        .read(true)
 +        .open(path)
 +        .unwrap_or_else(|e| panic_file(e, path, "open"));
 +    let mut buf = String::new();
 +    file.read_to_string(&mut buf)
 +        .unwrap_or_else(|e| panic_file(e, path, "read"));
 +    if let Some(new_contents) = f(&buf) {
 +        file.rewind().unwrap_or_else(|e| panic_file(e, path, "write"));
 +        file.write_all(new_contents.as_bytes())
 +            .unwrap_or_else(|e| panic_file(e, path, "write"));
 +        file.set_len(new_contents.len() as u64)
 +            .unwrap_or_else(|e| panic_file(e, path, "write"));
 +    }
 +}
 +
 +fn write_file(path: &Path, contents: &str) {
 +    fs::write(path, contents).unwrap_or_else(|e| panic_file(e, path, "write"));
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use super::*;
 +
 +    #[test]
 +    fn test_parse_contents() {
 +        static CONTENTS: &str = r#"
 +            declare_clippy_lint! {
 +                #[clippy::version = "Hello Clippy!"]
 +                pub PTR_ARG,
 +                style,
 +                "really long \
 +                text"
 +            }
 +
 +            declare_clippy_lint!{
 +                #[clippy::version = "Test version"]
 +                pub DOC_MARKDOWN,
 +                pedantic,
 +                "single line"
 +            }
 +        "#;
 +        let mut result = Vec::new();
 +        parse_contents(CONTENTS, "module_name", &mut result);
 +        for r in &mut result {
 +            r.declaration_range = Range::default();
 +        }
 +
 +        let expected = vec![
 +            Lint::new(
 +                "ptr_arg",
 +                "style",
 +                "\"really long text\"",
 +                "module_name",
 +                Range::default(),
++                String::new(),
 +            ),
 +            Lint::new(
 +                "doc_markdown",
 +                "pedantic",
 +                "\"single line\"",
 +                "module_name",
 +                Range::default(),
++                String::new(),
 +            ),
 +        ];
 +        assert_eq!(expected, result);
 +    }
 +
 +    #[test]
 +    fn test_parse_deprecated_contents() {
 +        static DEPRECATED_CONTENTS: &str = r#"
 +            /// some doc comment
 +            declare_deprecated_lint! {
 +                #[clippy::version = "I'm a version"]
 +                pub SHOULD_ASSERT_EQ,
 +                "`assert!()` will be more flexible with RFC 2011"
 +            }
 +        "#;
 +
 +        let mut result = Vec::new();
 +        parse_deprecated_contents(DEPRECATED_CONTENTS, &mut result);
 +        for r in &mut result {
 +            r.declaration_range = Range::default();
 +        }
 +
 +        let expected = vec![DeprecatedLint::new(
 +            "should_assert_eq",
 +            "\"`assert!()` will be more flexible with RFC 2011\"",
 +            Range::default(),
 +        )];
 +        assert_eq!(expected, result);
 +    }
 +
 +    #[test]
 +    fn test_usable_lints() {
 +        let lints = vec![
 +            Lint::new(
 +                "should_assert_eq2",
 +                "Not Deprecated",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
++                String::new(),
 +            ),
 +            Lint::new(
 +                "should_assert_eq2",
 +                "internal",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
++                String::new(),
 +            ),
 +            Lint::new(
 +                "should_assert_eq2",
 +                "internal_style",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
++                String::new(),
 +            ),
 +        ];
 +        let expected = vec![Lint::new(
 +            "should_assert_eq2",
 +            "Not Deprecated",
 +            "\"abc\"",
 +            "module_name",
 +            Range::default(),
++            String::new(),
 +        )];
 +        assert_eq!(expected, Lint::usable_lints(&lints));
 +    }
 +
 +    #[test]
 +    fn test_by_lint_group() {
 +        let lints = vec![
-             Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
++            Lint::new(
++                "should_assert_eq",
++                "group1",
++                "\"abc\"",
++                "module_name",
++                Range::default(),
++                String::new(),
++            ),
 +            Lint::new(
 +                "should_assert_eq2",
 +                "group2",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
++                String::new(),
++            ),
++            Lint::new(
++                "incorrect_match",
++                "group1",
++                "\"abc\"",
++                "module_name",
++                Range::default(),
++                String::new(),
 +            ),
-                 Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
-                 Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
 +        ];
 +        let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
 +        expected.insert(
 +            "group1".to_string(),
 +            vec![
-             Lint::new("abc", "group1", "\"abc\"", "module_name", Range::default()),
-             Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
-             Lint::new("internal", "internal_style", "\"abc\"", "module_name", Range::default()),
++                Lint::new(
++                    "should_assert_eq",
++                    "group1",
++                    "\"abc\"",
++                    "module_name",
++                    Range::default(),
++                    String::new(),
++                ),
++                Lint::new(
++                    "incorrect_match",
++                    "group1",
++                    "\"abc\"",
++                    "module_name",
++                    Range::default(),
++                    String::new(),
++                ),
 +            ],
 +        );
 +        expected.insert(
 +            "group2".to_string(),
 +            vec![Lint::new(
 +                "should_assert_eq2",
 +                "group2",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
++                String::new(),
 +            )],
 +        );
 +        assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
 +    }
 +
 +    #[test]
 +    fn test_gen_deprecated() {
 +        let lints = vec![
 +            DeprecatedLint::new(
 +                "should_assert_eq",
 +                "\"has been superseded by should_assert_eq2\"",
 +                Range::default(),
 +            ),
 +            DeprecatedLint::new("another_deprecated", "\"will be removed\"", Range::default()),
 +        ];
 +
 +        let expected = GENERATED_FILE_COMMENT.to_string()
 +            + &[
 +                "{",
 +                "    store.register_removed(",
 +                "        \"clippy::should_assert_eq\",",
 +                "        \"has been superseded by should_assert_eq2\",",
 +                "    );",
 +                "    store.register_removed(",
 +                "        \"clippy::another_deprecated\",",
 +                "        \"will be removed\",",
 +                "    );",
 +                "}",
 +            ]
 +            .join("\n")
 +            + "\n";
 +
 +        assert_eq!(expected, gen_deprecated(&lints));
 +    }
 +
 +    #[test]
 +    fn test_gen_lint_group_list() {
 +        let lints = vec![
++            Lint::new(
++                "abc",
++                "group1",
++                "\"abc\"",
++                "module_name",
++                Range::default(),
++                String::new(),
++            ),
++            Lint::new(
++                "should_assert_eq",
++                "group1",
++                "\"abc\"",
++                "module_name",
++                Range::default(),
++                String::new(),
++            ),
++            Lint::new(
++                "internal",
++                "internal_style",
++                "\"abc\"",
++                "module_name",
++                Range::default(),
++                String::new(),
++            ),
 +        ];
 +        let expected = GENERATED_FILE_COMMENT.to_string()
 +            + &[
 +                "store.register_group(true, \"clippy::group1\", Some(\"clippy_group1\"), vec![",
 +                "    LintId::of(module_name::ABC),",
 +                "    LintId::of(module_name::INTERNAL),",
 +                "    LintId::of(module_name::SHOULD_ASSERT_EQ),",
 +                "])",
 +            ]
 +            .join("\n")
 +            + "\n";
 +
 +        let result = gen_lint_group_list("group1", lints.iter());
 +
 +        assert_eq!(expected, result);
 +    }
 +}
index 27c2896e1e5cee71bfc27e48d7ecee0aff5edda5,0000000000000000000000000000000000000000..9464694a3b55ad16e0531810740e49fefd80f255
mode 100644,000000..100644
--- /dev/null
@@@ -1,89 -1,0 +1,89 @@@
-                 let expr_ty = typeck_results.expr_ty(&body.value);
 +use clippy_utils::diagnostics::span_lint_hir_and_then;
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::implements_trait;
 +use rustc_errors::Applicability;
 +use rustc_hir::{AsyncGeneratorKind, Body, BodyId, ExprKind, GeneratorKind, QPath};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for async blocks that yield values of types
 +    /// that can themselves be awaited.
 +    ///
 +    /// ### Why is this bad?
 +    /// An await is likely missing.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// async fn foo() {}
 +    ///
 +    /// fn bar() {
 +    ///   let x = async {
 +    ///     foo()
 +    ///   };
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// async fn foo() {}
 +    ///
 +    /// fn bar() {
 +    ///   let x = async {
 +    ///     foo().await
 +    ///   };
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.48.0"]
 +    pub ASYNC_YIELDS_ASYNC,
 +    correctness,
 +    "async blocks that return a type that can be awaited"
 +}
 +
 +declare_lint_pass!(AsyncYieldsAsync => [ASYNC_YIELDS_ASYNC]);
 +
 +impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync {
 +    fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
 +        use AsyncGeneratorKind::{Block, Closure};
 +        // For functions, with explicitly defined types, don't warn.
 +        // XXXkhuey maybe we should?
 +        if let Some(GeneratorKind::Async(Block | Closure)) = body.generator_kind {
 +            if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() {
 +                let body_id = BodyId {
 +                    hir_id: body.value.hir_id,
 +                };
 +                let typeck_results = cx.tcx.typeck_body(body_id);
++                let expr_ty = typeck_results.expr_ty(body.value);
 +
 +                if implements_trait(cx, expr_ty, future_trait_def_id, &[]) {
 +                    let return_expr_span = match &body.value.kind {
 +                        // XXXkhuey there has to be a better way.
 +                        ExprKind::Block(block, _) => block.expr.map(|e| e.span),
 +                        ExprKind::Path(QPath::Resolved(_, path)) => Some(path.span),
 +                        _ => None,
 +                    };
 +                    if let Some(return_expr_span) = return_expr_span {
 +                        span_lint_hir_and_then(
 +                            cx,
 +                            ASYNC_YIELDS_ASYNC,
 +                            body.value.hir_id,
 +                            return_expr_span,
 +                            "an async construct yields a type which is itself awaitable",
 +                            |db| {
 +                                db.span_label(body.value.span, "outer async construct");
 +                                db.span_label(return_expr_span, "awaitable value not awaited");
 +                                db.span_suggestion(
 +                                    return_expr_span,
 +                                    "consider awaiting this value",
 +                                    format!("{}.await", snippet(cx, return_expr_span, "..")),
 +                                    Applicability::MaybeIncorrect,
 +                                );
 +                            },
 +                        );
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
index 4bcbeacf9feb59abd13704dcfcaf163bb31a4628,0000000000000000000000000000000000000000..732dc2b433091b047abdffa296f7521ebbd7e3e9
mode 100644,000000..100644
--- /dev/null
@@@ -1,738 -1,0 +1,738 @@@
-         is_relevant_expr(cx, cx.tcx.typeck_body(eid), &cx.tcx.hir().body(eid).value)
 +//! checks for attributes
 +
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::macros::{is_panic, macro_backtrace};
 +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, meets_msrv};
 +use if_chain::if_chain;
 +use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
 +use rustc_errors::Applicability;
 +use rustc_hir::{
 +    Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind,
 +};
 +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +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::Span;
 +use rustc_span::sym;
 +use rustc_span::symbol::Symbol;
 +use semver::Version;
 +
 +static UNIX_SYSTEMS: &[&str] = &[
 +    "android",
 +    "dragonfly",
 +    "emscripten",
 +    "freebsd",
 +    "fuchsia",
 +    "haiku",
 +    "illumos",
 +    "ios",
 +    "l4re",
 +    "linux",
 +    "macos",
 +    "netbsd",
 +    "openbsd",
 +    "redox",
 +    "solaris",
 +    "vxworks",
 +];
 +
 +// NOTE: windows is excluded from the list because it's also a valid target family.
 +static NON_UNIX_SYSTEMS: &[&str] = &["hermit", "none", "wasi"];
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for items annotated with `#[inline(always)]`,
 +    /// unless the annotated function is empty or simply panics.
 +    ///
 +    /// ### Why is this bad?
 +    /// While there are valid uses of this annotation (and once
 +    /// you know when to use it, by all means `allow` this lint), it's a common
 +    /// newbie-mistake to pepper one's code with it.
 +    ///
 +    /// As a rule of thumb, before slapping `#[inline(always)]` on a function,
 +    /// measure if that additional function call really affects your runtime profile
 +    /// sufficiently to make up for the increase in compile time.
 +    ///
 +    /// ### Known problems
 +    /// False positives, big time. This lint is meant to be
 +    /// deactivated by everyone doing serious performance work. This means having
 +    /// done the measurement.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// #[inline(always)]
 +    /// fn not_quite_hot_code(..) { ... }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub INLINE_ALWAYS,
 +    pedantic,
 +    "use of `#[inline(always)]`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `extern crate` and `use` items annotated with
 +    /// lint attributes.
 +    ///
 +    /// This lint permits lint attributes for lints emitted on the items themself.
 +    /// For `use` items these lints are:
 +    /// * deprecated
 +    /// * unreachable_pub
 +    /// * unused_imports
 +    /// * clippy::enum_glob_use
 +    /// * clippy::macro_use_imports
 +    /// * clippy::wildcard_imports
 +    ///
 +    /// For `extern crate` items these lints are:
 +    /// * `unused_imports` on items with `#[macro_use]`
 +    ///
 +    /// ### Why is this bad?
 +    /// Lint attributes have no effect on crate imports. Most
 +    /// likely a `!` was forgotten.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// #[deny(dead_code)]
 +    /// extern crate foo;
 +    /// #[forbid(dead_code)]
 +    /// use foo::bar;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// #[allow(unused_imports)]
 +    /// use foo::baz;
 +    /// #[allow(unused_imports)]
 +    /// #[macro_use]
 +    /// extern crate baz;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub USELESS_ATTRIBUTE,
 +    correctness,
 +    "use of lint attributes on `extern crate` items"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `#[deprecated]` annotations with a `since`
 +    /// field that is not a valid semantic version.
 +    ///
 +    /// ### Why is this bad?
 +    /// For checking the version of the deprecation, it must be
 +    /// a valid semver. Failing that, the contained information is useless.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// #[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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for empty lines after outer attributes
 +    ///
 +    /// ### Why is this bad?
 +    /// Most likely the attribute was meant to be an inner attribute using a '!'.
 +    /// If it was meant to be an outer attribute, then the following item
 +    /// should not be separated by empty lines.
 +    ///
 +    /// ### Known problems
 +    /// Can cause false positives.
 +    ///
 +    /// From the clippy side it's difficult to detect empty lines between an attributes and the
 +    /// following item because empty lines and comments are not part of the AST. The parsing
 +    /// currently works for basic cases but is not perfect.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// #[allow(dead_code)]
 +    ///
 +    /// fn not_quite_good_code() { }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// // Good (as inner attribute)
 +    /// #![allow(dead_code)]
 +    ///
 +    /// fn this_is_fine() { }
 +    ///
 +    /// // or
 +    ///
 +    /// // Good (as outer attribute)
 +    /// #[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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category.
 +    ///
 +    /// ### Why is this bad?
 +    /// Restriction lints sometimes are in contrast with other lints or even go against idiomatic rust.
 +    /// These lints should only be enabled on a lint-by-lint basis and with careful consideration.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// #![deny(clippy::restriction)]
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// #![deny(clippy::as_conversions)]
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub BLANKET_CLIPPY_RESTRICTION_LINTS,
 +    suspicious,
 +    "enabling the complete restriction group"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it
 +    /// with `#[rustfmt::skip]`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Since tool_attributes ([rust-lang/rust#44690](https://github.com/rust-lang/rust/issues/44690))
 +    /// are stable now, they should be used instead of the old `cfg_attr(rustfmt)` attributes.
 +    ///
 +    /// ### Known problems
 +    /// This lint doesn't detect crate level inner attributes, because they get
 +    /// processed before the PreExpansionPass lints get executed. See
 +    /// [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765)
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// #[cfg_attr(rustfmt, rustfmt_skip)]
 +    /// fn main() { }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// #[rustfmt::skip]
 +    /// fn main() { }
 +    /// ```
 +    #[clippy::version = "1.32.0"]
 +    pub DEPRECATED_CFG_ATTR,
 +    complexity,
 +    "usage of `cfg_attr(rustfmt)` instead of tool attributes"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for cfg attributes having operating systems used in target family position.
 +    ///
 +    /// ### Why is this bad?
 +    /// The configuration option will not be recognised and the related item will not be included
 +    /// by the conditional compilation engine.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// #[cfg(linux)]
 +    /// fn conditional() { }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # mod hidden {
 +    /// #[cfg(target_os = "linux")]
 +    /// fn conditional() { }
 +    /// # }
 +    ///
 +    /// // or
 +    ///
 +    /// #[cfg(unix)]
 +    /// 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\")`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for attributes that allow lints without a reason.
 +    ///
 +    /// (This requires the `lint_reasons` feature)
 +    ///
 +    /// ### Why is this bad?
 +    /// Allowing a lint should always have a reason. This reason should be documented to
 +    /// ensure that others understand the reasoning
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// #![feature(lint_reasons)]
 +    ///
 +    /// #![allow(clippy::some_lint)]
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// #![feature(lint_reasons)]
 +    ///
 +    /// #![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")]
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub ALLOW_ATTRIBUTES_WITHOUT_REASON,
 +    restriction,
 +    "ensures that all `allow` and `expect` attributes have a reason"
 +}
 +
 +declare_lint_pass!(Attributes => [
 +    ALLOW_ATTRIBUTES_WITHOUT_REASON,
 +    INLINE_ALWAYS,
 +    DEPRECATED_SEMVER,
 +    USELESS_ATTRIBUTE,
 +    BLANKET_CLIPPY_RESTRICTION_LINTS,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Attributes {
 +    fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) {
 +        if let Some(items) = &attr.meta_item_list() {
 +            if let Some(ident) = attr.ident() {
 +                if is_lint_level(ident.name) {
 +                    check_clippy_lint_names(cx, ident.name, items);
 +                }
 +                if matches!(ident.name, sym::allow | sym::expect) {
 +                    check_lint_reason(cx, ident.name, items, attr);
 +                }
 +                if items.is_empty() || !attr.has_name(sym::deprecated) {
 +                    return;
 +                }
 +                for item in items {
 +                    if_chain! {
 +                        if let NestedMetaItem::MetaItem(mi) = &item;
 +                        if let MetaItemKind::NameValue(lit) = &mi.kind;
 +                        if mi.has_name(sym::since);
 +                        then {
 +                            check_semver(cx, item.span(), lit);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        if is_relevant_item(cx, item) {
 +            check_attrs(cx, item.span, item.ident.name, attrs);
 +        }
 +        match item.kind {
 +            ItemKind::ExternCrate(..) | ItemKind::Use(..) => {
 +                let skip_unused_imports = attrs.iter().any(|attr| attr.has_name(sym::macro_use));
 +
 +                for attr in attrs {
 +                    if in_external_macro(cx.sess(), attr.span) {
 +                        return;
 +                    }
 +                    if let Some(lint_list) = &attr.meta_item_list() {
 +                        if attr.ident().map_or(false, |ident| is_lint_level(ident.name)) {
 +                            for lint in lint_list {
 +                                match item.kind {
 +                                    ItemKind::Use(..) => {
 +                                        if is_word(lint, sym::unused_imports)
 +                                            || is_word(lint, sym::deprecated)
 +                                            || is_word(lint, sym!(unreachable_pub))
 +                                            || is_word(lint, sym!(unused))
 +                                            || extract_clippy_lint(lint).map_or(false, |s| {
 +                                                matches!(
 +                                                    s.as_str(),
 +                                                    "wildcard_imports"
 +                                                        | "enum_glob_use"
 +                                                        | "redundant_pub_crate"
 +                                                        | "macro_use_imports",
 +                                                )
 +                                            })
 +                                        {
 +                                            return;
 +                                        }
 +                                    },
 +                                    ItemKind::ExternCrate(..) => {
 +                                        if is_word(lint, sym::unused_imports) && skip_unused_imports {
 +                                            return;
 +                                        }
 +                                        if is_word(lint, sym!(unused_extern_crates)) {
 +                                            return;
 +                                        }
 +                                    },
 +                                    _ => {},
 +                                }
 +                            }
 +                            let line_span = first_line_of_span(cx, attr.span);
 +
 +                            if let Some(mut sugg) = snippet_opt(cx, line_span) {
 +                                if sugg.contains("#[") {
 +                                    span_lint_and_then(
 +                                        cx,
 +                                        USELESS_ATTRIBUTE,
 +                                        line_span,
 +                                        "useless lint attribute",
 +                                        |diag| {
 +                                            sugg = sugg.replacen("#[", "#![", 1);
 +                                            diag.span_suggestion(
 +                                                line_span,
 +                                                "if you just forgot a `!`, use",
 +                                                sugg,
 +                                                Applicability::MaybeIncorrect,
 +                                            );
 +                                        },
 +                                    );
 +                                }
 +                            }
 +                        }
 +                    }
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
 +        if is_relevant_impl(cx, item) {
 +            check_attrs(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id()));
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
 +        if is_relevant_trait(cx, item) {
 +            check_attrs(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id()));
 +        }
 +    }
 +}
 +
 +/// Returns the lint name if it is clippy lint.
 +fn extract_clippy_lint(lint: &NestedMetaItem) -> Option<Symbol> {
 +    if_chain! {
 +        if let Some(meta_item) = lint.meta_item();
 +        if meta_item.path.segments.len() > 1;
 +        if let tool_name = meta_item.path.segments[0].ident;
 +        if tool_name.name == sym::clippy;
 +        then {
 +            let lint_name = meta_item.path.segments.last().unwrap().ident.name;
 +            return Some(lint_name);
 +        }
 +    }
 +    None
 +}
 +
 +fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem]) {
 +    for lint in items {
 +        if let Some(lint_name) = extract_clippy_lint(lint) {
 +            if lint_name.as_str() == "restriction" && name != sym::allow {
 +                span_lint_and_help(
 +                    cx,
 +                    BLANKET_CLIPPY_RESTRICTION_LINTS,
 +                    lint.span(),
 +                    "restriction lints are not meant to be all enabled",
 +                    None,
 +                    "try enabling only the lints you really need",
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem], attr: &'_ Attribute) {
 +    // Check for the feature
 +    if !cx.tcx.sess.features_untracked().lint_reasons {
 +        return;
 +    }
 +
 +    // Check if the reason is present
 +    if let Some(item) = items.last().and_then(NestedMetaItem::meta_item)
 +        && let MetaItemKind::NameValue(_) = &item.kind
 +        && item.path == sym::reason
 +    {
 +        return;
 +    }
 +
 +    span_lint_and_help(
 +        cx,
 +        ALLOW_ATTRIBUTES_WITHOUT_REASON,
 +        attr.span,
 +        &format!("`{}` attribute without specifying a reason", name.as_str()),
 +        None,
 +        "try adding a reason at the end with `, reason = \"..\"`",
 +    );
 +}
 +
 +fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
 +    if let ItemKind::Fn(_, _, eid) = item.kind {
-         ImplItemKind::Fn(_, eid) => is_relevant_expr(cx, cx.tcx.typeck_body(eid), &cx.tcx.hir().body(eid).value),
++        is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value)
 +    } else {
 +        true
 +    }
 +}
 +
 +fn is_relevant_impl(cx: &LateContext<'_>, item: &ImplItem<'_>) -> bool {
 +    match item.kind {
-             is_relevant_expr(cx, cx.tcx.typeck_body(eid), &cx.tcx.hir().body(eid).value)
++        ImplItemKind::Fn(_, eid) => is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value),
 +        _ => false,
 +    }
 +}
 +
 +fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> bool {
 +    match item.kind {
 +        TraitItemKind::Fn(_, TraitFn::Required(_)) => true,
 +        TraitItemKind::Fn(_, TraitFn::Provided(eid)) => {
++            is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value)
 +        },
 +        _ => false,
 +    }
 +}
 +
 +fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, block: &Block<'_>) -> bool {
 +    block.stmts.first().map_or(
 +        block
 +            .expr
 +            .as_ref()
 +            .map_or(false, |e| is_relevant_expr(cx, typeck_results, e)),
 +        |stmt| match &stmt.kind {
 +            StmtKind::Local(_) => true,
 +            StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr),
 +            StmtKind::Item(_) => false,
 +        },
 +    )
 +}
 +
 +fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, expr: &Expr<'_>) -> bool {
 +    if macro_backtrace(expr.span).last().map_or(false, |macro_call| {
 +        is_panic(cx, macro_call.def_id) || cx.tcx.item_name(macro_call.def_id) == sym::unreachable
 +    }) {
 +        return false;
 +    }
 +    match &expr.kind {
 +        ExprKind::Block(block, _) => is_relevant_block(cx, typeck_results, block),
 +        ExprKind::Ret(Some(e)) => is_relevant_expr(cx, typeck_results, e),
 +        ExprKind::Ret(None) | ExprKind::Break(_, None) => false,
 +        _ => true,
 +    }
 +}
 +
 +fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) {
 +    if span.from_expansion() {
 +        return;
 +    }
 +
 +    for attr in attrs {
 +        if let Some(values) = attr.meta_item_list() {
 +            if values.len() != 1 || !attr.has_name(sym::inline) {
 +                continue;
 +            }
 +            if is_word(&values[0], sym::always) {
 +                span_lint(
 +                    cx,
 +                    INLINE_ALWAYS,
 +                    attr.span,
 +                    &format!(
 +                        "you have declared `#[inline(always)]` on `{}`. This is usually a bad idea",
 +                        name
 +                    ),
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn check_semver(cx: &LateContext<'_>, span: Span, lit: &Lit) {
 +    if let LitKind::Str(is, _) = lit.kind {
 +        if Version::parse(is.as_str()).is_ok() {
 +            return;
 +        }
 +    }
 +    span_lint(
 +        cx,
 +        DEPRECATED_SEMVER,
 +        span,
 +        "the since field must contain a semver-compliant version",
 +    );
 +}
 +
 +fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool {
 +    if let NestedMetaItem::MetaItem(mi) = &nmi {
 +        mi.is_word() && mi.has_name(expected)
 +    } else {
 +        false
 +    }
 +}
 +
 +pub struct EarlyAttributes {
 +    pub msrv: Option<RustcVersion>,
 +}
 +
 +impl_lint_pass!(EarlyAttributes => [
 +    DEPRECATED_CFG_ATTR,
 +    MISMATCHED_TARGET_OS,
 +    EMPTY_LINE_AFTER_OUTER_ATTR,
 +]);
 +
 +impl EarlyLintPass for EarlyAttributes {
 +    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
 +        check_empty_line_after_outer_attr(cx, item);
 +    }
 +
 +    fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
 +        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) {
 +    let mut iter = item.attrs.iter().peekable();
 +    while let Some(attr) = iter.next() {
 +        if matches!(attr.kind, AttrKind::Normal(..))
 +            && attr.style == AttrStyle::Outer
 +            && is_present_in_source(cx, attr.span)
 +        {
 +            let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt(), item.span.parent());
 +            let end_of_attr_to_next_attr_or_item = Span::new(
 +                attr.span.hi(),
 +                iter.peek().map_or(item.span.lo(), |next_attr| next_attr.span.lo()),
 +                item.span.ctxt(),
 +                item.span.parent(),
 +            );
 +
 +            if let Some(snippet) = snippet_opt(cx, end_of_attr_to_next_attr_or_item) {
 +                let lines = snippet.split('\n').collect::<Vec<_>>();
 +                let lines = without_block_comments(lines);
 +
 +                if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 {
 +                    span_lint(
 +                        cx,
 +                        EMPTY_LINE_AFTER_OUTER_ATTR,
 +                        begin_of_attr_to_item,
 +                        "found an empty line after an outer attribute. \
 +                        Perhaps you forgot to add a `!` to make it an inner attribute?",
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Option<RustcVersion>) {
 +    if_chain! {
 +        if meets_msrv(msrv, msrvs::TOOL_ATTRIBUTES);
 +        // check cfg_attr
 +        if attr.has_name(sym::cfg_attr);
 +        if let Some(items) = attr.meta_item_list();
 +        if items.len() == 2;
 +        // check for `rustfmt`
 +        if let Some(feature_item) = items[0].meta_item();
 +        if feature_item.has_name(sym::rustfmt);
 +        // check for `rustfmt_skip` and `rustfmt::skip`
 +        if let Some(skip_item) = &items[1].meta_item();
 +        if skip_item.has_name(sym!(rustfmt_skip))
 +            || skip_item
 +                .path
 +                .segments
 +                .last()
 +                .expect("empty path in attribute")
 +                .ident
 +                .name
 +                == sym::skip;
 +        // Only lint outer attributes, because custom inner attributes are unstable
 +        // Tracking issue: https://github.com/rust-lang/rust/issues/54726
 +        if attr.style == AttrStyle::Outer;
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                DEPRECATED_CFG_ATTR,
 +                attr.span,
 +                "`cfg_attr` is deprecated for rustfmt and got replaced by tool attributes",
 +                "use",
 +                "#[rustfmt::skip]".to_string(),
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) {
 +    fn find_os(name: &str) -> Option<&'static str> {
 +        UNIX_SYSTEMS
 +            .iter()
 +            .chain(NON_UNIX_SYSTEMS.iter())
 +            .find(|&&os| os == name)
 +            .copied()
 +    }
 +
 +    fn is_unix(name: &str) -> bool {
 +        UNIX_SYSTEMS.iter().any(|&os| os == name)
 +    }
 +
 +    fn find_mismatched_target_os(items: &[NestedMetaItem]) -> Vec<(&str, Span)> {
 +        let mut mismatched = Vec::new();
 +
 +        for item in items {
 +            if let NestedMetaItem::MetaItem(meta) = item {
 +                match &meta.kind {
 +                    MetaItemKind::List(list) => {
 +                        mismatched.extend(find_mismatched_target_os(list));
 +                    },
 +                    MetaItemKind::Word => {
 +                        if_chain! {
 +                            if let Some(ident) = meta.ident();
 +                            if let Some(os) = find_os(ident.name.as_str());
 +                            then {
 +                                mismatched.push((os, ident.span));
 +                            }
 +                        }
 +                    },
 +                    MetaItemKind::NameValue(..) => {},
 +                }
 +            }
 +        }
 +
 +        mismatched
 +    }
 +
 +    if_chain! {
 +        if attr.has_name(sym::cfg);
 +        if let Some(list) = attr.meta_item_list();
 +        let mismatched = find_mismatched_target_os(&list);
 +        if !mismatched.is_empty();
 +        then {
 +            let mess = "operating system used in target family position";
 +
 +            span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, mess, |diag| {
 +                // Avoid showing the unix suggestion multiple times in case
 +                // we have more than one mismatch for unix-like systems
 +                let mut unix_suggested = false;
 +
 +                for (os, span) in mismatched {
 +                    let sugg = format!("target_os = \"{}\"", os);
 +                    diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect);
 +
 +                    if !unix_suggested && is_unix(os) {
 +                        diag.help("did you mean `unix`?");
 +                        unix_suggested = true;
 +                    }
 +                }
 +            });
 +        }
 +    }
 +}
 +
 +fn is_lint_level(symbol: Symbol) -> bool {
 +    matches!(symbol, sym::allow | sym::expect | sym::warn | sym::deny | sym::forbid)
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a4b8cbb0d82aaf8294ee89a9b9303efb283f7b94
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,125 @@@
++use rustc_ast::{ExprPrecedence, LitKind};
++use rustc_hir::{Block, ExprKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++use clippy_utils::{diagnostics::span_lint_and_then, is_else_clause, source::snippet_block_with_applicability};
++use rustc_errors::Applicability;
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Instead of using an if statement to convert a bool to an int,
++    /// this lint suggests using a `from()` function or an `as` coercion.
++    ///
++    /// ### Why is this bad?
++    /// Coercion or `from()` is idiomatic way to convert bool to a number.
++    /// Both methods are guaranteed to return 1 for true, and 0 for false.
++    ///
++    /// See https://doc.rust-lang.org/std/primitive.bool.html#impl-From%3Cbool%3E
++    ///
++    /// ### Example
++    /// ```rust
++    /// # let condition = false;
++    /// if condition {
++    ///     1_i64
++    /// } else {
++    ///     0
++    /// };
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// # let condition = false;
++    /// i64::from(condition);
++    /// ```
++    /// or
++    /// ```rust
++    /// # let condition = false;
++    /// condition as i64;
++    /// ```
++    #[clippy::version = "1.65.0"]
++    pub BOOL_TO_INT_WITH_IF,
++    style,
++    "using if to convert bool to int"
++}
++declare_lint_pass!(BoolToIntWithIf => [BOOL_TO_INT_WITH_IF]);
++
++impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf {
++    fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
++        if !expr.span.from_expansion() {
++            check_if_else(ctx, expr);
++        }
++    }
++}
++
++fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
++    if let ExprKind::If(check, then, Some(else_)) = expr.kind
++        && let Some(then_lit) = int_literal(then)
++        && let Some(else_lit) = int_literal(else_)
++        && check_int_literal_equals_val(then_lit, 1)
++        && check_int_literal_equals_val(else_lit, 0)
++    {
++        let mut applicability = Applicability::MachineApplicable;
++        let snippet = snippet_block_with_applicability(ctx, check.span, "..", None, &mut applicability);
++        let snippet_with_braces = {
++            let need_parens = should_have_parentheses(check);
++            let (left_paren, right_paren) = if need_parens {("(", ")")} else {("", "")};
++            format!("{left_paren}{snippet}{right_paren}")
++        };
++
++        let ty = ctx.typeck_results().expr_ty(then_lit); // then and else must be of same type
++
++        let suggestion = {
++            let wrap_in_curly = is_else_clause(ctx.tcx, expr);
++            let (left_curly, right_curly) = if wrap_in_curly {("{", "}")} else {("", "")};
++            format!(
++                "{left_curly}{ty}::from({snippet}){right_curly}"
++            )
++        }; // when used in else clause if statement should be wrapped in curly braces
++
++        span_lint_and_then(ctx,
++            BOOL_TO_INT_WITH_IF,
++            expr.span,
++            "boolean to int conversion using if",
++            |diag| {
++            diag.span_suggestion(
++                expr.span,
++                "replace with from",
++                suggestion,
++                applicability,
++            );
++            diag.note(format!("`{snippet_with_braces} as {ty}` or `{snippet_with_braces}.into()` can also be valid options"));
++        });
++    };
++}
++
++// If block contains only a int literal expression, return literal expression
++fn int_literal<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>) -> Option<&'tcx rustc_hir::Expr<'tcx>> {
++    if let ExprKind::Block(block, _) = expr.kind
++        && let Block {
++            stmts: [],       // Shouldn't lint if statements with side effects
++            expr: Some(expr),
++            ..
++        } = block
++        && let ExprKind::Lit(lit) = &expr.kind
++        && let LitKind::Int(_, _) = lit.node
++    {
++        Some(expr)
++    } else {
++        None
++    }
++}
++
++fn check_int_literal_equals_val<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>, expected_value: u128) -> bool {
++    if let ExprKind::Lit(lit) = &expr.kind
++        && let LitKind::Int(val, _) = lit.node
++        && val == expected_value
++    {
++        true
++    } else {
++        false
++    }
++}
++
++fn should_have_parentheses<'tcx>(check: &'tcx rustc_hir::Expr<'tcx>) -> bool {
++    check.precedence().order() < ExprPrecedence::Cast.order()
++}
index 64c5de5104206c73ee9eecb29dced83d11cbf658,0000000000000000000000000000000000000000..be02f328e989a67051091196a4d37a2bc8dc673b
mode 100644,000000..100644
--- /dev/null
@@@ -1,244 -1,0 +1,245 @@@
-         Self { ty_bounds: vec![TyBound::Nothing], cx }
 +use clippy_utils::diagnostics::span_lint_hir_and_then;
 +use clippy_utils::numeric_literal;
 +use clippy_utils::source::snippet_opt;
 +use if_chain::if_chain;
 +use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
 +use rustc_errors::Applicability;
 +use rustc_hir::{
 +    intravisit::{walk_expr, walk_stmt, Visitor},
 +    Body, Expr, ExprKind, HirId, Lit, Stmt, StmtKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::{
 +    lint::in_external_macro,
 +    ty::{self, FloatTy, IntTy, PolyFnSig, Ty},
 +};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use std::iter;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type
 +    /// inference.
 +    ///
 +    /// Default numeric fallback means that if numeric types have not yet been bound to concrete
 +    /// types at the end of type inference, then integer type is bound to `i32`, and similarly
 +    /// floating type is bound to `f64`.
 +    ///
 +    /// See [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback.
 +    ///
 +    /// ### Why is this bad?
 +    /// For those who are very careful about types, default numeric fallback
 +    /// can be a pitfall that cause unexpected runtime behavior.
 +    ///
 +    /// ### Known problems
 +    /// This lint can only be allowed at the function level or above.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let i = 10;
 +    /// let f = 1.23;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// 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."
 +}
 +
 +declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]);
 +
 +impl<'tcx> LateLintPass<'tcx> for DefaultNumericFallback {
 +    fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
 +        let mut visitor = NumericFallbackVisitor::new(cx);
 +        visitor.visit_body(body);
 +    }
 +}
 +
 +struct NumericFallbackVisitor<'a, 'tcx> {
 +    /// Stack manages type bound of exprs. The top element holds current expr type.
 +    ty_bounds: Vec<TyBound<'tcx>>,
 +
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>) -> Self {
-             }
++        Self {
++            ty_bounds: vec![TyBound::Nothing],
++            cx,
++        }
 +    }
 +
 +    /// Check whether a passed literal has potential to cause fallback or not.
 +    fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>, emit_hir_id: HirId) {
 +        if_chain! {
 +                if !in_external_macro(self.cx.sess(), lit.span);
 +                if let Some(ty_bound) = self.ty_bounds.last();
 +                if matches!(lit.node,
 +                            LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed));
 +                if !ty_bound.is_numeric();
 +                then {
 +                    let (suffix, is_float) = match lit_ty.kind() {
 +                        ty::Int(IntTy::I32) => ("i32", false),
 +                        ty::Float(FloatTy::F64) => ("f64", true),
 +                        // Default numeric fallback never results in other types.
 +                        _ => return,
 +                    };
 +
 +                    let src = if let Some(src) = snippet_opt(self.cx, lit.span) {
 +                        src
 +                    } else {
 +                        match lit.node {
 +                            LitKind::Int(src, _) => format!("{}", src),
 +                            LitKind::Float(src, _) => format!("{}", src),
 +                            _ => return,
 +                        }
 +                    };
 +                    let sugg = numeric_literal::format(&src, Some(suffix), is_float);
 +                    span_lint_hir_and_then(
 +                        self.cx,
 +                        DEFAULT_NUMERIC_FALLBACK,
 +                        emit_hir_id,
 +                        lit.span,
 +                        "default numeric fallback might occur",
 +                        |diag| {
 +                            diag.span_suggestion(lit.span, "consider adding suffix", sugg, Applicability::MaybeIncorrect);
 +                        }
 +                    );
 +                }
 +        }
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        match &expr.kind {
 +            ExprKind::Call(func, args) => {
 +                if let Some(fn_sig) = fn_sig_opt(self.cx, func.hir_id) {
 +                    for (expr, bound) in iter::zip(*args, fn_sig.skip_binder().inputs()) {
 +                        // Push found arg type, then visit arg.
 +                        self.ty_bounds.push(TyBound::Ty(*bound));
 +                        self.visit_expr(expr);
 +                        self.ty_bounds.pop();
 +                    }
 +                    return;
 +                }
-                     for (expr, bound) in
-                         iter::zip(std::iter::once(*receiver).chain(args.iter()), fn_sig.inputs())
-                     {
++            },
 +
 +            ExprKind::MethodCall(_, receiver, args, _) => {
 +                if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) {
 +                    let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder();
-             }
++                    for (expr, bound) in iter::zip(std::iter::once(*receiver).chain(args.iter()), fn_sig.inputs()) {
 +                        self.ty_bounds.push(TyBound::Ty(*bound));
 +                        self.visit_expr(expr);
 +                        self.ty_bounds.pop();
 +                    }
 +                    return;
 +                }
-             }
++            },
 +
 +            ExprKind::Struct(_, fields, base) => {
 +                let ty = self.cx.typeck_results().expr_ty(expr);
 +                if_chain! {
 +                    if let Some(adt_def) = ty.ty_adt_def();
 +                    if adt_def.is_struct();
 +                    if let Some(variant) = adt_def.variants().iter().next();
 +                    then {
 +                        let fields_def = &variant.fields;
 +
 +                        // Push field type then visit each field expr.
 +                        for field in fields.iter() {
 +                            let bound =
 +                                fields_def
 +                                    .iter()
 +                                    .find_map(|f_def| {
 +                                        if f_def.ident(self.cx.tcx) == field.ident
 +                                            { Some(self.cx.tcx.type_of(f_def.did)) }
 +                                        else { None }
 +                                    });
 +                            self.ty_bounds.push(bound.into());
 +                            self.visit_expr(field.expr);
 +                            self.ty_bounds.pop();
 +                        }
 +
 +                        // Visit base with no bound.
 +                        if let Some(base) = base {
 +                            self.ty_bounds.push(TyBound::Nothing);
 +                            self.visit_expr(base);
 +                            self.ty_bounds.pop();
 +                        }
 +                        return;
 +                    }
 +                }
-             }
++            },
 +
 +            ExprKind::Lit(lit) => {
 +                let ty = self.cx.typeck_results().expr_ty(expr);
 +                self.check_lit(lit, ty, expr.hir_id);
 +                return;
-             _ => {}
++            },
 +
-             }
++            _ => {},
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +
 +    fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
 +        match stmt.kind {
 +            StmtKind::Local(local) => {
 +                if local.ty.is_some() {
 +                    self.ty_bounds.push(TyBound::Any);
 +                } else {
 +                    self.ty_bounds.push(TyBound::Nothing);
 +                }
++            },
 +
 +            _ => self.ty_bounds.push(TyBound::Nothing),
 +        }
 +
 +        walk_stmt(self, stmt);
 +        self.ty_bounds.pop();
 +    }
 +}
 +
 +fn fn_sig_opt<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<PolyFnSig<'tcx>> {
 +    let node_ty = cx.typeck_results().node_type_opt(hir_id)?;
 +    // We can't use `Ty::fn_sig` because it automatically performs substs, this may result in FNs.
 +    match node_ty.kind() {
 +        ty::FnDef(def_id, _) => Some(cx.tcx.fn_sig(*def_id)),
 +        ty::FnPtr(fn_sig) => Some(*fn_sig),
 +        _ => None,
 +    }
 +}
 +
 +#[derive(Debug, Clone, Copy)]
 +enum TyBound<'tcx> {
 +    Any,
 +    Ty(Ty<'tcx>),
 +    Nothing,
 +}
 +
 +impl<'tcx> TyBound<'tcx> {
 +    fn is_numeric(self) -> bool {
 +        match self {
 +            TyBound::Any => true,
 +            TyBound::Ty(t) => t.is_numeric(),
 +            TyBound::Nothing => false,
 +        }
 +    }
 +}
 +
 +impl<'tcx> From<Option<Ty<'tcx>>> for TyBound<'tcx> {
 +    fn from(v: Option<Ty<'tcx>>) -> Self {
 +        match v {
 +            Some(t) => TyBound::Ty(t),
 +            None => TyBound::Nothing,
 +        }
 +    }
 +}
index d1ab7fb67962ea83c19d93360024b334b704cbaf,0000000000000000000000000000000000000000..88e28018e5d00529f0f1a63da92e29075aec75cc
mode 100644,000000..100644
--- /dev/null
@@@ -1,1487 -1,0 +1,1484 @@@
-                         } else {
-                             return Some(Position::MethodReceiver)
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 +use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 +use clippy_utils::sugg::has_enclosing_paren;
 +use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res};
 +use clippy_utils::{
 +    fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, meets_msrv, msrvs, path_to_local,
 +    walk_to_expr_usage,
 +};
 +use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
 +use rustc_data_structures::fx::FxIndexMap;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_ty, Visitor};
 +use rustc_hir::{
 +    self as hir, def_id::DefId, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy,
 +    GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind,
 +    Path, QPath, TraitItem, TraitItemKind, TyKind, UnOp,
 +};
 +use rustc_index::bit_set::BitSet;
 +use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
 +use rustc_middle::ty::{
 +    self, subst::Subst, Binder, BoundVariableKind, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind,
 +    ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults,
 +};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP};
 +use rustc_trait_selection::infer::InferCtxtExt as _;
 +use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
 +use std::collections::VecDeque;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for explicit `deref()` or `deref_mut()` method calls.
 +    ///
 +    /// ### Why is this bad?
 +    /// Dereferencing by `&*x` or `&mut *x` is clearer and more concise,
 +    /// when not part of a method chain.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::ops::Deref;
 +    /// let a: &mut String = &mut String::from("foo");
 +    /// let b: &str = a.deref();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let a: &mut String = &mut String::from("foo");
 +    /// let b = &*a;
 +    /// ```
 +    ///
 +    /// This lint excludes:
 +    /// ```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) {}
 +    ///
 +    /// let x: &i32 = &&&&&&5;
 +    /// fun(&x);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # fn fun(_a: &i32) {}
 +    /// 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
 +    /// let x = Some("");
 +    /// if let Some(ref x) = x {
 +    ///     // use `x` here
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for dereferencing expressions which would be covered by auto-deref.
 +    ///
 +    /// ### Why is this bad?
 +    /// This unnecessarily complicates the code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = String::new();
 +    /// let y: &str = &*x;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = String::new();
 +    /// let y: &str = &x;
 +    /// ```
 +    #[clippy::version = "1.60.0"]
 +    pub EXPLICIT_AUTO_DEREF,
 +    complexity,
 +    "dereferencing when the compiler would automatically dereference"
 +}
 +
 +impl_lint_pass!(Dereferencing => [
 +    EXPLICIT_DEREF_METHODS,
 +    NEEDLESS_BORROW,
 +    REF_BINDING_TO_REFERENCE,
 +    EXPLICIT_AUTO_DEREF,
 +]);
 +
 +#[derive(Default)]
 +pub struct Dereferencing {
 +    state: Option<(State, StateData)>,
 +
 +    // While parsing a `deref` method call in ufcs form, the path to the function is itself an
 +    // 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>>,
 +
 +    // `IntoIterator` for arrays requires Rust 1.53.
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl Dereferencing {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self {
 +            msrv,
 +            ..Dereferencing::default()
 +        }
 +    }
 +}
 +
 +struct StateData {
 +    /// Span of the top level expression
 +    span: Span,
 +    hir_id: HirId,
 +    position: Position,
 +}
 +
 +struct DerefedBorrow {
 +    count: usize,
 +    msg: &'static str,
 +    snip_expr: Option<HirId>,
 +}
 +
 +enum State {
 +    // Any number of deref method calls.
 +    DerefMethod {
 +        // The number of calls in a sequence which changed the referenced type
 +        ty_changed_count: usize,
 +        is_final_ufcs: bool,
 +        /// The required mutability
 +        target_mut: Mutability,
 +    },
 +    DerefedBorrow(DerefedBorrow),
 +    ExplicitDeref {
 +        mutability: Option<Mutability>,
 +    },
 +    ExplicitDerefField {
 +        name: Symbol,
 +    },
 +    Reborrow {
 +        mutability: Mutability,
 +    },
 +    Borrow {
 +        mutability: Mutability,
 +    },
 +}
 +
 +// A reference operation considered by this lint pass
 +enum RefOp {
 +    Method(Mutability),
 +    Deref,
 +    AddrOf(Mutability),
 +}
 +
 +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)>,
 +    /// The [`HirId`] that the lint should be emitted at.
 +    hir_id: HirId,
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for Dereferencing {
 +    #[expect(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 expr.span.from_expansion() {
 +            if let Some((state, data)) = self.state.take() {
 +                report(cx, expr, state, data);
 +            }
 +            return;
 +        }
 +
 +        let typeck = cx.typeck_results();
 +        let (kind, sub_expr) = if let Some(x) = try_parse_ref_op(cx.tcx, typeck, expr) {
 +            x
 +        } else {
 +            // The whole chain of reference operations has been seen
 +            if let Some((state, data)) = self.state.take() {
 +                report(cx, expr, state, data);
 +            }
 +            return;
 +        };
 +
 +        match (self.state.take(), kind) {
 +            (None, kind) => {
 +                let expr_ty = typeck.expr_ty(expr);
 +                let (position, adjustments) = walk_parents(cx, expr, self.msrv);
 +
 +                match kind {
 +                    RefOp::Deref => {
 +                        if let Position::FieldAccess(name) = position
 +                            && !ty_contains_field(typeck.expr_ty(sub_expr), name)
 +                        {
 +                            self.state = Some((
 +                                State::ExplicitDerefField { name },
 +                                StateData { span: expr.span, hir_id: expr.hir_id, position },
 +                            ));
 +                        } else if position.is_deref_stable() {
 +                            self.state = Some((
 +                                State::ExplicitDeref { mutability: None },
 +                                StateData { span: expr.span, hir_id: expr.hir_id, position },
 +                            ));
 +                        }
 +                    }
 +                    RefOp::Method(target_mut)
 +                        if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
 +                            && position.lint_explicit_deref() =>
 +                    {
 +                        self.state = Some((
 +                            State::DerefMethod {
 +                                ty_changed_count: if deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)) {
 +                                    0
 +                                } else {
 +                                    1
 +                                },
 +                                is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
 +                                target_mut,
 +                            },
 +                            StateData {
 +                                span: expr.span,
 +                                hir_id: expr.hir_id,
 +                                position
 +                            },
 +                        ));
 +                    },
 +                    RefOp::AddrOf(mutability) => {
 +                        // Find the number of times the borrow is auto-derefed.
 +                        let mut iter = adjustments.iter();
 +                        let mut deref_count = 0usize;
 +                        let next_adjust = loop {
 +                            match iter.next() {
 +                                Some(adjust) => {
 +                                    if !matches!(adjust.kind, Adjust::Deref(_)) {
 +                                        break Some(adjust);
 +                                    } else if !adjust.target.is_ref() {
 +                                        deref_count += 1;
 +                                        break iter.next();
 +                                    }
 +                                    deref_count += 1;
 +                                },
 +                                None => break None,
 +                            };
 +                        };
 +
 +                        // Determine the required number of references before any can be removed. In all cases the
 +                        // reference made by the current expression will be removed. After that there are four cases to
 +                        // handle.
 +                        //
 +                        // 1. Auto-borrow will trigger in the current position, so no further references are required.
 +                        // 2. Auto-deref ends at a reference, or the underlying type, so one extra needs to be left to
 +                        //    handle the automatically inserted re-borrow.
 +                        // 3. Auto-deref hits a user-defined `Deref` impl, so at least one reference needs to exist to
 +                        //    start auto-deref.
 +                        // 4. If the chain of non-user-defined derefs ends with a mutable re-borrow, and re-borrow
 +                        //    adjustments will not be inserted automatically, then leave one further reference to avoid
 +                        //    moving a mutable borrow.
 +                        //    e.g.
 +                        //        fn foo<T>(x: &mut Option<&mut T>, y: &mut T) {
 +                        //            let x = match x {
 +                        //                // Removing the borrow will cause `x` to be moved
 +                        //                Some(x) => &mut *x,
 +                        //                None => y
 +                        //            };
 +                        //        }
 +                        let deref_msg =
 +                            "this expression creates a reference which is immediately dereferenced by the compiler";
 +                        let borrow_msg = "this expression borrows a value the compiler would automatically borrow";
 +                        let impl_msg = "the borrowed expression implements the required traits";
 +
 +                        let (required_refs, msg, snip_expr) = if position.can_auto_borrow() {
 +                            (1, if deref_count == 1 { borrow_msg } else { deref_msg }, None)
 +                        } else if let Position::ImplArg(hir_id) = position {
 +                            (0, impl_msg, Some(hir_id))
 +                        } else if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) =
 +                            next_adjust.map(|a| &a.kind)
 +                        {
 +                            if matches!(mutability, AutoBorrowMutability::Mut { .. }) && !position.is_reborrow_stable()
 +                            {
 +                                (3, deref_msg, None)
 +                            } else {
 +                                (2, deref_msg, None)
 +                            }
 +                        } else {
 +                            (2, deref_msg, None)
 +                        };
 +
 +                        if deref_count >= required_refs {
 +                            self.state = Some((
 +                                State::DerefedBorrow(DerefedBorrow {
 +                                    // One of the required refs is for the current borrow expression, the remaining ones
 +                                    // can't be removed without breaking the code. See earlier comment.
 +                                    count: deref_count - required_refs,
 +                                    msg,
 +                                    snip_expr,
 +                                }),
 +                                StateData { span: expr.span, hir_id: expr.hir_id, position },
 +                            ));
 +                        } else if position.is_deref_stable()
 +                            // Auto-deref doesn't combine with other adjustments
 +                            && next_adjust.map_or(true, |a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
 +                            && iter.all(|a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
 +                        {
 +                            self.state = Some((
 +                                State::Borrow { mutability },
 +                                StateData {
 +                                    span: expr.span,
 +                                    hir_id: expr.hir_id,
 +                                    position
 +                                },
 +                            ));
 +                        }
 +                    },
 +                    RefOp::Method(..) => (),
 +                }
 +            },
 +            (
 +                Some((
 +                    State::DerefMethod {
 +                        target_mut,
 +                        ty_changed_count,
 +                        ..
 +                    },
 +                    data,
 +                )),
 +                RefOp::Method(_),
 +            ) => {
 +                self.state = Some((
 +                    State::DerefMethod {
 +                        ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) {
 +                            ty_changed_count
 +                        } else {
 +                            ty_changed_count + 1
 +                        },
 +                        is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
 +                        target_mut,
 +                    },
 +                    data,
 +                ));
 +            },
 +            (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(_)) if state.count != 0 => {
 +                self.state = Some((
 +                    State::DerefedBorrow(DerefedBorrow {
 +                        count: state.count - 1,
 +                        ..state
 +                    }),
 +                    data,
 +                ));
 +            },
 +            (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(mutability)) => {
 +                let position = data.position;
 +                report(cx, expr, State::DerefedBorrow(state), data);
 +                if position.is_deref_stable() {
 +                    self.state = Some((
 +                        State::Borrow { mutability },
 +                        StateData {
 +                            span: expr.span,
 +                            hir_id: expr.hir_id,
 +                            position,
 +                        },
 +                    ));
 +                }
 +            },
 +            (Some((State::DerefedBorrow(state), data)), RefOp::Deref) => {
 +                let position = data.position;
 +                report(cx, expr, State::DerefedBorrow(state), data);
 +                if let Position::FieldAccess(name) = position
 +                    && !ty_contains_field(typeck.expr_ty(sub_expr), name)
 +                {
 +                    self.state = Some((
 +                        State::ExplicitDerefField { name },
 +                        StateData { span: expr.span, hir_id: expr.hir_id, position },
 +                    ));
 +                } else if position.is_deref_stable() {
 +                    self.state = Some((
 +                        State::ExplicitDeref { mutability: None },
 +                        StateData { span: expr.span, hir_id: expr.hir_id, position },
 +                    ));
 +                }
 +            },
 +
 +            (Some((State::Borrow { mutability }, data)), RefOp::Deref) => {
 +                if typeck.expr_ty(sub_expr).is_ref() {
 +                    self.state = Some((State::Reborrow { mutability }, data));
 +                } else {
 +                    self.state = Some((
 +                        State::ExplicitDeref {
 +                            mutability: Some(mutability),
 +                        },
 +                        data,
 +                    ));
 +                }
 +            },
 +            (Some((State::Reborrow { mutability }, data)), RefOp::Deref) => {
 +                self.state = Some((
 +                    State::ExplicitDeref {
 +                        mutability: Some(mutability),
 +                    },
 +                    data,
 +                ));
 +            },
 +            (state @ Some((State::ExplicitDeref { .. }, _)), RefOp::Deref) => {
 +                self.state = state;
 +            },
 +            (Some((State::ExplicitDerefField { name }, data)), RefOp::Deref)
 +                if !ty_contains_field(typeck.expr_ty(sub_expr), name) =>
 +            {
 +                self.state = Some((State::ExplicitDerefField { name }, 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())],
 +                            hir_id: pat.hir_id,
 +                        }),
 +                    );
 +                }
 +            }
 +        }
 +    }
 +
 +    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;
 +                let lint = if pat.always_deref {
 +                    NEEDLESS_BORROW
 +                } else {
 +                    REF_BINDING_TO_REFERENCE
 +                };
 +                span_lint_hir_and_then(
 +                    cx,
 +                    lint,
 +                    pat.hir_id,
 +                    pat.spans,
 +                    "this pattern creates a reference to a reference",
 +                    |diag| {
 +                        diag.multipart_suggestion("try this", replacements, app);
 +                    },
 +                );
 +            }
 +            self.current_body = None;
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +fn try_parse_ref_op<'tcx>(
 +    tcx: TyCtxt<'tcx>,
 +    typeck: &'tcx TypeckResults<'_>,
 +    expr: &'tcx Expr<'_>,
 +) -> Option<(RefOp, &'tcx Expr<'tcx>)> {
 +    let (def_id, arg) = match expr.kind {
 +        ExprKind::MethodCall(_, arg, [], _) => (typeck.type_dependent_def_id(expr.hir_id)?, arg),
 +        ExprKind::Call(
 +            Expr {
 +                kind: ExprKind::Path(path),
 +                hir_id,
 +                ..
 +            },
 +            [arg],
 +        ) => (typeck.qpath_res(path, *hir_id).opt_def_id()?, arg),
 +        ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_unsafe_ptr() => {
 +            return Some((RefOp::Deref, sub_expr));
 +        },
 +        ExprKind::AddrOf(BorrowKind::Ref, mutability, sub_expr) => return Some((RefOp::AddrOf(mutability), sub_expr)),
 +        _ => return None,
 +    };
 +    if tcx.is_diagnostic_item(sym::deref_method, def_id) {
 +        Some((RefOp::Method(Mutability::Not), arg))
 +    } else if tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()? {
 +        Some((RefOp::Method(Mutability::Mut), arg))
 +    } else {
 +        None
 +    }
 +}
 +
 +// Checks whether the type for a deref call actually changed the type, not just the mutability of
 +// the reference.
 +fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
 +    match (result_ty.kind(), arg_ty.kind()) {
 +        (ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => result_ty == arg_ty,
 +
 +        // The result type for a deref method is always a reference
 +        // Not matching the previous pattern means the argument type is not a reference
 +        // This means that the type did change
 +        _ => false,
 +    }
 +}
 +
 +/// The position of an expression relative to it's parent.
 +#[derive(Clone, Copy)]
 +enum Position {
 +    MethodReceiver,
 +    /// The method is defined on a reference type. e.g. `impl Foo for &T`
 +    MethodReceiverRefImpl,
 +    Callee,
 +    ImplArg(HirId),
 +    FieldAccess(Symbol),
 +    Postfix,
 +    Deref,
 +    /// Any other location which will trigger auto-deref to a specific time.
 +    /// Contains the precedence of the parent expression and whether the target type is sized.
 +    DerefStable(i8, bool),
 +    /// Any other location which will trigger auto-reborrowing.
 +    /// Contains the precedence of the parent expression.
 +    ReborrowStable(i8),
 +    /// Contains the precedence of the parent expression.
 +    Other(i8),
 +}
 +impl Position {
 +    fn is_deref_stable(self) -> bool {
 +        matches!(self, Self::DerefStable(..))
 +    }
 +
 +    fn is_reborrow_stable(self) -> bool {
 +        matches!(self, Self::DerefStable(..) | Self::ReborrowStable(_))
 +    }
 +
 +    fn can_auto_borrow(self) -> bool {
 +        matches!(self, Self::MethodReceiver | Self::FieldAccess(_) | Self::Callee)
 +    }
 +
 +    fn lint_explicit_deref(self) -> bool {
 +        matches!(self, Self::Other(_) | Self::DerefStable(..) | Self::ReborrowStable(_))
 +    }
 +
 +    fn precedence(self) -> i8 {
 +        match self {
 +            Self::MethodReceiver
 +            | Self::MethodReceiverRefImpl
 +            | Self::Callee
 +            | Self::FieldAccess(_)
 +            | Self::Postfix => PREC_POSTFIX,
 +            Self::ImplArg(_) | Self::Deref => PREC_PREFIX,
 +            Self::DerefStable(p, _) | Self::ReborrowStable(p) | Self::Other(p) => p,
 +        }
 +    }
 +}
 +
 +/// Walks up the parent expressions attempting to determine both how stable the auto-deref result
 +/// is, and which adjustments will be applied to it. Note this will not consider auto-borrow
 +/// locations as those follow different rules.
 +#[expect(clippy::too_many_lines)]
 +fn walk_parents<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    e: &'tcx Expr<'_>,
 +    msrv: Option<RustcVersion>,
 +) -> (Position, &'tcx [Adjustment<'tcx>]) {
 +    let mut adjustments = [].as_slice();
 +    let mut precedence = 0i8;
 +    let ctxt = e.span.ctxt();
 +    let position = walk_to_expr_usage(cx, e, &mut |parent, child_id| {
 +        // LocalTableInContext returns the wrong lifetime, so go use `expr_adjustments` instead.
 +        if adjustments.is_empty() && let Node::Expr(e) = cx.tcx.hir().get(child_id) {
 +            adjustments = cx.typeck_results().expr_adjustments(e);
 +        }
 +        match parent {
 +            Node::Local(Local { ty: Some(ty), span, .. }) if span.ctxt() == ctxt => {
 +                Some(binding_ty_auto_deref_stability(cx, ty, precedence, List::empty()))
 +            },
 +            Node::Item(&Item {
 +                kind: ItemKind::Static(..) | ItemKind::Const(..),
 +                def_id,
 +                span,
 +                ..
 +            })
 +            | Node::TraitItem(&TraitItem {
 +                kind: TraitItemKind::Const(..),
 +                def_id,
 +                span,
 +                ..
 +            })
 +            | Node::ImplItem(&ImplItem {
 +                kind: ImplItemKind::Const(..),
 +                def_id,
 +                span,
 +                ..
 +            }) if span.ctxt() == ctxt => {
 +                let ty = cx.tcx.type_of(def_id);
 +                Some(ty_auto_deref_stability(cx, ty, precedence).position_for_result(cx))
 +            },
 +
 +            Node::Item(&Item {
 +                kind: ItemKind::Fn(..),
 +                def_id,
 +                span,
 +                ..
 +            })
 +            | Node::TraitItem(&TraitItem {
 +                kind: TraitItemKind::Fn(..),
 +                def_id,
 +                span,
 +                ..
 +            })
 +            | Node::ImplItem(&ImplItem {
 +                kind: ImplItemKind::Fn(..),
 +                def_id,
 +                span,
 +                ..
 +            }) if span.ctxt() == ctxt => {
 +                let output = cx
 +                    .tcx
 +                    .erase_late_bound_regions(cx.tcx.fn_sig(def_id.to_def_id()).output());
 +                Some(ty_auto_deref_stability(cx, output, precedence).position_for_result(cx))
 +            },
 +
 +            Node::ExprField(field) if field.span.ctxt() == ctxt => match get_parent_expr_for_hir(cx, field.hir_id) {
 +                Some(Expr {
 +                    hir_id,
 +                    kind: ExprKind::Struct(path, ..),
 +                    ..
 +                }) => variant_of_res(cx, cx.qpath_res(path, *hir_id))
 +                    .and_then(|variant| variant.fields.iter().find(|f| f.name == field.ident.name))
 +                    .map(|field_def| {
 +                        ty_auto_deref_stability(cx, cx.tcx.type_of(field_def.did), precedence).position_for_arg()
 +                    }),
 +                _ => None,
 +            },
 +
 +            Node::Expr(parent) if parent.span.ctxt() == ctxt => match parent.kind {
 +                ExprKind::Ret(_) => {
 +                    let owner_id = cx.tcx.hir().body_owner(cx.enclosing_body.unwrap());
 +                    Some(
 +                        if let Node::Expr(
 +                            closure_expr @ Expr {
 +                                kind: ExprKind::Closure(closure),
 +                                ..
 +                            },
 +                        ) = cx.tcx.hir().get(owner_id)
 +                        {
 +                            closure_result_position(cx, closure, cx.typeck_results().expr_ty(closure_expr), precedence)
 +                        } else {
 +                            let output = cx
 +                                .tcx
 +                                .erase_late_bound_regions(cx.tcx.fn_sig(cx.tcx.hir().local_def_id(owner_id)).output());
 +                            ty_auto_deref_stability(cx, output, precedence).position_for_result(cx)
 +                        },
 +                    )
 +                },
 +                ExprKind::Closure(closure) => Some(closure_result_position(
 +                    cx,
 +                    closure,
 +                    cx.typeck_results().expr_ty(parent),
 +                    precedence,
 +                )),
 +                ExprKind::Call(func, _) if func.hir_id == child_id => {
 +                    (child_id == e.hir_id).then_some(Position::Callee)
 +                },
 +                ExprKind::Call(func, args) => args
 +                    .iter()
 +                    .position(|arg| arg.hir_id == child_id)
 +                    .zip(expr_sig(cx, func))
 +                    .and_then(|(i, sig)| {
 +                        sig.input_with_hir(i).map(|(hir_ty, ty)| match hir_ty {
 +                            // Type inference for closures can depend on how they're called. Only go by the explicit
 +                            // types here.
 +                            Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()),
 +                            None => {
 +                                if let ty::Param(param_ty) = ty.skip_binder().kind() {
 +                                    needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv)
 +                                } else {
 +                                    ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence)
 +                                        .position_for_arg()
 +                                }
 +                            },
 +                        })
 +                    }),
 +                ExprKind::MethodCall(_, receiver, args, _) => {
 +                    let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
 +                    if receiver.hir_id == child_id {
 +                        // Check for calls to trait methods where the trait is implemented on a reference.
 +                        // Two cases need to be handled:
 +                        // * `self` methods on `&T` will never have auto-borrow
 +                        // * `&self` methods on `&T` can have auto-borrow, but `&self` methods on `T` will take
 +                        //   priority.
 +                        if e.hir_id != child_id {
 +                            return Some(Position::ReborrowStable(precedence))
 +                        } else if let Some(trait_id) = cx.tcx.trait_of_item(id)
 +                            && let arg_ty = cx.tcx.erase_regions(cx.typeck_results().expr_ty_adjusted(e))
 +                            && let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
 +                            && let subs = match cx
 +                                .typeck_results()
 +                                .node_substs_opt(parent.hir_id)
 +                                .and_then(|subs| subs.get(1..))
 +                            {
 +                                Some(subs) => cx.tcx.mk_substs(subs.iter().copied()),
 +                                None => cx.tcx.mk_substs(std::iter::empty::<ty::subst::GenericArg<'_>>()),
 +                            } && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
 +                                // Trait methods taking `&self`
 +                                sub_ty
 +                            } else {
 +                                // Trait methods taking `self`
 +                                arg_ty
 +                            } && impl_ty.is_ref()
 +                            && cx.tcx.infer_ctxt().enter(|infcx|
 +                                infcx
 +                                    .type_implements_trait(trait_id, impl_ty, subs, cx.param_env)
 +                                    .must_apply_modulo_regions()
 +                            )
 +                        {
 +                            return Some(Position::MethodReceiverRefImpl)
-                     args.iter()
-                         .position(|arg| arg.hir_id == child_id)
-                         .map(|i| {
-                             let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i + 1];
-                             if let ty::Param(param_ty) = ty.kind() {
-                                 needless_borrow_impl_arg_position(cx, parent, i + 1, *param_ty, e, precedence, msrv)
-                             } else {
-                                 ty_auto_deref_stability(
-                                     cx,
-                                     cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i + 1)),
-                                     precedence,
-                                 )
-                                 .position_for_arg()
-                             }
-                         })
 +                        }
++                        return Some(Position::MethodReceiver);
 +                    }
++                    args.iter().position(|arg| arg.hir_id == child_id).map(|i| {
++                        let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i + 1];
++                        if let ty::Param(param_ty) = ty.kind() {
++                            needless_borrow_impl_arg_position(cx, parent, i + 1, *param_ty, e, precedence, msrv)
++                        } else {
++                            ty_auto_deref_stability(
++                                cx,
++                                cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i + 1)),
++                                precedence,
++                            )
++                            .position_for_arg()
++                        }
++                    })
 +                },
 +                ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess(name.name)),
 +                ExprKind::Unary(UnOp::Deref, child) if child.hir_id == e.hir_id => Some(Position::Deref),
 +                ExprKind::Match(child, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
 +                | ExprKind::Index(child, _)
 +                    if child.hir_id == e.hir_id =>
 +                {
 +                    Some(Position::Postfix)
 +                },
 +                _ if child_id == e.hir_id => {
 +                    precedence = parent.precedence().order();
 +                    None
 +                },
 +                _ => None,
 +            },
 +            _ => None,
 +        }
 +    })
 +    .unwrap_or(Position::Other(precedence));
 +    (position, adjustments)
 +}
 +
 +fn closure_result_position<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    closure: &'tcx Closure<'_>,
 +    ty: Ty<'tcx>,
 +    precedence: i8,
 +) -> Position {
 +    match closure.fn_decl.output {
 +        FnRetTy::Return(hir_ty) => {
 +            if let Some(sig) = ty_sig(cx, ty)
 +                && let Some(output) = sig.output()
 +            {
 +                binding_ty_auto_deref_stability(cx, hir_ty, precedence, output.bound_vars())
 +            } else {
 +                Position::Other(precedence)
 +            }
 +        },
 +        FnRetTy::DefaultReturn(_) => Position::Other(precedence),
 +    }
 +}
 +
 +// Checks the stability of auto-deref when assigned to a binding with the given explicit type.
 +//
 +// e.g.
 +// let x = Box::new(Box::new(0u32));
 +// let y1: &Box<_> = x.deref();
 +// let y2: &Box<_> = &x;
 +//
 +// Here `y1` and `y2` would resolve to different types, so the type `&Box<_>` is not stable when
 +// switching to auto-dereferencing.
 +fn binding_ty_auto_deref_stability<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    ty: &'tcx hir::Ty<'_>,
 +    precedence: i8,
 +    binder_args: &'tcx List<BoundVariableKind>,
 +) -> Position {
 +    let TyKind::Rptr(_, ty) = &ty.kind else {
 +        return Position::Other(precedence);
 +    };
 +    let mut ty = ty;
 +
 +    loop {
 +        break match ty.ty.kind {
 +            TyKind::Rptr(_, ref ref_ty) => {
 +                ty = ref_ty;
 +                continue;
 +            },
 +            TyKind::Path(
 +                QPath::TypeRelative(_, path)
 +                | QPath::Resolved(
 +                    _,
 +                    Path {
 +                        segments: [.., path], ..
 +                    },
 +                ),
 +            ) => {
 +                if let Some(args) = path.args
 +                    && args.args.iter().any(|arg| match arg {
 +                        GenericArg::Infer(_) => true,
 +                        GenericArg::Type(ty) => ty_contains_infer(ty),
 +                        _ => false,
 +                    })
 +                {
 +                    Position::ReborrowStable(precedence)
 +                } else {
 +                    Position::DerefStable(
 +                        precedence,
 +                        cx.tcx
 +                            .erase_late_bound_regions(Binder::bind_with_vars(
 +                                cx.typeck_results().node_type(ty.ty.hir_id),
 +                                binder_args,
 +                            ))
 +                            .is_sized(cx.tcx.at(DUMMY_SP), cx.param_env.without_caller_bounds()),
 +                    )
 +                }
 +            },
 +            TyKind::Slice(_) => Position::DerefStable(precedence, false),
 +            TyKind::Array(..) | TyKind::Ptr(_) | TyKind::BareFn(_) => Position::DerefStable(precedence, true),
 +            TyKind::Never
 +            | TyKind::Tup(_)
 +            | TyKind::Path(_) => Position::DerefStable(
 +                precedence,
 +                cx.tcx
 +                    .erase_late_bound_regions(Binder::bind_with_vars(
 +                        cx.typeck_results().node_type(ty.ty.hir_id),
 +                        binder_args,
 +                    ))
 +                    .is_sized(cx.tcx.at(DUMMY_SP), cx.param_env.without_caller_bounds()),
 +            ),
 +            TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(..) | TyKind::TraitObject(..) | TyKind::Err => {
 +                Position::ReborrowStable(precedence)
 +            },
 +        };
 +    }
 +}
 +
 +// Checks whether a type is inferred at some point.
 +// e.g. `_`, `Box<_>`, `[_]`
 +fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
 +    struct V(bool);
 +    impl Visitor<'_> for V {
 +        fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
 +            if self.0
 +                || matches!(
 +                    ty.kind,
 +                    TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(_) | TyKind::Err
 +                )
 +            {
 +                self.0 = true;
 +            } else {
 +                walk_ty(self, ty);
 +            }
 +        }
 +
 +        fn visit_generic_arg(&mut self, arg: &GenericArg<'_>) {
 +            if self.0 || matches!(arg, GenericArg::Infer(_)) {
 +                self.0 = true;
 +            } else if let GenericArg::Type(ty) = arg {
 +                self.visit_ty(ty);
 +            }
 +        }
 +    }
 +    let mut v = V(false);
 +    v.visit_ty(ty);
 +    v.0
 +}
 +
 +// Checks whether:
 +// * child is an expression of the form `&e` in an argument position requiring an `impl Trait`
 +// * `e`'s type implements `Trait` and is copyable
 +// If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`.
 +//   The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to
 +// be moved, but it cannot be.
 +fn needless_borrow_impl_arg_position<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    parent: &Expr<'tcx>,
 +    arg_index: usize,
 +    param_ty: ParamTy,
 +    mut expr: &Expr<'tcx>,
 +    precedence: i8,
 +    msrv: Option<RustcVersion>,
 +) -> Position {
 +    let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
 +    let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
 +
 +    let Some(callee_def_id) = fn_def_id(cx, parent) else { return Position::Other(precedence) };
 +    let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
 +    let substs_with_expr_ty = cx
 +        .typeck_results()
 +        .node_substs(if let ExprKind::Call(callee, _) = parent.kind {
 +            callee.hir_id
 +        } else {
 +            parent.hir_id
 +        });
 +
 +    let predicates = cx.tcx.param_env(callee_def_id).caller_bounds();
 +    let projection_predicates = predicates
 +        .iter()
 +        .filter_map(|predicate| {
 +            if let PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
 +                Some(projection_predicate)
 +            } else {
 +                None
 +            }
 +        })
 +        .collect::<Vec<_>>();
 +
 +    let mut trait_with_ref_mut_self_method = false;
 +
 +    // If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return.
 +    if predicates
 +        .iter()
 +        .filter_map(|predicate| {
 +            if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder()
 +                && trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx)
 +            {
 +                Some(trait_predicate.trait_ref.def_id)
 +            } else {
 +                None
 +            }
 +        })
 +        .inspect(|trait_def_id| {
 +            trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id);
 +        })
 +        .all(|trait_def_id| {
 +            Some(trait_def_id) == destruct_trait_def_id
 +                || Some(trait_def_id) == sized_trait_def_id
 +                || cx.tcx.is_diagnostic_item(sym::Any, trait_def_id)
 +        })
 +    {
 +        return Position::Other(precedence);
 +    }
 +
 +    // `substs_with_referent_ty` can be constructed outside of `check_referent` because the same
 +    // elements are modified each time `check_referent` is called.
 +    let mut substs_with_referent_ty = substs_with_expr_ty.to_vec();
 +
 +    let mut check_referent = |referent| {
 +        let referent_ty = cx.typeck_results().expr_ty(referent);
 +
 +        if !is_copy(cx, referent_ty) {
 +            return false;
 +        }
 +
 +        // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
 +        if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
 +            return false;
 +        }
 +
 +        if !replace_types(
 +            cx,
 +            param_ty,
 +            referent_ty,
 +            fn_sig,
 +            arg_index,
 +            &projection_predicates,
 +            &mut substs_with_referent_ty,
 +        ) {
 +            return false;
 +        }
 +
 +        predicates.iter().all(|predicate| {
 +            if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder()
 +                && cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id)
 +                && let ty::Param(param_ty) = trait_predicate.self_ty().kind()
 +                && let GenericArgKind::Type(ty) = substs_with_referent_ty[param_ty.index as usize].unpack()
 +                && ty.is_array()
 +                && !meets_msrv(msrv, msrvs::ARRAY_INTO_ITERATOR)
 +            {
 +                return false;
 +            }
 +
 +            let predicate = EarlyBinder(predicate).subst(cx.tcx, &substs_with_referent_ty);
 +            let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate);
 +            cx.tcx
 +                .infer_ctxt()
 +                .enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
 +        })
 +    };
 +
 +    let mut needless_borrow = false;
 +    while let ExprKind::AddrOf(_, _, referent) = expr.kind {
 +        if !check_referent(referent) {
 +            break;
 +        }
 +        expr = referent;
 +        needless_borrow = true;
 +    }
 +
 +    if needless_borrow {
 +        Position::ImplArg(expr.hir_id)
 +    } else {
 +        Position::Other(precedence)
 +    }
 +}
 +
 +fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
 +    cx.tcx
 +        .associated_items(trait_def_id)
 +        .in_definition_order()
 +        .any(|assoc_item| {
 +            if assoc_item.fn_has_self_parameter {
 +                let self_ty = cx.tcx.fn_sig(assoc_item.def_id).skip_binder().inputs()[0];
 +                matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut))
 +            } else {
 +                false
 +            }
 +        })
 +}
 +
 +// Iteratively replaces `param_ty` with `new_ty` in `substs`, and similarly for each resulting
 +// projected type that is a type parameter. Returns `false` if replacing the types would have an
 +// effect on the function signature beyond substituting `new_ty` for `param_ty`.
 +// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757
 +fn replace_types<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    param_ty: ParamTy,
 +    new_ty: Ty<'tcx>,
 +    fn_sig: FnSig<'tcx>,
 +    arg_index: usize,
 +    projection_predicates: &[ProjectionPredicate<'tcx>],
 +    substs: &mut [ty::GenericArg<'tcx>],
 +) -> bool {
 +    let mut replaced = BitSet::new_empty(substs.len());
 +
 +    let mut deque = VecDeque::with_capacity(substs.len());
 +    deque.push_back((param_ty, new_ty));
 +
 +    while let Some((param_ty, new_ty)) = deque.pop_front() {
 +        // If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in.
 +        if !fn_sig
 +            .inputs_and_output
 +            .iter()
 +            .enumerate()
 +            .all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx)))
 +        {
 +            return false;
 +        }
 +
 +        substs[param_ty.index as usize] = ty::GenericArg::from(new_ty);
 +
 +        // The `replaced.insert(...)` check provides some protection against infinite loops.
 +        if replaced.insert(param_ty.index) {
 +            for projection_predicate in projection_predicates {
 +                if projection_predicate.projection_ty.self_ty() == param_ty.to_ty(cx.tcx)
 +                    && let Some(term_ty) = projection_predicate.term.ty()
 +                    && let ty::Param(term_param_ty) = term_ty.kind()
 +                {
 +                    let item_def_id = projection_predicate.projection_ty.item_def_id;
 +                    let assoc_item = cx.tcx.associated_item(item_def_id);
 +                    let projection = cx.tcx
 +                        .mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(new_ty, &[]));
 +
 +                    if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
 +                        && substs[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty)
 +                    {
 +                        deque.push_back((*term_param_ty, projected_ty));
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    true
 +}
 +
 +struct TyPosition<'tcx> {
 +    position: Position,
 +    ty: Option<Ty<'tcx>>,
 +}
 +impl From<Position> for TyPosition<'_> {
 +    fn from(position: Position) -> Self {
 +        Self { position, ty: None }
 +    }
 +}
 +impl<'tcx> TyPosition<'tcx> {
 +    fn new_deref_stable_for_result(precedence: i8, ty: Ty<'tcx>) -> Self {
 +        Self {
 +            position: Position::ReborrowStable(precedence),
 +            ty: Some(ty),
 +        }
 +    }
 +    fn position_for_result(self, cx: &LateContext<'tcx>) -> Position {
 +        match (self.position, self.ty) {
 +            (Position::ReborrowStable(precedence), Some(ty)) => {
 +                Position::DerefStable(precedence, ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env))
 +            },
 +            (position, _) => position,
 +        }
 +    }
 +    fn position_for_arg(self) -> Position {
 +        self.position
 +    }
 +}
 +
 +// Checks whether a type is stable when switching to auto dereferencing,
 +fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedence: i8) -> TyPosition<'tcx> {
 +    let ty::Ref(_, mut ty, _) = *ty.kind() else {
 +        return Position::Other(precedence).into();
 +    };
 +
 +    loop {
 +        break match *ty.kind() {
 +            ty::Ref(_, ref_ty, _) => {
 +                ty = ref_ty;
 +                continue;
 +            },
 +            ty::Param(_) => TyPosition::new_deref_stable_for_result(precedence, ty),
 +            ty::Infer(_) | ty::Error(_) | ty::Bound(..) | ty::Opaque(..) | ty::Placeholder(_) | ty::Dynamic(..) => {
 +                Position::ReborrowStable(precedence).into()
 +            },
 +            ty::Adt(..) if ty.has_placeholders() || ty.has_opaque_types() => {
 +                Position::ReborrowStable(precedence).into()
 +            },
 +            ty::Adt(_, substs) if substs.has_param_types_or_consts() => {
 +                TyPosition::new_deref_stable_for_result(precedence, ty)
 +            },
 +            ty::Bool
 +            | ty::Char
 +            | ty::Int(_)
 +            | ty::Uint(_)
 +            | ty::Array(..)
 +            | ty::Float(_)
 +            | ty::RawPtr(..)
 +            | ty::FnPtr(_) => Position::DerefStable(precedence, true).into(),
 +            ty::Str | ty::Slice(..) => Position::DerefStable(precedence, false).into(),
 +            ty::Adt(..)
 +            | ty::Foreign(_)
 +            | ty::FnDef(..)
 +            | ty::Generator(..)
 +            | ty::GeneratorWitness(..)
 +            | ty::Closure(..)
 +            | ty::Never
 +            | ty::Tuple(_)
 +            | ty::Projection(_) => Position::DerefStable(
 +                precedence,
 +                ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env.without_caller_bounds()),
 +            )
 +            .into(),
 +        };
 +    }
 +}
 +
 +fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool {
 +    if let ty::Adt(adt, _) = *ty.kind() {
 +        adt.is_struct() && adt.all_fields().any(|f| f.name == name)
 +    } else {
 +        false
 +    }
 +}
 +
 +#[expect(clippy::needless_pass_by_value, clippy::too_many_lines)]
 +fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) {
 +    match state {
 +        State::DerefMethod {
 +            ty_changed_count,
 +            is_final_ufcs,
 +            target_mut,
 +        } => {
 +            let mut app = Applicability::MachineApplicable;
 +            let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
 +            let ty = cx.typeck_results().expr_ty(expr);
 +            let (_, ref_count) = peel_mid_ty_refs(ty);
 +            let deref_str = if ty_changed_count >= ref_count && ref_count != 0 {
 +                // a deref call changing &T -> &U requires two deref operators the first time
 +                // this occurs. One to remove the reference, a second to call the deref impl.
 +                "*".repeat(ty_changed_count + 1)
 +            } else {
 +                "*".repeat(ty_changed_count)
 +            };
 +            let addr_of_str = if ty_changed_count < ref_count {
 +                // Check if a reborrow from &mut T -> &T is required.
 +                if target_mut == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
 +                    "&*"
 +                } else {
 +                    ""
 +                }
 +            } else if target_mut == Mutability::Mut {
 +                "&mut "
 +            } else {
 +                "&"
 +            };
 +
 +            let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX {
 +                format!("({})", expr_str)
 +            } else {
 +                expr_str.into_owned()
 +            };
 +
 +            span_lint_and_sugg(
 +                cx,
 +                EXPLICIT_DEREF_METHODS,
 +                data.span,
 +                match target_mut {
 +                    Mutability::Not => "explicit `deref` method call",
 +                    Mutability::Mut => "explicit `deref_mut` method call",
 +                },
 +                "try this",
 +                format!("{}{}{}", addr_of_str, deref_str, expr_str),
 +                app,
 +            );
 +        },
 +        State::DerefedBorrow(state) => {
 +            let mut app = Applicability::MachineApplicable;
 +            let snip_expr = state.snip_expr.map_or(expr, |hir_id| cx.tcx.hir().expect_expr(hir_id));
 +            let (snip, snip_is_macro) = snippet_with_context(cx, snip_expr.span, data.span.ctxt(), "..", &mut app);
 +            span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| {
 +                let calls_field = matches!(expr.kind, ExprKind::Field(..)) && matches!(data.position, Position::Callee);
 +                let sugg = if !snip_is_macro
 +                    && !has_enclosing_paren(&snip)
 +                    && (expr.precedence().order() < data.position.precedence() || calls_field)
 +                {
 +                    format!("({})", snip)
 +                } else {
 +                    snip.into()
 +                };
 +                diag.span_suggestion(data.span, "change this to", sugg, app);
 +            });
 +        },
 +        State::ExplicitDeref { mutability } => {
 +            if matches!(
 +                expr.kind,
 +                ExprKind::Block(..)
 +                    | ExprKind::ConstBlock(_)
 +                    | ExprKind::If(..)
 +                    | ExprKind::Loop(..)
 +                    | ExprKind::Match(..)
 +            ) && matches!(data.position, Position::DerefStable(_, true))
 +            {
 +                // Rustc bug: auto deref doesn't work on block expression when targeting sized types.
 +                return;
 +            }
 +
 +            let (prefix, precedence) = if let Some(mutability) = mutability
 +                && !cx.typeck_results().expr_ty(expr).is_ref()
 +            {
 +                let prefix = match mutability {
 +                    Mutability::Not => "&",
 +                    Mutability::Mut => "&mut ",
 +                };
 +                (prefix, 0)
 +            } else {
 +                ("", data.position.precedence())
 +            };
 +            span_lint_hir_and_then(
 +                cx,
 +                EXPLICIT_AUTO_DEREF,
 +                data.hir_id,
 +                data.span,
 +                "deref which would be done by auto-deref",
 +                |diag| {
 +                    let mut app = Applicability::MachineApplicable;
 +                    let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
 +                    let sugg =
 +                        if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) {
 +                            format!("{}({})", prefix, snip)
 +                        } else {
 +                            format!("{}{}", prefix, snip)
 +                        };
 +                    diag.span_suggestion(data.span, "try this", sugg, app);
 +                },
 +            );
 +        },
 +        State::ExplicitDerefField { .. } => {
 +            if matches!(
 +                expr.kind,
 +                ExprKind::Block(..)
 +                    | ExprKind::ConstBlock(_)
 +                    | ExprKind::If(..)
 +                    | ExprKind::Loop(..)
 +                    | ExprKind::Match(..)
 +            ) && matches!(data.position, Position::DerefStable(_, true))
 +            {
 +                // Rustc bug: auto deref doesn't work on block expression when targeting sized types.
 +                return;
 +            }
 +
 +            span_lint_hir_and_then(
 +                cx,
 +                EXPLICIT_AUTO_DEREF,
 +                data.hir_id,
 +                data.span,
 +                "deref which would be done by auto-deref",
 +                |diag| {
 +                    let mut app = Applicability::MachineApplicable;
 +                    let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
 +                    diag.span_suggestion(data.span, "try this", snip.into_owned(), app);
 +                },
 +            );
 +        },
 +        State::Borrow { .. } | State::Reborrow { .. } => (),
 +    }
 +}
 +
 +impl Dereferencing {
 +    fn check_local_usage<'tcx>(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, 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,
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
index 4d7f4076d7b5134b85e08e04b254e420971f964f,0000000000000000000000000000000000000000..ef9eeecc6a934f794859510ef414041e51509164
mode 100644,000000..100644
--- /dev/null
@@@ -1,117 -1,0 +1,117 @@@
-     /// in generic types and the user defined `impl` maybe is more generalized or
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::{is_default_equivalent, peel_blocks};
 +use rustc_hir::{
 +    def::{DefKind, Res},
 +    Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects manual `std::default::Default` implementations that are identical to a derived implementation.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is less concise.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct Foo {
 +    ///     bar: bool
 +    /// }
 +    ///
 +    /// impl Default for Foo {
 +    ///     fn default() -> Self {
 +    ///         Self {
 +    ///             bar: false
 +    ///         }
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// #[derive(Default)]
 +    /// struct Foo {
 +    ///     bar: bool
 +    /// }
 +    /// ```
 +    ///
 +    /// ### Known problems
 +    /// Derive macros [sometimes use incorrect bounds](https://github.com/rust-lang/rust/issues/26925)
++    /// in generic types and the user defined `impl` may be more generalized or
 +    /// specialized than what derive will produce. This lint can't detect the manual `impl`
 +    /// has exactly equal bounds, and therefore this lint is disabled for types with
 +    /// generic parameters.
 +    #[clippy::version = "1.57.0"]
 +    pub DERIVABLE_IMPLS,
 +    complexity,
 +    "manual implementation of the `Default` trait which is equal to a derive"
 +}
 +
 +declare_lint_pass!(DerivableImpls => [DERIVABLE_IMPLS]);
 +
 +fn is_path_self(e: &Expr<'_>) -> bool {
 +    if let ExprKind::Path(QPath::Resolved(_, p)) = e.kind {
 +        matches!(p.res, Res::SelfCtor(..) | Res::Def(DefKind::Ctor(..), _))
 +    } else {
 +        false
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        if_chain! {
 +            if let ItemKind::Impl(Impl {
 +                of_trait: Some(ref trait_ref),
 +                items: [child],
 +                self_ty,
 +                ..
 +            }) = item.kind;
 +            if !cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived);
 +            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();
 +            if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir);
 +            if let ImplItemKind::Fn(_, b) = &impl_item.kind;
 +            if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b);
 +            if let Some(adt_def) = cx.tcx.type_of(item.def_id).ty_adt_def();
 +            if let attrs = cx.tcx.hir().attrs(item.hir_id());
 +            if !attrs.iter().any(|attr| attr.doc_str().is_some());
 +            if let child_attrs = cx.tcx.hir().attrs(impl_item_hir);
 +            if !child_attrs.iter().any(|attr| attr.doc_str().is_some());
 +            if adt_def.is_struct();
 +            then {
 +                if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind {
 +                    if let Some(PathSegment { args: Some(a), .. }) = p.segments.last() {
 +                        for arg in a.args {
 +                            if !matches!(arg, GenericArg::Lifetime(_)) {
 +                                return;
 +                            }
 +                        }
 +                    }
 +                }
 +                let should_emit = match peel_blocks(func_expr).kind {
 +                    ExprKind::Tup(fields) => fields.iter().all(|e| is_default_equivalent(cx, e)),
 +                    ExprKind::Call(callee, args)
 +                        if is_path_self(callee) => args.iter().all(|e| is_default_equivalent(cx, e)),
 +                    ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_equivalent(cx, ef.expr)),
 +                    _ => false,
 +                };
 +                if should_emit {
 +                    let path_string = cx.tcx.def_path_str(adt_def.did());
 +                    span_lint_and_help(
 +                        cx,
 +                        DERIVABLE_IMPLS,
 +                        item.span,
 +                        "this `impl` can be derived",
 +                        None,
 +                        &format!("try annotating `{}` with `#[derive(Default)]`", path_string),
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
index 512872cedc1ea04d6f45be24bb378f77fa7372cf,0000000000000000000000000000000000000000..eb158d850fa724b81a8ad83f209bd51697fa8e59
mode 100644,000000..100644
--- /dev/null
@@@ -1,849 -1,0 +1,849 @@@
-                     fpu.visit_expr(&body.value);
 +use clippy_utils::attrs::is_doc_hidden;
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_then};
 +use clippy_utils::macros::{is_panic, root_macro_call_first_node};
 +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, method_chain_args, return_ty};
 +use if_chain::if_chain;
 +use itertools::Itertools;
 +use rustc_ast::ast::{Async, AttrKind, Attribute, Fn, FnRetTy, ItemKind};
 +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, MultiSpan, SuggestionStyle};
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::{self, Visitor};
 +use rustc_hir::{AnonConst, Expr};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty;
 +use rustc_parse::maybe_new_parser_from_source_str;
 +use rustc_parse::parser::ForceCollect;
 +use rustc_session::parse::ParseSess;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::def_id::LocalDefId;
 +use rustc_span::edition::Edition;
 +use rustc_span::source_map::{BytePos, FilePathMapping, SourceMap, Span};
 +use rustc_span::{sym, FileName, Pos};
 +use std::io;
 +use std::ops::Range;
 +use std::thread;
 +use url::Url;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the presence of `_`, `::` or camel-case words
 +    /// outside ticks in documentation.
 +    ///
 +    /// ### Why is this bad?
 +    /// *Rustdoc* supports markdown formatting, `_`, `::` and
 +    /// camel-case probably indicates some code which should be included between
 +    /// ticks. `_` can also be used for emphasis in markdown, this lint tries to
 +    /// consider that.
 +    ///
 +    /// ### Known problems
 +    /// Lots of bad docs won’t be fixed, what the lint checks
 +    /// for is limited, and there are still false positives. HTML elements and their
 +    /// content are not linted.
 +    ///
 +    /// In addition, when writing documentation comments, including `[]` brackets
 +    /// inside a link text would trip the parser. Therefore, documenting link with
 +    /// `[`SmallVec<[T; INLINE_CAPACITY]>`]` and then [`SmallVec<[T; INLINE_CAPACITY]>`]: SmallVec
 +    /// would fail.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// /// Do something with the foo_bar parameter. See also
 +    /// /// that::other::module::foo.
 +    /// // ^ `foo_bar` and `that::other::module::foo` should be ticked.
 +    /// fn doit(foo_bar: usize) {}
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// // Link text with `[]` brackets should be written as following:
 +    /// /// Consume the array and return the inner
 +    /// /// [`SmallVec<[T; INLINE_CAPACITY]>`][SmallVec].
 +    /// /// [SmallVec]: SmallVec
 +    /// fn main() {}
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DOC_MARKDOWN,
 +    pedantic,
 +    "presence of `_`, `::` or camel-case outside backticks in documentation"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the doc comments of publicly visible
 +    /// unsafe functions and warns if there is no `# Safety` section.
 +    ///
 +    /// ### Why is this bad?
 +    /// Unsafe functions should document their safety
 +    /// preconditions, so that users can be sure they are using them safely.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    ///# type Universe = ();
 +    /// /// This function should really be documented
 +    /// pub unsafe fn start_apocalypse(u: &mut Universe) {
 +    ///     unimplemented!();
 +    /// }
 +    /// ```
 +    ///
 +    /// At least write a line about safety:
 +    ///
 +    /// ```rust
 +    ///# type Universe = ();
 +    /// /// # Safety
 +    /// ///
 +    /// /// This function should not be called before the horsemen are ready.
 +    /// pub unsafe fn start_apocalypse(u: &mut Universe) {
 +    ///     unimplemented!();
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub MISSING_SAFETY_DOC,
 +    style,
 +    "`pub unsafe fn` without `# Safety` docs"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks the doc comments of publicly visible functions that
 +    /// return a `Result` type and warns if there is no `# Errors` section.
 +    ///
 +    /// ### Why is this bad?
 +    /// Documenting the type of errors that can be returned from a
 +    /// function can help callers write code to handle the errors appropriately.
 +    ///
 +    /// ### Examples
 +    /// Since the following function returns a `Result` it has an `# Errors` section in
 +    /// its doc comment:
 +    ///
 +    /// ```rust
 +    ///# use std::io;
 +    /// /// # Errors
 +    /// ///
 +    /// /// Will return `Err` if `filename` does not exist or the user does not have
 +    /// /// permission to read it.
 +    /// pub fn read(filename: String) -> io::Result<String> {
 +    ///     unimplemented!();
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.41.0"]
 +    pub MISSING_ERRORS_DOC,
 +    pedantic,
 +    "`pub fn` returns `Result` without `# Errors` in doc comment"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks the doc comments of publicly visible functions that
 +    /// may panic and warns if there is no `# Panics` section.
 +    ///
 +    /// ### Why is this bad?
 +    /// Documenting the scenarios in which panicking occurs
 +    /// can help callers who do not want to panic to avoid those situations.
 +    ///
 +    /// ### Examples
 +    /// Since the following function may panic it has a `# Panics` section in
 +    /// its doc comment:
 +    ///
 +    /// ```rust
 +    /// /// # Panics
 +    /// ///
 +    /// /// Will panic if y is 0
 +    /// pub fn divide_by(x: i32, y: i32) -> i32 {
 +    ///     if y == 0 {
 +    ///         panic!("Cannot divide by 0")
 +    ///     } else {
 +    ///         x / y
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub MISSING_PANICS_DOC,
 +    pedantic,
 +    "`pub fn` may panic without `# Panics` in doc comment"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `fn main() { .. }` in doctests
 +    ///
 +    /// ### Why is this bad?
 +    /// The test can be shorter (and likely more readable)
 +    /// if the `fn main()` is left implicit.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// /// An example of a doctest with a `main()` function
 +    /// ///
 +    /// /// # Examples
 +    /// ///
 +    /// /// ```
 +    /// /// fn main() {
 +    /// ///     // this needs not be in an `fn`
 +    /// /// }
 +    /// /// ```
 +    /// fn needless_main() {
 +    ///     unimplemented!();
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.40.0"]
 +    pub NEEDLESS_DOCTEST_MAIN,
 +    style,
 +    "presence of `fn main() {` in code examples"
 +}
 +
 +#[expect(clippy::module_name_repetitions)]
 +#[derive(Clone)]
 +pub struct DocMarkdown {
 +    valid_idents: FxHashSet<String>,
 +    in_trait_impl: bool,
 +}
 +
 +impl DocMarkdown {
 +    pub fn new(valid_idents: FxHashSet<String>) -> Self {
 +        Self {
 +            valid_idents,
 +            in_trait_impl: false,
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(DocMarkdown =>
 +    [DOC_MARKDOWN, MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, NEEDLESS_DOCTEST_MAIN]
 +);
 +
 +impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
 +    fn check_crate(&mut self, cx: &LateContext<'tcx>) {
 +        let attrs = cx.tcx.hir().attrs(hir::CRATE_HIR_ID);
 +        check_attrs(cx, &self.valid_idents, attrs);
 +    }
 +
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        let headers = check_attrs(cx, &self.valid_idents, attrs);
 +        match item.kind {
 +            hir::ItemKind::Fn(ref sig, _, body_id) => {
 +                if !(is_entrypoint_fn(cx, item.def_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
 +                    let body = cx.tcx.hir().body(body_id);
 +                    let mut fpu = FindPanicUnwrap {
 +                        cx,
 +                        typeck_results: cx.tcx.typeck(item.def_id),
 +                        panic_span: None,
 +                    };
-             fpu.visit_expr(&body.value);
++                    fpu.visit_expr(body.value);
 +                    lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span);
 +                }
 +            },
 +            hir::ItemKind::Impl(impl_) => {
 +                self.in_trait_impl = impl_.of_trait.is_some();
 +            },
 +            hir::ItemKind::Trait(_, unsafety, ..) => {
 +                if !headers.safety && unsafety == hir::Unsafety::Unsafe {
 +                    span_lint(
 +                        cx,
 +                        MISSING_SAFETY_DOC,
 +                        item.span,
 +                        "docs for unsafe trait missing `# Safety` section",
 +                    );
 +                }
 +            },
 +            _ => (),
 +        }
 +    }
 +
 +    fn check_item_post(&mut self, _cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +        if let hir::ItemKind::Impl { .. } = item.kind {
 +            self.in_trait_impl = false;
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        let headers = check_attrs(cx, &self.valid_idents, attrs);
 +        if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind {
 +            if !in_external_macro(cx.tcx.sess, item.span) {
 +                lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, None, None);
 +            }
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        let headers = check_attrs(cx, &self.valid_idents, attrs);
 +        if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) {
 +            return;
 +        }
 +        if let hir::ImplItemKind::Fn(ref sig, body_id) = item.kind {
 +            let body = cx.tcx.hir().body(body_id);
 +            let mut fpu = FindPanicUnwrap {
 +                cx,
 +                typeck_results: cx.tcx.typeck(item.def_id),
 +                panic_span: None,
 +            };
-                 let ret_ty = typeck.expr_ty(&body.value);
++            fpu.visit_expr(body.value);
 +            lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span);
 +        }
 +    }
 +}
 +
 +fn lint_for_missing_headers<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    def_id: LocalDefId,
 +    span: impl Into<MultiSpan> + Copy,
 +    sig: &hir::FnSig<'_>,
 +    headers: DocHeaders,
 +    body_id: Option<hir::BodyId>,
 +    panic_span: Option<Span>,
 +) {
 +    if !cx.access_levels.is_exported(def_id) {
 +        return; // Private functions do not require doc comments
 +    }
 +
 +    // do not lint if any parent has `#[doc(hidden)]` attribute (#7347)
 +    if cx
 +        .tcx
 +        .hir()
 +        .parent_iter(cx.tcx.hir().local_def_id_to_hir_id(def_id))
 +        .any(|(id, _node)| is_doc_hidden(cx.tcx.hir().attrs(id)))
 +    {
 +        return;
 +    }
 +
 +    if !headers.safety && sig.header.unsafety == hir::Unsafety::Unsafe {
 +        span_lint(
 +            cx,
 +            MISSING_SAFETY_DOC,
 +            span,
 +            "unsafe function's docs miss `# Safety` section",
 +        );
 +    }
 +    if !headers.panics && panic_span.is_some() {
 +        span_lint_and_note(
 +            cx,
 +            MISSING_PANICS_DOC,
 +            span,
 +            "docs for function which may panic missing `# Panics` section",
 +            panic_span,
 +            "first possible panic found here",
 +        );
 +    }
 +    if !headers.errors {
 +        let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
 +        if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::Result) {
 +            span_lint(
 +                cx,
 +                MISSING_ERRORS_DOC,
 +                span,
 +                "docs for function returning `Result` missing `# Errors` section",
 +            );
 +        } else {
 +            if_chain! {
 +                if let Some(body_id) = body_id;
 +                if let Some(future) = cx.tcx.lang_items().future_trait();
 +                let typeck = cx.tcx.typeck_body(body_id);
 +                let body = cx.tcx.hir().body(body_id);
-             let receiver_ty = self.typeck_results.expr_ty(&arglists[0].0).peel_refs();
++                let ret_ty = typeck.expr_ty(body.value);
 +                if implements_trait(cx, ret_ty, future, &[]);
 +                if let ty::Opaque(_, subs) = ret_ty.kind();
 +                if let Some(gen) = subs.types().next();
 +                if let ty::Generator(_, subs, _) = gen.kind();
 +                if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym::Result);
 +                then {
 +                    span_lint(
 +                        cx,
 +                        MISSING_ERRORS_DOC,
 +                        span,
 +                        "docs for function returning `Result` missing `# Errors` section",
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/// Cleanup documentation decoration.
 +///
 +/// We can't use `rustc_ast::attr::AttributeMethods::with_desugared_doc` or
 +/// `rustc_ast::parse::lexer::comments::strip_doc_comment_decoration` because we
 +/// need to keep track of
 +/// the spans but this function is inspired from the later.
 +#[expect(clippy::cast_possible_truncation)]
 +#[must_use]
 +pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: Span) -> (String, Vec<(usize, Span)>) {
 +    // one-line comments lose their prefix
 +    if comment_kind == CommentKind::Line {
 +        let mut doc = doc.to_owned();
 +        doc.push('\n');
 +        let len = doc.len();
 +        // +3 skips the opening delimiter
 +        return (doc, vec![(len, span.with_lo(span.lo() + BytePos(3)))]);
 +    }
 +
 +    let mut sizes = vec![];
 +    let mut contains_initial_stars = false;
 +    for line in doc.lines() {
 +        let offset = line.as_ptr() as usize - doc.as_ptr() as usize;
 +        debug_assert_eq!(offset as u32 as usize, offset);
 +        contains_initial_stars |= line.trim_start().starts_with('*');
 +        // +1 adds the newline, +3 skips the opening delimiter
 +        sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(3 + offset as u32))));
 +    }
 +    if !contains_initial_stars {
 +        return (doc.to_string(), sizes);
 +    }
 +    // remove the initial '*'s if any
 +    let mut no_stars = String::with_capacity(doc.len());
 +    for line in doc.lines() {
 +        let mut chars = line.chars();
 +        for c in &mut chars {
 +            if c.is_whitespace() {
 +                no_stars.push(c);
 +            } else {
 +                no_stars.push(if c == '*' { ' ' } else { c });
 +                break;
 +            }
 +        }
 +        no_stars.push_str(chars.as_str());
 +        no_stars.push('\n');
 +    }
 +
 +    (no_stars, sizes)
 +}
 +
 +#[derive(Copy, Clone)]
 +struct DocHeaders {
 +    safety: bool,
 +    errors: bool,
 +    panics: bool,
 +}
 +
 +fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &'a [Attribute]) -> DocHeaders {
 +    use pulldown_cmark::{BrokenLink, CowStr, Options};
 +    /// We don't want the parser to choke on intra doc links. Since we don't
 +    /// actually care about rendering them, just pretend that all broken links are
 +    /// point to a fake address.
 +    #[expect(clippy::unnecessary_wraps)] // we're following a type signature
 +    fn fake_broken_link_callback<'a>(_: BrokenLink<'_>) -> Option<(CowStr<'a>, CowStr<'a>)> {
 +        Some(("fake".into(), "fake".into()))
 +    }
 +
 +    let mut doc = String::new();
 +    let mut spans = vec![];
 +
 +    for attr in attrs {
 +        if let AttrKind::DocComment(comment_kind, comment) = attr.kind {
 +            let (comment, current_spans) = strip_doc_comment_decoration(comment.as_str(), comment_kind, attr.span);
 +            spans.extend_from_slice(&current_spans);
 +            doc.push_str(&comment);
 +        } else if attr.has_name(sym::doc) {
 +            // ignore mix of sugared and non-sugared doc
 +            // don't trigger the safety or errors check
 +            return DocHeaders {
 +                safety: true,
 +                errors: true,
 +                panics: true,
 +            };
 +        }
 +    }
 +
 +    let mut current = 0;
 +    for &mut (ref mut offset, _) in &mut spans {
 +        let offset_copy = *offset;
 +        *offset = current;
 +        current += offset_copy;
 +    }
 +
 +    if doc.is_empty() {
 +        return DocHeaders {
 +            safety: false,
 +            errors: false,
 +            panics: false,
 +        };
 +    }
 +
 +    let mut cb = fake_broken_link_callback;
 +
 +    let parser =
 +        pulldown_cmark::Parser::new_with_broken_link_callback(&doc, Options::empty(), Some(&mut cb)).into_offset_iter();
 +    // Iterate over all `Events` and combine consecutive events into one
 +    let events = parser.coalesce(|previous, current| {
 +        use pulldown_cmark::Event::Text;
 +
 +        let previous_range = previous.1;
 +        let current_range = current.1;
 +
 +        match (previous.0, current.0) {
 +            (Text(previous), Text(current)) => {
 +                let mut previous = previous.to_string();
 +                previous.push_str(&current);
 +                Ok((Text(previous.into()), previous_range))
 +            },
 +            (previous, current) => Err(((previous, previous_range), (current, current_range))),
 +        }
 +    });
 +    check_doc(cx, valid_idents, events, &spans)
 +}
 +
 +const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"];
 +
 +fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize>)>>(
 +    cx: &LateContext<'_>,
 +    valid_idents: &FxHashSet<String>,
 +    events: Events,
 +    spans: &[(usize, Span)],
 +) -> DocHeaders {
 +    // true if a safety header was found
 +    use pulldown_cmark::Event::{
 +        Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
 +    };
 +    use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph};
 +    use pulldown_cmark::{CodeBlockKind, CowStr};
 +
 +    let mut headers = DocHeaders {
 +        safety: false,
 +        errors: false,
 +        panics: false,
 +    };
 +    let mut in_code = false;
 +    let mut in_link = None;
 +    let mut in_heading = false;
 +    let mut is_rust = false;
 +    let mut edition = None;
 +    let mut ticks_unbalanced = false;
 +    let mut text_to_check: Vec<(CowStr<'_>, Span)> = Vec::new();
 +    let mut paragraph_span = spans.get(0).expect("function isn't called if doc comment is empty").1;
 +    for (event, range) in events {
 +        match event {
 +            Start(CodeBlock(ref kind)) => {
 +                in_code = true;
 +                if let CodeBlockKind::Fenced(lang) = kind {
 +                    for item in lang.split(',') {
 +                        if item == "ignore" {
 +                            is_rust = false;
 +                            break;
 +                        }
 +                        if let Some(stripped) = item.strip_prefix("edition") {
 +                            is_rust = true;
 +                            edition = stripped.parse::<Edition>().ok();
 +                        } else if item.is_empty() || RUST_CODE.contains(&item) {
 +                            is_rust = true;
 +                        }
 +                    }
 +                }
 +            },
 +            End(CodeBlock(_)) => {
 +                in_code = false;
 +                is_rust = false;
 +            },
 +            Start(Link(_, url, _)) => in_link = Some(url),
 +            End(Link(..)) => in_link = None,
 +            Start(Heading(_, _, _) | Paragraph | Item) => {
 +                if let Start(Heading(_, _, _)) = event {
 +                    in_heading = true;
 +                }
 +                ticks_unbalanced = false;
 +                let (_, span) = get_current_span(spans, range.start);
 +                paragraph_span = first_line_of_span(cx, span);
 +            },
 +            End(Heading(_, _, _) | Paragraph | Item) => {
 +                if let End(Heading(_, _, _)) = event {
 +                    in_heading = false;
 +                }
 +                if ticks_unbalanced {
 +                    span_lint_and_help(
 +                        cx,
 +                        DOC_MARKDOWN,
 +                        paragraph_span,
 +                        "backticks are unbalanced",
 +                        None,
 +                        "a backtick may be missing a pair",
 +                    );
 +                } else {
 +                    for (text, span) in text_to_check {
 +                        check_text(cx, valid_idents, &text, span);
 +                    }
 +                }
 +                text_to_check = Vec::new();
 +            },
 +            Start(_tag) | End(_tag) => (), // We don't care about other tags
 +            Html(_html) => (),             // HTML is weird, just ignore it
 +            SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (),
 +            FootnoteReference(text) | Text(text) => {
 +                let (begin, span) = get_current_span(spans, range.start);
 +                paragraph_span = paragraph_span.with_hi(span.hi());
 +                ticks_unbalanced |= text.contains('`') && !in_code;
 +                if Some(&text) == in_link.as_ref() || ticks_unbalanced {
 +                    // Probably a link of the form `<http://example.com>`
 +                    // Which are represented as a link to "http://example.com" with
 +                    // text "http://example.com" by pulldown-cmark
 +                    continue;
 +                }
 +                let trimmed_text = text.trim();
 +                headers.safety |= in_heading && trimmed_text == "Safety";
 +                headers.safety |= in_heading && trimmed_text == "Implementation safety";
 +                headers.safety |= in_heading && trimmed_text == "Implementation Safety";
 +                headers.errors |= in_heading && trimmed_text == "Errors";
 +                headers.panics |= in_heading && trimmed_text == "Panics";
 +                if in_code {
 +                    if is_rust {
 +                        let edition = edition.unwrap_or_else(|| cx.tcx.sess.edition());
 +                        check_code(cx, &text, edition, span);
 +                    }
 +                } else {
 +                    // Adjust for the beginning of the current `Event`
 +                    let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin));
 +                    text_to_check.push((text, span));
 +                }
 +            },
 +        }
 +    }
 +    headers
 +}
 +
 +fn get_current_span(spans: &[(usize, Span)], idx: usize) -> (usize, Span) {
 +    let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) {
 +        Ok(o) => o,
 +        Err(e) => e - 1,
 +    };
 +    spans[index]
 +}
 +
 +fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
 +    fn has_needless_main(code: String, edition: Edition) -> bool {
 +        rustc_driver::catch_fatal_errors(|| {
 +            rustc_span::create_session_globals_then(edition, || {
 +                let filename = FileName::anon_source_code(&code);
 +
 +                let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
 +                let fallback_bundle =
 +                    rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
 +                let emitter = EmitterWriter::new(
 +                    Box::new(io::sink()),
 +                    None,
 +                    None,
 +                    fallback_bundle,
 +                    false,
 +                    false,
 +                    false,
 +                    None,
 +                    false,
 +                );
 +                let handler = Handler::with_emitter(false, None, Box::new(emitter));
 +                let sess = ParseSess::with_span_handler(handler, sm);
 +
 +                let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) {
 +                    Ok(p) => p,
 +                    Err(errs) => {
 +                        drop(errs);
 +                        return false;
 +                    },
 +                };
 +
 +                let mut relevant_main_found = false;
 +                loop {
 +                    match parser.parse_item(ForceCollect::No) {
 +                        Ok(Some(item)) => match &item.kind {
 +                            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,
 +                                    FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
 +                                    FnRetTy::Ty(_) => false,
 +                                };
 +
 +                                if returns_nothing && !is_async && !block.stmts.is_empty() {
 +                                    // This main function should be linted, but only if there are no other functions
 +                                    relevant_main_found = true;
 +                                } else {
 +                                    // This main function should not be linted, we're done
 +                                    return false;
 +                                }
 +                            },
 +                            // Tests with one of these items are ignored
 +                            ItemKind::Static(..)
 +                            | ItemKind::Const(..)
 +                            | ItemKind::ExternCrate(..)
 +                            | ItemKind::ForeignMod(..)
 +                            // Another function was found; this case is ignored
 +                            | ItemKind::Fn(..) => return false,
 +                            _ => {},
 +                        },
 +                        Ok(None) => break,
 +                        Err(e) => {
 +                            e.cancel();
 +                            return false;
 +                        },
 +                    }
 +                }
 +
 +                relevant_main_found
 +            })
 +        })
 +        .ok()
 +        .unwrap_or_default()
 +    }
 +
 +    // Because of the global session, we need to create a new session in a different thread with
 +    // the edition we need.
 +    let text = text.to_owned();
 +    if thread::spawn(move || has_needless_main(text, edition))
 +        .join()
 +        .expect("thread::spawn failed")
 +    {
 +        span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
 +    }
 +}
 +
 +fn check_text(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, text: &str, span: Span) {
 +    for word in text.split(|c: char| c.is_whitespace() || c == '\'') {
 +        // Trim punctuation as in `some comment (see foo::bar).`
 +        //                                                   ^^
 +        // Or even as in `_foo bar_` which is emphasized. Also preserve `::` as a prefix/suffix.
 +        let mut word = word.trim_matches(|c: char| !c.is_alphanumeric() && c != ':');
 +
 +        // Remove leading or trailing single `:` which may be part of a sentence.
 +        if word.starts_with(':') && !word.starts_with("::") {
 +            word = word.trim_start_matches(':');
 +        }
 +        if word.ends_with(':') && !word.ends_with("::") {
 +            word = word.trim_end_matches(':');
 +        }
 +
 +        if valid_idents.contains(word) || word.chars().all(|c| c == ':') {
 +            continue;
 +        }
 +
 +        // Adjust for the current word
 +        let offset = word.as_ptr() as usize - text.as_ptr() as usize;
 +        let span = Span::new(
 +            span.lo() + BytePos::from_usize(offset),
 +            span.lo() + BytePos::from_usize(offset + word.len()),
 +            span.ctxt(),
 +            span.parent(),
 +        );
 +
 +        check_word(cx, word, span);
 +    }
 +}
 +
 +fn check_word(cx: &LateContext<'_>, word: &str, span: Span) {
 +    /// Checks if a string is camel-case, i.e., contains at least two uppercase
 +    /// letters (`Clippy` is ok) and one lower-case letter (`NASA` is ok).
 +    /// Plurals are also excluded (`IDs` is ok).
 +    fn is_camel_case(s: &str) -> bool {
 +        if s.starts_with(|c: char| c.is_ascii_digit()) {
 +            return false;
 +        }
 +
 +        let s = s.strip_suffix('s').unwrap_or(s);
 +
 +        s.chars().all(char::is_alphanumeric)
 +            && s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1
 +            && s.chars().filter(|&c| c.is_lowercase()).take(1).count() > 0
 +    }
 +
 +    fn has_underscore(s: &str) -> bool {
 +        s != "_" && !s.contains("\\_") && s.contains('_')
 +    }
 +
 +    fn has_hyphen(s: &str) -> bool {
 +        s != "-" && s.contains('-')
 +    }
 +
 +    if let Ok(url) = Url::parse(word) {
 +        // try to get around the fact that `foo::bar` parses as a valid URL
 +        if !url.cannot_be_a_base() {
 +            span_lint(
 +                cx,
 +                DOC_MARKDOWN,
 +                span,
 +                "you should put bare URLs between `<`/`>` or make a proper Markdown link",
 +            );
 +
 +            return;
 +        }
 +    }
 +
 +    // We assume that mixed-case words are not meant to be put inside backticks. (Issue #2343)
 +    if has_underscore(word) && has_hyphen(word) {
 +        return;
 +    }
 +
 +    if has_underscore(word) || word.contains("::") || is_camel_case(word) {
 +        let mut applicability = Applicability::MachineApplicable;
 +
 +        span_lint_and_then(
 +            cx,
 +            DOC_MARKDOWN,
 +            span,
 +            "item in documentation is missing backticks",
 +            |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,
 +                );
 +            },
 +        );
 +    }
 +}
 +
 +struct FindPanicUnwrap<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    panic_span: Option<Span>,
 +    typeck_results: &'tcx ty::TypeckResults<'tcx>,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
 +    type NestedFilter = nested_filter::OnlyBodies;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if self.panic_span.is_some() {
 +            return;
 +        }
 +
 +        if let Some(macro_call) = root_macro_call_first_node(self.cx, expr) {
 +            if is_panic(self.cx, macro_call.def_id)
 +                || matches!(
 +                    self.cx.tcx.item_name(macro_call.def_id).as_str(),
 +                    "assert" | "assert_eq" | "assert_ne" | "todo"
 +                )
 +            {
 +                self.panic_span = Some(macro_call.span);
 +            }
 +        }
 +
 +        // check for `unwrap`
 +        if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
++            let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs();
 +            if is_type_diagnostic_item(self.cx, receiver_ty, sym::Option)
 +                || is_type_diagnostic_item(self.cx, receiver_ty, sym::Result)
 +            {
 +                self.panic_span = Some(expr.span);
 +            }
 +        }
 +
 +        // and check sub-expressions
 +        intravisit::walk_expr(self, expr);
 +    }
 +
 +    // Panics in const blocks will cause compilation to fail.
 +    fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +}
index 1342a4697b991664122c18fe8071fda8054fa81c,0000000000000000000000000000000000000000..53bc617a4f5b78929510599109619bcb29753018
mode 100644,000000..100644
--- /dev/null
@@@ -1,242 -1,0 +1,241 @@@
-                 if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, &body.value) {
 +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::ty::is_type_diagnostic_item;
 +use clippy_utils::usage::local_used_after_expr;
 +use clippy_utils::{higher, is_adjusted, path_to_local, path_to_local_id};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::{Closure, Expr, ExprKind, Param, PatKind, Unsafety};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
 +use rustc_middle::ty::binding::BindingMode;
 +use rustc_middle::ty::subst::Subst;
 +use rustc_middle::ty::{self, ClosureKind, Ty, TypeVisitable};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::symbol::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for closures which just call another function where
 +    /// the function can be called directly. `unsafe` functions or calls where types
 +    /// get adjusted are ignored.
 +    ///
 +    /// ### Why is this bad?
 +    /// Needlessly creating a closure adds code for no benefit
 +    /// and gives the optimizer more work.
 +    ///
 +    /// ### Known problems
 +    /// If creating the closure inside the closure has a side-
 +    /// effect then moving the closure creation out will change when that side-
 +    /// effect runs.
 +    /// See [#1439](https://github.com/rust-lang/rust-clippy/issues/1439) for more details.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// xs.map(|x| foo(x))
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// // where `foo(_)` is a plain function that takes the exact argument type of `x`.
 +    /// xs.map(foo)
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub REDUNDANT_CLOSURE,
 +    style,
 +    "redundant closures, i.e., `|a| foo(a)` (which can be written as just `foo`)"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for closures which only invoke a method on the closure
 +    /// argument and can be replaced by referencing the method directly.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's unnecessary to create the closure.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// Some('a').map(|s| s.to_uppercase());
 +    /// ```
 +    /// may be rewritten as
 +    /// ```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"
 +}
 +
 +declare_lint_pass!(EtaReduction => [REDUNDANT_CLOSURE, REDUNDANT_CLOSURE_FOR_METHOD_CALLS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for EtaReduction {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +        let body = match expr.kind {
 +            ExprKind::Closure(&Closure { body, .. }) => cx.tcx.hir().body(body),
 +            _ => return,
 +        };
 +        if body.value.span.from_expansion() {
 +            if body.params.is_empty() {
-             if !is_adjusted(cx, &body.value);
++                if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, body.value) {
 +                    // replace `|| vec![]` with `Vec::new`
 +                    span_lint_and_sugg(
 +                        cx,
 +                        REDUNDANT_CLOSURE,
 +                        expr.span,
 +                        "redundant closure",
 +                        "replace the closure with `Vec::new`",
 +                        "std::vec::Vec::new".into(),
 +                        Applicability::MachineApplicable,
 +                    );
 +                }
 +            }
 +            // skip `foo(|| macro!())`
 +            return;
 +        }
 +
 +        let closure_ty = cx.typeck_results().expr_ty(expr);
 +
 +        if_chain!(
-             if !is_adjusted(cx, &body.value);
++            if !is_adjusted(cx, body.value);
 +            if let ExprKind::Call(callee, args) = body.value.kind;
 +            if let ExprKind::Path(_) = callee.kind;
 +            if check_inputs(cx, body.params, None, args);
 +            let callee_ty = cx.typeck_results().expr_ty_adjusted(callee);
 +            let call_ty = cx.typeck_results().type_dependent_def_id(body.value.hir_id)
 +                .map_or(callee_ty, |id| cx.tcx.type_of(id));
 +            if check_sig(cx, closure_ty, call_ty);
 +            let substs = cx.typeck_results().node_substs(callee.hir_id);
 +            // This fixes some false positives that I don't entirely understand
 +            if substs.is_empty() || !cx.typeck_results().expr_ty(expr).has_late_bound_regions();
 +            // A type param function ref like `T::f` is not 'static, however
 +            // it is if cast like `T::f as fn()`. This seems like a rustc bug.
 +            if !substs.types().any(|t| matches!(t.kind(), ty::Param(_)));
 +            let callee_ty_unadjusted = cx.typeck_results().expr_ty(callee).peel_refs();
 +            if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Arc);
 +            if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Rc);
 +            then {
 +                span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
 +                    if let Some(mut snippet) = snippet_opt(cx, callee.span) {
 +                        if_chain! {
 +                            if let ty::Closure(_, substs) = callee_ty.peel_refs().kind();
 +                            if substs.as_closure().kind() == ClosureKind::FnMut;
 +                            if path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr));
 +
 +                            then {
 +                                // Mutable closure is used after current expr; we cannot consume it.
 +                                snippet = format!("&mut {}", snippet);
 +                            }
 +                        }
 +                        diag.span_suggestion(
 +                            expr.span,
 +                            "replace the closure with the function itself",
 +                            snippet,
 +                            Applicability::MachineApplicable,
 +                        );
 +                    }
 +                });
 +            }
 +        );
 +
 +        if_chain!(
-     std::iter::zip(params, receiver.into_iter().chain(call_args.iter()))
-         .all(|(param, arg)| check_inputs(param, arg))
++            if !is_adjusted(cx, body.value);
 +            if let ExprKind::MethodCall(path, receiver, args, _) = body.value.kind;
 +            if check_inputs(cx, body.params, Some(receiver), args);
 +            let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap();
 +            let substs = cx.typeck_results().node_substs(body.value.hir_id);
 +            let call_ty = cx.tcx.bound_type_of(method_def_id).subst(cx.tcx, substs);
 +            if check_sig(cx, closure_ty, call_ty);
 +            then {
 +                span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| {
 +                    let name = get_ufcs_type_name(cx, method_def_id);
 +                    diag.span_suggestion(
 +                        expr.span,
 +                        "replace the closure with the method itself",
 +                        format!("{}::{}", name, path.ident.name),
 +                        Applicability::MachineApplicable,
 +                    );
 +                })
 +            }
 +        );
 +    }
 +}
 +
 +fn check_inputs(
 +    cx: &LateContext<'_>,
 +    params: &[Param<'_>],
 +    receiver: Option<&Expr<'_>>,
 +    call_args: &[Expr<'_>],
 +) -> bool {
 +    if receiver.map_or(params.len() != call_args.len(), |_| params.len() != call_args.len() + 1) {
 +        return false;
 +    }
 +    let binding_modes = cx.typeck_results().pat_binding_modes();
 +    let check_inputs = |param: &Param<'_>, arg| {
 +        match param.pat.kind {
 +            PatKind::Binding(_, id, ..) if path_to_local_id(arg, id) => {},
 +            _ => return false,
 +        }
 +        // checks that parameters are not bound as `ref` or `ref mut`
 +        if let Some(BindingMode::BindByReference(_)) = binding_modes.get(param.pat.hir_id) {
 +            return false;
 +        }
 +
 +        match *cx.typeck_results().expr_adjustments(arg) {
 +            [] => true,
 +            [
 +                Adjustment {
 +                    kind: Adjust::Deref(None),
 +                    ..
 +                },
 +                Adjustment {
 +                    kind: Adjust::Borrow(AutoBorrow::Ref(_, mu2)),
 +                    ..
 +                },
 +            ] => {
 +                // re-borrow with the same mutability is allowed
 +                let ty = cx.typeck_results().expr_ty(arg);
 +                matches!(*ty.kind(), ty::Ref(.., mu1) if mu1 == mu2.into())
 +            },
 +            _ => false,
 +        }
 +    };
++    std::iter::zip(params, receiver.into_iter().chain(call_args.iter())).all(|(param, arg)| check_inputs(param, arg))
 +}
 +
 +fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tcx>) -> bool {
 +    let call_sig = call_ty.fn_sig(cx.tcx);
 +    if call_sig.unsafety() == Unsafety::Unsafe {
 +        return false;
 +    }
 +    if !closure_ty.has_late_bound_regions() {
 +        return true;
 +    }
 +    let substs = match closure_ty.kind() {
 +        ty::Closure(_, substs) => substs,
 +        _ => return false,
 +    };
 +    let closure_sig = cx.tcx.signature_unclosure(substs.as_closure().sig(), Unsafety::Normal);
 +    cx.tcx.erase_late_bound_regions(closure_sig) == cx.tcx.erase_late_bound_regions(call_sig)
 +}
 +
 +fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: DefId) -> String {
 +    let assoc_item = cx.tcx.associated_item(method_def_id);
 +    let def_id = assoc_item.container_id(cx.tcx);
 +    match assoc_item.container {
 +        ty::TraitContainer => cx.tcx.def_path_str(def_id),
 +        ty::ImplContainer => {
 +            let ty = cx.tcx.type_of(def_id);
 +            match ty.kind() {
 +                ty::Adt(adt, _) => cx.tcx.def_path_str(adt.did()),
 +                _ => ty.to_string(),
 +            }
 +        },
 +    }
 +}
index 790eea63f58c49e048e58caf039c5e4360765a61,0000000000000000000000000000000000000000..1f69f34a229df4952c4d3b6f5932d6b5b4450703
mode 100644,000000..100644
--- /dev/null
@@@ -1,132 -1,0 +1,132 @@@
-                 let receiver_ty = self.typeck_results.expr_ty(&arglists[0].0).peel_refs();
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::macros::{is_panic, root_macro_call_first_node};
 +use clippy_utils::method_chain_args;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use if_chain::if_chain;
 +use rustc_hir as hir;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{sym, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`
 +    ///
 +    /// ### Why is this bad?
 +    /// `TryFrom` should be used if there's a possibility of failure.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct Foo(i32);
 +    ///
 +    /// impl From<String> for Foo {
 +    ///     fn from(s: String) -> Self {
 +    ///         Foo(s.parse().unwrap())
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// struct Foo(i32);
 +    ///
 +    /// impl TryFrom<String> for Foo {
 +    ///     type Error = ();
 +    ///     fn try_from(s: String) -> Result<Self, Self::Error> {
 +    ///         if let Ok(parsed) = s.parse() {
 +    ///             Ok(Foo(parsed))
 +    ///         } else {
 +    ///             Err(())
 +    ///         }
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FALLIBLE_IMPL_FROM,
 +    nursery,
 +    "Warn on impls of `From<..>` that contain `panic!()` or `unwrap()`"
 +}
 +
 +declare_lint_pass!(FallibleImplFrom => [FALLIBLE_IMPL_FROM]);
 +
 +impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +        // check for `impl From<???> for ..`
 +        if_chain! {
 +            if let hir::ItemKind::Impl(impl_) = &item.kind;
 +            if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.def_id);
 +            if cx.tcx.is_diagnostic_item(sym::From, impl_trait_ref.def_id);
 +            then {
 +                lint_impl_body(cx, item.span, impl_.items);
 +            }
 +        }
 +    }
 +}
 +
 +fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[hir::ImplItemRef]) {
 +    use rustc_hir::intravisit::{self, Visitor};
 +    use rustc_hir::{Expr, ImplItemKind};
 +
 +    struct FindPanicUnwrap<'a, 'tcx> {
 +        lcx: &'a LateContext<'tcx>,
 +        typeck_results: &'tcx ty::TypeckResults<'tcx>,
 +        result: Vec<Span>,
 +    }
 +
 +    impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
 +        fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +            if let Some(macro_call) = root_macro_call_first_node(self.lcx, expr) {
 +                if is_panic(self.lcx, macro_call.def_id) {
 +                    self.result.push(expr.span);
 +                }
 +            }
 +
 +            // check for `unwrap`
 +            if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
-                 fpu.visit_expr(&body.value);
++                let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs();
 +                if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option)
 +                    || is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result)
 +                {
 +                    self.result.push(expr.span);
 +                }
 +            }
 +
 +            // and check sub-expressions
 +            intravisit::walk_expr(self, expr);
 +        }
 +    }
 +
 +    for impl_item in impl_items {
 +        if_chain! {
 +            if impl_item.ident.name == sym::from;
 +            if let ImplItemKind::Fn(_, body_id) =
 +                cx.tcx.hir().impl_item(impl_item.id).kind;
 +            then {
 +                // check the body for `begin_panic` or `unwrap`
 +                let body = cx.tcx.hir().body(body_id);
 +                let mut fpu = FindPanicUnwrap {
 +                    lcx: cx,
 +                    typeck_results: cx.tcx.typeck(impl_item.id.def_id),
 +                    result: Vec::new(),
 +                };
++                fpu.visit_expr(body.value);
 +
 +                // if we've found one, lint
 +                if !fpu.result.is_empty() {
 +                    span_lint_and_then(
 +                        cx,
 +                        FALLIBLE_IMPL_FROM,
 +                        impl_span,
 +                        "consider implementing `TryFrom` instead",
 +                        move |diag| {
 +                            diag.help(
 +                                "`From` is intended for infallible conversions only. \
 +                                Use `TryFrom` if there's a possibility for the conversion to fail");
 +                            diag.span_note(fpu.result, "potential failure(s)");
 +                        });
 +                }
 +            }
 +        }
 +    }
 +}
index 728db41d600438e39803cc21dee554a1e0879820,0000000000000000000000000000000000000000..ba53a9678801ce2fbfa407a5988356a2ef081a4e
mode 100644,000000..100644
--- /dev/null
@@@ -1,736 -1,0 +1,736 @@@
-         let method = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
-             "exp"
 +use clippy_utils::consts::{
 +    constant, constant_simple, Constant,
 +    Constant::{Int, F32, F64},
 +};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::higher;
 +use clippy_utils::{eq_expr_value, get_parent_expr, in_constant, numeric_literal, peel_blocks, sugg};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Spanned;
 +
 +use rustc_ast::ast;
 +use std::f32::consts as f32_consts;
 +use std::f64::consts as f64_consts;
 +use sugg::Sugg;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Looks for floating-point expressions that
 +    /// can be expressed using built-in methods to improve accuracy
 +    /// at the cost of performance.
 +    ///
 +    /// ### Why is this bad?
 +    /// Negatively impacts accuracy.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let a = 3f32;
 +    /// let _ = a.powf(1.0 / 3.0);
 +    /// let _ = (1.0 + a).ln();
 +    /// let _ = a.exp() - 1.0;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let a = 3f32;
 +    /// let _ = a.cbrt();
 +    /// let _ = a.ln_1p();
 +    /// let _ = a.exp_m1();
 +    /// ```
 +    #[clippy::version = "1.43.0"]
 +    pub IMPRECISE_FLOPS,
 +    nursery,
 +    "usage of imprecise floating point operations"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Looks for floating-point expressions that
 +    /// can be expressed using built-in methods to improve both
 +    /// accuracy and performance.
 +    ///
 +    /// ### Why is this bad?
 +    /// Negatively impacts accuracy and performance.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::f32::consts::E;
 +    ///
 +    /// let a = 3f32;
 +    /// let _ = (2f32).powf(a);
 +    /// let _ = E.powf(a);
 +    /// let _ = a.powf(1.0 / 2.0);
 +    /// let _ = a.log(2.0);
 +    /// let _ = a.log(10.0);
 +    /// let _ = a.log(E);
 +    /// let _ = a.powf(2.0);
 +    /// let _ = a * 2.0 + 4.0;
 +    /// let _ = if a < 0.0 {
 +    ///     -a
 +    /// } else {
 +    ///     a
 +    /// };
 +    /// let _ = if a < 0.0 {
 +    ///     a
 +    /// } else {
 +    ///     -a
 +    /// };
 +    /// ```
 +    ///
 +    /// is better expressed as
 +    ///
 +    /// ```rust
 +    /// use std::f32::consts::E;
 +    ///
 +    /// let a = 3f32;
 +    /// let _ = a.exp2();
 +    /// let _ = a.exp();
 +    /// let _ = a.sqrt();
 +    /// let _ = a.log2();
 +    /// let _ = a.log10();
 +    /// let _ = a.ln();
 +    /// let _ = a.powi(2);
 +    /// let _ = a.mul_add(2.0, 4.0);
 +    /// let _ = a.abs();
 +    /// let _ = -a.abs();
 +    /// ```
 +    #[clippy::version = "1.43.0"]
 +    pub SUBOPTIMAL_FLOPS,
 +    nursery,
 +    "usage of sub-optimal floating point operations"
 +}
 +
 +declare_lint_pass!(FloatingPointArithmetic => [
 +    IMPRECISE_FLOPS,
 +    SUBOPTIMAL_FLOPS
 +]);
 +
 +// Returns the specialized log method for a given base if base is constant
 +// and is one of 2, 10 and e
 +fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>) -> Option<&'static str> {
 +    if let Some((value, _)) = constant(cx, cx.typeck_results(), base) {
 +        if F32(2.0) == value || F64(2.0) == value {
 +            return Some("log2");
 +        } else if F32(10.0) == value || F64(10.0) == value {
 +            return Some("log10");
 +        } else if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
 +            return Some("ln");
 +        }
 +    }
 +
 +    None
 +}
 +
 +// Adds type suffixes and parenthesis to method receivers if necessary
 +fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Sugg<'a> {
 +    let mut suggestion = Sugg::hir(cx, expr, "..");
 +
 +    if let ExprKind::Unary(UnOp::Neg, inner_expr) = &expr.kind {
 +        expr = inner_expr;
 +    }
 +
 +    if_chain! {
 +        // if the expression is a float literal and it is unsuffixed then
 +        // add a suffix so the suggestion is valid and unambiguous
 +        if let ty::Float(float_ty) = cx.typeck_results().expr_ty(expr).kind();
 +        if let ExprKind::Lit(lit) = &expr.kind;
 +        if let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node;
 +        then {
 +            let op = format!(
 +                "{}{}{}",
 +                suggestion,
 +                // Check for float literals without numbers following the decimal
 +                // separator such as `2.` and adds a trailing zero
 +                if sym.as_str().ends_with('.') {
 +                    "0"
 +                } else {
 +                    ""
 +                },
 +                float_ty.name_str()
 +            ).into();
 +
 +            suggestion = match suggestion {
 +                Sugg::MaybeParen(_) => Sugg::MaybeParen(op),
 +                _ => Sugg::NonParen(op)
 +            };
 +        }
 +    }
 +
 +    suggestion.maybe_par()
 +}
 +
 +fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
 +    if let Some(method) = get_specialized_log_method(cx, &args[0]) {
 +        span_lint_and_sugg(
 +            cx,
 +            SUBOPTIMAL_FLOPS,
 +            expr.span,
 +            "logarithm for bases 2, 10 and e can be computed more accurately",
 +            "consider using",
 +            format!("{}.{}()", Sugg::hir(cx, receiver, "..").maybe_par(), method),
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
 +
 +// TODO: Lint expressions of the form `(x + y).ln()` where y > 1 and
 +// suggest usage of `(x + (y - 1)).ln_1p()` instead
 +fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) {
 +    if let ExprKind::Binary(
 +        Spanned {
 +            node: BinOpKind::Add, ..
 +        },
 +        lhs,
 +        rhs,
 +    ) = receiver.kind
 +    {
 +        let recv = match (
 +            constant(cx, cx.typeck_results(), lhs),
 +            constant(cx, cx.typeck_results(), rhs),
 +        ) {
 +            (Some((value, _)), _) if F32(1.0) == value || F64(1.0) == value => rhs,
 +            (_, Some((value, _))) if F32(1.0) == value || F64(1.0) == value => lhs,
 +            _ => return,
 +        };
 +
 +        span_lint_and_sugg(
 +            cx,
 +            IMPRECISE_FLOPS,
 +            expr.span,
 +            "ln(1 + x) can be computed more accurately",
 +            "consider using",
 +            format!("{}.ln_1p()", prepare_receiver_sugg(cx, recv)),
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
 +
 +// Returns an integer if the float constant is a whole number and it can be
 +// converted to an integer without loss of precision. For now we only check
 +// ranges [-16777215, 16777216) for type f32 as whole number floats outside
 +// this range are lossy and ambiguous.
 +#[expect(clippy::cast_possible_truncation)]
 +fn get_integer_from_float_constant(value: &Constant) -> Option<i32> {
 +    match value {
 +        F32(num) if num.fract() == 0.0 => {
 +            if (-16_777_215.0..16_777_216.0).contains(num) {
 +                Some(num.round() as i32)
 +            } else {
 +                None
 +            }
 +        },
 +        F64(num) if num.fract() == 0.0 => {
 +            if (-2_147_483_648.0..2_147_483_648.0).contains(num) {
 +                Some(num.round() as i32)
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
 +    // Check receiver
 +    if let Some((value, _)) = constant(cx, cx.typeck_results(), receiver) {
-             "exp2"
++        if let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
++            Some("exp")
 +        } else if F32(2.0) == value || F64(2.0) == value {
-             return;
-         };
-         span_lint_and_sugg(
-             cx,
-             SUBOPTIMAL_FLOPS,
-             expr.span,
-             "exponent for bases 2 and e can be computed more accurately",
-             "consider using",
-             format!("{}.{}()", prepare_receiver_sugg(cx, &args[0]), method),
-             Applicability::MachineApplicable,
-         );
++            Some("exp2")
 +        } else {
++            None
++        } {
++            span_lint_and_sugg(
++                cx,
++                SUBOPTIMAL_FLOPS,
++                expr.span,
++                "exponent for bases 2 and e can be computed more accurately",
++                "consider using",
++                format!("{}.{}()", prepare_receiver_sugg(cx, &args[0]), method),
++                Applicability::MachineApplicable,
++            );
++        }
 +    }
 +
 +    // Check argument
 +    if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[0]) {
 +        let (lint, help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value {
 +            (
 +                SUBOPTIMAL_FLOPS,
 +                "square-root of a number can be computed more efficiently and accurately",
 +                format!("{}.sqrt()", Sugg::hir(cx, receiver, "..").maybe_par()),
 +            )
 +        } else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value {
 +            (
 +                IMPRECISE_FLOPS,
 +                "cube-root of a number can be computed more accurately",
 +                format!("{}.cbrt()", Sugg::hir(cx, receiver, "..").maybe_par()),
 +            )
 +        } else if let Some(exponent) = get_integer_from_float_constant(&value) {
 +            (
 +                SUBOPTIMAL_FLOPS,
 +                "exponentiation with integer powers can be computed more efficiently",
 +                format!(
 +                    "{}.powi({})",
 +                    Sugg::hir(cx, receiver, "..").maybe_par(),
 +                    numeric_literal::format(&exponent.to_string(), None, false)
 +                ),
 +            )
 +        } else {
 +            return;
 +        };
 +
 +        span_lint_and_sugg(
 +            cx,
 +            lint,
 +            expr.span,
 +            help,
 +            "consider using",
 +            suggestion,
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
 +
 +fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
 +    if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[0]) {
 +        if value == Int(2) {
 +            if let Some(parent) = get_parent_expr(cx, expr) {
 +                if let Some(grandparent) = get_parent_expr(cx, parent) {
 +                    if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = grandparent.kind
 +                    {
 +                        if method_name.as_str() == "sqrt" && detect_hypot(cx, receiver).is_some() {
 +                            return;
 +                        }
 +                    }
 +                }
 +
 +                if let ExprKind::Binary(
 +                    Spanned {
 +                        node: BinOpKind::Add, ..
 +                    },
 +                    lhs,
 +                    rhs,
 +                ) = parent.kind
 +                {
 +                    let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs };
 +
 +                    span_lint_and_sugg(
 +                        cx,
 +                        SUBOPTIMAL_FLOPS,
 +                        parent.span,
 +                        "multiply and add expressions can be calculated more efficiently and accurately",
 +                        "consider using",
 +                        format!(
 +                            "{}.mul_add({}, {})",
 +                            Sugg::hir(cx, receiver, "..").maybe_par(),
 +                            Sugg::hir(cx, receiver, ".."),
 +                            Sugg::hir(cx, other_addend, ".."),
 +                        ),
 +                        Applicability::MachineApplicable,
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option<String> {
 +    if let ExprKind::Binary(
 +        Spanned {
 +            node: BinOpKind::Add, ..
 +        },
 +        add_lhs,
 +        add_rhs,
 +    ) = receiver.kind
 +    {
 +        // check if expression of the form x * x + y * y
 +        if_chain! {
 +            if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, lmul_lhs, lmul_rhs) = add_lhs.kind;
 +            if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, rmul_lhs, rmul_rhs) = add_rhs.kind;
 +            if eq_expr_value(cx, lmul_lhs, lmul_rhs);
 +            if eq_expr_value(cx, rmul_lhs, rmul_rhs);
 +            then {
 +                return Some(format!("{}.hypot({})", Sugg::hir(cx, lmul_lhs, "..").maybe_par(), Sugg::hir(cx, rmul_lhs, "..")));
 +            }
 +        }
 +
 +        // check if expression of the form x.powi(2) + y.powi(2)
 +        if_chain! {
 +            if let ExprKind::MethodCall(
 +                PathSegment { ident: lmethod_name, .. },
 +                largs_0, [largs_1, ..],
 +                _
 +            ) = &add_lhs.kind;
 +            if let ExprKind::MethodCall(
 +                PathSegment { ident: rmethod_name, .. },
 +                rargs_0, [rargs_1, ..],
 +                _
 +            ) = &add_rhs.kind;
 +            if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi";
 +            if let Some((lvalue, _)) = constant(cx, cx.typeck_results(), largs_1);
 +            if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), rargs_1);
 +            if Int(2) == lvalue && Int(2) == rvalue;
 +            then {
 +                return Some(format!("{}.hypot({})", Sugg::hir(cx, largs_0, "..").maybe_par(), Sugg::hir(cx, rargs_0, "..")));
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn check_hypot(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) {
 +    if let Some(message) = detect_hypot(cx, receiver) {
 +        span_lint_and_sugg(
 +            cx,
 +            IMPRECISE_FLOPS,
 +            expr.span,
 +            "hypotenuse can be computed more accurately",
 +            "consider using",
 +            message,
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
 +
 +// TODO: Lint expressions of the form `x.exp() - y` where y > 1
 +// and suggest usage of `x.exp_m1() - (y - 1)` instead
 +fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if_chain! {
 +        if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, lhs, rhs) = expr.kind;
 +        if cx.typeck_results().expr_ty(lhs).is_floating_point();
 +        if let Some((value, _)) = constant(cx, cx.typeck_results(), rhs);
 +        if F32(1.0) == value || F64(1.0) == value;
 +        if let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind;
 +        if cx.typeck_results().expr_ty(self_arg).is_floating_point();
 +        if path.ident.name.as_str() == "exp";
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                IMPRECISE_FLOPS,
 +                expr.span,
 +                "(e.pow(x) - 1) can be computed more accurately",
 +                "consider using",
 +                format!(
 +                    "{}.exp_m1()",
 +                    Sugg::hir(cx, self_arg, "..").maybe_par()
 +                ),
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    }
 +}
 +
 +fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> {
 +    if_chain! {
 +        if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, lhs, rhs) = &expr.kind;
 +        if cx.typeck_results().expr_ty(lhs).is_floating_point();
 +        if cx.typeck_results().expr_ty(rhs).is_floating_point();
 +        then {
 +            return Some((lhs, rhs));
 +        }
 +    }
 +
 +    None
 +}
 +
 +// TODO: Fix rust-lang/rust-clippy#4735
 +fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if let ExprKind::Binary(
 +        Spanned {
 +            node: BinOpKind::Add, ..
 +        },
 +        lhs,
 +        rhs,
 +    ) = &expr.kind
 +    {
 +        if let Some(parent) = get_parent_expr(cx, expr) {
 +            if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = parent.kind {
 +                if method_name.as_str() == "sqrt" && detect_hypot(cx, receiver).is_some() {
 +                    return;
 +                }
 +            }
 +        }
 +
 +        let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) {
 +            (inner_lhs, inner_rhs, rhs)
 +        } else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) {
 +            (inner_lhs, inner_rhs, lhs)
 +        } else {
 +            return;
 +        };
 +
 +        span_lint_and_sugg(
 +            cx,
 +            SUBOPTIMAL_FLOPS,
 +            expr.span,
 +            "multiply and add expressions can be calculated more efficiently and accurately",
 +            "consider using",
 +            format!(
 +                "{}.mul_add({}, {})",
 +                prepare_receiver_sugg(cx, recv),
 +                Sugg::hir(cx, arg1, ".."),
 +                Sugg::hir(cx, arg2, ".."),
 +            ),
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
 +
 +/// Returns true iff expr is an expression which tests whether or not
 +/// test is positive or an expression which tests whether or not test
 +/// is nonnegative.
 +/// Used for check-custom-abs function below
 +fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
 +    if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
 +        match op {
 +            BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && eq_expr_value(cx, left, test),
 +            BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && eq_expr_value(cx, right, test),
 +            _ => false,
 +        }
 +    } else {
 +        false
 +    }
 +}
 +
 +/// See [`is_testing_positive`]
 +fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
 +    if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
 +        match op {
 +            BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && eq_expr_value(cx, right, test),
 +            BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && eq_expr_value(cx, left, test),
 +            _ => false,
 +        }
 +    } else {
 +        false
 +    }
 +}
 +
 +/// Returns true iff expr is some zero literal
 +fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    match constant_simple(cx, cx.typeck_results(), expr) {
 +        Some(Constant::Int(i)) => i == 0,
 +        Some(Constant::F32(f)) => f == 0.0,
 +        Some(Constant::F64(f)) => f == 0.0,
 +        _ => false,
 +    }
 +}
 +
 +/// If the two expressions are negations of each other, then it returns
 +/// a tuple, in which the first element is true iff expr1 is the
 +/// positive expressions, and the second element is the positive
 +/// one of the two expressions
 +/// If the two expressions are not negations of each other, then it
 +/// returns None.
 +fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a>) -> Option<(bool, &'a Expr<'a>)> {
 +    if let ExprKind::Unary(UnOp::Neg, expr1_negated) = &expr1.kind {
 +        if eq_expr_value(cx, expr1_negated, expr2) {
 +            return Some((false, expr2));
 +        }
 +    }
 +    if let ExprKind::Unary(UnOp::Neg, expr2_negated) = &expr2.kind {
 +        if eq_expr_value(cx, expr1, expr2_negated) {
 +            return Some((true, expr1));
 +        }
 +    }
 +    None
 +}
 +
 +fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if_chain! {
 +        if let Some(higher::If { cond, then, r#else: Some(r#else) }) = higher::If::hir(expr);
 +        let if_body_expr = peel_blocks(then);
 +        let else_body_expr = peel_blocks(r#else);
 +        if let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr);
 +        then {
 +            let positive_abs_sugg = (
 +                "manual implementation of `abs` method",
 +                format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_par()),
 +            );
 +            let negative_abs_sugg = (
 +                "manual implementation of negation of `abs` method",
 +                format!("-{}.abs()", Sugg::hir(cx, body, "..").maybe_par()),
 +            );
 +            let sugg = if is_testing_positive(cx, cond, body) {
 +                if if_expr_positive {
 +                    positive_abs_sugg
 +                } else {
 +                    negative_abs_sugg
 +                }
 +            } else if is_testing_negative(cx, cond, body) {
 +                if if_expr_positive {
 +                    negative_abs_sugg
 +                } else {
 +                    positive_abs_sugg
 +                }
 +            } else {
 +                return;
 +            };
 +            span_lint_and_sugg(
 +                cx,
 +                SUBOPTIMAL_FLOPS,
 +                expr.span,
 +                sugg.0,
 +                "try",
 +                sugg.1,
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    }
 +}
 +
 +fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool {
 +    if_chain! {
 +        if let ExprKind::MethodCall(PathSegment { ident: method_name_a, .. }, _, args_a, _) = expr_a.kind;
 +        if let ExprKind::MethodCall(PathSegment { ident: method_name_b, .. }, _, args_b, _) = expr_b.kind;
 +        then {
 +            return method_name_a.as_str() == method_name_b.as_str() &&
 +                args_a.len() == args_b.len() &&
 +                (
 +                    ["ln", "log2", "log10"].contains(&method_name_a.as_str()) ||
 +                    method_name_a.as_str() == "log" && args_a.len() == 1 && eq_expr_value(cx, &args_a[0], &args_b[0])
 +                );
 +        }
 +    }
 +
 +    false
 +}
 +
 +fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    // check if expression of the form x.logN() / y.logN()
 +    if_chain! {
 +        if let ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Div, ..
 +            },
 +            lhs,
 +            rhs,
 +        ) = &expr.kind;
 +        if are_same_base_logs(cx, lhs, rhs);
 +        if let ExprKind::MethodCall(_, largs_self, ..) = &lhs.kind;
 +        if let ExprKind::MethodCall(_, rargs_self, ..) = &rhs.kind;
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                SUBOPTIMAL_FLOPS,
 +                expr.span,
 +                "log base can be expressed more clearly",
 +                "consider using",
 +                format!("{}.log({})", Sugg::hir(cx, largs_self, "..").maybe_par(), Sugg::hir(cx, rargs_self, ".."),),
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if_chain! {
 +        if let ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Div, ..
 +            },
 +            div_lhs,
 +            div_rhs,
 +        ) = &expr.kind;
 +        if let ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Mul, ..
 +            },
 +            mul_lhs,
 +            mul_rhs,
 +        ) = &div_lhs.kind;
 +        if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), div_rhs);
 +        if let Some((lvalue, _)) = constant(cx, cx.typeck_results(), mul_rhs);
 +        then {
 +            // TODO: also check for constant values near PI/180 or 180/PI
 +            if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) &&
 +               (F32(180_f32) == lvalue || F64(180_f64) == lvalue)
 +            {
 +                let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
 +                if_chain! {
 +                    if let ExprKind::Lit(ref literal) = mul_lhs.kind;
 +                    if let ast::LitKind::Float(ref value, float_type) = literal.node;
 +                    if float_type == ast::LitFloatType::Unsuffixed;
 +                    then {
 +                        if value.as_str().ends_with('.') {
 +                            proposal = format!("{}0_f64.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
 +                        } else {
 +                            proposal = format!("{}_f64.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
 +                        }
 +                    }
 +                }
 +                span_lint_and_sugg(
 +                    cx,
 +                    SUBOPTIMAL_FLOPS,
 +                    expr.span,
 +                    "conversion to degrees can be done more accurately",
 +                    "consider using",
 +                    proposal,
 +                    Applicability::MachineApplicable,
 +                );
 +            } else if
 +                (F32(180_f32) == rvalue || F64(180_f64) == rvalue) &&
 +                (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue)
 +            {
 +                let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
 +                if_chain! {
 +                    if let ExprKind::Lit(ref literal) = mul_lhs.kind;
 +                    if let ast::LitKind::Float(ref value, float_type) = literal.node;
 +                    if float_type == ast::LitFloatType::Unsuffixed;
 +                    then {
 +                        if value.as_str().ends_with('.') {
 +                            proposal = format!("{}0_f64.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
 +                        } else {
 +                            proposal = format!("{}_f64.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
 +                        }
 +                    }
 +                }
 +                span_lint_and_sugg(
 +                    cx,
 +                    SUBOPTIMAL_FLOPS,
 +                    expr.span,
 +                    "conversion to radians can be done more accurately",
 +                    "consider using",
 +                    proposal,
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +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, receiver, args, _) = &expr.kind {
 +            let recv_ty = cx.typeck_results().expr_ty(receiver);
 +
 +            if recv_ty.is_floating_point() {
 +                match path.ident.name.as_str() {
 +                    "ln" => check_ln1p(cx, expr, receiver),
 +                    "log" => check_log_base(cx, expr, receiver, args),
 +                    "powf" => check_powf(cx, expr, receiver, args),
 +                    "powi" => check_powi(cx, expr, receiver, args),
 +                    "sqrt" => check_hypot(cx, expr, receiver),
 +                    _ => {},
 +                }
 +            }
 +        } else {
 +            check_expm1(cx, expr);
 +            check_mul_add(cx, expr);
 +            check_custom_abs(cx, expr);
 +            check_log_division(cx, expr);
 +            check_radians(cx, expr);
 +        }
 +    }
 +}
index a17b23f5edc8609074d202d7f85d974be80d84dc,0000000000000000000000000000000000000000..00a4937763eb5ad780c10c278f22531ec2ade832
mode 100644,000000..100644
--- /dev/null
@@@ -1,277 -1,0 +1,277 @@@
-     intravisit::walk_expr(&mut v, &body.value);
 +use rustc_ast::ast::Attribute;
 +use rustc_errors::Applicability;
 +use rustc_hir::def_id::{DefIdSet, LocalDefId};
 +use rustc_hir::{self as hir, def::Res, intravisit, QPath};
 +use rustc_lint::{LateContext, LintContext};
 +use rustc_middle::{
 +    lint::in_external_macro,
 +    ty::{self, Ty},
 +};
 +use rustc_span::{sym, Span};
 +
 +use clippy_utils::attrs::is_proc_macro;
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::is_must_use_ty;
 +use clippy_utils::{match_def_path, return_ty, trait_ref_of_method};
 +
 +use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT};
 +
 +pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +    let attrs = cx.tcx.hir().attrs(item.hir_id());
 +    let attr = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use);
 +    if let hir::ItemKind::Fn(ref sig, _generics, ref body_id) = item.kind {
 +        let is_public = cx.access_levels.is_exported(item.def_id);
 +        let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
 +        if let Some(attr) = attr {
 +            check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
 +        } else if is_public && !is_proc_macro(cx.sess(), attrs) && !attrs.iter().any(|a| a.has_name(sym::no_mangle)) {
 +            check_must_use_candidate(
 +                cx,
 +                sig.decl,
 +                cx.tcx.hir().body(*body_id),
 +                item.span,
 +                item.def_id,
 +                item.span.with_hi(sig.decl.output.span().hi()),
 +                "this function could have a `#[must_use]` attribute",
 +            );
 +        }
 +    }
 +}
 +
 +pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
 +    if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind {
 +        let is_public = cx.access_levels.is_exported(item.def_id);
 +        let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        let attr = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use);
 +        if let Some(attr) = attr {
 +            check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
 +        } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.def_id).is_none() {
 +            check_must_use_candidate(
 +                cx,
 +                sig.decl,
 +                cx.tcx.hir().body(*body_id),
 +                item.span,
 +                item.def_id,
 +                item.span.with_hi(sig.decl.output.span().hi()),
 +                "this method could have a `#[must_use]` attribute",
 +            );
 +        }
 +    }
 +}
 +
 +pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
 +    if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind {
 +        let is_public = cx.access_levels.is_exported(item.def_id);
 +        let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
 +
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        let attr = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use);
 +        if let Some(attr) = attr {
 +            check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
 +        } else if let hir::TraitFn::Provided(eid) = *eid {
 +            let body = cx.tcx.hir().body(eid);
 +            if attr.is_none() && is_public && !is_proc_macro(cx.sess(), attrs) {
 +                check_must_use_candidate(
 +                    cx,
 +                    sig.decl,
 +                    body,
 +                    item.span,
 +                    item.def_id,
 +                    item.span.with_hi(sig.decl.output.span().hi()),
 +                    "this method could have a `#[must_use]` attribute",
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn check_needless_must_use(
 +    cx: &LateContext<'_>,
 +    decl: &hir::FnDecl<'_>,
 +    item_id: hir::HirId,
 +    item_span: Span,
 +    fn_header_span: Span,
 +    attr: &Attribute,
 +) {
 +    if in_external_macro(cx.sess(), item_span) {
 +        return;
 +    }
 +    if returns_unit(decl) {
 +        span_lint_and_then(
 +            cx,
 +            MUST_USE_UNIT,
 +            fn_header_span,
 +            "this unit-returning function has a `#[must_use]` attribute",
 +            |diag| {
 +                diag.span_suggestion(attr.span, "remove the attribute", "", Applicability::MachineApplicable);
 +            },
 +        );
 +    } else if attr.value_str().is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) {
 +        span_lint_and_help(
 +            cx,
 +            DOUBLE_MUST_USE,
 +            fn_header_span,
 +            "this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`",
 +            None,
 +            "either add some descriptive text or remove the attribute",
 +        );
 +    }
 +}
 +
 +fn check_must_use_candidate<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    decl: &'tcx hir::FnDecl<'_>,
 +    body: &'tcx hir::Body<'_>,
 +    item_span: Span,
 +    item_id: LocalDefId,
 +    fn_span: Span,
 +    msg: &str,
 +) {
 +    if has_mutable_arg(cx, body)
 +        || mutates_static(cx, body)
 +        || in_external_macro(cx.sess(), item_span)
 +        || returns_unit(decl)
 +        || !cx.access_levels.is_exported(item_id)
 +        || is_must_use_ty(cx, return_ty(cx, cx.tcx.hir().local_def_id_to_hir_id(item_id)))
 +    {
 +        return;
 +    }
 +    span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| {
 +        if let Some(snippet) = snippet_opt(cx, fn_span) {
 +            diag.span_suggestion(
 +                fn_span,
 +                "add the attribute",
 +                format!("#[must_use] {}", snippet),
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    });
 +}
 +
 +fn returns_unit(decl: &hir::FnDecl<'_>) -> bool {
 +    match decl.output {
 +        hir::FnRetTy::DefaultReturn(_) => true,
 +        hir::FnRetTy::Return(ty) => match ty.kind {
 +            hir::TyKind::Tup(tys) => tys.is_empty(),
 +            hir::TyKind::Never => true,
 +            _ => false,
 +        },
 +    }
 +}
 +
 +fn has_mutable_arg(cx: &LateContext<'_>, body: &hir::Body<'_>) -> bool {
 +    let mut tys = DefIdSet::default();
 +    body.params.iter().any(|param| is_mutable_pat(cx, param.pat, &mut tys))
 +}
 +
 +fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet) -> bool {
 +    if let hir::PatKind::Wild = pat.kind {
 +        return false; // ignore `_` patterns
 +    }
 +    if cx.tcx.has_typeck_results(pat.hir_id.owner.to_def_id()) {
 +        is_mutable_ty(cx, cx.tcx.typeck(pat.hir_id.owner).pat_ty(pat), pat.span, tys)
 +    } else {
 +        false
 +    }
 +}
 +
 +static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]];
 +
 +fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut DefIdSet) -> bool {
 +    match *ty.kind() {
 +        // primitive types are never mutable
 +        ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false,
 +        ty::Adt(adt, substs) => {
 +            tys.insert(adt.did()) && !ty.is_freeze(cx.tcx.at(span), cx.param_env)
 +                || KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did(), path))
 +                    && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys))
 +        },
 +        ty::Tuple(substs) => substs.iter().any(|ty| is_mutable_ty(cx, ty, span, tys)),
 +        ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, span, tys),
 +        ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => {
 +            mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, span, tys)
 +        },
 +        // calling something constitutes a side effect, so return true on all callables
 +        // also never calls need not be used, so return true for them, too
 +        _ => true,
 +    }
 +}
 +
 +struct StaticMutVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    mutates_static: bool,
 +}
 +
 +impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> {
 +    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
 +        use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall};
 +
 +        if self.mutates_static {
 +            return;
 +        }
 +        match expr.kind {
 +            Call(_, args) => {
 +                let mut tys = DefIdSet::default();
 +                for arg in args {
 +                    if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
 +                        && is_mutable_ty(
 +                            self.cx,
 +                            self.cx.tcx.typeck(arg.hir_id.owner).expr_ty(arg),
 +                            arg.span,
 +                            &mut tys,
 +                        )
 +                        && is_mutated_static(arg)
 +                    {
 +                        self.mutates_static = true;
 +                        return;
 +                    }
 +                    tys.clear();
 +                }
 +            },
 +            MethodCall(_, receiver, args, _) => {
 +                let mut tys = DefIdSet::default();
 +                for arg in std::iter::once(receiver).chain(args.iter()) {
 +                    if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
 +                        && is_mutable_ty(
 +                            self.cx,
 +                            self.cx.tcx.typeck(arg.hir_id.owner).expr_ty(arg),
 +                            arg.span,
 +                            &mut tys,
 +                        )
 +                        && is_mutated_static(arg)
 +                    {
 +                        self.mutates_static = true;
 +                        return;
 +                    }
 +                    tys.clear();
 +                }
 +            },
 +            Assign(target, ..) | AssignOp(_, target, _) | AddrOf(_, hir::Mutability::Mut, target) => {
 +                self.mutates_static |= is_mutated_static(target);
 +            },
 +            _ => {},
 +        }
 +    }
 +}
 +
 +fn is_mutated_static(e: &hir::Expr<'_>) -> bool {
 +    use hir::ExprKind::{Field, Index, Path};
 +
 +    match e.kind {
 +        Path(QPath::Resolved(_, path)) => !matches!(path.res, Res::Local(_)),
 +        Path(_) => true,
 +        Field(inner, _) | Index(inner, _) => is_mutated_static(inner),
 +        _ => false,
 +    }
 +}
 +
 +fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool {
 +    let mut v = StaticMutVisitor {
 +        cx,
 +        mutates_static: false,
 +    };
++    intravisit::walk_expr(&mut v, body.value);
 +    v.mutates_static
 +}
index af520a493eda168b649d8bc9de9ce742de1b9169,0000000000000000000000000000000000000000..9591405cb06f7b2d836d55f2c42148faf1ac0947
mode 100644,000000..100644
--- /dev/null
@@@ -1,100 -1,0 +1,100 @@@
- use rustc_typeck::hir_ty_to_ty;
 +use rustc_errors::Diagnostic;
 +use rustc_hir as hir;
 +use rustc_lint::{LateContext, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_span::{sym, Span};
-         && let ty = hir_ty_to_ty(cx.tcx, hir_ty)
 +
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
 +use clippy_utils::trait_ref_of_method;
 +use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item};
 +
 +use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR};
 +
 +/// The type of the `Err`-variant in a `std::result::Result` returned by the
 +/// given `FnDecl`
 +fn result_err_ty<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    decl: &hir::FnDecl<'tcx>,
++    id: hir::def_id::LocalDefId,
 +    item_span: Span,
 +) -> Option<(&'tcx hir::Ty<'tcx>, Ty<'tcx>)> {
 +    if !in_external_macro(cx.sess(), item_span)
 +        && let hir::FnRetTy::Return(hir_ty) = decl.output
-         && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span)
++        && let ty = cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).output())
 +        && is_type_diagnostic_item(cx, ty, sym::Result)
 +        && let ty::Adt(_, substs) = ty.kind()
 +    {
 +        let err_ty = substs.type_at(1);
 +        Some((hir_ty, err_ty))
 +    } else {
 +        None
 +    }
 +}
 +
 +pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, large_err_threshold: u64) {
 +    if let hir::ItemKind::Fn(ref sig, _generics, _) = item.kind
-         && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span)
++        && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.def_id, item.span)
 +    {
 +        if cx.access_levels.is_exported(item.def_id) {
 +            let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
 +            check_result_unit_err(cx, err_ty, fn_header_span);
 +        }
 +        check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold);
 +    }
 +}
 +
 +pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::ImplItem<'tcx>, large_err_threshold: u64) {
 +    // Don't lint if method is a trait's implementation, we can't do anything about those
 +    if let hir::ImplItemKind::Fn(ref sig, _) = item.kind
-         if let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span) {
++        && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.def_id, item.span)
 +        && trait_ref_of_method(cx, item.def_id).is_none()
 +    {
 +        if cx.access_levels.is_exported(item.def_id) {
 +            let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
 +            check_result_unit_err(cx, err_ty, fn_header_span);
 +        }
 +        check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold);
 +    }
 +}
 +
 +pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::TraitItem<'tcx>, large_err_threshold: u64) {
 +    if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
 +        let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
++        if let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.def_id, item.span) {
 +            if cx.access_levels.is_exported(item.def_id) {
 +                check_result_unit_err(cx, err_ty, fn_header_span);
 +            }
 +            check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold);
 +        }
 +    }
 +}
 +
 +fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: Span) {
 +    if err_ty.is_unit() {
 +        span_lint_and_help(
 +            cx,
 +            RESULT_UNIT_ERR,
 +            fn_header_span,
 +            "this returns a `Result<_, ()>`",
 +            None,
 +            "use a custom `Error` type instead",
 +        );
 +    }
 +}
 +
 +fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty_span: Span, large_err_threshold: u64) {
 +    let ty_size = approx_ty_size(cx, err_ty);
 +    if ty_size >= large_err_threshold {
 +        span_lint_and_then(
 +            cx,
 +            RESULT_LARGE_ERR,
 +            hir_ty_span,
 +            "the `Err`-variant returned from this function is very large",
 +            |diag: &mut Diagnostic| {
 +                diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes"));
 +                diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`"));
 +            },
 +        );
 +    }
 +}
index a6610ade37e56291400196d6ab02553ba11abe7f,0000000000000000000000000000000000000000..feec8ec2e23f8f802c2b249f0931427eea795162
mode 100644,000000..100644
--- /dev/null
@@@ -1,250 -1,0 +1,250 @@@
-         let res_ty = cx.typeck_results().expr_ty(&body.value);
 +use clippy_utils::{
 +    diagnostics::span_lint_hir_and_then,
 +    get_async_fn_body, is_async_fn,
 +    source::{snippet_with_applicability, snippet_with_context, walk_span_to_context},
 +    visitors::expr_visitor_no_bodies,
 +};
 +use rustc_errors::Applicability;
 +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;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{Span, SyntaxContext};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for missing return statements at the end of a block.
 +    ///
 +    /// ### Why is this bad?
 +    /// Actually omitting the return keyword is idiomatic Rust code. Programmers
 +    /// coming from other languages might prefer the expressiveness of `return`. It's possible to miss
 +    /// the last returning statement because the only difference is a missing `;`. Especially in bigger
 +    /// code with multiple return paths having a `return` keyword makes it easier to find the
 +    /// corresponding statements.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn foo(x: usize) -> usize {
 +    ///     x
 +    /// }
 +    /// ```
 +    /// add return
 +    /// ```rust
 +    /// fn foo(x: usize) -> usize {
 +    ///     return x;
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.33.0"]
 +    pub IMPLICIT_RETURN,
 +    restriction,
 +    "use a return statement like `return expr` instead of an expression"
 +}
 +
 +declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]);
 +
 +fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) {
 +    let mut app = Applicability::MachineApplicable;
 +    let snip = snippet_with_applicability(cx, span, "..", &mut app);
 +    span_lint_hir_and_then(
 +        cx,
 +        IMPLICIT_RETURN,
 +        emission_place,
 +        span,
 +        "missing `return` statement",
 +        |diag| {
 +            diag.span_suggestion(span, "add `return` as shown", format!("return {}", snip), app);
 +        },
 +    );
 +}
 +
 +fn lint_break(cx: &LateContext<'_>, emission_place: HirId, break_span: Span, expr_span: Span) {
 +    let mut app = Applicability::MachineApplicable;
 +    let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0;
 +    span_lint_hir_and_then(
 +        cx,
 +        IMPLICIT_RETURN,
 +        emission_place,
 +        break_span,
 +        "missing `return` statement",
 +        |diag| {
 +            diag.span_suggestion(
 +                break_span,
 +                "change `break` to `return` as shown",
 +                format!("return {}", snip),
 +                app,
 +            );
 +        },
 +    );
 +}
 +
 +#[derive(Clone, Copy, PartialEq, Eq)]
 +enum LintLocation {
 +    /// The lint was applied to a parent expression.
 +    Parent,
 +    /// The lint was applied to this expression, a child, or not applied.
 +    Inner,
 +}
 +impl LintLocation {
 +    fn still_parent(self, b: bool) -> Self {
 +        if b { self } else { Self::Inner }
 +    }
 +
 +    fn is_parent(self) -> bool {
 +        self == Self::Parent
 +    }
 +}
 +
 +// Gets the call site if the span is in a child context. Otherwise returns `None`.
 +fn get_call_site(span: Span, ctxt: SyntaxContext) -> Option<Span> {
 +    (span.ctxt() != ctxt).then(|| walk_span_to_context(span, ctxt).unwrap_or(span))
 +}
 +
 +fn lint_implicit_returns(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    // The context of the function body.
 +    ctxt: SyntaxContext,
 +    // Whether the expression is from a macro expansion.
 +    call_site_span: Option<Span>,
 +) -> LintLocation {
 +    match expr.kind {
 +        ExprKind::Block(
 +            Block {
 +                expr: Some(block_expr), ..
 +            },
 +            _,
 +        ) => lint_implicit_returns(
 +            cx,
 +            block_expr,
 +            ctxt,
 +            call_site_span.or_else(|| get_call_site(block_expr.span, ctxt)),
 +        )
 +        .still_parent(call_site_span.is_some()),
 +
 +        ExprKind::If(_, then_expr, Some(else_expr)) => {
 +            // Both `then_expr` or `else_expr` are required to be blocks in the same context as the `if`. Don't
 +            // bother checking.
 +            let res = lint_implicit_returns(cx, then_expr, ctxt, call_site_span).still_parent(call_site_span.is_some());
 +            if res.is_parent() {
 +                // The return was added as a parent of this if expression.
 +                return res;
 +            }
 +            lint_implicit_returns(cx, else_expr, ctxt, call_site_span).still_parent(call_site_span.is_some())
 +        },
 +
 +        ExprKind::Match(_, arms, _) => {
 +            for arm in arms {
 +                let res = lint_implicit_returns(
 +                    cx,
 +                    arm.body,
 +                    ctxt,
 +                    call_site_span.or_else(|| get_call_site(arm.body.span, ctxt)),
 +                )
 +                .still_parent(call_site_span.is_some());
 +                if res.is_parent() {
 +                    // The return was added as a parent of this match expression.
 +                    return res;
 +                }
 +            }
 +            LintLocation::Inner
 +        },
 +
 +        ExprKind::Loop(block, ..) => {
 +            let mut add_return = false;
 +            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.hir_id, e.span, sub_expr.span);
 +                            }
 +                        } 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 {
 +                #[expect(clippy::option_if_let_else)]
 +                if let Some(span) = call_site_span {
 +                    lint_return(cx, expr.hir_id, span);
 +                    LintLocation::Parent
 +                } else {
 +                    lint_return(cx, expr.hir_id, expr.span);
 +                    LintLocation::Inner
 +                }
 +            } else {
 +                LintLocation::Inner
 +            }
 +        },
 +
 +        // If expressions without an else clause, and blocks without a final expression can only be the final expression
 +        // if they are divergent, or return the unit type.
 +        ExprKind::If(_, _, None) | ExprKind::Block(Block { expr: None, .. }, _) | ExprKind::Ret(_) => {
 +            LintLocation::Inner
 +        },
 +
 +        // Any divergent expression doesn't need a return statement.
 +        ExprKind::MethodCall(..)
 +        | ExprKind::Call(..)
 +        | ExprKind::Binary(..)
 +        | ExprKind::Unary(..)
 +        | ExprKind::Index(..)
 +            if cx.typeck_results().expr_ty(expr).is_never() =>
 +        {
 +            LintLocation::Inner
 +        },
 +
 +        _ =>
 +        {
 +            #[expect(clippy::option_if_let_else)]
 +            if let Some(span) = call_site_span {
 +                lint_return(cx, expr.hir_id, span);
 +                LintLocation::Parent
 +            } else {
 +                lint_return(cx, expr.hir_id, expr.span);
 +                LintLocation::Inner
 +            }
 +        },
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for ImplicitReturn {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        kind: FnKind<'tcx>,
 +        decl: &'tcx FnDecl<'_>,
 +        body: &'tcx Body<'_>,
 +        span: Span,
 +        _: HirId,
 +    ) {
 +        if (!matches!(kind, FnKind::Closure) && matches!(decl.output, FnRetTy::DefaultReturn(_)))
 +            || span.ctxt() != body.value.span.ctxt()
 +            || in_external_macro(cx.sess(), span)
 +        {
 +            return;
 +        }
 +
-             &body.value
++        let res_ty = cx.typeck_results().expr_ty(body.value);
 +        if res_ty.is_unit() || res_ty.is_never() {
 +            return;
 +        }
 +
 +        let expr = if is_async_fn(kind) {
 +            match get_async_fn_body(cx.tcx, body) {
 +                Some(e) => e,
 +                None => return,
 +            }
 +        } else {
++            body.value
 +        };
 +        lint_implicit_returns(cx, expr, expr.span.ctxt(), None);
 +    }
 +}
index d55a8e1ead17d11eb02f135c361010dca7fb9a2c,0000000000000000000000000000000000000000..8c2c96fa105af251ec8796c5e76d122d89394694
mode 100644,000000..100644
--- /dev/null
@@@ -1,258 -1,0 +1,260 @@@
-             }
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 +use clippy_utils::{higher, match_def_path, path_def_id, paths};
 +use rustc_hir::{BorrowKind, Closure, Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::symbol::{sym, Symbol};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for iteration that is guaranteed to be infinite.
 +    ///
 +    /// ### Why is this bad?
 +    /// While there may be places where this is acceptable
 +    /// (e.g., in event streams), in most cases this is simply an error.
 +    ///
 +    /// ### Example
 +    /// ```no_run
 +    /// use std::iter;
 +    ///
 +    /// iter::repeat(1_u8).collect::<Vec<_>>();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub INFINITE_ITER,
 +    correctness,
 +    "infinite iteration"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for iteration that may be infinite.
 +    ///
 +    /// ### Why is this bad?
 +    /// While there may be places where this is acceptable
 +    /// (e.g., in event streams), in most cases this is simply an error.
 +    ///
 +    /// ### Known problems
 +    /// The code may have a condition to stop iteration, but
 +    /// this lint is not clever enough to analyze it.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let infinite_iter = 0..;
 +    /// # #[allow(unused)]
 +    /// [0..].iter().zip(infinite_iter.take_while(|x| *x > 5));
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MAYBE_INFINITE_ITER,
 +    pedantic,
 +    "possible infinite iteration"
 +}
 +
 +declare_lint_pass!(InfiniteIter => [INFINITE_ITER, MAYBE_INFINITE_ITER]);
 +
 +impl<'tcx> LateLintPass<'tcx> for InfiniteIter {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        let (lint, msg) = match complete_infinite_iter(cx, expr) {
 +            Infinite => (INFINITE_ITER, "infinite iteration detected"),
 +            MaybeInfinite => (MAYBE_INFINITE_ITER, "possible infinite iteration detected"),
 +            Finite => {
 +                return;
-                     return is_infinite(cx, &body.value);
++            },
 +        };
 +        span_lint(cx, lint, expr.span, msg);
 +    }
 +}
 +
 +#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 +enum Finiteness {
 +    Infinite,
 +    MaybeInfinite,
 +    Finite,
 +}
 +
 +use self::Finiteness::{Finite, Infinite, MaybeInfinite};
 +
 +impl Finiteness {
 +    #[must_use]
 +    fn and(self, b: Self) -> Self {
 +        match (self, b) {
 +            (Finite, _) | (_, Finite) => Finite,
 +            (MaybeInfinite, _) | (_, MaybeInfinite) => MaybeInfinite,
 +            _ => Infinite,
 +        }
 +    }
 +
 +    #[must_use]
 +    fn or(self, b: Self) -> Self {
 +        match (self, b) {
 +            (Infinite, _) | (_, Infinite) => Infinite,
 +            (MaybeInfinite, _) | (_, MaybeInfinite) => MaybeInfinite,
 +            _ => Finite,
 +        }
 +    }
 +}
 +
 +impl From<bool> for Finiteness {
 +    #[must_use]
 +    fn from(b: bool) -> Self {
 +        if b { Infinite } else { Finite }
 +    }
 +}
 +
 +/// This tells us what to look for to know if the iterator returned by
 +/// this method is infinite
 +#[derive(Copy, Clone)]
 +enum Heuristic {
 +    /// infinite no matter what
 +    Always,
 +    /// infinite if the first argument is
 +    First,
 +    /// infinite if any of the supplied arguments is
 +    Any,
 +    /// infinite if all of the supplied arguments are
 +    All,
 +}
 +
 +use self::Heuristic::{All, Always, Any, First};
 +
 +/// a slice of (method name, number of args, heuristic, bounds) tuples
 +/// that will be used to determine whether the method in question
 +/// returns an infinite or possibly infinite iterator. The finiteness
 +/// is an upper bound, e.g., some methods can return a possibly
 +/// infinite iterator at worst, e.g., `take_while`.
 +const HEURISTICS: [(&str, usize, Heuristic, Finiteness); 19] = [
 +    ("zip", 1, All, Infinite),
 +    ("chain", 1, Any, Infinite),
 +    ("cycle", 0, Always, Infinite),
 +    ("map", 1, First, Infinite),
 +    ("by_ref", 0, First, Infinite),
 +    ("cloned", 0, First, Infinite),
 +    ("rev", 0, First, Infinite),
 +    ("inspect", 0, First, Infinite),
 +    ("enumerate", 0, First, Infinite),
 +    ("peekable", 1, First, Infinite),
 +    ("fuse", 0, First, Infinite),
 +    ("skip", 1, First, Infinite),
 +    ("skip_while", 0, First, Infinite),
 +    ("filter", 1, First, Infinite),
 +    ("filter_map", 1, First, Infinite),
 +    ("flat_map", 1, First, Infinite),
 +    ("unzip", 0, First, Infinite),
 +    ("take_while", 1, First, MaybeInfinite),
 +    ("scan", 2, First, MaybeInfinite),
 +];
 +
 +fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
 +    match expr.kind {
 +        ExprKind::MethodCall(method, receiver, args, _) => {
 +            for &(name, len, heuristic, cap) in &HEURISTICS {
 +                if method.ident.name.as_str() == name && args.len() == len {
 +                    return (match heuristic {
 +                        Always => Infinite,
 +                        First => is_infinite(cx, receiver),
 +                        Any => is_infinite(cx, receiver).or(is_infinite(cx, &args[0])),
 +                        All => is_infinite(cx, receiver).and(is_infinite(cx, &args[0])),
 +                    })
 +                    .and(cap);
 +                }
 +            }
 +            if method.ident.name == sym!(flat_map) && args.len() == 1 {
 +                if let ExprKind::Closure(&Closure { body, .. }) = args[0].kind {
 +                    let body = cx.tcx.hir().body(body);
-                 let not_double_ended =
-                     cx.tcx.get_diagnostic_item(sym::DoubleEndedIterator).map_or(false, |id| {
++                    return is_infinite(cx, body.value);
 +                }
 +            }
 +            Finite
 +        },
 +        ExprKind::Block(block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)),
 +        ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e),
 +        ExprKind::Call(path, _) => path_def_id(cx, path)
 +            .map_or(false, |id| match_def_path(cx, id, &paths::ITER_REPEAT))
 +            .into(),
 +        ExprKind::Struct(..) => higher::Range::hir(expr).map_or(false, |r| r.end.is_none()).into(),
 +        _ => Finite,
 +    }
 +}
 +
 +/// the names and argument lengths of methods that *may* exhaust their
 +/// iterators
 +const POSSIBLY_COMPLETING_METHODS: [(&str, usize); 6] = [
 +    ("find", 1),
 +    ("rfind", 1),
 +    ("position", 1),
 +    ("rposition", 1),
 +    ("any", 1),
 +    ("all", 1),
 +];
 +
 +/// the names and argument lengths of methods that *always* exhaust
 +/// their iterators
 +const COMPLETING_METHODS: [(&str, usize); 12] = [
 +    ("count", 0),
 +    ("fold", 2),
 +    ("for_each", 1),
 +    ("partition", 1),
 +    ("max", 0),
 +    ("max_by", 1),
 +    ("max_by_key", 1),
 +    ("min", 0),
 +    ("min_by", 1),
 +    ("min_by_key", 1),
 +    ("sum", 0),
 +    ("product", 0),
 +];
 +
 +/// the paths of types that are known to be infinitely allocating
 +const INFINITE_COLLECTORS: &[Symbol] = &[
 +    sym::BinaryHeap,
 +    sym::BTreeMap,
 +    sym::BTreeSet,
 +    sym::HashMap,
 +    sym::HashSet,
 +    sym::LinkedList,
 +    sym::Vec,
 +    sym::VecDeque,
 +];
 +
 +fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
 +    match expr.kind {
 +        ExprKind::MethodCall(method, receiver, args, _) => {
 +            for &(name, len) in &COMPLETING_METHODS {
 +                if method.ident.name.as_str() == name && args.len() == len {
 +                    return is_infinite(cx, receiver);
 +                }
 +            }
 +            for &(name, len) in &POSSIBLY_COMPLETING_METHODS {
 +                if method.ident.name.as_str() == name && args.len() == len {
 +                    return MaybeInfinite.and(is_infinite(cx, receiver));
 +                }
 +            }
 +            if method.ident.name == sym!(last) && args.is_empty() {
++                let not_double_ended = cx
++                    .tcx
++                    .get_diagnostic_item(sym::DoubleEndedIterator)
++                    .map_or(false, |id| {
 +                        !implements_trait(cx, cx.typeck_results().expr_ty(receiver), id, &[])
 +                    });
 +                if not_double_ended {
 +                    return is_infinite(cx, receiver);
 +                }
 +            } else if method.ident.name == sym!(collect) {
 +                let ty = cx.typeck_results().expr_ty(expr);
 +                if INFINITE_COLLECTORS
 +                    .iter()
 +                    .any(|diag_item| is_type_diagnostic_item(cx, ty, *diag_item))
 +                {
 +                    return is_infinite(cx, receiver);
 +                }
 +            }
 +        },
 +        ExprKind::Binary(op, l, r) => {
 +            if op.node.is_comparison() {
 +                return is_infinite(cx, l).and(is_infinite(cx, r)).and(MaybeInfinite);
 +            }
 +        }, // TODO: ExprKind::Loop + Match
 +        _ => (),
 +    }
 +    Finite
 +}
index c58df126d62442a056d63437518699c3d189012c,0000000000000000000000000000000000000000..eb13d0869c037d522b4b3d34b0a66886c7f324db
mode 100644,000000..100644
--- /dev/null
@@@ -1,200 -1,0 +1,220 @@@
- use clippy_utils::{diagnostics::span_lint_and_then, ty::is_copy};
 +//! lint when there is a large size difference between variants on an enum
 +
 +use clippy_utils::source::snippet_with_applicability;
- use rustc_middle::ty::layout::LayoutOf;
- use rustc_middle::ty::{Adt, Ty};
++use clippy_utils::{diagnostics::span_lint_and_then, ty::approx_ty_size, ty::is_copy};
 +use rustc_errors::Applicability;
 +use rustc_hir::{Item, ItemKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::lint::in_external_macro;
-     /// Enum size is bounded by the largest variant. Having a
++use rustc_middle::ty::{Adt, AdtDef, GenericArg, List, Ty};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for large size differences between variants on
 +    /// `enum`s.
 +    ///
 +    /// ### Why is this bad?
-     /// The lint will ignore generic types if the layout depends on the
-     /// generics, even if the size difference will be large anyway.
++    /// Enum size is bounded by the largest variant. Having one
 +    /// large variant can penalize the memory layout of that enum.
 +    ///
 +    /// ### Known problems
 +    /// This lint obviously cannot take the distribution of
 +    /// variants in your running program into account. It is possible that the
 +    /// smaller variants make up less than 1% of all instances, in which case
 +    /// the overhead is negligible and the boxing is counter-productive. Always
 +    /// measure the change this lint suggests.
 +    ///
 +    /// For types that implement `Copy`, the suggestion to `Box` a variant's
 +    /// data would require removing the trait impl. The types can of course
 +    /// still be `Clone`, but that is worse ergonomically. Depending on the
 +    /// use case it may be possible to store the large data in an auxiliary
 +    /// structure (e.g. Arena or ECS).
 +    ///
-             let adt = ty.ty_adt_def().expect("already checked whether this is an enum");
++    /// The lint will ignore the impact of generic types to the type layout by
++    /// assuming every type parameter is zero-sized. Depending on your use case,
++    /// this may lead to a false positive.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// enum Test {
 +    ///     A(i32),
 +    ///     B([i32; 8000]),
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// // Possibly better
 +    /// enum Test2 {
 +    ///     A(i32),
 +    ///     B(Box<[i32; 8000]>),
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub LARGE_ENUM_VARIANT,
 +    perf,
 +    "large size difference between variants on an enum"
 +}
 +
 +#[derive(Copy, Clone)]
 +pub struct LargeEnumVariant {
 +    maximum_size_difference_allowed: u64,
 +}
 +
 +impl LargeEnumVariant {
 +    #[must_use]
 +    pub fn new(maximum_size_difference_allowed: u64) -> Self {
 +        Self {
 +            maximum_size_difference_allowed,
 +        }
 +    }
 +}
 +
 +struct FieldInfo {
 +    ind: usize,
 +    size: u64,
 +}
 +
 +struct VariantInfo {
 +    ind: usize,
 +    size: u64,
 +    fields_size: Vec<FieldInfo>,
 +}
 +
++fn variants_size<'tcx>(
++    cx: &LateContext<'tcx>,
++    adt: AdtDef<'tcx>,
++    subst: &'tcx List<GenericArg<'tcx>>,
++) -> Vec<VariantInfo> {
++    let mut variants_size = adt
++        .variants()
++        .iter()
++        .enumerate()
++        .map(|(i, variant)| {
++            let mut fields_size = variant
++                .fields
++                .iter()
++                .enumerate()
++                .map(|(i, f)| FieldInfo {
++                    ind: i,
++                    size: approx_ty_size(cx, f.ty(cx.tcx, subst)),
++                })
++                .collect::<Vec<_>>();
++            fields_size.sort_by(|a, b| (a.size.cmp(&b.size)));
++
++            VariantInfo {
++                ind: i,
++                size: fields_size.iter().map(|info| info.size).sum(),
++                fields_size,
++            }
++        })
++        .collect::<Vec<_>>();
++    variants_size.sort_by(|a, b| (b.size.cmp(&a.size)));
++    variants_size
++}
++
 +impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]);
 +
 +impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) {
 +        if in_external_macro(cx.tcx.sess, item.span) {
 +            return;
 +        }
 +        if let ItemKind::Enum(ref def, _) = item.kind {
 +            let ty = cx.tcx.type_of(item.def_id);
-             let mut variants_size: Vec<VariantInfo> = Vec::new();
-             for (i, variant) in adt.variants().iter().enumerate() {
-                 let mut fields_size = Vec::new();
-                 for (i, f) in variant.fields.iter().enumerate() {
-                     let ty = cx.tcx.type_of(f.did);
-                     // don't lint variants which have a field of generic type.
-                     match cx.layout_of(ty) {
-                         Ok(l) => {
-                             let fsize = l.size.bytes();
-                             fields_size.push(FieldInfo { ind: i, size: fsize });
-                         },
-                         Err(_) => {
-                             return;
-                         },
-                     }
-                 }
-                 let size: u64 = fields_size.iter().map(|info| info.size).sum();
-                 variants_size.push(VariantInfo {
-                     ind: i,
-                     size,
-                     fields_size,
-                 });
-             }
-             variants_size.sort_by(|a, b| (b.size.cmp(&a.size)));
++            let (adt, subst) = match ty.kind() {
++                Adt(adt, subst) => (adt, subst),
++                _ => panic!("already checked whether this is an enum"),
++            };
 +            if adt.variants().len() <= 1 {
 +                return;
 +            }
-                     def.variants[variants_size[0].ind].span,
++            let variants_size = variants_size(cx, *adt, subst);
 +
 +            let mut difference = variants_size[0].size - variants_size[1].size;
 +            if difference > self.maximum_size_difference_allowed {
 +                let help_text = "consider boxing the large fields to reduce the total size of the enum";
 +                span_lint_and_then(
 +                    cx,
 +                    LARGE_ENUM_VARIANT,
-                             &format!("this variant is {} bytes", variants_size[0].size),
++                    item.span,
 +                    "large size difference between variants",
 +                    |diag| {
++                        diag.span_label(
++                            item.span,
++                            format!("the entire enum is at least {} bytes", approx_ty_size(cx, ty)),
++                        );
 +                        diag.span_label(
 +                            def.variants[variants_size[0].ind].span,
-                         diag.span_note(
++                            format!("the largest variant contains at least {} bytes", variants_size[0].size),
 +                        );
-                             &format!("and the second-largest variant is {} bytes:", variants_size[1].size),
++                        diag.span_label(
 +                            def.variants[variants_size[1].ind].span,
-                         variants_size[0].fields_size.sort_by(|a, b| (a.size.cmp(&b.size)));
++                            &if variants_size[1].fields_size.is_empty() {
++                                "the second-largest variant carries no data at all".to_owned()
++                            } else {
++                                format!(
++                                    "the second-largest variant contains at least {} bytes",
++                                    variants_size[1].size
++                                )
++                            },
 +                        );
 +
 +                        let fields = def.variants[variants_size[0].ind].data.fields();
 +                        let mut applicability = Applicability::MaybeIncorrect;
 +                        if is_copy(cx, ty) || maybe_copy(cx, ty) {
 +                            diag.span_note(
 +                                item.ident.span,
 +                                "boxing a variant would require the type no longer be `Copy`",
 +                            );
 +                        } else {
 +                            let sugg: Vec<(Span, String)> = variants_size[0]
 +                                .fields_size
 +                                .iter()
 +                                .rev()
 +                                .map_while(|val| {
 +                                    if difference > self.maximum_size_difference_allowed {
 +                                        difference = difference.saturating_sub(val.size);
 +                                        Some((
 +                                            fields[val.ind].ty.span,
 +                                            format!(
 +                                                "Box<{}>",
 +                                                snippet_with_applicability(
 +                                                    cx,
 +                                                    fields[val.ind].ty.span,
 +                                                    "..",
 +                                                    &mut applicability
 +                                                )
 +                                                .into_owned()
 +                                            ),
 +                                        ))
 +                                    } else {
 +                                        None
 +                                    }
 +                                })
 +                                .collect();
 +
 +                            if !sugg.is_empty() {
 +                                diag.multipart_suggestion(help_text, sugg, Applicability::MaybeIncorrect);
 +                                return;
 +                            }
 +                        }
 +                        diag.span_help(def.variants[variants_size[0].ind].span, help_text);
 +                    },
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn maybe_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    if let Adt(_def, substs) = ty.kind()
 +        && substs.types().next().is_some()
 +        && let Some(copy_trait) = cx.tcx.lang_items().copy_trait()
 +    {
 +        return cx.tcx.non_blanket_impls_for_ty(copy_trait, ty).next().is_some();
 +    }
 +    false
 +}
index 3cbdaff407b04383f07bf24c84dfb71d52bed93d,0000000000000000000000000000000000000000..7ae8ef830faea952e65ef77b77e83dcffcbcc2f1
mode 100644,000000..100644
--- /dev/null
@@@ -1,496 -1,0 +1,508 @@@
-     if let (&ExprKind::MethodCall(method_path, receiver, args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind) {
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed};
 +use if_chain::if_chain;
 +use rustc_ast::ast::LitKind;
 +use rustc_errors::Applicability;
 +use rustc_hir::def_id::DefIdSet;
 +use rustc_hir::{
 +    def_id::DefId, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, ImplItem, ImplItemKind, ImplicitSelfKind, Item,
 +    ItemKind, Mutability, Node, TraitItemRef, TyKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::{self, AssocKind, FnSig, Ty};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{
 +    source_map::{Span, Spanned, Symbol},
 +    symbol::sym,
 +};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for getting the length of something via `.len()`
 +    /// just to compare to zero, and suggests using `.is_empty()` where applicable.
 +    ///
 +    /// ### Why is this bad?
 +    /// Some structures can answer `.is_empty()` much faster
 +    /// than calculating their length. So it is good to get into the habit of using
 +    /// `.is_empty()`, and having it is cheap.
 +    /// Besides, it makes the intent clearer than a manual comparison in some contexts.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// if x.len() == 0 {
 +    ///     ..
 +    /// }
 +    /// if y.len() != 0 {
 +    ///     ..
 +    /// }
 +    /// ```
 +    /// instead use
 +    /// ```ignore
 +    /// if x.is_empty() {
 +    ///     ..
 +    /// }
 +    /// if !y.is_empty() {
 +    ///     ..
 +    /// }
 +    /// ```
 +    #[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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for items that implement `.len()` but not
 +    /// `.is_empty()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is good custom to have both methods, because for
 +    /// some data structures, asking about the length will be a costly operation,
 +    /// whereas `.is_empty()` can usually answer in constant time. Also it used to
 +    /// lead to false positives on the [`len_zero`](#len_zero) lint – currently that
 +    /// lint will ignore such entities.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// impl X {
 +    ///     pub fn len(&self) -> usize {
 +    ///         ..
 +    ///     }
 +    /// }
 +    /// ```
 +    #[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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for comparing to an empty slice such as `""` or `[]`,
 +    /// and suggests using `.is_empty()` where applicable.
 +    ///
 +    /// ### Why is this bad?
 +    /// Some structures can answer `.is_empty()` much faster
 +    /// than checking for equality. So it is good to get into the habit of using
 +    /// `.is_empty()`, and having it is cheap.
 +    /// Besides, it makes the intent clearer than a manual comparison in some contexts.
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```ignore
 +    /// if s == "" {
 +    ///     ..
 +    /// }
 +    ///
 +    /// if arr == [] {
 +    ///     ..
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```ignore
 +    /// if s.is_empty() {
 +    ///     ..
 +    /// }
 +    ///
 +    /// if arr.is_empty() {
 +    ///     ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub COMPARISON_TO_EMPTY,
 +    style,
 +    "checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead"
 +}
 +
 +declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMPTY]);
 +
 +impl<'tcx> LateLintPass<'tcx> for LenZero {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        if item.span.from_expansion() {
 +            return;
 +        }
 +
 +        if let ItemKind::Trait(_, _, _, _, trait_items) = item.kind {
 +            check_trait_items(cx, item, trait_items);
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
 +        if_chain! {
 +            if item.ident.name == sym::len;
 +            if let ImplItemKind::Fn(sig, _) = &item.kind;
 +            if sig.decl.implicit_self.has_implicit_self();
 +            if cx.access_levels.is_exported(item.def_id);
 +            if matches!(sig.decl.output, FnRetTy::Return(_));
 +            if let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id());
 +            if imp.of_trait.is_none();
 +            if let TyKind::Path(ty_path) = &imp.self_ty.kind;
 +            if let Some(ty_id) = cx.qpath_res(ty_path, imp.self_ty.hir_id).opt_def_id();
 +            if let Some(local_id) = ty_id.as_local();
 +            let ty_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id);
 +            if !is_lint_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id);
 +            if let Some(output) = parse_len_output(cx, cx.tcx.fn_sig(item.def_id).skip_binder());
 +            then {
 +                let (name, kind) = match cx.tcx.hir().find(ty_hir_id) {
 +                    Some(Node::ForeignItem(x)) => (x.ident.name, "extern type"),
 +                    Some(Node::Item(x)) => match x.kind {
 +                        ItemKind::Struct(..) => (x.ident.name, "struct"),
 +                        ItemKind::Enum(..) => (x.ident.name, "enum"),
 +                        ItemKind::Union(..) => (x.ident.name, "union"),
 +                        _ => (x.ident.name, "type"),
 +                    }
 +                    _ => return,
 +                };
 +                check_for_is_empty(cx, sig.span, sig.decl.implicit_self, output, ty_id, name, kind)
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        if let ExprKind::Binary(Spanned { node: cmp, .. }, left, right) = expr.kind {
 +            match cmp {
 +                BinOpKind::Eq => {
 +                    check_cmp(cx, expr.span, left, right, "", 0); // len == 0
 +                    check_cmp(cx, expr.span, right, left, "", 0); // 0 == len
 +                },
 +                BinOpKind::Ne => {
 +                    check_cmp(cx, expr.span, left, right, "!", 0); // len != 0
 +                    check_cmp(cx, expr.span, right, left, "!", 0); // 0 != len
 +                },
 +                BinOpKind::Gt => {
 +                    check_cmp(cx, expr.span, left, right, "!", 0); // len > 0
 +                    check_cmp(cx, expr.span, right, left, "", 1); // 1 > len
 +                },
 +                BinOpKind::Lt => {
 +                    check_cmp(cx, expr.span, left, right, "", 1); // len < 1
 +                    check_cmp(cx, expr.span, right, left, "!", 0); // 0 < len
 +                },
 +                BinOpKind::Ge => check_cmp(cx, expr.span, left, right, "!", 1), // len >= 1
 +                BinOpKind::Le => check_cmp(cx, expr.span, right, left, "!", 1), // 1 <= len
 +                _ => (),
 +            }
 +        }
 +    }
 +}
 +
 +fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items: &[TraitItemRef]) {
 +    fn is_named_self(cx: &LateContext<'_>, item: &TraitItemRef, name: Symbol) -> bool {
 +        item.ident.name == name
 +            && if let AssocItemKind::Fn { has_self } = item.kind {
 +                has_self && { cx.tcx.fn_sig(item.id.def_id).inputs().skip_binder().len() == 1 }
 +            } else {
 +                false
 +            }
 +    }
 +
 +    // fill the set with current and super traits
 +    fn fill_trait_set(traitt: DefId, set: &mut DefIdSet, cx: &LateContext<'_>) {
 +        if set.insert(traitt) {
 +            for supertrait in rustc_trait_selection::traits::supertrait_def_ids(cx.tcx, traitt) {
 +                fill_trait_set(supertrait, set, cx);
 +            }
 +        }
 +    }
 +
 +    if cx.access_levels.is_exported(visited_trait.def_id) && trait_items.iter().any(|i| is_named_self(cx, i, sym::len))
 +    {
 +        let mut current_and_super_traits = DefIdSet::default();
 +        fill_trait_set(visited_trait.def_id.to_def_id(), &mut current_and_super_traits, cx);
 +        let is_empty = sym!(is_empty);
 +
 +        let is_empty_method_found = current_and_super_traits
 +            .iter()
 +            .flat_map(|&i| cx.tcx.associated_items(i).filter_by_name_unhygienic(is_empty))
 +            .any(|i| {
 +                i.kind == ty::AssocKind::Fn
 +                    && i.fn_has_self_parameter
 +                    && cx.tcx.fn_sig(i.def_id).inputs().skip_binder().len() == 1
 +            });
 +
 +        if !is_empty_method_found {
 +            span_lint(
 +                cx,
 +                LEN_WITHOUT_IS_EMPTY,
 +                visited_trait.span,
 +                &format!(
 +                    "trait `{}` has a `len` method but no (possibly inherited) `is_empty` method",
 +                    visited_trait.ident.name
 +                ),
 +            );
 +        }
 +    }
 +}
 +
 +#[derive(Debug, Clone, Copy)]
 +enum LenOutput<'tcx> {
 +    Integral,
 +    Option(DefId),
 +    Result(DefId, Ty<'tcx>),
 +}
 +fn parse_len_output<'tcx>(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option<LenOutput<'tcx>> {
 +    match *sig.output().kind() {
 +        ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral),
 +        ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) => {
 +            subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did()))
 +        },
 +        ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => subs
 +            .type_at(0)
 +            .is_integral()
 +            .then(|| LenOutput::Result(adt.did(), subs.type_at(1))),
 +        _ => None,
 +    }
 +}
 +
 +impl<'tcx> LenOutput<'tcx> {
 +    fn matches_is_empty_output(self, ty: Ty<'tcx>) -> bool {
 +        match (self, ty.kind()) {
 +            (_, &ty::Bool) => true,
 +            (Self::Option(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(),
 +            (Self::Result(id, err_ty), &ty::Adt(adt, subs)) if id == adt.did() => {
 +                subs.type_at(0).is_bool() && subs.type_at(1) == err_ty
 +            },
 +            _ => false,
 +        }
 +    }
 +
 +    fn expected_sig(self, self_kind: ImplicitSelfKind) -> String {
 +        let self_ref = match self_kind {
 +            ImplicitSelfKind::ImmRef => "&",
 +            ImplicitSelfKind::MutRef => "&mut ",
 +            _ => "",
 +        };
 +        match self {
 +            Self::Integral => format!("expected signature: `({}self) -> bool`", self_ref),
 +            Self::Option(_) => format!(
 +                "expected signature: `({}self) -> bool` or `({}self) -> Option<bool>",
 +                self_ref, self_ref
 +            ),
 +            Self::Result(..) => format!(
 +                "expected signature: `({}self) -> bool` or `({}self) -> Result<bool>",
 +                self_ref, self_ref
 +            ),
 +        }
 +    }
 +}
 +
 +/// Checks if the given signature matches the expectations for `is_empty`
 +fn check_is_empty_sig<'tcx>(sig: FnSig<'tcx>, self_kind: ImplicitSelfKind, len_output: LenOutput<'tcx>) -> bool {
 +    match &**sig.inputs_and_output {
 +        [arg, res] if len_output.matches_is_empty_output(*res) => {
 +            matches!(
 +                (arg.kind(), self_kind),
 +                (ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::ImmRef)
 +                    | (ty::Ref(_, _, Mutability::Mut), ImplicitSelfKind::MutRef)
 +            ) || (!arg.is_ref() && matches!(self_kind, ImplicitSelfKind::Imm | ImplicitSelfKind::Mut))
 +        },
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the given type has an `is_empty` method with the appropriate signature.
 +fn check_for_is_empty<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    span: Span,
 +    self_kind: ImplicitSelfKind,
 +    output: LenOutput<'tcx>,
 +    impl_ty: DefId,
 +    item_name: Symbol,
 +    item_kind: &str,
 +) {
 +    let is_empty = Symbol::intern("is_empty");
 +    let is_empty = cx
 +        .tcx
 +        .inherent_impls(impl_ty)
 +        .iter()
 +        .flat_map(|&id| cx.tcx.associated_items(id).filter_by_name_unhygienic(is_empty))
 +        .find(|item| item.kind == AssocKind::Fn);
 +
 +    let (msg, is_empty_span, self_kind) = match is_empty {
 +        None => (
 +            format!(
 +                "{} `{}` has a public `len` method, but no `is_empty` method",
 +                item_kind,
 +                item_name.as_str(),
 +            ),
 +            None,
 +            None,
 +        ),
 +        Some(is_empty) if !cx.access_levels.is_exported(is_empty.def_id.expect_local()) => (
 +            format!(
 +                "{} `{}` has a public `len` method, but a private `is_empty` method",
 +                item_kind,
 +                item_name.as_str(),
 +            ),
 +            Some(cx.tcx.def_span(is_empty.def_id)),
 +            None,
 +        ),
 +        Some(is_empty)
 +            if !(is_empty.fn_has_self_parameter
 +                && check_is_empty_sig(cx.tcx.fn_sig(is_empty.def_id).skip_binder(), self_kind, output)) =>
 +        {
 +            (
 +                format!(
 +                    "{} `{}` has a public `len` method, but the `is_empty` method has an unexpected signature",
 +                    item_kind,
 +                    item_name.as_str(),
 +                ),
 +                Some(cx.tcx.def_span(is_empty.def_id)),
 +                Some(self_kind),
 +            )
 +        },
 +        Some(_) => return,
 +    };
 +
 +    span_lint_and_then(cx, LEN_WITHOUT_IS_EMPTY, span, &msg, |db| {
 +        if let Some(span) = is_empty_span {
 +            db.span_note(span, "`is_empty` defined here");
 +        }
 +        if let Some(self_kind) = self_kind {
 +            db.note(&output.expected_sig(self_kind));
 +        }
 +    });
 +}
 +
 +fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) {
-         check_len(cx, span, method_path.ident.name, receiver, args, &lit.node, op, compare_to);
++    if let (&ExprKind::MethodCall(method_path, receiver, args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind)
++    {
 +        // check if we are in an is_empty() method
 +        if let Some(name) = get_item_name(cx, method) {
 +            if name.as_str() == "is_empty" {
 +                return;
 +            }
 +        }
 +
++        check_len(
++            cx,
++            span,
++            method_path.ident.name,
++            receiver,
++            args,
++            &lit.node,
++            op,
++            compare_to,
++        );
 +    } else {
 +        check_empty_expr(cx, span, method, lit, op);
 +    }
 +}
 +
++// FIXME(flip1995): Figure out how to reduce the number of arguments
++#[allow(clippy::too_many_arguments)]
 +fn check_len(
 +    cx: &LateContext<'_>,
 +    span: Span,
 +    method_name: Symbol,
 +    receiver: &Expr<'_>,
 +    args: &[Expr<'_>],
 +    lit: &LitKind,
 +    op: &str,
 +    compare_to: u32,
 +) {
 +    if let LitKind::Int(lit, _) = *lit {
 +        // check if length is compared to the specified number
 +        if lit != u128::from(compare_to) {
 +            return;
 +        }
 +
 +        if method_name == sym::len && args.is_empty() && has_is_empty(cx, receiver) {
 +            let mut applicability = Applicability::MachineApplicable;
 +            span_lint_and_sugg(
 +                cx,
 +                LEN_ZERO,
 +                span,
 +                &format!("length comparison to {}", if compare_to == 0 { "zero" } else { "one" }),
 +                &format!("using `{}is_empty` is clearer and more explicit", op),
 +                format!(
 +                    "{}{}.is_empty()",
 +                    op,
 +                    snippet_with_applicability(cx, receiver.span, "_", &mut applicability)
 +                ),
 +                applicability,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str) {
 +    if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) {
 +        let mut applicability = Applicability::MachineApplicable;
 +        span_lint_and_sugg(
 +            cx,
 +            COMPARISON_TO_EMPTY,
 +            span,
 +            "comparison to empty slice",
 +            &format!("using `{}is_empty` is clearer and more explicit", op),
 +            format!(
 +                "{}{}.is_empty()",
 +                op,
 +                snippet_with_applicability(cx, lit1.span, "_", &mut applicability)
 +            ),
 +            applicability,
 +        );
 +    }
 +}
 +
 +fn is_empty_string(expr: &Expr<'_>) -> bool {
 +    if let ExprKind::Lit(ref lit) = expr.kind {
 +        if let LitKind::Str(lit, _) = lit.node {
 +            let lit = lit.as_str();
 +            return lit.is_empty();
 +        }
 +    }
 +    false
 +}
 +
 +fn is_empty_array(expr: &Expr<'_>) -> bool {
 +    if let ExprKind::Array(arr) = expr.kind {
 +        return arr.is_empty();
 +    }
 +    false
 +}
 +
 +/// Checks if this type has an `is_empty` method.
 +fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    /// Gets an `AssocItem` and return true if it matches `is_empty(self)`.
 +    fn is_is_empty(cx: &LateContext<'_>, item: &ty::AssocItem) -> bool {
 +        if item.kind == ty::AssocKind::Fn {
 +            let sig = cx.tcx.fn_sig(item.def_id);
 +            let ty = sig.skip_binder();
 +            ty.inputs().len() == 1
 +        } else {
 +            false
 +        }
 +    }
 +
 +    /// Checks the inherent impl's items for an `is_empty(self)` method.
 +    fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool {
 +        let is_empty = sym!(is_empty);
 +        cx.tcx.inherent_impls(id).iter().any(|imp| {
 +            cx.tcx
 +                .associated_items(*imp)
 +                .filter_by_name_unhygienic(is_empty)
 +                .any(|item| is_is_empty(cx, item))
 +        })
 +    }
 +
 +    let ty = &cx.typeck_results().expr_ty(expr).peel_refs();
 +    match ty.kind() {
 +        ty::Dynamic(tt, ..) => tt.principal().map_or(false, |principal| {
 +            let is_empty = sym!(is_empty);
 +            cx.tcx
 +                .associated_items(principal.def_id())
 +                .filter_by_name_unhygienic(is_empty)
 +                .any(|item| is_is_empty(cx, item))
 +        }),
 +        ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id),
 +        ty::Adt(id, _) => has_is_empty_impl(cx, id.did()),
 +        ty::Array(..) | ty::Slice(..) | ty::Str => true,
 +        _ => false,
 +    }
 +}
index 134cbbf7b5c66ad5f218173022533dc55b19c77b,0000000000000000000000000000000000000000..1f85382347aa1d992ad9ccc526ceb52e818de481
mode 100644,000000..100644
--- /dev/null
@@@ -1,362 -1,0 +1,363 @@@
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_group(true, "clippy::all", Some("clippy_all"), vec![
 +    LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE),
 +    LintId::of(approx_const::APPROX_CONSTANT),
 +    LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
 +    LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
 +    LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
 +    LintId::of(attrs::DEPRECATED_CFG_ATTR),
 +    LintId::of(attrs::DEPRECATED_SEMVER),
 +    LintId::of(attrs::MISMATCHED_TARGET_OS),
 +    LintId::of(attrs::USELESS_ATTRIBUTE),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
 +    LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
 +    LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
++    LintId::of(bool_to_int_with_if::BOOL_TO_INT_WITH_IF),
 +    LintId::of(booleans::NONMINIMAL_BOOL),
 +    LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR),
 +    LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
 +    LintId::of(casts::CAST_ABS_TO_UNSIGNED),
 +    LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
 +    LintId::of(casts::CAST_ENUM_TRUNCATION),
 +    LintId::of(casts::CAST_REF_TO_MUT),
 +    LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
 +    LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS),
 +    LintId::of(casts::CHAR_LIT_AS_U8),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
 +    LintId::of(casts::UNNECESSARY_CAST),
 +    LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
 +    LintId::of(collapsible_if::COLLAPSIBLE_IF),
 +    LintId::of(comparison_chain::COMPARISON_CHAIN),
 +    LintId::of(copies::IFS_SAME_COND),
 +    LintId::of(copies::IF_SAME_THEN_ELSE),
 +    LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
 +    LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
 +    LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY),
 +    LintId::of(dereference::EXPLICIT_AUTO_DEREF),
 +    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),
 +    LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ),
 +    LintId::of(disallowed_methods::DISALLOWED_METHODS),
 +    LintId::of(disallowed_names::DISALLOWED_NAMES),
 +    LintId::of(disallowed_types::DISALLOWED_TYPES),
 +    LintId::of(doc::MISSING_SAFETY_DOC),
 +    LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
 +    LintId::of(double_parens::DOUBLE_PARENS),
 +    LintId::of(drop_forget_ref::DROP_COPY),
 +    LintId::of(drop_forget_ref::DROP_NON_DROP),
 +    LintId::of(drop_forget_ref::DROP_REF),
 +    LintId::of(drop_forget_ref::FORGET_COPY),
 +    LintId::of(drop_forget_ref::FORGET_NON_DROP),
 +    LintId::of(drop_forget_ref::FORGET_REF),
 +    LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS),
 +    LintId::of(duplicate_mod::DUPLICATE_MOD),
 +    LintId::of(entry::MAP_ENTRY),
 +    LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
 +    LintId::of(enum_variants::ENUM_VARIANT_NAMES),
 +    LintId::of(enum_variants::MODULE_INCEPTION),
 +    LintId::of(escape::BOXED_LOCAL),
 +    LintId::of(eta_reduction::REDUNDANT_CLOSURE),
 +    LintId::of(explicit_write::EXPLICIT_WRITE),
 +    LintId::of(float_literal::EXCESSIVE_PRECISION),
 +    LintId::of(format::USELESS_FORMAT),
 +    LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
 +    LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
 +    LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
 +    LintId::of(format_impl::RECURSIVE_FORMAT_IMPL),
 +    LintId::of(formatting::POSSIBLE_MISSING_COMMA),
 +    LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
 +    LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
 +    LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
 +    LintId::of(from_over_into::FROM_OVER_INTO),
 +    LintId::of(from_str_radix_10::FROM_STR_RADIX_10),
 +    LintId::of(functions::DOUBLE_MUST_USE),
 +    LintId::of(functions::MUST_USE_UNIT),
 +    LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
 +    LintId::of(functions::RESULT_LARGE_ERR),
 +    LintId::of(functions::RESULT_UNIT_ERR),
 +    LintId::of(functions::TOO_MANY_ARGUMENTS),
 +    LintId::of(if_let_mutex::IF_LET_MUTEX),
 +    LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
 +    LintId::of(infinite_iter::INFINITE_ITER),
 +    LintId::of(inherent_to_string::INHERENT_TO_STRING),
 +    LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
 +    LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
 +    LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
 +    LintId::of(int_plus_one::INT_PLUS_ONE),
 +    LintId::of(invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED),
 +    LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
 +    LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
 +    LintId::of(len_zero::COMPARISON_TO_EMPTY),
 +    LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY),
 +    LintId::of(len_zero::LEN_ZERO),
 +    LintId::of(let_underscore::LET_UNDERSCORE_LOCK),
 +    LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES),
 +    LintId::of(lifetimes::NEEDLESS_LIFETIMES),
 +    LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING),
 +    LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES),
 +    LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS),
 +    LintId::of(loops::EMPTY_LOOP),
 +    LintId::of(loops::EXPLICIT_COUNTER_LOOP),
 +    LintId::of(loops::FOR_KV_MAP),
 +    LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
 +    LintId::of(loops::ITER_NEXT_LOOP),
 +    LintId::of(loops::MANUAL_FIND),
 +    LintId::of(loops::MANUAL_FLATTEN),
 +    LintId::of(loops::MANUAL_MEMCPY),
 +    LintId::of(loops::MISSING_SPIN_LOOP),
 +    LintId::of(loops::MUT_RANGE_BOUND),
 +    LintId::of(loops::NEEDLESS_COLLECT),
 +    LintId::of(loops::NEEDLESS_RANGE_LOOP),
 +    LintId::of(loops::NEVER_LOOP),
 +    LintId::of(loops::SAME_ITEM_PUSH),
 +    LintId::of(loops::SINGLE_ELEMENT_LOOP),
 +    LintId::of(loops::WHILE_IMMUTABLE_CONDITION),
 +    LintId::of(loops::WHILE_LET_LOOP),
 +    LintId::of(loops::WHILE_LET_ON_ITERATOR),
 +    LintId::of(main_recursion::MAIN_RECURSION),
 +    LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
 +    LintId::of(manual_bits::MANUAL_BITS),
 +    LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
 +    LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID),
 +    LintId::of(manual_retain::MANUAL_RETAIN),
 +    LintId::of(manual_strip::MANUAL_STRIP),
 +    LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
 +    LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
 +    LintId::of(match_result_ok::MATCH_RESULT_OK),
 +    LintId::of(matches::COLLAPSIBLE_MATCH),
 +    LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
 +    LintId::of(matches::MANUAL_MAP),
 +    LintId::of(matches::MANUAL_UNWRAP_OR),
 +    LintId::of(matches::MATCH_AS_REF),
 +    LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
 +    LintId::of(matches::MATCH_OVERLAPPING_ARM),
 +    LintId::of(matches::MATCH_REF_PATS),
 +    LintId::of(matches::MATCH_SINGLE_BINDING),
 +    LintId::of(matches::MATCH_STR_CASE_MISMATCH),
 +    LintId::of(matches::NEEDLESS_MATCH),
 +    LintId::of(matches::REDUNDANT_PATTERN_MATCHING),
 +    LintId::of(matches::SINGLE_MATCH),
 +    LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
 +    LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
 +    LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
 +    LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
 +    LintId::of(methods::BIND_INSTEAD_OF_MAP),
 +    LintId::of(methods::BYTES_COUNT_TO_LEN),
 +    LintId::of(methods::BYTES_NTH),
 +    LintId::of(methods::CHARS_LAST_CMP),
 +    LintId::of(methods::CHARS_NEXT_CMP),
 +    LintId::of(methods::CLONE_DOUBLE_REF),
 +    LintId::of(methods::CLONE_ON_COPY),
 +    LintId::of(methods::COLLAPSIBLE_STR_REPLACE),
 +    LintId::of(methods::ERR_EXPECT),
 +    LintId::of(methods::EXPECT_FUN_CALL),
 +    LintId::of(methods::EXTEND_WITH_DRAIN),
 +    LintId::of(methods::FILTER_MAP_IDENTITY),
 +    LintId::of(methods::FILTER_NEXT),
 +    LintId::of(methods::FLAT_MAP_IDENTITY),
 +    LintId::of(methods::GET_FIRST),
 +    LintId::of(methods::GET_LAST_WITH_LEN),
 +    LintId::of(methods::INSPECT_FOR_EACH),
 +    LintId::of(methods::INTO_ITER_ON_REF),
 +    LintId::of(methods::IS_DIGIT_ASCII_RADIX),
 +    LintId::of(methods::ITERATOR_STEP_BY_ZERO),
 +    LintId::of(methods::ITER_CLONED_COLLECT),
 +    LintId::of(methods::ITER_COUNT),
 +    LintId::of(methods::ITER_NEXT_SLICE),
 +    LintId::of(methods::ITER_NTH),
 +    LintId::of(methods::ITER_NTH_ZERO),
 +    LintId::of(methods::ITER_OVEREAGER_CLONED),
 +    LintId::of(methods::ITER_SKIP_NEXT),
 +    LintId::of(methods::MANUAL_FILTER_MAP),
 +    LintId::of(methods::MANUAL_FIND_MAP),
 +    LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
 +    LintId::of(methods::MANUAL_SPLIT_ONCE),
 +    LintId::of(methods::MANUAL_STR_REPEAT),
 +    LintId::of(methods::MAP_CLONE),
 +    LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
 +    LintId::of(methods::MAP_FLATTEN),
 +    LintId::of(methods::MAP_IDENTITY),
 +    LintId::of(methods::MUT_MUTEX_LOCK),
 +    LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
 +    LintId::of(methods::NEEDLESS_OPTION_TAKE),
 +    LintId::of(methods::NEEDLESS_SPLITN),
 +    LintId::of(methods::NEW_RET_NO_SELF),
 +    LintId::of(methods::NONSENSICAL_OPEN_OPTIONS),
 +    LintId::of(methods::NO_EFFECT_REPLACE),
 +    LintId::of(methods::OBFUSCATED_IF_ELSE),
 +    LintId::of(methods::OK_EXPECT),
 +    LintId::of(methods::OPTION_AS_REF_DEREF),
 +    LintId::of(methods::OPTION_FILTER_MAP),
 +    LintId::of(methods::OPTION_MAP_OR_NONE),
 +    LintId::of(methods::OR_FUN_CALL),
 +    LintId::of(methods::OR_THEN_UNWRAP),
 +    LintId::of(methods::RANGE_ZIP_WITH_LEN),
 +    LintId::of(methods::REPEAT_ONCE),
 +    LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
 +    LintId::of(methods::SEARCH_IS_SOME),
 +    LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
 +    LintId::of(methods::SINGLE_CHAR_ADD_STR),
 +    LintId::of(methods::SINGLE_CHAR_PATTERN),
 +    LintId::of(methods::SKIP_WHILE_NEXT),
 +    LintId::of(methods::STRING_EXTEND_CHARS),
 +    LintId::of(methods::SUSPICIOUS_MAP),
 +    LintId::of(methods::SUSPICIOUS_SPLITN),
 +    LintId::of(methods::SUSPICIOUS_TO_OWNED),
 +    LintId::of(methods::UNINIT_ASSUMED_INIT),
 +    LintId::of(methods::UNIT_HASH),
 +    LintId::of(methods::UNNECESSARY_FILTER_MAP),
 +    LintId::of(methods::UNNECESSARY_FIND_MAP),
 +    LintId::of(methods::UNNECESSARY_FOLD),
 +    LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
 +    LintId::of(methods::UNNECESSARY_SORT_BY),
 +    LintId::of(methods::UNNECESSARY_TO_OWNED),
 +    LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
 +    LintId::of(methods::USELESS_ASREF),
 +    LintId::of(methods::VEC_RESIZE_TO_ZERO),
 +    LintId::of(methods::WRONG_SELF_CONVENTION),
 +    LintId::of(methods::ZST_OFFSET),
 +    LintId::of(minmax::MIN_MAX),
 +    LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
 +    LintId::of(misc::TOPLEVEL_REF_ARG),
 +    LintId::of(misc::ZERO_PTR),
 +    LintId::of(misc_early::BUILTIN_TYPE_SHADOW),
 +    LintId::of(misc_early::DOUBLE_NEG),
 +    LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
 +    LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
 +    LintId::of(misc_early::REDUNDANT_PATTERN),
 +    LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
 +    LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
 +    LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION),
 +    LintId::of(multi_assignments::MULTI_ASSIGNMENTS),
 +    LintId::of(mut_key::MUTABLE_KEY_TYPE),
 +    LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
 +    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_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
 +    LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
 +    LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS),
 +    LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
 +    LintId::of(needless_update::NEEDLESS_UPDATE),
 +    LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
 +    LintId::of(neg_multiply::NEG_MULTIPLY),
 +    LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
 +    LintId::of(no_effect::NO_EFFECT),
 +    LintId::of(no_effect::UNNECESSARY_OPERATION),
 +    LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
 +    LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
 +    LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
 +    LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
 +    LintId::of(octal_escapes::OCTAL_ESCAPES),
 +    LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
 +    LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
 +    LintId::of(operators::ASSIGN_OP_PATTERN),
 +    LintId::of(operators::BAD_BIT_MASK),
 +    LintId::of(operators::CMP_NAN),
 +    LintId::of(operators::CMP_OWNED),
 +    LintId::of(operators::DOUBLE_COMPARISONS),
 +    LintId::of(operators::DURATION_SUBSEC),
 +    LintId::of(operators::EQ_OP),
 +    LintId::of(operators::ERASING_OP),
 +    LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS),
 +    LintId::of(operators::IDENTITY_OP),
 +    LintId::of(operators::INEFFECTIVE_BIT_MASK),
 +    LintId::of(operators::MISREFACTORED_ASSIGN_OP),
 +    LintId::of(operators::MODULO_ONE),
 +    LintId::of(operators::OP_REF),
 +    LintId::of(operators::PTR_EQ),
 +    LintId::of(operators::SELF_ASSIGNMENT),
 +    LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
 +    LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
 +    LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
 +    LintId::of(partialeq_to_none::PARTIALEQ_TO_NONE),
 +    LintId::of(precedence::PRECEDENCE),
 +    LintId::of(ptr::CMP_NULL),
 +    LintId::of(ptr::INVALID_NULL_PTR_USAGE),
 +    LintId::of(ptr::MUT_FROM_REF),
 +    LintId::of(ptr::PTR_ARG),
 +    LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
 +    LintId::of(question_mark::QUESTION_MARK),
 +    LintId::of(ranges::MANUAL_RANGE_CONTAINS),
 +    LintId::of(ranges::REVERSED_EMPTY_RANGES),
 +    LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
 +    LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC),
 +    LintId::of(redundant_clone::REDUNDANT_CLONE),
 +    LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
 +    LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),
 +    LintId::of(redundant_slicing::REDUNDANT_SLICING),
 +    LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
 +    LintId::of(reference::DEREF_ADDROF),
 +    LintId::of(regex::INVALID_REGEX),
 +    LintId::of(returns::LET_AND_RETURN),
 +    LintId::of(returns::NEEDLESS_RETURN),
 +    LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
 +    LintId::of(serde_api::SERDE_API_MISUSE),
 +    LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
 +    LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
 +    LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
 +    LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
 +    LintId::of(strings::TRIM_SPLIT_WHITESPACE),
 +    LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
 +    LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
 +    LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
 +    LintId::of(swap::ALMOST_SWAPPED),
 +    LintId::of(swap::MANUAL_SWAP),
 +    LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF),
 +    LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
 +    LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT),
 +    LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
 +    LintId::of(transmute::CROSSPOINTER_TRANSMUTE),
 +    LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS),
 +    LintId::of(transmute::TRANSMUTE_BYTES_TO_STR),
 +    LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
 +    LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
 +    LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
 +    LintId::of(transmute::TRANSMUTING_NULL),
 +    LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
 +    LintId::of(transmute::USELESS_TRANSMUTE),
 +    LintId::of(transmute::WRONG_TRANSMUTE),
 +    LintId::of(types::BORROWED_BOX),
 +    LintId::of(types::BOX_COLLECTION),
 +    LintId::of(types::REDUNDANT_ALLOCATION),
 +    LintId::of(types::TYPE_COMPLEXITY),
 +    LintId::of(types::VEC_BOX),
 +    LintId::of(unicode::INVISIBLE_CHARACTERS),
 +    LintId::of(uninit_vec::UNINIT_VEC),
 +    LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
 +    LintId::of(unit_types::LET_UNIT_VALUE),
 +    LintId::of(unit_types::UNIT_ARG),
 +    LintId::of(unit_types::UNIT_CMP),
 +    LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
 +    LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
 +    LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
 +    LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
 +    LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
 +    LintId::of(unused_peekable::UNUSED_PEEKABLE),
 +    LintId::of(unused_unit::UNUSED_UNIT),
 +    LintId::of(unwrap::PANICKING_UNWRAP),
 +    LintId::of(unwrap::UNNECESSARY_UNWRAP),
 +    LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
 +    LintId::of(useless_conversion::USELESS_CONVERSION),
 +    LintId::of(vec::USELESS_VEC),
 +    LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
 +    LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS),
 +    LintId::of(write::PRINTLN_EMPTY_STRING),
 +    LintId::of(write::PRINT_LITERAL),
 +    LintId::of(write::PRINT_WITH_NEWLINE),
 +    LintId::of(write::WRITELN_EMPTY_STRING),
 +    LintId::of(write::WRITE_LITERAL),
 +    LintId::of(write::WRITE_WITH_NEWLINE),
 +    LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO),
 +])
index fd20e016578a1bad268088cfe5dac73f0182b881,0000000000000000000000000000000000000000..962e67220069a604992b0040cf38afec55d06656
mode 100644,000000..100644
--- /dev/null
@@@ -1,609 -1,0 +1,610 @@@
-     operators::ARITHMETIC,
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_lints(&[
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::CLIPPY_LINTS_INTERNAL,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::COMPILER_LINT_FUNCTIONS,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::DEFAULT_DEPRECATION_REASON,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::DEFAULT_LINT,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::IF_CHAIN_STYLE,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::INTERNING_DEFINED_SYMBOL,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::INVALID_PATHS,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::LINT_WITHOUT_LINT_PASS,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::MISSING_MSRV_ATTR_IMPL,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::OUTER_EXPN_EXPN_DATA,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::PRODUCE_ICE,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::UNNECESSARY_SYMBOL_STR,
 +    almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE,
 +    approx_const::APPROX_CONSTANT,
 +    as_conversions::AS_CONVERSIONS,
 +    asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
 +    asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
 +    assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
 +    assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES,
 +    async_yields_async::ASYNC_YIELDS_ASYNC,
 +    attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON,
 +    attrs::BLANKET_CLIPPY_RESTRICTION_LINTS,
 +    attrs::DEPRECATED_CFG_ATTR,
 +    attrs::DEPRECATED_SEMVER,
 +    attrs::EMPTY_LINE_AFTER_OUTER_ATTR,
 +    attrs::INLINE_ALWAYS,
 +    attrs::MISMATCHED_TARGET_OS,
 +    attrs::USELESS_ATTRIBUTE,
 +    await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE,
 +    await_holding_invalid::AWAIT_HOLDING_LOCK,
 +    await_holding_invalid::AWAIT_HOLDING_REFCELL_REF,
 +    blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS,
 +    bool_assert_comparison::BOOL_ASSERT_COMPARISON,
++    bool_to_int_with_if::BOOL_TO_INT_WITH_IF,
 +    booleans::NONMINIMAL_BOOL,
 +    booleans::OVERLY_COMPLEX_BOOL_EXPR,
 +    borrow_deref_ref::BORROW_DEREF_REF,
 +    cargo::CARGO_COMMON_METADATA,
 +    cargo::MULTIPLE_CRATE_VERSIONS,
 +    cargo::NEGATIVE_FEATURE_NAMES,
 +    cargo::REDUNDANT_FEATURE_NAMES,
 +    cargo::WILDCARD_DEPENDENCIES,
 +    casts::AS_UNDERSCORE,
 +    casts::BORROW_AS_PTR,
 +    casts::CAST_ABS_TO_UNSIGNED,
 +    casts::CAST_ENUM_CONSTRUCTOR,
 +    casts::CAST_ENUM_TRUNCATION,
 +    casts::CAST_LOSSLESS,
 +    casts::CAST_POSSIBLE_TRUNCATION,
 +    casts::CAST_POSSIBLE_WRAP,
 +    casts::CAST_PRECISION_LOSS,
 +    casts::CAST_PTR_ALIGNMENT,
 +    casts::CAST_REF_TO_MUT,
 +    casts::CAST_SIGN_LOSS,
 +    casts::CAST_SLICE_DIFFERENT_SIZES,
 +    casts::CAST_SLICE_FROM_RAW_PARTS,
 +    casts::CHAR_LIT_AS_U8,
 +    casts::FN_TO_NUMERIC_CAST,
 +    casts::FN_TO_NUMERIC_CAST_ANY,
 +    casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
 +    casts::PTR_AS_PTR,
 +    casts::UNNECESSARY_CAST,
 +    checked_conversions::CHECKED_CONVERSIONS,
 +    cognitive_complexity::COGNITIVE_COMPLEXITY,
 +    collapsible_if::COLLAPSIBLE_ELSE_IF,
 +    collapsible_if::COLLAPSIBLE_IF,
 +    comparison_chain::COMPARISON_CHAIN,
 +    copies::BRANCHES_SHARING_CODE,
 +    copies::IFS_SAME_COND,
 +    copies::IF_SAME_THEN_ELSE,
 +    copies::SAME_FUNCTIONS_IN_IF_CONDITION,
 +    copy_iterator::COPY_ITERATOR,
 +    crate_in_macro_def::CRATE_IN_MACRO_DEF,
 +    create_dir::CREATE_DIR,
 +    dbg_macro::DBG_MACRO,
 +    default::DEFAULT_TRAIT_ACCESS,
 +    default::FIELD_REASSIGN_WITH_DEFAULT,
 +    default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY,
 +    default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK,
 +    default_union_representation::DEFAULT_UNION_REPRESENTATION,
 +    dereference::EXPLICIT_AUTO_DEREF,
 +    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::DERIVE_PARTIAL_EQ_WITHOUT_EQ,
 +    derive::EXPL_IMPL_CLONE_ON_COPY,
 +    derive::UNSAFE_DERIVE_DESERIALIZE,
 +    disallowed_methods::DISALLOWED_METHODS,
 +    disallowed_names::DISALLOWED_NAMES,
 +    disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS,
 +    disallowed_types::DISALLOWED_TYPES,
 +    doc::DOC_MARKDOWN,
 +    doc::MISSING_ERRORS_DOC,
 +    doc::MISSING_PANICS_DOC,
 +    doc::MISSING_SAFETY_DOC,
 +    doc::NEEDLESS_DOCTEST_MAIN,
 +    doc_link_with_quotes::DOC_LINK_WITH_QUOTES,
 +    double_parens::DOUBLE_PARENS,
 +    drop_forget_ref::DROP_COPY,
 +    drop_forget_ref::DROP_NON_DROP,
 +    drop_forget_ref::DROP_REF,
 +    drop_forget_ref::FORGET_COPY,
 +    drop_forget_ref::FORGET_NON_DROP,
 +    drop_forget_ref::FORGET_REF,
 +    drop_forget_ref::UNDROPPED_MANUALLY_DROPS,
 +    duplicate_mod::DUPLICATE_MOD,
 +    else_if_without_else::ELSE_IF_WITHOUT_ELSE,
 +    empty_drop::EMPTY_DROP,
 +    empty_enum::EMPTY_ENUM,
 +    empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS,
 +    entry::MAP_ENTRY,
 +    enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT,
 +    enum_variants::ENUM_VARIANT_NAMES,
 +    enum_variants::MODULE_INCEPTION,
 +    enum_variants::MODULE_NAME_REPETITIONS,
 +    equatable_if_let::EQUATABLE_IF_LET,
 +    escape::BOXED_LOCAL,
 +    eta_reduction::REDUNDANT_CLOSURE,
 +    eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
 +    excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
 +    excessive_bools::STRUCT_EXCESSIVE_BOOLS,
 +    exhaustive_items::EXHAUSTIVE_ENUMS,
 +    exhaustive_items::EXHAUSTIVE_STRUCTS,
 +    exit::EXIT,
 +    explicit_write::EXPLICIT_WRITE,
 +    fallible_impl_from::FALLIBLE_IMPL_FROM,
 +    float_literal::EXCESSIVE_PRECISION,
 +    float_literal::LOSSY_FLOAT_LITERAL,
 +    floating_point_arithmetic::IMPRECISE_FLOPS,
 +    floating_point_arithmetic::SUBOPTIMAL_FLOPS,
 +    format::USELESS_FORMAT,
 +    format_args::FORMAT_IN_FORMAT_ARGS,
 +    format_args::TO_STRING_IN_FORMAT_ARGS,
 +    format_impl::PRINT_IN_FORMAT_IMPL,
 +    format_impl::RECURSIVE_FORMAT_IMPL,
 +    format_push_string::FORMAT_PUSH_STRING,
 +    formatting::POSSIBLE_MISSING_COMMA,
 +    formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
 +    formatting::SUSPICIOUS_ELSE_FORMATTING,
 +    formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
 +    from_over_into::FROM_OVER_INTO,
 +    from_str_radix_10::FROM_STR_RADIX_10,
 +    functions::DOUBLE_MUST_USE,
 +    functions::MUST_USE_CANDIDATE,
 +    functions::MUST_USE_UNIT,
 +    functions::NOT_UNSAFE_PTR_ARG_DEREF,
 +    functions::RESULT_LARGE_ERR,
 +    functions::RESULT_UNIT_ERR,
 +    functions::TOO_MANY_ARGUMENTS,
 +    functions::TOO_MANY_LINES,
 +    future_not_send::FUTURE_NOT_SEND,
 +    if_let_mutex::IF_LET_MUTEX,
 +    if_not_else::IF_NOT_ELSE,
 +    if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
 +    implicit_hasher::IMPLICIT_HASHER,
 +    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,
 +    infinite_iter::MAYBE_INFINITE_ITER,
 +    inherent_impl::MULTIPLE_INHERENT_IMPL,
 +    inherent_to_string::INHERENT_TO_STRING,
 +    inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY,
 +    init_numbered_fields::INIT_NUMBERED_FIELDS,
 +    inline_fn_without_body::INLINE_FN_WITHOUT_BODY,
 +    int_plus_one::INT_PLUS_ONE,
 +    invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS,
 +    invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED,
 +    items_after_statements::ITEMS_AFTER_STATEMENTS,
 +    iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR,
 +    large_const_arrays::LARGE_CONST_ARRAYS,
 +    large_enum_variant::LARGE_ENUM_VARIANT,
 +    large_include_file::LARGE_INCLUDE_FILE,
 +    large_stack_arrays::LARGE_STACK_ARRAYS,
 +    len_zero::COMPARISON_TO_EMPTY,
 +    len_zero::LEN_WITHOUT_IS_EMPTY,
 +    len_zero::LEN_ZERO,
 +    let_if_seq::USELESS_LET_IF_SEQ,
 +    let_underscore::LET_UNDERSCORE_DROP,
 +    let_underscore::LET_UNDERSCORE_LOCK,
 +    let_underscore::LET_UNDERSCORE_MUST_USE,
 +    lifetimes::EXTRA_UNUSED_LIFETIMES,
 +    lifetimes::NEEDLESS_LIFETIMES,
 +    literal_representation::DECIMAL_LITERAL_REPRESENTATION,
 +    literal_representation::INCONSISTENT_DIGIT_GROUPING,
 +    literal_representation::LARGE_DIGIT_GROUPS,
 +    literal_representation::MISTYPED_LITERAL_SUFFIXES,
 +    literal_representation::UNREADABLE_LITERAL,
 +    literal_representation::UNUSUAL_BYTE_GROUPINGS,
 +    loops::EMPTY_LOOP,
 +    loops::EXPLICIT_COUNTER_LOOP,
 +    loops::EXPLICIT_INTO_ITER_LOOP,
 +    loops::EXPLICIT_ITER_LOOP,
 +    loops::FOR_KV_MAP,
 +    loops::FOR_LOOPS_OVER_FALLIBLES,
 +    loops::ITER_NEXT_LOOP,
 +    loops::MANUAL_FIND,
 +    loops::MANUAL_FLATTEN,
 +    loops::MANUAL_MEMCPY,
 +    loops::MISSING_SPIN_LOOP,
 +    loops::MUT_RANGE_BOUND,
 +    loops::NEEDLESS_COLLECT,
 +    loops::NEEDLESS_RANGE_LOOP,
 +    loops::NEVER_LOOP,
 +    loops::SAME_ITEM_PUSH,
 +    loops::SINGLE_ELEMENT_LOOP,
 +    loops::WHILE_IMMUTABLE_CONDITION,
 +    loops::WHILE_LET_LOOP,
 +    loops::WHILE_LET_ON_ITERATOR,
 +    macro_use::MACRO_USE_IMPORTS,
 +    main_recursion::MAIN_RECURSION,
 +    manual_assert::MANUAL_ASSERT,
 +    manual_async_fn::MANUAL_ASYNC_FN,
 +    manual_bits::MANUAL_BITS,
 +    manual_instant_elapsed::MANUAL_INSTANT_ELAPSED,
 +    manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
 +    manual_rem_euclid::MANUAL_REM_EUCLID,
 +    manual_retain::MANUAL_RETAIN,
 +    manual_string_new::MANUAL_STRING_NEW,
 +    manual_strip::MANUAL_STRIP,
 +    map_unit_fn::OPTION_MAP_UNIT_FN,
 +    map_unit_fn::RESULT_MAP_UNIT_FN,
 +    match_result_ok::MATCH_RESULT_OK,
 +    matches::COLLAPSIBLE_MATCH,
 +    matches::INFALLIBLE_DESTRUCTURING_MATCH,
 +    matches::MANUAL_MAP,
 +    matches::MANUAL_UNWRAP_OR,
 +    matches::MATCH_AS_REF,
 +    matches::MATCH_BOOL,
 +    matches::MATCH_LIKE_MATCHES_MACRO,
 +    matches::MATCH_ON_VEC_ITEMS,
 +    matches::MATCH_OVERLAPPING_ARM,
 +    matches::MATCH_REF_PATS,
 +    matches::MATCH_SAME_ARMS,
 +    matches::MATCH_SINGLE_BINDING,
 +    matches::MATCH_STR_CASE_MISMATCH,
 +    matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
 +    matches::MATCH_WILD_ERR_ARM,
 +    matches::NEEDLESS_MATCH,
 +    matches::REDUNDANT_PATTERN_MATCHING,
 +    matches::REST_PAT_IN_FULLY_BOUND_STRUCTS,
 +    matches::SIGNIFICANT_DROP_IN_SCRUTINEE,
 +    matches::SINGLE_MATCH,
 +    matches::SINGLE_MATCH_ELSE,
 +    matches::TRY_ERR,
 +    matches::WILDCARD_ENUM_MATCH_ARM,
 +    matches::WILDCARD_IN_OR_PATTERNS,
 +    mem_forget::MEM_FORGET,
 +    mem_replace::MEM_REPLACE_OPTION_WITH_NONE,
 +    mem_replace::MEM_REPLACE_WITH_DEFAULT,
 +    mem_replace::MEM_REPLACE_WITH_UNINIT,
 +    methods::BIND_INSTEAD_OF_MAP,
 +    methods::BYTES_COUNT_TO_LEN,
 +    methods::BYTES_NTH,
 +    methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
 +    methods::CHARS_LAST_CMP,
 +    methods::CHARS_NEXT_CMP,
 +    methods::CLONED_INSTEAD_OF_COPIED,
 +    methods::CLONE_DOUBLE_REF,
 +    methods::CLONE_ON_COPY,
 +    methods::CLONE_ON_REF_PTR,
 +    methods::COLLAPSIBLE_STR_REPLACE,
 +    methods::ERR_EXPECT,
 +    methods::EXPECT_FUN_CALL,
 +    methods::EXPECT_USED,
 +    methods::EXTEND_WITH_DRAIN,
 +    methods::FILETYPE_IS_FILE,
 +    methods::FILTER_MAP_IDENTITY,
 +    methods::FILTER_MAP_NEXT,
 +    methods::FILTER_NEXT,
 +    methods::FLAT_MAP_IDENTITY,
 +    methods::FLAT_MAP_OPTION,
 +    methods::FROM_ITER_INSTEAD_OF_COLLECT,
 +    methods::GET_FIRST,
 +    methods::GET_LAST_WITH_LEN,
 +    methods::GET_UNWRAP,
 +    methods::IMPLICIT_CLONE,
 +    methods::INEFFICIENT_TO_STRING,
 +    methods::INSPECT_FOR_EACH,
 +    methods::INTO_ITER_ON_REF,
 +    methods::IS_DIGIT_ASCII_RADIX,
 +    methods::ITERATOR_STEP_BY_ZERO,
 +    methods::ITER_CLONED_COLLECT,
 +    methods::ITER_COUNT,
 +    methods::ITER_NEXT_SLICE,
 +    methods::ITER_NTH,
 +    methods::ITER_NTH_ZERO,
 +    methods::ITER_ON_EMPTY_COLLECTIONS,
 +    methods::ITER_ON_SINGLE_ITEMS,
 +    methods::ITER_OVEREAGER_CLONED,
 +    methods::ITER_SKIP_NEXT,
 +    methods::ITER_WITH_DRAIN,
 +    methods::MANUAL_FILTER_MAP,
 +    methods::MANUAL_FIND_MAP,
 +    methods::MANUAL_OK_OR,
 +    methods::MANUAL_SATURATING_ARITHMETIC,
 +    methods::MANUAL_SPLIT_ONCE,
 +    methods::MANUAL_STR_REPEAT,
 +    methods::MAP_CLONE,
 +    methods::MAP_COLLECT_RESULT_UNIT,
 +    methods::MAP_ERR_IGNORE,
 +    methods::MAP_FLATTEN,
 +    methods::MAP_IDENTITY,
 +    methods::MAP_UNWRAP_OR,
 +    methods::MUT_MUTEX_LOCK,
 +    methods::NAIVE_BYTECOUNT,
 +    methods::NEEDLESS_OPTION_AS_DEREF,
 +    methods::NEEDLESS_OPTION_TAKE,
 +    methods::NEEDLESS_SPLITN,
 +    methods::NEW_RET_NO_SELF,
 +    methods::NONSENSICAL_OPEN_OPTIONS,
 +    methods::NO_EFFECT_REPLACE,
 +    methods::OBFUSCATED_IF_ELSE,
 +    methods::OK_EXPECT,
 +    methods::OPTION_AS_REF_DEREF,
 +    methods::OPTION_FILTER_MAP,
 +    methods::OPTION_MAP_OR_NONE,
 +    methods::OR_FUN_CALL,
 +    methods::OR_THEN_UNWRAP,
 +    methods::PATH_BUF_PUSH_OVERWRITE,
 +    methods::RANGE_ZIP_WITH_LEN,
 +    methods::REPEAT_ONCE,
 +    methods::RESULT_MAP_OR_INTO_OPTION,
 +    methods::SEARCH_IS_SOME,
 +    methods::SHOULD_IMPLEMENT_TRAIT,
 +    methods::SINGLE_CHAR_ADD_STR,
 +    methods::SINGLE_CHAR_PATTERN,
 +    methods::SKIP_WHILE_NEXT,
 +    methods::STABLE_SORT_PRIMITIVE,
 +    methods::STRING_EXTEND_CHARS,
 +    methods::SUSPICIOUS_MAP,
 +    methods::SUSPICIOUS_SPLITN,
 +    methods::SUSPICIOUS_TO_OWNED,
 +    methods::UNINIT_ASSUMED_INIT,
 +    methods::UNIT_HASH,
 +    methods::UNNECESSARY_FILTER_MAP,
 +    methods::UNNECESSARY_FIND_MAP,
 +    methods::UNNECESSARY_FOLD,
 +    methods::UNNECESSARY_JOIN,
 +    methods::UNNECESSARY_LAZY_EVALUATIONS,
 +    methods::UNNECESSARY_SORT_BY,
 +    methods::UNNECESSARY_TO_OWNED,
 +    methods::UNWRAP_OR_ELSE_DEFAULT,
 +    methods::UNWRAP_USED,
 +    methods::USELESS_ASREF,
 +    methods::VEC_RESIZE_TO_ZERO,
 +    methods::VERBOSE_FILE_READS,
 +    methods::WRONG_SELF_CONVENTION,
 +    methods::ZST_OFFSET,
 +    minmax::MIN_MAX,
 +    misc::SHORT_CIRCUIT_STATEMENT,
 +    misc::TOPLEVEL_REF_ARG,
 +    misc::USED_UNDERSCORE_BINDING,
 +    misc::ZERO_PTR,
 +    misc_early::BUILTIN_TYPE_SHADOW,
 +    misc_early::DOUBLE_NEG,
 +    misc_early::DUPLICATE_UNDERSCORE_ARGUMENT,
 +    misc_early::MIXED_CASE_HEX_LITERALS,
 +    misc_early::REDUNDANT_PATTERN,
 +    misc_early::SEPARATED_LITERAL_SUFFIX,
 +    misc_early::UNNEEDED_FIELD_PATTERN,
 +    misc_early::UNNEEDED_WILDCARD_PATTERN,
 +    misc_early::UNSEPARATED_LITERAL_SUFFIX,
 +    misc_early::ZERO_PREFIXED_LITERAL,
 +    mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER,
 +    missing_const_for_fn::MISSING_CONST_FOR_FN,
 +    missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS,
 +    missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES,
 +    missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS,
 +    mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION,
 +    mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION,
 +    module_style::MOD_MODULE_FILES,
 +    module_style::SELF_NAMED_MODULE_FILES,
 +    multi_assignments::MULTI_ASSIGNMENTS,
 +    mut_key::MUTABLE_KEY_TYPE,
 +    mut_mut::MUT_MUT,
 +    mut_reference::UNNECESSARY_MUT_PASSED,
 +    mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
 +    mutex_atomic::MUTEX_ATOMIC,
 +    mutex_atomic::MUTEX_INTEGER,
 +    needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE,
 +    needless_bool::BOOL_COMPARISON,
 +    needless_bool::NEEDLESS_BOOL,
 +    needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
 +    needless_continue::NEEDLESS_CONTINUE,
 +    needless_for_each::NEEDLESS_FOR_EACH,
 +    needless_late_init::NEEDLESS_LATE_INIT,
 +    needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS,
 +    needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
 +    needless_question_mark::NEEDLESS_QUESTION_MARK,
 +    needless_update::NEEDLESS_UPDATE,
 +    neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD,
 +    neg_multiply::NEG_MULTIPLY,
 +    new_without_default::NEW_WITHOUT_DEFAULT,
 +    no_effect::NO_EFFECT,
 +    no_effect::NO_EFFECT_UNDERSCORE_BINDING,
 +    no_effect::UNNECESSARY_OPERATION,
 +    non_copy_const::BORROW_INTERIOR_MUTABLE_CONST,
 +    non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST,
 +    non_expressive_names::JUST_UNDERSCORES_AND_DIGITS,
 +    non_expressive_names::MANY_SINGLE_CHAR_NAMES,
 +    non_expressive_names::SIMILAR_NAMES,
 +    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,
 +    only_used_in_recursion::ONLY_USED_IN_RECURSION,
 +    operators::ABSURD_EXTREME_COMPARISONS,
++    operators::ARITHMETIC_SIDE_EFFECTS,
 +    operators::ASSIGN_OP_PATTERN,
 +    operators::BAD_BIT_MASK,
 +    operators::CMP_NAN,
 +    operators::CMP_OWNED,
 +    operators::DOUBLE_COMPARISONS,
 +    operators::DURATION_SUBSEC,
 +    operators::EQ_OP,
 +    operators::ERASING_OP,
 +    operators::FLOAT_ARITHMETIC,
 +    operators::FLOAT_CMP,
 +    operators::FLOAT_CMP_CONST,
 +    operators::FLOAT_EQUALITY_WITHOUT_ABS,
 +    operators::IDENTITY_OP,
 +    operators::INEFFECTIVE_BIT_MASK,
 +    operators::INTEGER_ARITHMETIC,
 +    operators::INTEGER_DIVISION,
 +    operators::MISREFACTORED_ASSIGN_OP,
 +    operators::MODULO_ARITHMETIC,
 +    operators::MODULO_ONE,
 +    operators::NEEDLESS_BITWISE_BOOL,
 +    operators::OP_REF,
 +    operators::PTR_EQ,
 +    operators::SELF_ASSIGNMENT,
 +    operators::VERBOSE_BIT_MASK,
 +    option_env_unwrap::OPTION_ENV_UNWRAP,
 +    option_if_let_else::OPTION_IF_LET_ELSE,
 +    overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
 +    panic_in_result_fn::PANIC_IN_RESULT_FN,
 +    panic_unimplemented::PANIC,
 +    panic_unimplemented::TODO,
 +    panic_unimplemented::UNIMPLEMENTED,
 +    panic_unimplemented::UNREACHABLE,
 +    partialeq_ne_impl::PARTIALEQ_NE_IMPL,
 +    partialeq_to_none::PARTIALEQ_TO_NONE,
 +    pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE,
 +    pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF,
 +    pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
 +    precedence::PRECEDENCE,
 +    ptr::CMP_NULL,
 +    ptr::INVALID_NULL_PTR_USAGE,
 +    ptr::MUT_FROM_REF,
 +    ptr::PTR_ARG,
 +    ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
 +    pub_use::PUB_USE,
 +    question_mark::QUESTION_MARK,
 +    ranges::MANUAL_RANGE_CONTAINS,
 +    ranges::RANGE_MINUS_ONE,
 +    ranges::RANGE_PLUS_ONE,
 +    ranges::REVERSED_EMPTY_RANGES,
 +    rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT,
 +    read_zero_byte_vec::READ_ZERO_BYTE_VEC,
 +    redundant_clone::REDUNDANT_CLONE,
 +    redundant_closure_call::REDUNDANT_CLOSURE_CALL,
 +    redundant_else::REDUNDANT_ELSE,
 +    redundant_field_names::REDUNDANT_FIELD_NAMES,
 +    redundant_pub_crate::REDUNDANT_PUB_CRATE,
 +    redundant_slicing::DEREF_BY_SLICING,
 +    redundant_slicing::REDUNDANT_SLICING,
 +    redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
 +    ref_option_ref::REF_OPTION_REF,
 +    reference::DEREF_ADDROF,
 +    regex::INVALID_REGEX,
 +    regex::TRIVIAL_REGEX,
 +    return_self_not_must_use::RETURN_SELF_NOT_MUST_USE,
 +    returns::LET_AND_RETURN,
 +    returns::NEEDLESS_RETURN,
 +    same_name_method::SAME_NAME_METHOD,
 +    self_named_constructors::SELF_NAMED_CONSTRUCTORS,
 +    semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED,
 +    serde_api::SERDE_API_MISUSE,
 +    shadow::SHADOW_REUSE,
 +    shadow::SHADOW_SAME,
 +    shadow::SHADOW_UNRELATED,
 +    single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES,
 +    single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
 +    size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
 +    slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
 +    std_instead_of_core::ALLOC_INSTEAD_OF_CORE,
 +    std_instead_of_core::STD_INSTEAD_OF_ALLOC,
 +    std_instead_of_core::STD_INSTEAD_OF_CORE,
 +    strings::STRING_ADD,
 +    strings::STRING_ADD_ASSIGN,
 +    strings::STRING_FROM_UTF8_AS_BYTES,
 +    strings::STRING_LIT_AS_BYTES,
 +    strings::STRING_SLICE,
 +    strings::STRING_TO_STRING,
 +    strings::STR_TO_STRING,
 +    strings::TRIM_SPLIT_WHITESPACE,
 +    strlen_on_c_strings::STRLEN_ON_C_STRINGS,
 +    suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS,
 +    suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL,
 +    suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL,
 +    swap::ALMOST_SWAPPED,
 +    swap::MANUAL_SWAP,
 +    swap_ptr_to_ref::SWAP_PTR_TO_REF,
 +    tabs_in_doc_comments::TABS_IN_DOC_COMMENTS,
 +    temporary_assignment::TEMPORARY_ASSIGNMENT,
 +    to_digit_is_some::TO_DIGIT_IS_SOME,
 +    trailing_empty_array::TRAILING_EMPTY_ARRAY,
 +    trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS,
 +    trait_bounds::TYPE_REPETITION_IN_BOUNDS,
 +    transmute::CROSSPOINTER_TRANSMUTE,
 +    transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
 +    transmute::TRANSMUTE_BYTES_TO_STR,
 +    transmute::TRANSMUTE_FLOAT_TO_INT,
 +    transmute::TRANSMUTE_INT_TO_BOOL,
 +    transmute::TRANSMUTE_INT_TO_CHAR,
 +    transmute::TRANSMUTE_INT_TO_FLOAT,
 +    transmute::TRANSMUTE_NUM_TO_BYTES,
 +    transmute::TRANSMUTE_PTR_TO_PTR,
 +    transmute::TRANSMUTE_PTR_TO_REF,
 +    transmute::TRANSMUTE_UNDEFINED_REPR,
 +    transmute::TRANSMUTING_NULL,
 +    transmute::UNSOUND_COLLECTION_TRANSMUTE,
 +    transmute::USELESS_TRANSMUTE,
 +    transmute::WRONG_TRANSMUTE,
 +    types::BORROWED_BOX,
 +    types::BOX_COLLECTION,
 +    types::LINKEDLIST,
 +    types::OPTION_OPTION,
 +    types::RC_BUFFER,
 +    types::RC_MUTEX,
 +    types::REDUNDANT_ALLOCATION,
 +    types::TYPE_COMPLEXITY,
 +    types::VEC_BOX,
 +    undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS,
 +    unicode::INVISIBLE_CHARACTERS,
 +    unicode::NON_ASCII_LITERAL,
 +    unicode::UNICODE_NOT_NFC,
 +    uninit_vec::UNINIT_VEC,
 +    unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD,
 +    unit_types::LET_UNIT_VALUE,
 +    unit_types::UNIT_ARG,
 +    unit_types::UNIT_CMP,
 +    unnamed_address::FN_ADDRESS_COMPARISONS,
 +    unnamed_address::VTABLE_ADDRESS_COMPARISONS,
 +    unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS,
 +    unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
 +    unnecessary_wraps::UNNECESSARY_WRAPS,
 +    unnested_or_patterns::UNNESTED_OR_PATTERNS,
 +    unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
 +    unused_async::UNUSED_ASYNC,
 +    unused_io_amount::UNUSED_IO_AMOUNT,
 +    unused_peekable::UNUSED_PEEKABLE,
 +    unused_rounding::UNUSED_ROUNDING,
 +    unused_self::UNUSED_SELF,
 +    unused_unit::UNUSED_UNIT,
 +    unwrap::PANICKING_UNWRAP,
 +    unwrap::UNNECESSARY_UNWRAP,
 +    unwrap_in_result::UNWRAP_IN_RESULT,
 +    upper_case_acronyms::UPPER_CASE_ACRONYMS,
 +    use_self::USE_SELF,
 +    useless_conversion::USELESS_CONVERSION,
 +    vec::USELESS_VEC,
 +    vec_init_then_push::VEC_INIT_THEN_PUSH,
 +    wildcard_imports::ENUM_GLOB_USE,
 +    wildcard_imports::WILDCARD_IMPORTS,
 +    write::POSITIONAL_NAMED_FORMAT_PARAMETERS,
 +    write::PRINTLN_EMPTY_STRING,
 +    write::PRINT_LITERAL,
 +    write::PRINT_STDERR,
 +    write::PRINT_STDOUT,
 +    write::PRINT_WITH_NEWLINE,
 +    write::USE_DEBUG,
 +    write::WRITELN_EMPTY_STRING,
 +    write::WRITE_LITERAL,
 +    write::WRITE_WITH_NEWLINE,
 +    zero_div_zero::ZERO_DIVIDED_BY_ZERO,
 +    zero_sized_map_values::ZERO_SIZED_MAP_VALUES,
 +])
index dd1e1e1a8e33d82088578a75adcaa49cf7db7128,0000000000000000000000000000000000000000..6eb9b3d3b9b7aa61be5ce4de886d42d3c3dabd14
mode 100644,000000..100644
--- /dev/null
@@@ -1,88 -1,0 +1,88 @@@
-     LintId::of(operators::ARITHMETIC),
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
 +    LintId::of(as_conversions::AS_CONVERSIONS),
 +    LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
 +    LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
 +    LintId::of(assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES),
 +    LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON),
 +    LintId::of(casts::AS_UNDERSCORE),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST_ANY),
 +    LintId::of(create_dir::CREATE_DIR),
 +    LintId::of(dbg_macro::DBG_MACRO),
 +    LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK),
 +    LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION),
 +    LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS),
 +    LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE),
 +    LintId::of(empty_drop::EMPTY_DROP),
 +    LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS),
 +    LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS),
 +    LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS),
 +    LintId::of(exit::EXIT),
 +    LintId::of(float_literal::LOSSY_FLOAT_LITERAL),
 +    LintId::of(format_push_string::FORMAT_PUSH_STRING),
 +    LintId::of(if_then_some_else_none::IF_THEN_SOME_ELSE_NONE),
 +    LintId::of(implicit_return::IMPLICIT_RETURN),
 +    LintId::of(indexing_slicing::INDEXING_SLICING),
 +    LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL),
 +    LintId::of(large_include_file::LARGE_INCLUDE_FILE),
 +    LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE),
 +    LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION),
 +    LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS),
 +    LintId::of(matches::TRY_ERR),
 +    LintId::of(matches::WILDCARD_ENUM_MATCH_ARM),
 +    LintId::of(mem_forget::MEM_FORGET),
 +    LintId::of(methods::CLONE_ON_REF_PTR),
 +    LintId::of(methods::EXPECT_USED),
 +    LintId::of(methods::FILETYPE_IS_FILE),
 +    LintId::of(methods::GET_UNWRAP),
 +    LintId::of(methods::MAP_ERR_IGNORE),
 +    LintId::of(methods::UNWRAP_USED),
 +    LintId::of(methods::VERBOSE_FILE_READS),
 +    LintId::of(misc_early::SEPARATED_LITERAL_SUFFIX),
 +    LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
 +    LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
 +    LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS),
 +    LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES),
 +    LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
 +    LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION),
 +    LintId::of(module_style::MOD_MODULE_FILES),
 +    LintId::of(module_style::SELF_NAMED_MODULE_FILES),
++    LintId::of(operators::ARITHMETIC_SIDE_EFFECTS),
 +    LintId::of(operators::FLOAT_ARITHMETIC),
 +    LintId::of(operators::FLOAT_CMP_CONST),
 +    LintId::of(operators::INTEGER_ARITHMETIC),
 +    LintId::of(operators::INTEGER_DIVISION),
 +    LintId::of(operators::MODULO_ARITHMETIC),
 +    LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN),
 +    LintId::of(panic_unimplemented::PANIC),
 +    LintId::of(panic_unimplemented::TODO),
 +    LintId::of(panic_unimplemented::UNIMPLEMENTED),
 +    LintId::of(panic_unimplemented::UNREACHABLE),
 +    LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
 +    LintId::of(pub_use::PUB_USE),
 +    LintId::of(redundant_slicing::DEREF_BY_SLICING),
 +    LintId::of(same_name_method::SAME_NAME_METHOD),
 +    LintId::of(shadow::SHADOW_REUSE),
 +    LintId::of(shadow::SHADOW_SAME),
 +    LintId::of(shadow::SHADOW_UNRELATED),
 +    LintId::of(single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES),
 +    LintId::of(std_instead_of_core::ALLOC_INSTEAD_OF_CORE),
 +    LintId::of(std_instead_of_core::STD_INSTEAD_OF_ALLOC),
 +    LintId::of(std_instead_of_core::STD_INSTEAD_OF_CORE),
 +    LintId::of(strings::STRING_ADD),
 +    LintId::of(strings::STRING_SLICE),
 +    LintId::of(strings::STRING_TO_STRING),
 +    LintId::of(strings::STR_TO_STRING),
 +    LintId::of(types::RC_BUFFER),
 +    LintId::of(types::RC_MUTEX),
 +    LintId::of(undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS),
 +    LintId::of(unicode::NON_ASCII_LITERAL),
 +    LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
 +    LintId::of(unwrap_in_result::UNWRAP_IN_RESULT),
 +    LintId::of(write::PRINT_STDERR),
 +    LintId::of(write::PRINT_STDOUT),
 +    LintId::of(write::USE_DEBUG),
 +])
index b5cb078e7a3ccce2fc5a8f0b7ed93b68cca24585,0000000000000000000000000000000000000000..05d2ec2e9e1e6002c1d2c3eb2eff1842b49a2da6
mode 100644,000000..100644
--- /dev/null
@@@ -1,128 -1,0 +1,129 @@@
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_group(true, "clippy::style", Some("clippy_style"), vec![
 +    LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
 +    LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
 +    LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
++    LintId::of(bool_to_int_with_if::BOOL_TO_INT_WITH_IF),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
 +    LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
 +    LintId::of(collapsible_if::COLLAPSIBLE_IF),
 +    LintId::of(comparison_chain::COMPARISON_CHAIN),
 +    LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
 +    LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY),
 +    LintId::of(dereference::NEEDLESS_BORROW),
 +    LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ),
 +    LintId::of(disallowed_methods::DISALLOWED_METHODS),
 +    LintId::of(disallowed_names::DISALLOWED_NAMES),
 +    LintId::of(disallowed_types::DISALLOWED_TYPES),
 +    LintId::of(doc::MISSING_SAFETY_DOC),
 +    LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
 +    LintId::of(enum_variants::ENUM_VARIANT_NAMES),
 +    LintId::of(enum_variants::MODULE_INCEPTION),
 +    LintId::of(eta_reduction::REDUNDANT_CLOSURE),
 +    LintId::of(float_literal::EXCESSIVE_PRECISION),
 +    LintId::of(from_over_into::FROM_OVER_INTO),
 +    LintId::of(from_str_radix_10::FROM_STR_RADIX_10),
 +    LintId::of(functions::DOUBLE_MUST_USE),
 +    LintId::of(functions::MUST_USE_UNIT),
 +    LintId::of(functions::RESULT_UNIT_ERR),
 +    LintId::of(inherent_to_string::INHERENT_TO_STRING),
 +    LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
 +    LintId::of(len_zero::COMPARISON_TO_EMPTY),
 +    LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY),
 +    LintId::of(len_zero::LEN_ZERO),
 +    LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING),
 +    LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS),
 +    LintId::of(loops::FOR_KV_MAP),
 +    LintId::of(loops::NEEDLESS_RANGE_LOOP),
 +    LintId::of(loops::SAME_ITEM_PUSH),
 +    LintId::of(loops::WHILE_LET_ON_ITERATOR),
 +    LintId::of(main_recursion::MAIN_RECURSION),
 +    LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
 +    LintId::of(manual_bits::MANUAL_BITS),
 +    LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
 +    LintId::of(match_result_ok::MATCH_RESULT_OK),
 +    LintId::of(matches::COLLAPSIBLE_MATCH),
 +    LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
 +    LintId::of(matches::MANUAL_MAP),
 +    LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
 +    LintId::of(matches::MATCH_OVERLAPPING_ARM),
 +    LintId::of(matches::MATCH_REF_PATS),
 +    LintId::of(matches::REDUNDANT_PATTERN_MATCHING),
 +    LintId::of(matches::SINGLE_MATCH),
 +    LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
 +    LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
 +    LintId::of(methods::BYTES_NTH),
 +    LintId::of(methods::CHARS_LAST_CMP),
 +    LintId::of(methods::CHARS_NEXT_CMP),
 +    LintId::of(methods::ERR_EXPECT),
 +    LintId::of(methods::GET_FIRST),
 +    LintId::of(methods::INTO_ITER_ON_REF),
 +    LintId::of(methods::IS_DIGIT_ASCII_RADIX),
 +    LintId::of(methods::ITER_CLONED_COLLECT),
 +    LintId::of(methods::ITER_NEXT_SLICE),
 +    LintId::of(methods::ITER_NTH_ZERO),
 +    LintId::of(methods::ITER_SKIP_NEXT),
 +    LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
 +    LintId::of(methods::MAP_CLONE),
 +    LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
 +    LintId::of(methods::MUT_MUTEX_LOCK),
 +    LintId::of(methods::NEW_RET_NO_SELF),
 +    LintId::of(methods::OBFUSCATED_IF_ELSE),
 +    LintId::of(methods::OK_EXPECT),
 +    LintId::of(methods::OPTION_MAP_OR_NONE),
 +    LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
 +    LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
 +    LintId::of(methods::SINGLE_CHAR_ADD_STR),
 +    LintId::of(methods::STRING_EXTEND_CHARS),
 +    LintId::of(methods::UNNECESSARY_FOLD),
 +    LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
 +    LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
 +    LintId::of(methods::WRONG_SELF_CONVENTION),
 +    LintId::of(misc::TOPLEVEL_REF_ARG),
 +    LintId::of(misc::ZERO_PTR),
 +    LintId::of(misc_early::BUILTIN_TYPE_SHADOW),
 +    LintId::of(misc_early::DOUBLE_NEG),
 +    LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
 +    LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
 +    LintId::of(misc_early::REDUNDANT_PATTERN),
 +    LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
 +    LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
 +    LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS),
 +    LintId::of(neg_multiply::NEG_MULTIPLY),
 +    LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
 +    LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
 +    LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
 +    LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
 +    LintId::of(operators::ASSIGN_OP_PATTERN),
 +    LintId::of(operators::OP_REF),
 +    LintId::of(operators::PTR_EQ),
 +    LintId::of(partialeq_to_none::PARTIALEQ_TO_NONE),
 +    LintId::of(ptr::CMP_NULL),
 +    LintId::of(ptr::PTR_ARG),
 +    LintId::of(question_mark::QUESTION_MARK),
 +    LintId::of(ranges::MANUAL_RANGE_CONTAINS),
 +    LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),
 +    LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
 +    LintId::of(returns::LET_AND_RETURN),
 +    LintId::of(returns::NEEDLESS_RETURN),
 +    LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
 +    LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
 +    LintId::of(strings::TRIM_SPLIT_WHITESPACE),
 +    LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
 +    LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
 +    LintId::of(unit_types::LET_UNIT_VALUE),
 +    LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
 +    LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
 +    LintId::of(unused_unit::UNUSED_UNIT),
 +    LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
 +    LintId::of(write::PRINTLN_EMPTY_STRING),
 +    LintId::of(write::PRINT_LITERAL),
 +    LintId::of(write::PRINT_WITH_NEWLINE),
 +    LintId::of(write::WRITELN_EMPTY_STRING),
 +    LintId::of(write::WRITE_LITERAL),
 +    LintId::of(write::WRITE_WITH_NEWLINE),
 +])
index c70aa79ac8dcffdcfcece1c391a850f547a3e61b,0000000000000000000000000000000000000000..e984254bf2919ae397bc80e990fd9b24ba28108c
mode 100644,000000..100644
--- /dev/null
@@@ -1,965 -1,0 +1,971 @@@
-             store.register_late_pass(|_| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new()));
 +#![feature(array_windows)]
 +#![feature(binary_heap_into_iter_sorted)]
 +#![feature(box_patterns)]
 +#![feature(control_flow_enum)]
 +#![feature(drain_filter)]
 +#![feature(iter_intersperse)]
 +#![feature(let_chains)]
 +#![feature(let_else)]
 +#![feature(lint_reasons)]
 +#![feature(never_type)]
 +#![feature(once_cell)]
 +#![feature(rustc_private)]
 +#![feature(stmt_expr_attributes)]
 +#![recursion_limit = "512"]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)]
 +#![warn(trivial_casts, trivial_numeric_casts)]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +// warn on rustc internal lints
 +#![warn(rustc::internal)]
 +// Disable this rustc lint for now, as it was also done in rustc
 +#![allow(rustc::potential_query_instability)]
 +
 +// FIXME: switch to something more ergonomic here, once available.
 +// (Currently there is no way to opt into sysroot crates without `extern crate`.)
 +extern crate rustc_arena;
 +extern crate rustc_ast;
 +extern crate rustc_ast_pretty;
 +extern crate rustc_attr;
 +extern crate rustc_data_structures;
 +extern crate rustc_driver;
 +extern crate rustc_errors;
 +extern crate rustc_hir;
 +extern crate rustc_hir_pretty;
 +extern crate rustc_index;
 +extern crate rustc_infer;
 +extern crate rustc_lexer;
 +extern crate rustc_lint;
 +extern crate rustc_middle;
 +extern crate rustc_mir_dataflow;
 +extern crate rustc_parse;
 +extern crate rustc_parse_format;
 +extern crate rustc_session;
 +extern crate rustc_span;
 +extern crate rustc_target;
 +extern crate rustc_trait_selection;
 +extern crate rustc_typeck;
 +
 +#[macro_use]
 +extern crate clippy_utils;
 +
 +use clippy_utils::parse_msrv;
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_lint::LintId;
 +use rustc_semver::RustcVersion;
 +use rustc_session::Session;
 +
 +/// Macro used to declare a Clippy lint.
 +///
 +/// Every lint declaration consists of 4 parts:
 +///
 +/// 1. The documentation, which is used for the website
 +/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions.
 +/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or
 +///    `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of.
 +/// 4. The `description` that contains a short explanation on what's wrong with code where the
 +///    lint is triggered.
 +///
 +/// Currently the categories `style`, `correctness`, `suspicious`, `complexity` and `perf` are
 +/// enabled by default. As said in the README.md of this repository, if the lint level mapping
 +/// changes, please update README.md.
 +///
 +/// # Example
 +///
 +/// ```
 +/// #![feature(rustc_private)]
 +/// extern crate rustc_session;
 +/// use rustc_session::declare_tool_lint;
 +/// use clippy_lints::declare_clippy_lint;
 +///
 +/// declare_clippy_lint! {
 +///     /// ### What it does
 +///     /// Checks for ... (describe what the lint matches).
 +///     ///
 +///     /// ### Why is this bad?
 +///     /// Supply the reason for linting the code.
 +///     ///
 +///     /// ### Example
 +///     /// ```rust
 +///     /// Insert a short example of code that triggers the lint
 +///     /// ```
 +///     ///
 +///     /// Use instead:
 +///     /// ```rust
 +///     /// Insert a short example of improved code that doesn't trigger the lint
 +///     /// ```
 +///     pub LINT_NAME,
 +///     pedantic,
 +///     "description"
 +/// }
 +/// ```
 +/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
 +#[macro_export]
 +macro_rules! declare_clippy_lint {
 +    { $(#[$attr:meta])* pub $name:tt, style, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, correctness, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, suspicious, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, perf, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, pedantic, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, restriction, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, cargo, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, nursery, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, internal, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, internal_warn, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +}
 +
 +#[cfg(feature = "internal")]
 +pub mod deprecated_lints;
 +#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
 +mod utils;
 +
 +mod renamed_lints;
 +
 +// begin lints modules, do not remove this comment, it’s used in `update_lints`
 +mod almost_complete_letter_range;
 +mod approx_const;
 +mod as_conversions;
 +mod asm_syntax;
 +mod assertions_on_constants;
 +mod assertions_on_result_states;
 +mod async_yields_async;
 +mod attrs;
 +mod await_holding_invalid;
 +mod blocks_in_if_conditions;
 +mod bool_assert_comparison;
++mod bool_to_int_with_if;
 +mod booleans;
 +mod borrow_deref_ref;
 +mod cargo;
 +mod casts;
 +mod checked_conversions;
 +mod cognitive_complexity;
 +mod collapsible_if;
 +mod comparison_chain;
 +mod copies;
 +mod copy_iterator;
 +mod crate_in_macro_def;
 +mod create_dir;
 +mod dbg_macro;
 +mod default;
 +mod default_instead_of_iter_empty;
 +mod default_numeric_fallback;
 +mod default_union_representation;
 +mod dereference;
 +mod derivable_impls;
 +mod derive;
 +mod disallowed_methods;
 +mod disallowed_names;
 +mod disallowed_script_idents;
 +mod disallowed_types;
 +mod doc;
 +mod doc_link_with_quotes;
 +mod double_parens;
 +mod drop_forget_ref;
 +mod duplicate_mod;
 +mod else_if_without_else;
 +mod empty_drop;
 +mod empty_enum;
 +mod empty_structs_with_brackets;
 +mod entry;
 +mod enum_clike;
 +mod enum_variants;
 +mod equatable_if_let;
 +mod escape;
 +mod eta_reduction;
 +mod excessive_bools;
 +mod exhaustive_items;
 +mod exit;
 +mod explicit_write;
 +mod fallible_impl_from;
 +mod float_literal;
 +mod floating_point_arithmetic;
 +mod format;
 +mod format_args;
 +mod format_impl;
 +mod format_push_string;
 +mod formatting;
 +mod from_over_into;
 +mod from_str_radix_10;
 +mod functions;
 +mod future_not_send;
 +mod if_let_mutex;
 +mod if_not_else;
 +mod if_then_some_else_none;
 +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;
 +mod inherent_to_string;
 +mod init_numbered_fields;
 +mod inline_fn_without_body;
 +mod int_plus_one;
 +mod invalid_upcast_comparisons;
 +mod invalid_utf8_in_unchecked;
 +mod items_after_statements;
 +mod iter_not_returning_iterator;
 +mod large_const_arrays;
 +mod large_enum_variant;
 +mod large_include_file;
 +mod large_stack_arrays;
 +mod len_zero;
 +mod let_if_seq;
 +mod let_underscore;
 +mod lifetimes;
 +mod literal_representation;
 +mod loops;
 +mod macro_use;
 +mod main_recursion;
 +mod manual_assert;
 +mod manual_async_fn;
 +mod manual_bits;
 +mod manual_instant_elapsed;
 +mod manual_non_exhaustive;
 +mod manual_rem_euclid;
 +mod manual_retain;
 +mod manual_string_new;
 +mod manual_strip;
 +mod map_unit_fn;
 +mod match_result_ok;
 +mod matches;
 +mod mem_forget;
 +mod mem_replace;
 +mod methods;
 +mod minmax;
 +mod misc;
 +mod misc_early;
 +mod mismatching_type_param_order;
 +mod missing_const_for_fn;
 +mod missing_doc;
 +mod missing_enforced_import_rename;
 +mod missing_inline;
 +mod mixed_read_write_in_expression;
 +mod module_style;
 +mod multi_assignments;
 +mod mut_key;
 +mod mut_mut;
 +mod mut_reference;
 +mod mutable_debug_assertion;
 +mod mutex_atomic;
 +mod needless_arbitrary_self_type;
 +mod needless_bool;
 +mod needless_borrowed_ref;
 +mod needless_continue;
 +mod needless_for_each;
 +mod needless_late_init;
 +mod needless_parens_on_range_literals;
 +mod needless_pass_by_value;
 +mod needless_question_mark;
 +mod needless_update;
 +mod neg_cmp_op_on_partial_ord;
 +mod neg_multiply;
 +mod new_without_default;
 +mod no_effect;
 +mod non_copy_const;
 +mod non_expressive_names;
 +mod non_octal_unix_permissions;
 +mod non_send_fields_in_send_ty;
 +mod nonstandard_macro_braces;
 +mod octal_escapes;
 +mod only_used_in_recursion;
 +mod operators;
 +mod option_env_unwrap;
 +mod option_if_let_else;
 +mod overflow_check_conditional;
 +mod panic_in_result_fn;
 +mod panic_unimplemented;
 +mod partialeq_ne_impl;
 +mod partialeq_to_none;
 +mod pass_by_ref_or_value;
 +mod pattern_type_mismatch;
 +mod precedence;
 +mod ptr;
 +mod ptr_offset_with_cast;
 +mod pub_use;
 +mod question_mark;
 +mod ranges;
 +mod rc_clone_in_vec_init;
 +mod read_zero_byte_vec;
 +mod redundant_clone;
 +mod redundant_closure_call;
 +mod redundant_else;
 +mod redundant_field_names;
 +mod redundant_pub_crate;
 +mod redundant_slicing;
 +mod redundant_static_lifetimes;
 +mod ref_option_ref;
 +mod reference;
 +mod regex;
 +mod return_self_not_must_use;
 +mod returns;
 +mod same_name_method;
 +mod self_named_constructors;
 +mod semicolon_if_nothing_returned;
 +mod serde_api;
 +mod shadow;
 +mod single_char_lifetime_names;
 +mod single_component_path_imports;
 +mod size_of_in_element_count;
 +mod slow_vector_initialization;
 +mod std_instead_of_core;
 +mod strings;
 +mod strlen_on_c_strings;
 +mod suspicious_operation_groupings;
 +mod suspicious_trait_impl;
 +mod swap;
 +mod swap_ptr_to_ref;
 +mod tabs_in_doc_comments;
 +mod temporary_assignment;
 +mod to_digit_is_some;
 +mod trailing_empty_array;
 +mod trait_bounds;
 +mod transmute;
 +mod types;
 +mod undocumented_unsafe_blocks;
 +mod unicode;
 +mod uninit_vec;
 +mod unit_return_expecting_ord;
 +mod unit_types;
 +mod unnamed_address;
 +mod unnecessary_owned_empty_strings;
 +mod unnecessary_self_imports;
 +mod unnecessary_wraps;
 +mod unnested_or_patterns;
 +mod unsafe_removed_from_name;
 +mod unused_async;
 +mod unused_io_amount;
 +mod unused_peekable;
 +mod unused_rounding;
 +mod unused_self;
 +mod unused_unit;
 +mod unwrap;
 +mod unwrap_in_result;
 +mod upper_case_acronyms;
 +mod use_self;
 +mod useless_conversion;
 +mod vec;
 +mod vec_init_then_push;
 +mod wildcard_imports;
 +mod write;
 +mod zero_div_zero;
 +mod zero_sized_map_values;
 +// end lints modules, do not remove this comment, it’s used in `update_lints`
 +
 +pub use crate::utils::conf::Conf;
 +use crate::utils::conf::{format_error, TryConf};
 +
 +/// Register all pre expansion lints
 +///
 +/// Pre-expansion lints run before any macro expansion has happened.
 +///
 +/// Note that due to the architecture of the compiler, currently `cfg_attr` attributes on crate
 +/// 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, 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(move || Box::new(attrs::EarlyAttributes { msrv }));
 +}
 +
 +fn read_msrv(conf: &Conf, sess: &Session) -> Option<RustcVersion> {
 +    let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION")
 +        .ok()
 +        .and_then(|v| parse_msrv(&v, None, None));
 +    let clippy_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
 +        })
 +    });
 +
 +    if let Some(cargo_msrv) = cargo_msrv {
 +        if let Some(clippy_msrv) = clippy_msrv {
 +            // if both files have an msrv, let's compare them and emit a warning if they differ
 +            if clippy_msrv != cargo_msrv {
 +                sess.warn(&format!(
 +                    "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{}` from `clippy.toml`",
 +                    clippy_msrv
 +                ));
 +            }
 +
 +            Some(clippy_msrv)
 +        } else {
 +            Some(cargo_msrv)
 +        }
 +    } else {
 +        clippy_msrv
 +    }
 +}
 +
 +#[doc(hidden)]
 +pub fn read_conf(sess: &Session) -> Conf {
 +    let file_name = match utils::conf::lookup_conf_file() {
 +        Ok(Some(path)) => path,
 +        Ok(None) => return Conf::default(),
 +        Err(error) => {
 +            sess.struct_err(&format!("error finding Clippy's configuration file: {}", error))
 +                .emit();
 +            return Conf::default();
 +        },
 +    };
 +
 +    let TryConf { conf, errors, warnings } = utils::conf::read(&file_name);
 +    // all conf errors are non-fatal, we just use the default conf in case of error
 +    for error in errors {
 +        sess.err(&format!(
 +            "error reading Clippy's configuration file `{}`: {}",
 +            file_name.display(),
 +            format_error(error)
 +        ));
 +    }
 +
 +    for warning in warnings {
 +        sess.struct_warn(&format!(
 +            "error reading Clippy's configuration file `{}`: {}",
 +            file_name.display(),
 +            format_error(warning)
 +        ))
 +        .emit();
 +    }
 +
 +    conf
 +}
 +
 +/// Register all lints and lint groups with the rustc plugin registry
 +///
 +/// Used in `./src/driver.rs`.
 +#[expect(clippy::too_many_lines)]
 +pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
 +    register_removed_non_tool_lints(store);
 +
 +    include!("lib.deprecated.rs");
 +
 +    include!("lib.register_lints.rs");
 +    include!("lib.register_restriction.rs");
 +    include!("lib.register_pedantic.rs");
 +
 +    #[cfg(feature = "internal")]
 +    include!("lib.register_internal.rs");
 +
 +    include!("lib.register_all.rs");
 +    include!("lib.register_style.rs");
 +    include!("lib.register_complexity.rs");
 +    include!("lib.register_correctness.rs");
 +    include!("lib.register_suspicious.rs");
 +    include!("lib.register_perf.rs");
 +    include!("lib.register_cargo.rs");
 +    include!("lib.register_nursery.rs");
 +
 +    #[cfg(feature = "internal")]
 +    {
 +        if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) {
-     let arithmetic_allowed = conf.arithmetic_allowed.clone();
-     store.register_late_pass(move |_| Box::new(operators::arithmetic::Arithmetic::new(arithmetic_allowed.clone())));
++            store.register_late_pass(|| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new()));
 +            return;
 +        }
 +    }
 +
 +    // all the internal lints
 +    #[cfg(feature = "internal")]
 +    {
 +        store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal));
 +        store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce));
 +        store.register_late_pass(|_| Box::new(utils::internal_lints::CollapsibleCalls));
 +        store.register_late_pass(|_| Box::new(utils::internal_lints::CompilerLintFunctions::new()));
 +        store.register_late_pass(|_| Box::new(utils::internal_lints::IfChainStyle));
 +        store.register_late_pass(|_| Box::new(utils::internal_lints::InvalidPaths));
 +        store.register_late_pass(|_| Box::new(utils::internal_lints::InterningDefinedSymbol::default()));
 +        store.register_late_pass(|_| Box::new(utils::internal_lints::LintWithoutLintPass::default()));
 +        store.register_late_pass(|_| Box::new(utils::internal_lints::MatchTypeOnDiagItem));
 +        store.register_late_pass(|_| Box::new(utils::internal_lints::OuterExpnDataPass));
 +        store.register_late_pass(|_| Box::new(utils::internal_lints::MsrvAttrImpl));
 +    }
 +
++    let arithmetic_side_effects_allowed = conf.arithmetic_side_effects_allowed.clone();
++    store.register_late_pass(move |_| {
++        Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(
++            arithmetic_side_effects_allowed.clone(),
++        ))
++    });
 +    store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
 +    store.register_late_pass(|_| Box::new(utils::author::Author));
 +    let await_holding_invalid_types = conf.await_holding_invalid_types.clone();
 +    store.register_late_pass(move |_| {
 +        Box::new(await_holding_invalid::AwaitHolding::new(
 +            await_holding_invalid_types.clone(),
 +        ))
 +    });
 +    store.register_late_pass(|_| Box::new(serde_api::SerdeApi));
 +    let vec_box_size_threshold = conf.vec_box_size_threshold;
 +    let type_complexity_threshold = conf.type_complexity_threshold;
 +    let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
 +    store.register_late_pass(move |_| {
 +        Box::new(types::Types::new(
 +            vec_box_size_threshold,
 +            type_complexity_threshold,
 +            avoid_breaking_exported_api,
 +        ))
 +    });
 +    store.register_late_pass(|_| Box::new(booleans::NonminimalBool));
 +    store.register_late_pass(|_| Box::new(enum_clike::UnportableVariant));
 +    store.register_late_pass(|_| Box::new(float_literal::FloatLiteral));
 +    store.register_late_pass(|_| Box::new(ptr::Ptr));
 +    store.register_late_pass(|_| Box::new(needless_bool::NeedlessBool));
 +    store.register_late_pass(|_| Box::new(needless_bool::BoolComparison));
 +    store.register_late_pass(|_| Box::new(needless_for_each::NeedlessForEach));
 +    store.register_late_pass(|_| Box::new(misc::MiscLints));
 +    store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction));
 +    store.register_late_pass(|_| Box::new(mut_mut::MutMut));
 +    store.register_late_pass(|_| Box::new(mut_reference::UnnecessaryMutPassed));
 +    store.register_late_pass(|_| Box::new(len_zero::LenZero));
 +    store.register_late_pass(|_| Box::new(attrs::Attributes));
 +    store.register_late_pass(|_| Box::new(blocks_in_if_conditions::BlocksInIfConditions));
 +    store.register_late_pass(|_| Box::new(unicode::Unicode));
 +    store.register_late_pass(|_| Box::new(uninit_vec::UninitVec));
 +    store.register_late_pass(|_| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd));
 +    store.register_late_pass(|_| Box::new(strings::StringAdd));
 +    store.register_late_pass(|_| Box::new(implicit_return::ImplicitReturn));
 +    store.register_late_pass(|_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub));
 +    store.register_late_pass(|_| Box::new(default_numeric_fallback::DefaultNumericFallback));
 +    store.register_late_pass(|_| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor));
 +    store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
 +    store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
 +
 +    let msrv = read_msrv(conf, sess);
 +    let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
 +    let allow_expect_in_tests = conf.allow_expect_in_tests;
 +    let allow_unwrap_in_tests = conf.allow_unwrap_in_tests;
 +    store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv)));
 +    store.register_late_pass(move |_| {
 +        Box::new(methods::Methods::new(
 +            avoid_breaking_exported_api,
 +            msrv,
 +            allow_expect_in_tests,
 +            allow_unwrap_in_tests,
 +        ))
 +    });
 +    store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv)));
 +    store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv)));
 +    store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv)));
 +    store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(ranges::Ranges::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(needless_question_mark::NeedlessQuestionMark));
 +    store.register_late_pass(move |_| Box::new(casts::Casts::new(msrv)));
 +    store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
 +    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(shadow::Shadow::default()));
 +    store.register_late_pass(|_| Box::new(unit_types::UnitTypes));
 +    store.register_late_pass(|_| Box::new(loops::Loops));
 +    store.register_late_pass(|_| Box::new(main_recursion::MainRecursion::default()));
 +    store.register_late_pass(|_| Box::new(lifetimes::Lifetimes));
 +    store.register_late_pass(|_| Box::new(entry::HashMapPass));
 +    store.register_late_pass(|_| Box::new(minmax::MinMaxPass));
 +    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_borrowed_ref::NeedlessBorrowedRef));
 +    store.register_late_pass(|_| Box::new(borrow_deref_ref::BorrowDerefRef));
 +    store.register_late_pass(|_| Box::new(no_effect::NoEffect));
 +    store.register_late_pass(|_| Box::new(temporary_assignment::TemporaryAssignment));
 +    store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv)));
 +    let cognitive_complexity_threshold = conf.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(|_| Box::new(panic_unimplemented::PanicUnimplemented));
 +    store.register_late_pass(|_| Box::new(strings::StringLitAsBytes));
 +    store.register_late_pass(|_| Box::new(derive::Derive));
 +    store.register_late_pass(|_| Box::new(derivable_impls::DerivableImpls));
 +    store.register_late_pass(|_| Box::new(drop_forget_ref::DropForgetRef));
 +    store.register_late_pass(|_| Box::new(empty_enum::EmptyEnum));
 +    store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons));
 +    store.register_late_pass(|_| Box::new(regex::Regex));
 +    store.register_late_pass(|_| Box::new(copies::CopyAndPaste));
 +    store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator));
 +    store.register_late_pass(|_| Box::new(format::UselessFormat));
 +    store.register_late_pass(|_| Box::new(swap::Swap));
 +    store.register_late_pass(|_| Box::new(overflow_check_conditional::OverflowCheckConditional));
 +    store.register_late_pass(|_| Box::new(new_without_default::NewWithoutDefault::default()));
 +    let disallowed_names = conf.disallowed_names.iter().cloned().collect::<FxHashSet<_>>();
 +    store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(disallowed_names.clone())));
 +    let too_many_arguments_threshold = conf.too_many_arguments_threshold;
 +    let too_many_lines_threshold = conf.too_many_lines_threshold;
 +    let large_error_threshold = conf.large_error_threshold;
 +    store.register_late_pass(move |_| {
 +        Box::new(functions::Functions::new(
 +            too_many_arguments_threshold,
 +            too_many_lines_threshold,
 +            large_error_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));
 +    store.register_late_pass(|_| Box::new(mem_forget::MemForget));
 +    store.register_late_pass(|_| Box::new(let_if_seq::LetIfSeq));
 +    store.register_late_pass(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence));
 +    store.register_late_pass(|_| Box::new(missing_doc::MissingDoc::new()));
 +    store.register_late_pass(|_| Box::new(missing_inline::MissingInline));
 +    store.register_late_pass(move |_| Box::new(exhaustive_items::ExhaustiveItems));
 +    store.register_late_pass(|_| Box::new(match_result_ok::MatchResultOk));
 +    store.register_late_pass(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl));
 +    store.register_late_pass(|_| Box::new(unused_io_amount::UnusedIoAmount));
 +    let enum_variant_size_threshold = conf.enum_variant_size_threshold;
 +    store.register_late_pass(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold)));
 +    store.register_late_pass(|_| Box::new(explicit_write::ExplicitWrite));
 +    store.register_late_pass(|_| Box::new(needless_pass_by_value::NeedlessPassByValue));
 +    let pass_by_ref_or_value = pass_by_ref_or_value::PassByRefOrValue::new(
 +        conf.trivial_copy_size_limit,
 +        conf.pass_by_value_size_limit,
 +        conf.avoid_breaking_exported_api,
 +        &sess.target,
 +    );
 +    store.register_late_pass(move |_| Box::new(pass_by_ref_or_value));
 +    store.register_late_pass(|_| Box::new(ref_option_ref::RefOptionRef));
 +    store.register_late_pass(|_| Box::new(infinite_iter::InfiniteIter));
 +    store.register_late_pass(|_| Box::new(inline_fn_without_body::InlineFnWithoutBody));
 +    store.register_late_pass(|_| Box::new(useless_conversion::UselessConversion::default()));
 +    store.register_late_pass(|_| Box::new(implicit_hasher::ImplicitHasher));
 +    store.register_late_pass(|_| Box::new(fallible_impl_from::FallibleImplFrom));
 +    store.register_late_pass(|_| Box::new(question_mark::QuestionMark));
 +    store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings));
 +    store.register_late_pass(|_| Box::new(suspicious_trait_impl::SuspiciousImpl));
 +    store.register_late_pass(|_| Box::new(map_unit_fn::MapUnit));
 +    store.register_late_pass(|_| Box::new(inherent_impl::MultipleInherentImpl));
 +    store.register_late_pass(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd));
 +    store.register_late_pass(|_| Box::new(unwrap::Unwrap));
 +    store.register_late_pass(|_| Box::new(indexing_slicing::IndexingSlicing));
 +    store.register_late_pass(|_| Box::new(non_copy_const::NonCopyConst));
 +    store.register_late_pass(|_| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
 +    store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone));
 +    store.register_late_pass(|_| Box::new(slow_vector_initialization::SlowVectorInit));
 +    store.register_late_pass(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)));
 +    store.register_late_pass(|_| Box::new(assertions_on_constants::AssertionsOnConstants));
 +    store.register_late_pass(|_| Box::new(assertions_on_result_states::AssertionsOnResultStates));
 +    store.register_late_pass(|_| Box::new(inherent_to_string::InherentToString));
 +    let max_trait_bounds = conf.max_trait_bounds;
 +    store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(max_trait_bounds)));
 +    store.register_late_pass(|_| Box::new(comparison_chain::ComparisonChain));
 +    store.register_late_pass(|_| Box::new(mut_key::MutableKeyType));
 +    store.register_early_pass(|| Box::new(reference::DerefAddrOf));
 +    store.register_early_pass(|| Box::new(double_parens::DoubleParens));
 +    store.register_late_pass(|_| Box::new(format_impl::FormatImpl::new()));
 +    store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval));
 +    store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse));
 +    store.register_early_pass(|| Box::new(int_plus_one::IntPlusOne));
 +    store.register_early_pass(|| Box::new(formatting::Formatting));
 +    store.register_early_pass(|| Box::new(misc_early::MiscEarlyLints));
 +    store.register_early_pass(|| Box::new(redundant_closure_call::RedundantClosureCall));
 +    store.register_late_pass(|_| Box::new(redundant_closure_call::RedundantClosureCall));
 +    store.register_early_pass(|| Box::new(unused_unit::UnusedUnit));
 +    store.register_late_pass(|_| Box::new(returns::Return));
 +    store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf));
 +    store.register_early_pass(|| Box::new(items_after_statements::ItemsAfterStatements));
 +    store.register_early_pass(|| Box::new(precedence::Precedence));
 +    store.register_late_pass(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals));
 +    store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue));
 +    store.register_early_pass(|| Box::new(redundant_else::RedundantElse));
 +    store.register_late_pass(|_| Box::new(create_dir::CreateDir));
 +    store.register_early_pass(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType));
 +    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,
 +        ))
 +    });
 +    let literal_representation_threshold = conf.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_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(|_| Box::new(default::Default::default()));
 +    store.register_late_pass(move |_| Box::new(unused_self::UnusedSelf::new(avoid_breaking_exported_api)));
 +    store.register_late_pass(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall));
 +    store.register_late_pass(|_| Box::new(exit::Exit));
 +    store.register_late_pass(|_| Box::new(to_digit_is_some::ToDigitIsSome));
 +    let array_size_threshold = conf.array_size_threshold;
 +    store.register_late_pass(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(array_size_threshold)));
 +    store.register_late_pass(move |_| Box::new(large_const_arrays::LargeConstArrays::new(array_size_threshold)));
 +    store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic));
 +    store.register_early_pass(|| Box::new(as_conversions::AsConversions));
 +    store.register_late_pass(|_| Box::new(let_underscore::LetUnderscore));
 +    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(|| 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)));
 +    store.register_late_pass(|_| Box::new(redundant_pub_crate::RedundantPubCrate::default()));
 +    store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress));
 +    store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv)));
 +    store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse));
 +    store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend));
 +    store.register_late_pass(|_| Box::new(if_let_mutex::IfLetMutex));
 +    store.register_late_pass(|_| Box::new(if_not_else::IfNotElse));
 +    store.register_late_pass(|_| Box::new(equatable_if_let::PatternEquality));
 +    store.register_late_pass(|_| Box::new(manual_async_fn::ManualAsyncFn));
 +    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,
 +        })
 +    });
 +    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()));
 +    store.register_late_pass(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch));
 +    store.register_late_pass(|_| Box::new(unwrap_in_result::UnwrapInResult));
 +    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_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(empty_drop::EmptyDrop));
 +    store.register_late_pass(|_| Box::new(strings::StrToString));
 +    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(redundant_slicing::RedundantSlicing));
 +    store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10));
 +    store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv)));
 +    store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison));
 +    store.register_early_pass(move || Box::new(module_style::ModStyle));
 +    store.register_late_pass(|_| Box::new(unused_async::UnusedAsync));
 +    let disallowed_types = conf.disallowed_types.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(),
 +        ))
 +    });
 +    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));
 +    store.register_late_pass(move |_| Box::new(self_named_constructors::SelfNamedConstructors));
 +    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(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks));
 +    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));
 +    store.register_late_pass(|_| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
 +    store.register_late_pass(|_| Box::new(init_numbered_fields::NumberedFields));
 +    store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
 +    store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv)));
 +    store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation));
 +    store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes));
 +    store.register_late_pass(|_| Box::new(only_used_in_recursion::OnlyUsedInRecursion::default()));
 +    let allow_dbg_in_tests = conf.allow_dbg_in_tests;
 +    store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests)));
 +    let cargo_ignore_publish = conf.cargo_ignore_publish;
 +    store.register_late_pass(move |_| {
 +        Box::new(cargo::Cargo {
 +            ignore_publish: cargo_ignore_publish,
 +        })
 +    });
 +    store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
 +    store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets));
 +    store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
 +    store.register_early_pass(|| Box::new(pub_use::PubUse));
 +    store.register_late_pass(|_| Box::new(format_push_string::FormatPushString));
 +    let max_include_file_size = conf.max_include_file_size;
 +    store.register_late_pass(move |_| Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size)));
 +    store.register_late_pass(|_| Box::new(strings::TrimSplitWhitespace));
 +    store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
 +    store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default()));
 +    store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding));
 +    store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv)));
 +    store.register_late_pass(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef));
 +    store.register_late_pass(|_| Box::new(mismatching_type_param_order::TypeParamMismatch));
 +    store.register_late_pass(|_| Box::new(read_zero_byte_vec::ReadZeroByteVec));
 +    store.register_late_pass(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
 +    store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv)));
 +    let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
 +    store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
 +    store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked));
 +    store.register_late_pass(|_| Box::new(std_instead_of_core::StdReexports::default()));
 +    store.register_late_pass(|_| Box::new(manual_instant_elapsed::ManualInstantElapsed));
 +    store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone));
 +    store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew));
 +    store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable));
 +    store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments));
++    store.register_late_pass(|_| Box::new(bool_to_int_with_if::BoolToIntWithIf));
 +    // add lints here, do not remove this comment, it's used in `new_lint`
 +}
 +
 +#[rustfmt::skip]
 +fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) {
 +    store.register_removed(
 +        "should_assert_eq",
 +        "`assert!()` will be more flexible with RFC 2011",
 +    );
 +    store.register_removed(
 +        "extend_from_slice",
 +        "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice",
 +    );
 +    store.register_removed(
 +        "range_step_by_zero",
 +        "`iterator.step_by(0)` panics nowadays",
 +    );
 +    store.register_removed(
 +        "unstable_as_slice",
 +        "`Vec::as_slice` has been stabilized in 1.7",
 +    );
 +    store.register_removed(
 +        "unstable_as_mut_slice",
 +        "`Vec::as_mut_slice` has been stabilized in 1.7",
 +    );
 +    store.register_removed(
 +        "misaligned_transmute",
 +        "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr",
 +    );
 +    store.register_removed(
 +        "assign_ops",
 +        "using compound assignment operators (e.g., `+=`) is harmless",
 +    );
 +    store.register_removed(
 +        "if_let_redundant_pattern_matching",
 +        "this lint has been changed to redundant_pattern_matching",
 +    );
 +    store.register_removed(
 +        "unsafe_vector_initialization",
 +        "the replacement suggested by this lint had substantially different behavior",
 +    );
 +    store.register_removed(
 +        "reverse_range_loop",
 +        "this lint is now included in reversed_empty_ranges",
 +    );
 +}
 +
 +/// Register renamed lints.
 +///
 +/// Used in `./src/driver.rs`.
 +pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
 +    for (old_name, new_name) in renamed_lints::RENAMED_LINTS {
 +        ls.register_renamed(old_name, new_name);
 +    }
 +}
 +
 +// only exists to let the dogfood integration test works.
 +// Don't run clippy as an executable directly
 +#[allow(dead_code)]
 +fn main() {
 +    panic!("Please use the cargo-clippy executable");
 +}
index 5995675bd969c7ca3e3c98d1bdedfedab8e22bdc,0000000000000000000000000000000000000000..f2b6e0b7ef9ba933b0ec5e1e7cef7f4195fe514a
mode 100644,000000..100644
--- /dev/null
@@@ -1,620 -1,0 +1,620 @@@
-         checker.visit_expr(&body.value);
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::trait_ref_of_method;
 +use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 +use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter};
 +use rustc_hir::intravisit::{
 +    walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound,
 +    walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor,
 +};
 +use rustc_hir::FnRetTy::Return;
 +use rustc_hir::{
 +    BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem,
 +    ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, PredicateOrigin,
 +    TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::nested_filter as middle_nested_filter;
 +use rustc_middle::ty::TyCtxt;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::def_id::LocalDefId;
 +use rustc_span::source_map::Span;
 +use rustc_span::symbol::{kw, Ident, Symbol};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for lifetime annotations which can be removed by
 +    /// relying on lifetime elision.
 +    ///
 +    /// ### Why is this bad?
 +    /// The additional lifetimes make the code look more
 +    /// complicated, while there is nothing out of the ordinary going on. Removing
 +    /// them leads to more readable code.
 +    ///
 +    /// ### Known problems
 +    /// - We bail out if the function has a `where` clause where lifetimes
 +    /// are mentioned due to potential false positives.
 +    /// - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the
 +    /// placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Unnecessary lifetime annotations
 +    /// fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 {
 +    ///     x
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// fn elided(x: &u8, y: u8) -> &u8 {
 +    ///     x
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEEDLESS_LIFETIMES,
 +    complexity,
 +    "using explicit lifetimes for references in function arguments when elision rules \
 +     would allow omitting them"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for lifetimes in generics that are never used
 +    /// anywhere else.
 +    ///
 +    /// ### Why is this bad?
 +    /// The additional lifetimes make the code look more
 +    /// complicated, while there is nothing out of the ordinary going on. Removing
 +    /// them leads to more readable code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // unnecessary lifetimes
 +    /// fn unused_lifetime<'a>(x: u8) {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// fn no_lifetime(x: u8) {
 +    ///     // ...
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub EXTRA_UNUSED_LIFETIMES,
 +    complexity,
 +    "unused lifetimes in function definitions"
 +}
 +
 +declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Lifetimes {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        if let ItemKind::Fn(ref sig, generics, id) = item.kind {
 +            check_fn_inner(cx, sig.decl, Some(id), None, generics, item.span, true);
 +        } else if let ItemKind::Impl(impl_) = item.kind {
 +            if !item.span.from_expansion() {
 +                report_extra_impl_lifetimes(cx, impl_);
 +            }
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
 +        if let ImplItemKind::Fn(ref sig, id) = item.kind {
 +            let report_extra_lifetimes = trait_ref_of_method(cx, item.def_id).is_none();
 +            check_fn_inner(
 +                cx,
 +                sig.decl,
 +                Some(id),
 +                None,
 +                item.generics,
 +                item.span,
 +                report_extra_lifetimes,
 +            );
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
 +        if let TraitItemKind::Fn(ref sig, ref body) = item.kind {
 +            let (body, trait_sig) = match *body {
 +                TraitFn::Required(sig) => (None, Some(sig)),
 +                TraitFn::Provided(id) => (Some(id), None),
 +            };
 +            check_fn_inner(cx, sig.decl, body, trait_sig, item.generics, item.span, true);
 +        }
 +    }
 +}
 +
 +/// The lifetime of a &-reference.
 +#[derive(PartialEq, Eq, Hash, Debug, Clone)]
 +enum RefLt {
 +    Unnamed,
 +    Static,
 +    Named(LocalDefId),
 +}
 +
 +fn check_fn_inner<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    decl: &'tcx FnDecl<'_>,
 +    body: Option<BodyId>,
 +    trait_sig: Option<&[Ident]>,
 +    generics: &'tcx Generics<'_>,
 +    span: Span,
 +    report_extra_lifetimes: bool,
 +) {
 +    if span.from_expansion() || has_where_lifetimes(cx, generics) {
 +        return;
 +    }
 +
 +    let types = generics
 +        .params
 +        .iter()
 +        .filter(|param| matches!(param.kind, GenericParamKind::Type { .. }));
 +    for typ in types {
 +        for pred in generics.bounds_for_param(cx.tcx.hir().local_def_id(typ.hir_id)) {
 +            if pred.origin == PredicateOrigin::WhereClause {
 +                // has_where_lifetimes checked that this predicate contains no lifetime.
 +                continue;
 +            }
 +
 +            for bound in pred.bounds {
 +                let mut visitor = RefVisitor::new(cx);
 +                walk_param_bound(&mut visitor, bound);
 +                if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) {
 +                    return;
 +                }
 +                if let GenericBound::Trait(ref trait_ref, _) = *bound {
 +                    let params = &trait_ref
 +                        .trait_ref
 +                        .path
 +                        .segments
 +                        .last()
 +                        .expect("a path must have at least one segment")
 +                        .args;
 +                    if let Some(params) = *params {
 +                        let lifetimes = params.args.iter().filter_map(|arg| match arg {
 +                            GenericArg::Lifetime(lt) => Some(lt),
 +                            _ => None,
 +                        });
 +                        for bound in lifetimes {
 +                            if bound.name != LifetimeName::Static && !bound.is_elided() {
 +                                return;
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +    if could_use_elision(cx, decl, body, trait_sig, generics.params) {
 +        span_lint(
 +            cx,
 +            NEEDLESS_LIFETIMES,
 +            span.with_hi(decl.output.span().hi()),
 +            "explicit lifetimes given in parameter types where they could be elided \
 +             (or replaced with `'_` if needed by type declaration)",
 +        );
 +    }
 +    if report_extra_lifetimes {
 +        self::report_extra_lifetimes(cx, decl, generics);
 +    }
 +}
 +
 +// elision doesn't work for explicit self types, see rust-lang/rust#69064
 +fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option<Ident>) -> bool {
 +    if_chain! {
 +        if let Some(ident) = ident;
 +        if ident.name == kw::SelfLower;
 +        if !func.implicit_self.has_implicit_self();
 +
 +        if let Some(self_ty) = func.inputs.first();
 +        then {
 +            let mut visitor = RefVisitor::new(cx);
 +            visitor.visit_ty(self_ty);
 +
 +            !visitor.all_lts().is_empty()
 +        } else {
 +            false
 +        }
 +    }
 +}
 +
 +fn could_use_elision<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    func: &'tcx FnDecl<'_>,
 +    body: Option<BodyId>,
 +    trait_sig: Option<&[Ident]>,
 +    named_generics: &'tcx [GenericParam<'_>],
 +) -> bool {
 +    // There are two scenarios where elision works:
 +    // * no output references, all input references have different LT
 +    // * output references, exactly one input reference with same LT
 +    // All lifetimes must be unnamed, 'static or defined without bounds on the
 +    // level of the current item.
 +
 +    // check named LTs
 +    let allowed_lts = allowed_lts_from(cx.tcx, named_generics);
 +
 +    // these will collect all the lifetimes for references in arg/return types
 +    let mut input_visitor = RefVisitor::new(cx);
 +    let mut output_visitor = RefVisitor::new(cx);
 +
 +    // extract lifetimes in input argument types
 +    for arg in func.inputs {
 +        input_visitor.visit_ty(arg);
 +    }
 +    // extract lifetimes in output type
 +    if let Return(ty) = func.output {
 +        output_visitor.visit_ty(ty);
 +    }
 +    for lt in named_generics {
 +        input_visitor.visit_generic_param(lt);
 +    }
 +
 +    if input_visitor.abort() || output_visitor.abort() {
 +        return false;
 +    }
 +
 +    let input_lts = input_visitor.lts;
 +    let output_lts = output_visitor.lts;
 +
 +    if let Some(trait_sig) = trait_sig {
 +        if explicit_self_type(cx, func, trait_sig.first().copied()) {
 +            return false;
 +        }
 +    }
 +
 +    if let Some(body_id) = body {
 +        let body = cx.tcx.hir().body(body_id);
 +
 +        let first_ident = body.params.first().and_then(|param| param.pat.simple_ident());
 +        if explicit_self_type(cx, func, first_ident) {
 +            return false;
 +        }
 +
 +        let mut checker = BodyLifetimeChecker {
 +            lifetimes_used_in_body: false,
 +        };
++        checker.visit_expr(body.value);
 +        if checker.lifetimes_used_in_body {
 +            return false;
 +        }
 +    }
 +
 +    // check for lifetimes from higher scopes
 +    for lt in input_lts.iter().chain(output_lts.iter()) {
 +        if !allowed_lts.contains(lt) {
 +            return false;
 +        }
 +    }
 +
 +    // check for higher-ranked trait bounds
 +    if !input_visitor.nested_elision_site_lts.is_empty() || !output_visitor.nested_elision_site_lts.is_empty() {
 +        let allowed_lts: FxHashSet<_> = allowed_lts
 +            .iter()
 +            .filter_map(|lt| match lt {
 +                RefLt::Named(def_id) => Some(cx.tcx.item_name(def_id.to_def_id())),
 +                _ => None,
 +            })
 +            .collect();
 +        for lt in input_visitor.nested_elision_site_lts {
 +            if let RefLt::Named(def_id) = lt {
 +                if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) {
 +                    return false;
 +                }
 +            }
 +        }
 +        for lt in output_visitor.nested_elision_site_lts {
 +            if let RefLt::Named(def_id) = lt {
 +                if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) {
 +                    return false;
 +                }
 +            }
 +        }
 +    }
 +
 +    // no input lifetimes? easy case!
 +    if input_lts.is_empty() {
 +        false
 +    } else if output_lts.is_empty() {
 +        // no output lifetimes, check distinctness of input lifetimes
 +
 +        // only unnamed and static, ok
 +        let unnamed_and_static = input_lts.iter().all(|lt| *lt == RefLt::Unnamed || *lt == RefLt::Static);
 +        if unnamed_and_static {
 +            return false;
 +        }
 +        // we have no output reference, so we only need all distinct lifetimes
 +        input_lts.len() == unique_lifetimes(&input_lts)
 +    } else {
 +        // we have output references, so we need one input reference,
 +        // and all output lifetimes must be the same
 +        if unique_lifetimes(&output_lts) > 1 {
 +            return false;
 +        }
 +        if input_lts.len() == 1 {
 +            match (&input_lts[0], &output_lts[0]) {
 +                (&RefLt::Named(n1), &RefLt::Named(n2)) if n1 == n2 => true,
 +                (&RefLt::Named(_), &RefLt::Unnamed) => true,
 +                _ => false, /* already elided, different named lifetimes
 +                             * or something static going on */
 +            }
 +        } else {
 +            false
 +        }
 +    }
 +}
 +
 +fn allowed_lts_from(tcx: TyCtxt<'_>, named_generics: &[GenericParam<'_>]) -> FxHashSet<RefLt> {
 +    let mut allowed_lts = FxHashSet::default();
 +    for par in named_generics.iter() {
 +        if let GenericParamKind::Lifetime { .. } = par.kind {
 +            allowed_lts.insert(RefLt::Named(tcx.hir().local_def_id(par.hir_id)));
 +        }
 +    }
 +    allowed_lts.insert(RefLt::Unnamed);
 +    allowed_lts.insert(RefLt::Static);
 +    allowed_lts
 +}
 +
 +/// Number of unique lifetimes in the given vector.
 +#[must_use]
 +fn unique_lifetimes(lts: &[RefLt]) -> usize {
 +    lts.iter().collect::<FxHashSet<_>>().len()
 +}
 +
 +const CLOSURE_TRAIT_BOUNDS: [LangItem; 3] = [LangItem::Fn, LangItem::FnMut, LangItem::FnOnce];
 +
 +/// A visitor usable for `rustc_front::visit::walk_ty()`.
 +struct RefVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    lts: Vec<RefLt>,
 +    nested_elision_site_lts: Vec<RefLt>,
 +    unelided_trait_object_lifetime: bool,
 +}
 +
 +impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>) -> Self {
 +        Self {
 +            cx,
 +            lts: Vec::new(),
 +            nested_elision_site_lts: Vec::new(),
 +            unelided_trait_object_lifetime: false,
 +        }
 +    }
 +
 +    fn record(&mut self, lifetime: &Option<Lifetime>) {
 +        if let Some(ref lt) = *lifetime {
 +            if lt.name == LifetimeName::Static {
 +                self.lts.push(RefLt::Static);
 +            } else if let LifetimeName::Param(_, ParamName::Fresh) = lt.name {
 +                // Fresh lifetimes generated should be ignored.
 +                self.lts.push(RefLt::Unnamed);
 +            } else if lt.is_elided() {
 +                self.lts.push(RefLt::Unnamed);
 +            } else if let LifetimeName::Param(def_id, _) = lt.name {
 +                self.lts.push(RefLt::Named(def_id));
 +            } else {
 +                self.lts.push(RefLt::Unnamed);
 +            }
 +        } else {
 +            self.lts.push(RefLt::Unnamed);
 +        }
 +    }
 +
 +    fn all_lts(&self) -> Vec<RefLt> {
 +        self.lts
 +            .iter()
 +            .chain(self.nested_elision_site_lts.iter())
 +            .cloned()
 +            .collect::<Vec<_>>()
 +    }
 +
 +    fn abort(&self) -> bool {
 +        self.unelided_trait_object_lifetime
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
 +    // for lifetimes as parameters of generics
 +    fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
 +        self.record(&Some(*lifetime));
 +    }
 +
 +    fn visit_poly_trait_ref(&mut self, poly_tref: &'tcx PolyTraitRef<'tcx>, tbm: TraitBoundModifier) {
 +        let trait_ref = &poly_tref.trait_ref;
 +        if CLOSURE_TRAIT_BOUNDS.iter().any(|&item| {
 +            self.cx
 +                .tcx
 +                .lang_items()
 +                .require(item)
 +                .map_or(false, |id| Some(id) == trait_ref.trait_def_id())
 +        }) {
 +            let mut sub_visitor = RefVisitor::new(self.cx);
 +            sub_visitor.visit_trait_ref(trait_ref);
 +            self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
 +        } else {
 +            walk_poly_trait_ref(self, poly_tref, tbm);
 +        }
 +    }
 +
 +    fn visit_ty(&mut self, ty: &'tcx Ty<'_>) {
 +        match ty.kind {
 +            TyKind::OpaqueDef(item, bounds, _) => {
 +                let map = self.cx.tcx.hir();
 +                let item = map.item(item);
 +                let len = self.lts.len();
 +                walk_item(self, item);
 +                self.lts.truncate(len);
 +                self.lts.extend(bounds.iter().filter_map(|bound| match bound {
 +                    GenericArg::Lifetime(l) => Some(if let LifetimeName::Param(def_id, _) = l.name {
 +                        RefLt::Named(def_id)
 +                    } else {
 +                        RefLt::Unnamed
 +                    }),
 +                    _ => None,
 +                }));
 +            },
 +            TyKind::BareFn(&BareFnTy { decl, .. }) => {
 +                let mut sub_visitor = RefVisitor::new(self.cx);
 +                sub_visitor.visit_fn_decl(decl);
 +                self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
 +            },
 +            TyKind::TraitObject(bounds, ref lt, _) => {
 +                if !lt.is_elided() {
 +                    self.unelided_trait_object_lifetime = true;
 +                }
 +                for bound in bounds {
 +                    self.visit_poly_trait_ref(bound, TraitBoundModifier::None);
 +                }
 +            },
 +            _ => walk_ty(self, ty),
 +        }
 +    }
 +}
 +
 +/// Are any lifetimes mentioned in the `where` clause? If so, we don't try to
 +/// reason about elision.
 +fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_>) -> bool {
 +    for predicate in generics.predicates {
 +        match *predicate {
 +            WherePredicate::RegionPredicate(..) => return true,
 +            WherePredicate::BoundPredicate(ref pred) => {
 +                // a predicate like F: Trait or F: for<'a> Trait<'a>
 +                let mut visitor = RefVisitor::new(cx);
 +                // walk the type F, it may not contain LT refs
 +                walk_ty(&mut visitor, pred.bounded_ty);
 +                if !visitor.all_lts().is_empty() {
 +                    return true;
 +                }
 +                // if the bounds define new lifetimes, they are fine to occur
 +                let allowed_lts = allowed_lts_from(cx.tcx, pred.bound_generic_params);
 +                // now walk the bounds
 +                for bound in pred.bounds.iter() {
 +                    walk_param_bound(&mut visitor, bound);
 +                }
 +                // and check that all lifetimes are allowed
 +                if visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)) {
 +                    return true;
 +                }
 +            },
 +            WherePredicate::EqPredicate(ref pred) => {
 +                let mut visitor = RefVisitor::new(cx);
 +                walk_ty(&mut visitor, pred.lhs_ty);
 +                walk_ty(&mut visitor, pred.rhs_ty);
 +                if !visitor.lts.is_empty() {
 +                    return true;
 +                }
 +            },
 +        }
 +    }
 +    false
 +}
 +
 +struct LifetimeChecker<'cx, 'tcx, F> {
 +    cx: &'cx LateContext<'tcx>,
 +    map: FxHashMap<Symbol, Span>,
 +    phantom: std::marker::PhantomData<F>,
 +}
 +
 +impl<'cx, 'tcx, F> LifetimeChecker<'cx, 'tcx, F> {
 +    fn new(cx: &'cx LateContext<'tcx>, map: FxHashMap<Symbol, Span>) -> LifetimeChecker<'cx, 'tcx, F> {
 +        Self {
 +            cx,
 +            map,
 +            phantom: std::marker::PhantomData,
 +        }
 +    }
 +}
 +
 +impl<'cx, 'tcx, F> Visitor<'tcx> for LifetimeChecker<'cx, 'tcx, F>
 +where
 +    F: NestedFilter<'tcx>,
 +{
 +    type Map = rustc_middle::hir::map::Map<'tcx>;
 +    type NestedFilter = F;
 +
 +    // for lifetimes as parameters of generics
 +    fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
 +        self.map.remove(&lifetime.name.ident().name);
 +    }
 +
 +    fn visit_generic_param(&mut self, param: &'tcx GenericParam<'_>) {
 +        // don't actually visit `<'a>` or `<'a: 'b>`
 +        // we've already visited the `'a` declarations and
 +        // don't want to spuriously remove them
 +        // `'b` in `'a: 'b` is useless unless used elsewhere in
 +        // a non-lifetime bound
 +        if let GenericParamKind::Type { .. } = param.kind {
 +            walk_generic_param(self, param);
 +        }
 +    }
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +}
 +
 +fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) {
 +    let hs = generics
 +        .params
 +        .iter()
 +        .filter_map(|par| match par.kind {
 +            GenericParamKind::Lifetime { .. } => Some((par.name.ident().name, par.span)),
 +            _ => None,
 +        })
 +        .collect();
 +    let mut checker = LifetimeChecker::<hir_nested_filter::None>::new(cx, hs);
 +
 +    walk_generics(&mut checker, generics);
 +    walk_fn_decl(&mut checker, func);
 +
 +    for &v in checker.map.values() {
 +        span_lint(
 +            cx,
 +            EXTRA_UNUSED_LIFETIMES,
 +            v,
 +            "this lifetime isn't used in the function definition",
 +        );
 +    }
 +}
 +
 +fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'_>) {
 +    let hs = impl_
 +        .generics
 +        .params
 +        .iter()
 +        .filter_map(|par| match par.kind {
 +            GenericParamKind::Lifetime { .. } => Some((par.name.ident().name, par.span)),
 +            _ => None,
 +        })
 +        .collect();
 +    let mut checker = LifetimeChecker::<middle_nested_filter::All>::new(cx, hs);
 +
 +    walk_generics(&mut checker, impl_.generics);
 +    if let Some(ref trait_ref) = impl_.of_trait {
 +        walk_trait_ref(&mut checker, trait_ref);
 +    }
 +    walk_ty(&mut checker, impl_.self_ty);
 +    for item in impl_.items {
 +        walk_impl_item_ref(&mut checker, item);
 +    }
 +
 +    for &v in checker.map.values() {
 +        span_lint(cx, EXTRA_UNUSED_LIFETIMES, v, "this lifetime isn't used in the impl");
 +    }
 +}
 +
 +struct BodyLifetimeChecker {
 +    lifetimes_used_in_body: bool,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker {
 +    // for lifetimes as parameters of generics
 +    fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
 +        if lifetime.name.ident().name != kw::UnderscoreLifetime && lifetime.name.ident().name != kw::StaticLifetime {
 +            self.lifetimes_used_in_body = true;
 +        }
 +    }
 +}
index ffcf83e4605e2d3cbb84986e80fadbbc7b21a5da,0000000000000000000000000000000000000000..8ab640051b635fb3da6e6122b6e94359f049a64d
mode 100644,000000..100644
--- /dev/null
@@@ -1,382 -1,0 +1,382 @@@
-                 self.visit_expr(&body.value);
 +use super::NEEDLESS_RANGE_LOOP;
 +use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::has_iter_method;
 +use clippy_utils::visitors::is_local_used;
 +use clippy_utils::{contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq};
 +use if_chain::if_chain;
 +use rustc_ast::ast;
 +use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::intravisit::{walk_expr, Visitor};
 +use rustc_hir::{BinOpKind, BorrowKind, Closure, Expr, ExprKind, HirId, Mutability, Pat, PatKind, QPath};
 +use rustc_lint::LateContext;
 +use rustc_middle::middle::region;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_span::symbol::{sym, Symbol};
 +use std::iter::{self, Iterator};
 +use std::mem;
 +
 +/// Checks for looping over a range and then indexing a sequence with it.
 +/// The iteratee must be a range literal.
 +#[expect(clippy::too_many_lines)]
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    pat: &'tcx Pat<'_>,
 +    arg: &'tcx Expr<'_>,
 +    body: &'tcx Expr<'_>,
 +    expr: &'tcx Expr<'_>,
 +) {
 +    if let Some(higher::Range {
 +        start: Some(start),
 +        ref end,
 +        limits,
 +    }) = higher::Range::hir(arg)
 +    {
 +        // the var must be a single name
 +        if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind {
 +            let mut visitor = VarVisitor {
 +                cx,
 +                var: canonical_id,
 +                indexed_mut: FxHashSet::default(),
 +                indexed_indirectly: FxHashMap::default(),
 +                indexed_directly: FxHashMap::default(),
 +                referenced: FxHashSet::default(),
 +                nonindex: false,
 +                prefer_mutable: false,
 +            };
 +            walk_expr(&mut visitor, body);
 +
 +            // linting condition: we only indexed one variable, and indexed it directly
 +            if visitor.indexed_indirectly.is_empty() && visitor.indexed_directly.len() == 1 {
 +                let (indexed, (indexed_extent, indexed_ty)) = visitor
 +                    .indexed_directly
 +                    .into_iter()
 +                    .next()
 +                    .expect("already checked that we have exactly 1 element");
 +
 +                // ensure that the indexed variable was declared before the loop, see #601
 +                if let Some(indexed_extent) = indexed_extent {
 +                    let parent_def_id = cx.tcx.hir().get_parent_item(expr.hir_id);
 +                    let region_scope_tree = cx.tcx.region_scope_tree(parent_def_id);
 +                    let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id).unwrap();
 +                    if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) {
 +                        return;
 +                    }
 +                }
 +
 +                // don't lint if the container that is indexed does not have .iter() method
 +                let has_iter = has_iter_method(cx, indexed_ty);
 +                if has_iter.is_none() {
 +                    return;
 +                }
 +
 +                // don't lint if the container that is indexed into is also used without
 +                // indexing
 +                if visitor.referenced.contains(&indexed) {
 +                    return;
 +                }
 +
 +                let starts_at_zero = is_integer_const(cx, start, 0);
 +
 +                let skip = if starts_at_zero {
 +                    String::new()
 +                } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start) {
 +                    return;
 +                } else {
 +                    format!(".skip({})", snippet(cx, start.span, ".."))
 +                };
 +
 +                let mut end_is_start_plus_val = false;
 +
 +                let take = if let Some(end) = *end {
 +                    let mut take_expr = end;
 +
 +                    if let ExprKind::Binary(ref op, left, right) = end.kind {
 +                        if op.node == BinOpKind::Add {
 +                            let start_equal_left = SpanlessEq::new(cx).eq_expr(start, left);
 +                            let start_equal_right = SpanlessEq::new(cx).eq_expr(start, right);
 +
 +                            if start_equal_left {
 +                                take_expr = right;
 +                            } else if start_equal_right {
 +                                take_expr = left;
 +                            }
 +
 +                            end_is_start_plus_val = start_equal_left | start_equal_right;
 +                        }
 +                    }
 +
 +                    if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) {
 +                        String::new()
 +                    } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr) {
 +                        return;
 +                    } else {
 +                        match limits {
 +                            ast::RangeLimits::Closed => {
 +                                let take_expr = sugg::Sugg::hir(cx, take_expr, "<count>");
 +                                format!(".take({})", take_expr + sugg::ONE)
 +                            },
 +                            ast::RangeLimits::HalfOpen => {
 +                                format!(".take({})", snippet(cx, take_expr.span, ".."))
 +                            },
 +                        }
 +                    }
 +                } else {
 +                    String::new()
 +                };
 +
 +                let (ref_mut, method) = if visitor.indexed_mut.contains(&indexed) {
 +                    ("mut ", "iter_mut")
 +                } else {
 +                    ("", "iter")
 +                };
 +
 +                let take_is_empty = take.is_empty();
 +                let mut method_1 = take;
 +                let mut method_2 = skip;
 +
 +                if end_is_start_plus_val {
 +                    mem::swap(&mut method_1, &mut method_2);
 +                }
 +
 +                if visitor.nonindex {
 +                    span_lint_and_then(
 +                        cx,
 +                        NEEDLESS_RANGE_LOOP,
 +                        arg.span,
 +                        &format!("the loop variable `{}` is used to index `{}`", ident.name, indexed),
 +                        |diag| {
 +                            multispan_sugg(
 +                                diag,
 +                                "consider using an iterator",
 +                                vec![
 +                                    (pat.span, format!("({}, <item>)", ident.name)),
 +                                    (
 +                                        arg.span,
 +                                        format!("{}.{}().enumerate(){}{}", indexed, method, method_1, method_2),
 +                                    ),
 +                                ],
 +                            );
 +                        },
 +                    );
 +                } else {
 +                    let repl = if starts_at_zero && take_is_empty {
 +                        format!("&{}{}", ref_mut, indexed)
 +                    } else {
 +                        format!("{}.{}(){}{}", indexed, method, method_1, method_2)
 +                    };
 +
 +                    span_lint_and_then(
 +                        cx,
 +                        NEEDLESS_RANGE_LOOP,
 +                        arg.span,
 +                        &format!("the loop variable `{}` is only used to index `{}`", ident.name, indexed),
 +                        |diag| {
 +                            multispan_sugg(
 +                                diag,
 +                                "consider using an iterator",
 +                                vec![(pat.span, "<item>".to_string()), (arg.span, repl)],
 +                            );
 +                        },
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool {
 +    if_chain! {
 +        if let ExprKind::MethodCall(method, recv, [], _) = expr.kind;
 +        if method.ident.name == sym::len;
 +        if let ExprKind::Path(QPath::Resolved(_, path)) = recv.kind;
 +        if path.segments.len() == 1;
 +        if path.segments[0].ident.name == var;
 +        then {
 +            return true;
 +        }
 +    }
 +
 +    false
 +}
 +
 +fn is_end_eq_array_len<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    end: &Expr<'_>,
 +    limits: ast::RangeLimits,
 +    indexed_ty: Ty<'tcx>,
 +) -> bool {
 +    if_chain! {
 +        if let ExprKind::Lit(ref lit) = end.kind;
 +        if let ast::LitKind::Int(end_int, _) = lit.node;
 +        if let ty::Array(_, arr_len_const) = indexed_ty.kind();
 +        if let Some(arr_len) = arr_len_const.try_eval_usize(cx.tcx, cx.param_env);
 +        then {
 +            return match limits {
 +                ast::RangeLimits::Closed => end_int + 1 >= arr_len.into(),
 +                ast::RangeLimits::HalfOpen => end_int >= arr_len.into(),
 +            };
 +        }
 +    }
 +
 +    false
 +}
 +
 +struct VarVisitor<'a, 'tcx> {
 +    /// context reference
 +    cx: &'a LateContext<'tcx>,
 +    /// var name to look for as index
 +    var: HirId,
 +    /// indexed variables that are used mutably
 +    indexed_mut: FxHashSet<Symbol>,
 +    /// indirectly indexed variables (`v[(i + 4) % N]`), the extend is `None` for global
 +    indexed_indirectly: FxHashMap<Symbol, Option<region::Scope>>,
 +    /// subset of `indexed` of vars that are indexed directly: `v[i]`
 +    /// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]`
 +    indexed_directly: FxHashMap<Symbol, (Option<region::Scope>, Ty<'tcx>)>,
 +    /// Any names that are used outside an index operation.
 +    /// Used to detect things like `&mut vec` used together with `vec[i]`
 +    referenced: FxHashSet<Symbol>,
 +    /// has the loop variable been used in expressions other than the index of
 +    /// an index op?
 +    nonindex: bool,
 +    /// Whether we are inside the `$` in `&mut $` or `$ = foo` or `$.bar`, where bar
 +    /// takes `&mut self`
 +    prefer_mutable: bool,
 +}
 +
 +impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
 +    fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool {
 +        if_chain! {
 +            // the indexed container is referenced by a name
 +            if let ExprKind::Path(ref seqpath) = seqexpr.kind;
 +            if let QPath::Resolved(None, seqvar) = *seqpath;
 +            if seqvar.segments.len() == 1;
 +            if is_local_used(self.cx, idx, self.var);
 +            then {
 +                if self.prefer_mutable {
 +                    self.indexed_mut.insert(seqvar.segments[0].ident.name);
 +                }
 +                let index_used_directly = matches!(idx.kind, ExprKind::Path(_));
 +                let res = self.cx.qpath_res(seqpath, seqexpr.hir_id);
 +                match res {
 +                    Res::Local(hir_id) => {
 +                        let parent_def_id = self.cx.tcx.hir().get_parent_item(expr.hir_id);
 +                        let extent = self.cx
 +                            .tcx
 +                            .region_scope_tree(parent_def_id)
 +                            .var_scope(hir_id.local_id)
 +                            .unwrap();
 +                        if index_used_directly {
 +                            self.indexed_directly.insert(
 +                                seqvar.segments[0].ident.name,
 +                                (Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)),
 +                            );
 +                        } else {
 +                            self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent));
 +                        }
 +                        return false;  // no need to walk further *on the variable*
 +                    }
 +                    Res::Def(DefKind::Static (_)| DefKind::Const, ..) => {
 +                        if index_used_directly {
 +                            self.indexed_directly.insert(
 +                                seqvar.segments[0].ident.name,
 +                                (None, self.cx.typeck_results().node_type(seqexpr.hir_id)),
 +                            );
 +                        } else {
 +                            self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None);
 +                        }
 +                        return false;  // no need to walk further *on the variable*
 +                    }
 +                    _ => (),
 +                }
 +            }
 +        }
 +        true
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            // a range index op
 +            if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &expr.kind;
 +            if (meth.ident.name == sym::index && match_trait_method(self.cx, expr, &paths::INDEX))
 +                || (meth.ident.name == sym::index_mut && match_trait_method(self.cx, expr, &paths::INDEX_MUT));
 +            if !self.check(args_1, args_0, expr);
 +            then { return }
 +        }
 +
 +        if_chain! {
 +            // an index op
 +            if let ExprKind::Index(seqexpr, idx) = expr.kind;
 +            if !self.check(idx, seqexpr, expr);
 +            then { return }
 +        }
 +
 +        if_chain! {
 +            // directly using a variable
 +            if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind;
 +            if let Res::Local(local_id) = path.res;
 +            then {
 +                if local_id == self.var {
 +                    self.nonindex = true;
 +                } else {
 +                    // not the correct variable, but still a variable
 +                    self.referenced.insert(path.segments[0].ident.name);
 +                }
 +            }
 +        }
 +
 +        let old = self.prefer_mutable;
 +        match expr.kind {
 +            ExprKind::AssignOp(_, lhs, rhs) | ExprKind::Assign(lhs, rhs, _) => {
 +                self.prefer_mutable = true;
 +                self.visit_expr(lhs);
 +                self.prefer_mutable = false;
 +                self.visit_expr(rhs);
 +            },
 +            ExprKind::AddrOf(BorrowKind::Ref, mutbl, expr) => {
 +                if mutbl == Mutability::Mut {
 +                    self.prefer_mutable = true;
 +                }
 +                self.visit_expr(expr);
 +            },
 +            ExprKind::Call(f, args) => {
 +                self.visit_expr(f);
 +                for expr in args {
 +                    let ty = self.cx.typeck_results().expr_ty_adjusted(expr);
 +                    self.prefer_mutable = false;
 +                    if let ty::Ref(_, _, mutbl) = *ty.kind() {
 +                        if mutbl == Mutability::Mut {
 +                            self.prefer_mutable = true;
 +                        }
 +                    }
 +                    self.visit_expr(expr);
 +                }
 +            },
 +            ExprKind::MethodCall(_, receiver, args, _) => {
 +                let def_id = self.cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
 +                for (ty, expr) in iter::zip(
 +                    self.cx.tcx.fn_sig(def_id).inputs().skip_binder(),
 +                    std::iter::once(receiver).chain(args.iter()),
 +                ) {
 +                    self.prefer_mutable = false;
 +                    if let ty::Ref(_, _, mutbl) = *ty.kind() {
 +                        if mutbl == Mutability::Mut {
 +                            self.prefer_mutable = true;
 +                        }
 +                    }
 +                    self.visit_expr(expr);
 +                }
 +            },
 +            ExprKind::Closure(&Closure { body, .. }) => {
 +                let body = self.cx.tcx.hir().body(body);
++                self.visit_expr(body.value);
 +            },
 +            _ => walk_expr(self, expr),
 +        }
 +        self.prefer_mutable = old;
 +    }
 +}
index 2c54033f8597502c6fb8db8c276f6f91d450377c,0000000000000000000000000000000000000000..deb21894f36a9709cc1cf20815a8c660c9638cd2
mode 100644,000000..100644
--- /dev/null
@@@ -1,362 -1,0 +1,362 @@@
-         v.visit_expr(&cx.tcx.hir().body(cx.enclosing_body.unwrap()).value);
 +use super::WHILE_LET_ON_ITERATOR;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::higher;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::{
 +    get_enclosing_loop_or_multi_call_closure, is_refutable, is_trait_method, match_def_path, paths,
 +    visitors::is_res_used,
 +};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_expr, Visitor};
 +use rustc_hir::{def::Res, Closure, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp};
 +use rustc_lint::LateContext;
 +use rustc_middle::hir::nested_filter::OnlyBodies;
 +use rustc_middle::ty::adjustment::Adjust;
 +use rustc_span::{symbol::sym, Symbol};
 +
 +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +    let (scrutinee_expr, iter_expr_struct, iter_expr, some_pat, loop_expr) = if_chain! {
 +        if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr);
 +        // check for `Some(..)` pattern
 +        if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = let_pat.kind;
 +        if let Res::Def(_, pat_did) = pat_path.res;
 +        if match_def_path(cx, pat_did, &paths::OPTION_SOME);
 +        // check for call to `Iterator::next`
 +        if let ExprKind::MethodCall(method_name, iter_expr, [], _) = let_expr.kind;
 +        if method_name.ident.name == sym::next;
 +        if is_trait_method(cx, let_expr, sym::Iterator);
 +        if let Some(iter_expr_struct) = try_parse_iter_expr(cx, iter_expr);
 +        // get the loop containing the match expression
 +        if !uses_iter(cx, &iter_expr_struct, if_then);
 +        then {
 +            (let_expr, iter_expr_struct, iter_expr, some_pat, expr)
 +        } else {
 +            return;
 +        }
 +    };
 +
 +    let mut applicability = Applicability::MachineApplicable;
 +    let loop_var = if let Some(some_pat) = some_pat.first() {
 +        if is_refutable(cx, some_pat) {
 +            // Refutable patterns don't work with for loops.
 +            return;
 +        }
 +        snippet_with_applicability(cx, some_pat.span, "..", &mut applicability)
 +    } else {
 +        "_".into()
 +    };
 +
 +    // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be
 +    // borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used
 +    // afterwards a mutable borrow of a field isn't necessary.
 +    let by_ref = if cx.typeck_results().expr_ty(iter_expr).ref_mutability() == Some(Mutability::Mut)
 +        || !iter_expr_struct.can_move
 +        || !iter_expr_struct.fields.is_empty()
 +        || needs_mutable_borrow(cx, &iter_expr_struct, loop_expr)
 +    {
 +        ".by_ref()"
 +    } else {
 +        ""
 +    };
 +
 +    let iterator = snippet_with_applicability(cx, iter_expr.span, "_", &mut applicability);
 +    span_lint_and_sugg(
 +        cx,
 +        WHILE_LET_ON_ITERATOR,
 +        expr.span.with_hi(scrutinee_expr.span.hi()),
 +        "this loop could be written as a `for` loop",
 +        "try",
 +        format!("for {} in {}{}", loop_var, iterator, by_ref),
 +        applicability,
 +    );
 +}
 +
 +#[derive(Debug)]
 +struct IterExpr {
 +    /// The fields used, in order of child to parent.
 +    fields: Vec<Symbol>,
 +    /// The path being used.
 +    path: Res,
 +    /// Whether or not the iterator can be moved.
 +    can_move: bool,
 +}
 +
 +/// Parses any expression to find out which field of which variable is used. Will return `None` if
 +/// the expression might have side effects.
 +fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExpr> {
 +    let mut fields = Vec::new();
 +    let mut can_move = true;
 +    loop {
 +        if cx
 +            .typeck_results()
 +            .expr_adjustments(e)
 +            .iter()
 +            .any(|a| matches!(a.kind, Adjust::Deref(Some(..))))
 +        {
 +            // Custom deref impls need to borrow the whole value as it's captured by reference
 +            can_move = false;
 +            fields.clear();
 +        }
 +        match e.kind {
 +            ExprKind::Path(ref path) => {
 +                break Some(IterExpr {
 +                    fields,
 +                    path: cx.qpath_res(path, e.hir_id),
 +                    can_move,
 +                });
 +            },
 +            ExprKind::Field(base, name) => {
 +                fields.push(name.name);
 +                e = base;
 +            },
 +            // Dereferencing a pointer has no side effects and doesn't affect which field is being used.
 +            ExprKind::Unary(UnOp::Deref, base) if cx.typeck_results().expr_ty(base).is_ref() => e = base,
 +
 +            // Shouldn't have side effects, but there's no way to trace which field is used. So forget which fields have
 +            // already been seen.
 +            ExprKind::Index(base, idx) if !idx.can_have_side_effects() => {
 +                can_move = false;
 +                fields.clear();
 +                e = base;
 +            },
 +            ExprKind::Unary(UnOp::Deref, base) => {
 +                can_move = false;
 +                fields.clear();
 +                e = base;
 +            },
 +
 +            // No effect and doesn't affect which field is being used.
 +            ExprKind::DropTemps(base) | ExprKind::AddrOf(_, _, base) | ExprKind::Type(base, _) => e = base,
 +            _ => break None,
 +        }
 +    }
 +}
 +
 +fn is_expr_same_field(cx: &LateContext<'_>, mut e: &Expr<'_>, mut fields: &[Symbol], path_res: Res) -> bool {
 +    loop {
 +        match (&e.kind, fields) {
 +            (&ExprKind::Field(base, name), [head_field, tail_fields @ ..]) if name.name == *head_field => {
 +                e = base;
 +                fields = tail_fields;
 +            },
 +            (ExprKind::Path(path), []) => {
 +                break cx.qpath_res(path, e.hir_id) == path_res;
 +            },
 +            (&(ExprKind::DropTemps(base) | ExprKind::AddrOf(_, _, base) | ExprKind::Type(base, _)), _) => e = base,
 +            _ => break false,
 +        }
 +    }
 +}
 +
 +/// Checks if the given expression is the same field as, is a child of, or is the parent of the
 +/// given field. Used to check if the expression can be used while the given field is borrowed
 +/// mutably. e.g. if checking for `x.y`, then `x.y`, `x.y.z`, and `x` will all return true, but
 +/// `x.z`, and `y` will return false.
 +fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fields: &[Symbol], path_res: Res) -> bool {
 +    match expr.kind {
 +        ExprKind::Field(base, name) => {
 +            if let Some((head_field, tail_fields)) = fields.split_first() {
 +                if name.name == *head_field && is_expr_same_field(cx, base, tail_fields, path_res) {
 +                    return true;
 +                }
 +                // Check if the expression is a parent field
 +                let mut fields_iter = tail_fields.iter();
 +                while let Some(field) = fields_iter.next() {
 +                    if *field == name.name && is_expr_same_field(cx, base, fields_iter.as_slice(), path_res) {
 +                        return true;
 +                    }
 +                }
 +            }
 +
 +            // Check if the expression is a child field.
 +            let mut e = base;
 +            loop {
 +                match e.kind {
 +                    ExprKind::Field(..) if is_expr_same_field(cx, e, fields, path_res) => break true,
 +                    ExprKind::Field(base, _) | ExprKind::DropTemps(base) | ExprKind::Type(base, _) => e = base,
 +                    ExprKind::Path(ref path) if fields.is_empty() => {
 +                        break cx.qpath_res(path, e.hir_id) == path_res;
 +                    },
 +                    _ => break false,
 +                }
 +            }
 +        },
 +        // If the path matches, this is either an exact match, or the expression is a parent of the field.
 +        ExprKind::Path(ref path) => cx.qpath_res(path, expr.hir_id) == path_res,
 +        ExprKind::DropTemps(base) | ExprKind::Type(base, _) | ExprKind::AddrOf(_, _, base) => {
 +            is_expr_same_child_or_parent_field(cx, base, fields, path_res)
 +        },
 +        _ => false,
 +    }
 +}
 +
 +/// Strips off all field and path expressions. This will return true if a field or path has been
 +/// skipped. Used to skip them after failing to check for equality.
 +fn skip_fields_and_path<'tcx>(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool) {
 +    let mut e = expr;
 +    let e = loop {
 +        match e.kind {
 +            ExprKind::Field(base, _) | ExprKind::DropTemps(base) | ExprKind::Type(base, _) => e = base,
 +            ExprKind::Path(_) => return (None, true),
 +            _ => break e,
 +        }
 +    };
 +    (Some(e), e.hir_id != expr.hir_id)
 +}
 +
 +/// Checks if the given expression uses the iterator.
 +fn uses_iter<'tcx>(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr<'_>) -> bool {
 +    struct V<'a, 'b, 'tcx> {
 +        cx: &'a LateContext<'tcx>,
 +        iter_expr: &'b IterExpr,
 +        uses_iter: bool,
 +    }
 +    impl<'tcx> Visitor<'tcx> for V<'_, '_, 'tcx> {
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if self.uses_iter {
 +                // return
 +            } else if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) {
 +                self.uses_iter = true;
 +            } else if let (e, true) = skip_fields_and_path(e) {
 +                if let Some(e) = e {
 +                    self.visit_expr(e);
 +                }
 +            } else if let ExprKind::Closure(&Closure { body: id, .. }) = e.kind {
 +                if is_res_used(self.cx, self.iter_expr.path, id) {
 +                    self.uses_iter = true;
 +                }
 +            } else {
 +                walk_expr(self, e);
 +            }
 +        }
 +    }
 +
 +    let mut v = V {
 +        cx,
 +        iter_expr,
 +        uses_iter: false,
 +    };
 +    v.visit_expr(container);
 +    v.uses_iter
 +}
 +
 +#[expect(clippy::too_many_lines)]
 +fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: &Expr<'_>) -> bool {
 +    struct AfterLoopVisitor<'a, 'b, 'tcx> {
 +        cx: &'a LateContext<'tcx>,
 +        iter_expr: &'b IterExpr,
 +        loop_id: HirId,
 +        after_loop: bool,
 +        used_iter: bool,
 +    }
 +    impl<'tcx> Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> {
 +        type NestedFilter = OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.cx.tcx.hir()
 +        }
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if self.used_iter {
 +                return;
 +            }
 +            if self.after_loop {
 +                if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) {
 +                    self.used_iter = true;
 +                } else if let (e, true) = skip_fields_and_path(e) {
 +                    if let Some(e) = e {
 +                        self.visit_expr(e);
 +                    }
 +                } else if let ExprKind::Closure(&Closure { body: id, .. }) = e.kind {
 +                    self.used_iter = is_res_used(self.cx, self.iter_expr.path, id);
 +                } else {
 +                    walk_expr(self, e);
 +                }
 +            } else if self.loop_id == e.hir_id {
 +                self.after_loop = true;
 +            } else {
 +                walk_expr(self, e);
 +            }
 +        }
 +    }
 +
 +    struct NestedLoopVisitor<'a, 'b, 'tcx> {
 +        cx: &'a LateContext<'tcx>,
 +        iter_expr: &'b IterExpr,
 +        local_id: HirId,
 +        loop_id: HirId,
 +        after_loop: bool,
 +        found_local: bool,
 +        used_after: bool,
 +    }
 +    impl<'a, 'b, 'tcx> Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> {
 +        type NestedFilter = OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.cx.tcx.hir()
 +        }
 +
 +        fn visit_local(&mut self, l: &'tcx Local<'_>) {
 +            if !self.after_loop {
 +                l.pat.each_binding_or_first(&mut |_, id, _, _| {
 +                    if id == self.local_id {
 +                        self.found_local = true;
 +                    }
 +                });
 +            }
 +            if let Some(e) = l.init {
 +                self.visit_expr(e);
 +            }
 +        }
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if self.used_after {
 +                return;
 +            }
 +            if self.after_loop {
 +                if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) {
 +                    self.used_after = true;
 +                } else if let (e, true) = skip_fields_and_path(e) {
 +                    if let Some(e) = e {
 +                        self.visit_expr(e);
 +                    }
 +                } else if let ExprKind::Closure(&Closure { body: id, .. }) = e.kind {
 +                    self.used_after = is_res_used(self.cx, self.iter_expr.path, id);
 +                } else {
 +                    walk_expr(self, e);
 +                }
 +            } else if e.hir_id == self.loop_id {
 +                self.after_loop = true;
 +            } else {
 +                walk_expr(self, e);
 +            }
 +        }
 +    }
 +
 +    if let Some(e) = get_enclosing_loop_or_multi_call_closure(cx, loop_expr) {
 +        let local_id = match iter_expr.path {
 +            Res::Local(id) => id,
 +            _ => return true,
 +        };
 +        let mut v = NestedLoopVisitor {
 +            cx,
 +            iter_expr,
 +            local_id,
 +            loop_id: loop_expr.hir_id,
 +            after_loop: false,
 +            found_local: false,
 +            used_after: false,
 +        };
 +        v.visit_expr(e);
 +        v.used_after || !v.found_local
 +    } else {
 +        let mut v = AfterLoopVisitor {
 +            cx,
 +            iter_expr,
 +            loop_id: loop_expr.hir_id,
 +            after_loop: false,
 +            used_iter: false,
 +        };
++        v.visit_expr(cx.tcx.hir().body(cx.enclosing_body.unwrap()).value);
 +        v.used_iter
 +    }
 +}
index bc16f17b6196e420184860ce3dd731f9eeeebdbe,0000000000000000000000000000000000000000..a3aa2e4b389afe74f1e40f96da6fb5ea4eab66c9
mode 100644,000000..100644
--- /dev/null
@@@ -1,51 -1,0 +1,51 @@@
-                                 "match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable",
 +use clippy_utils::diagnostics::span_lint_and_note;
 +use clippy_utils::macros::{is_panic, root_macro_call};
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::visitors::is_local_used;
 +use clippy_utils::{is_wild, peel_blocks_with_stmt};
 +use rustc_hir::{Arm, Expr, PatKind};
 +use rustc_lint::LateContext;
 +use rustc_span::symbol::{kw, sym};
 +
 +use super::MATCH_WILD_ERR_ARM;
 +
 +pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'tcx>]) {
 +    let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs();
 +    if is_type_diagnostic_item(cx, ex_ty, sym::Result) {
 +        for arm in arms {
 +            if let PatKind::TupleStruct(ref path, inner, _) = arm.pat.kind {
 +                let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false));
 +                if path_str == "Err" {
 +                    let mut matching_wild = inner.iter().any(is_wild);
 +                    let mut ident_bind_name = kw::Underscore;
 +                    if !matching_wild {
 +                        // Looking for unused bindings (i.e.: `_e`)
 +                        for pat in inner.iter() {
 +                            if let PatKind::Binding(_, id, ident, None) = pat.kind {
 +                                if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) {
 +                                    ident_bind_name = ident.name;
 +                                    matching_wild = true;
 +                                }
 +                            }
 +                        }
 +                    }
 +                    if_chain! {
 +                        if matching_wild;
 +                        if let Some(macro_call) = root_macro_call(peel_blocks_with_stmt(arm.body).span);
 +                        if is_panic(cx, macro_call.def_id);
 +                        then {
 +                            // `Err(_)` or `Err(_e)` arm with `panic!` found
 +                            span_lint_and_note(cx,
 +                                MATCH_WILD_ERR_ARM,
 +                                arm.pat.span,
 +                                &format!("`Err({})` matches all errors", ident_bind_name),
 +                                None,
++                                "match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable",
 +                            );
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
index 1bf1c4d1078952cab5deb3f2c756cfb78d1842b4,0000000000000000000000000000000000000000..56bcdc01fe4d61092502ca6cffa46ac5c0b730fb
mode 100644,000000..100644
--- /dev/null
@@@ -1,250 -1,0 +1,246 @@@
-                 left_in.len() + {
-                     if left_pos.is_some() { 1 } else { 0 }
-                 },
-                 right_in.len() + {
-                     if right_pos.is_some() { 1 } else { 0 }
-                 },
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::{expr_block, snippet};
 +use clippy_utils::ty::{implements_trait, match_type, peel_mid_ty_refs};
 +use clippy_utils::{
 +    is_lint_allowed, is_unit_expr, is_wild, paths, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs,
 +};
 +use core::cmp::max;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, Pat, PatKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, Ty};
 +
 +use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE};
 +
 +#[rustfmt::skip]
 +pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
 +    if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
 +        if expr.span.from_expansion() {
 +            // Don't lint match expressions present in
 +            // macro_rules! block
 +            return;
 +        }
 +        if let PatKind::Or(..) = arms[0].pat.kind {
 +            // don't lint for or patterns for now, this makes
 +            // the lint noisy in unnecessary situations
 +            return;
 +        }
 +        let els = arms[1].body;
 +        let els = if is_unit_expr(peel_blocks(els)) {
 +            None
 +        } else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind {
 +            if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() {
 +                // single statement/expr "else" block, don't lint
 +                return;
 +            }
 +            // block with 2+ statements or 1 expr and 1+ statement
 +            Some(els)
 +        } else {
 +            // not a block, don't lint
 +            return;
 +        };
 +
 +        let ty = cx.typeck_results().expr_ty(ex);
 +        if *ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id) {
 +            check_single_pattern(cx, ex, arms, expr, els);
 +            check_opt_like(cx, ex, arms, expr, ty, els);
 +        }
 +    }
 +}
 +
 +fn check_single_pattern(
 +    cx: &LateContext<'_>,
 +    ex: &Expr<'_>,
 +    arms: &[Arm<'_>],
 +    expr: &Expr<'_>,
 +    els: Option<&Expr<'_>>,
 +) {
 +    if is_wild(arms[1].pat) {
 +        report_single_pattern(cx, ex, arms, expr, els);
 +    }
 +}
 +
 +fn report_single_pattern(
 +    cx: &LateContext<'_>,
 +    ex: &Expr<'_>,
 +    arms: &[Arm<'_>],
 +    expr: &Expr<'_>,
 +    els: Option<&Expr<'_>>,
 +) {
 +    let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
 +    let els_str = els.map_or(String::new(), |els| {
 +        format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span)))
 +    });
 +
 +    let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
 +    let (msg, sugg) = if_chain! {
 +        if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind;
 +        let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex));
 +        if let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait();
 +        if let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait();
 +        if ty.is_integral() || ty.is_char() || ty.is_str()
 +            || (implements_trait(cx, ty, spe_trait_id, &[])
 +                && implements_trait(cx, ty, pe_trait_id, &[ty.into()]));
 +        then {
 +            // scrutinee derives PartialEq and the pattern is a constant.
 +            let pat_ref_count = match pat.kind {
 +                // string literals are already a reference.
 +                PatKind::Lit(Expr { kind: ExprKind::Lit(lit), .. }) if lit.node.is_str() => pat_ref_count + 1,
 +                _ => pat_ref_count,
 +            };
 +            // References are only implicitly added to the pattern, so no overflow here.
 +            // e.g. will work: match &Some(_) { Some(_) => () }
 +            // will not: match Some(_) { &Some(_) => () }
 +            let ref_count_diff = ty_ref_count - pat_ref_count;
 +
 +            // Try to remove address of expressions first.
 +            let (ex, removed) = peel_n_hir_expr_refs(ex, ref_count_diff);
 +            let ref_count_diff = ref_count_diff - removed;
 +
 +            let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`";
 +            let sugg = format!(
 +                "if {} == {}{} {}{}",
 +                snippet(cx, ex.span, ".."),
 +                // PartialEq for different reference counts may not exist.
 +                "&".repeat(ref_count_diff),
 +                snippet(cx, arms[0].pat.span, ".."),
 +                expr_block(cx, arms[0].body, None, "..", Some(expr.span)),
 +                els_str,
 +            );
 +            (msg, sugg)
 +        } else {
 +            let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`";
 +            let sugg = format!(
 +                "if let {} = {} {}{}",
 +                snippet(cx, arms[0].pat.span, ".."),
 +                snippet(cx, ex.span, ".."),
 +                expr_block(cx, arms[0].body, None, "..", Some(expr.span)),
 +                els_str,
 +            );
 +            (msg, sugg)
 +        }
 +    };
 +
 +    span_lint_and_sugg(
 +        cx,
 +        lint,
 +        expr.span,
 +        msg,
 +        "try this",
 +        sugg,
 +        Applicability::HasPlaceholders,
 +    );
 +}
 +
 +fn check_opt_like<'a>(
 +    cx: &LateContext<'a>,
 +    ex: &Expr<'_>,
 +    arms: &[Arm<'_>],
 +    expr: &Expr<'_>,
 +    ty: Ty<'a>,
 +    els: Option<&Expr<'_>>,
 +) {
 +    // We don't want to lint if the second arm contains an enum which could
 +    // have more variants in the future.
 +    if form_exhaustive_matches(cx, ty, arms[0].pat, arms[1].pat) {
 +        report_single_pattern(cx, ex, arms, expr, els);
 +    }
 +}
 +
 +/// Returns `true` if all of the types in the pattern are enums which we know
 +/// won't be expanded in the future
 +fn pat_in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'a>, pat: &Pat<'_>) -> bool {
 +    let mut paths_and_types = Vec::new();
 +    collect_pat_paths(&mut paths_and_types, cx, pat, ty);
 +    paths_and_types.iter().all(|ty| in_candidate_enum(cx, *ty))
 +}
 +
 +/// Returns `true` if the given type is an enum we know won't be expanded in the future
 +fn in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'_>) -> bool {
 +    // list of candidate `Enum`s we know will never get any more members
 +    let candidates = [&paths::COW, &paths::OPTION, &paths::RESULT];
 +
 +    for candidate_ty in candidates {
 +        if match_type(cx, ty, candidate_ty) {
 +            return true;
 +        }
 +    }
 +    false
 +}
 +
 +/// Collects types from the given pattern
 +fn collect_pat_paths<'a>(acc: &mut Vec<Ty<'a>>, cx: &LateContext<'a>, pat: &Pat<'_>, ty: Ty<'a>) {
 +    match pat.kind {
 +        PatKind::Tuple(inner, _) => inner.iter().for_each(|p| {
 +            let p_ty = cx.typeck_results().pat_ty(p);
 +            collect_pat_paths(acc, cx, p, p_ty);
 +        }),
 +        PatKind::TupleStruct(..) | PatKind::Binding(BindingAnnotation::NONE, .., None) | PatKind::Path(_) => {
 +            acc.push(ty);
 +        },
 +        _ => {},
 +    }
 +}
 +
 +/// Returns true if the given arm of pattern matching contains wildcard patterns.
 +fn contains_only_wilds(pat: &Pat<'_>) -> bool {
 +    match pat.kind {
 +        PatKind::Wild => true,
 +        PatKind::Tuple(inner, _) | PatKind::TupleStruct(_, inner, ..) => inner.iter().all(contains_only_wilds),
 +        _ => false,
 +    }
 +}
 +
 +/// Returns true if the given patterns forms only exhaustive matches that don't contain enum
 +/// patterns without a wildcard.
 +fn form_exhaustive_matches<'a>(cx: &LateContext<'a>, ty: Ty<'a>, left: &Pat<'_>, right: &Pat<'_>) -> bool {
 +    match (&left.kind, &right.kind) {
 +        (PatKind::Wild, _) | (_, PatKind::Wild) => true,
 +        (PatKind::Tuple(left_in, left_pos), PatKind::Tuple(right_in, right_pos)) => {
 +            // We don't actually know the position and the presence of the `..` (dotdot) operator
 +            // in the arms, so we need to evaluate the correct offsets here in order to iterate in
 +            // both arms at the same time.
 +            let left_pos = left_pos.as_opt_usize();
 +            let right_pos = right_pos.as_opt_usize();
 +            let len = max(
++                left_in.len() + usize::from(left_pos.is_some()),
++                right_in.len() + usize::from(right_pos.is_some()),
 +            );
 +            let mut left_pos = left_pos.unwrap_or(usize::MAX);
 +            let mut right_pos = right_pos.unwrap_or(usize::MAX);
 +            let mut left_dot_space = 0;
 +            let mut right_dot_space = 0;
 +            for i in 0..len {
 +                let mut found_dotdot = false;
 +                if i == left_pos {
 +                    left_dot_space += 1;
 +                    if left_dot_space < len - left_in.len() {
 +                        left_pos += 1;
 +                    }
 +                    found_dotdot = true;
 +                }
 +                if i == right_pos {
 +                    right_dot_space += 1;
 +                    if right_dot_space < len - right_in.len() {
 +                        right_pos += 1;
 +                    }
 +                    found_dotdot = true;
 +                }
 +                if found_dotdot {
 +                    continue;
 +                }
 +                if !contains_only_wilds(&left_in[i - left_dot_space])
 +                    && !contains_only_wilds(&right_in[i - right_dot_space])
 +                {
 +                    return false;
 +                }
 +            }
 +            true
 +        },
 +        (PatKind::TupleStruct(..), PatKind::Path(_)) => pat_in_candidate_enum(cx, ty, right),
 +        (PatKind::TupleStruct(..), PatKind::TupleStruct(_, inner, _)) => {
 +            pat_in_candidate_enum(cx, ty, right) && inner.iter().all(contains_only_wilds)
 +        },
 +        _ => false,
 +    }
 +}
index 2f117e4dcc3746abd71f918d0e3efa88661a9c0f,0000000000000000000000000000000000000000..22f5635a5bccb803ddad652db33ce738039449ac
mode 100644,000000..100644
--- /dev/null
@@@ -1,190 -1,0 +1,190 @@@
-                 let closure_expr = peel_blocks(&closure_body.value);
 +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::{peel_blocks, visitors::find_all_ret_expressions};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
 +use rustc_hir::{LangItem, QPath};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::DefIdTree;
 +use rustc_span::Span;
 +
 +pub(crate) struct OptionAndThenSome;
 +
 +impl BindInsteadOfMap for OptionAndThenSome {
 +    const VARIANT_LANG_ITEM: LangItem = LangItem::OptionSome;
 +    const BAD_METHOD_NAME: &'static str = "and_then";
 +    const GOOD_METHOD_NAME: &'static str = "map";
 +}
 +
 +pub(crate) struct ResultAndThenOk;
 +
 +impl BindInsteadOfMap for ResultAndThenOk {
 +    const VARIANT_LANG_ITEM: LangItem = LangItem::ResultOk;
 +    const BAD_METHOD_NAME: &'static str = "and_then";
 +    const GOOD_METHOD_NAME: &'static str = "map";
 +}
 +
 +pub(crate) struct ResultOrElseErrInfo;
 +
 +impl BindInsteadOfMap for ResultOrElseErrInfo {
 +    const VARIANT_LANG_ITEM: LangItem = LangItem::ResultErr;
 +    const BAD_METHOD_NAME: &'static str = "or_else";
 +    const GOOD_METHOD_NAME: &'static str = "map_err";
 +}
 +
 +pub(crate) trait BindInsteadOfMap {
 +    const VARIANT_LANG_ITEM: LangItem;
 +    const BAD_METHOD_NAME: &'static str;
 +    const GOOD_METHOD_NAME: &'static str;
 +
 +    fn no_op_msg(cx: &LateContext<'_>) -> Option<String> {
 +        let variant_id = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM).ok()?;
 +        let item_id = cx.tcx.parent(variant_id);
 +        Some(format!(
 +            "using `{}.{}({})`, which is a no-op",
 +            cx.tcx.item_name(item_id),
 +            Self::BAD_METHOD_NAME,
 +            cx.tcx.item_name(variant_id),
 +        ))
 +    }
 +
 +    fn lint_msg(cx: &LateContext<'_>) -> Option<String> {
 +        let variant_id = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM).ok()?;
 +        let item_id = cx.tcx.parent(variant_id);
 +        Some(format!(
 +            "using `{}.{}(|x| {}(y))`, which is more succinctly expressed as `{}(|x| y)`",
 +            cx.tcx.item_name(item_id),
 +            Self::BAD_METHOD_NAME,
 +            cx.tcx.item_name(variant_id),
 +            Self::GOOD_METHOD_NAME
 +        ))
 +    }
 +
 +    fn lint_closure_autofixable(
 +        cx: &LateContext<'_>,
 +        expr: &hir::Expr<'_>,
 +        recv: &hir::Expr<'_>,
 +        closure_expr: &hir::Expr<'_>,
 +        closure_args_span: Span,
 +    ) -> bool {
 +        if_chain! {
 +            if let hir::ExprKind::Call(some_expr, [inner_expr]) = closure_expr.kind;
 +            if let hir::ExprKind::Path(QPath::Resolved(_, path)) = some_expr.kind;
 +            if Self::is_variant(cx, path.res);
 +            if !contains_return(inner_expr);
 +            if let Some(msg) = Self::lint_msg(cx);
 +            then {
 +                let some_inner_snip = if inner_expr.span.from_expansion() {
 +                    snippet_with_macro_callsite(cx, inner_expr.span, "_")
 +                } else {
 +                    snippet(cx, inner_expr.span, "_")
 +                };
 +
 +                let closure_args_snip = snippet(cx, closure_args_span, "..");
 +                let option_snip = snippet(cx, recv.span, "..");
 +                let note = format!("{}.{}({} {})", option_snip, Self::GOOD_METHOD_NAME, closure_args_snip, some_inner_snip);
 +                span_lint_and_sugg(
 +                    cx,
 +                    BIND_INSTEAD_OF_MAP,
 +                    expr.span,
 +                    &msg,
 +                    "try this",
 +                    note,
 +                    Applicability::MachineApplicable,
 +                );
 +                true
 +            } else {
 +                false
 +            }
 +        }
 +    }
 +
 +    fn lint_closure(cx: &LateContext<'_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) -> bool {
 +        let mut suggs = Vec::new();
 +        let can_sugg: bool = find_all_ret_expressions(cx, closure_expr, |ret_expr| {
 +            if_chain! {
 +                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);
 +                if !contains_return(arg);
 +                then {
 +                    suggs.push((ret_expr.span, arg.span.source_callsite()));
 +                    true
 +                } else {
 +                    false
 +                }
 +            }
 +        });
 +        let (span, msg) = if_chain! {
 +            if can_sugg;
 +            if let hir::ExprKind::MethodCall(segment, ..) = expr.kind;
 +            if let Some(msg) = Self::lint_msg(cx);
 +            then { (segment.ident.span, msg) } else { return false; }
 +        };
 +        span_lint_and_then(cx, BIND_INSTEAD_OF_MAP, expr.span, &msg, |diag| {
 +            multispan_sugg_with_applicability(
 +                diag,
 +                "try this",
 +                Applicability::MachineApplicable,
 +                std::iter::once((span, Self::GOOD_METHOD_NAME.into())).chain(
 +                    suggs
 +                        .into_iter()
 +                        .map(|(span1, span2)| (span1, snippet(cx, span2, "_").into())),
 +                ),
 +            );
 +        });
 +        true
 +    }
 +
 +    /// Lint use of `_.and_then(|x| Some(y))` for `Option`s
 +    fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) -> bool {
 +        if_chain! {
 +            if let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def();
 +            if let Ok(vid) = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM);
 +            if adt.did() == cx.tcx.parent(vid);
 +            then {} else { return false; }
 +        }
 +
 +        match arg.kind {
 +            hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) => {
 +                let closure_body = cx.tcx.hir().body(body);
++                let closure_expr = peel_blocks(closure_body.value);
 +
 +                if Self::lint_closure_autofixable(cx, expr, recv, closure_expr, fn_decl_span) {
 +                    true
 +                } else {
 +                    Self::lint_closure(cx, expr, closure_expr)
 +                }
 +            },
 +            // `_.and_then(Some)` case, which is no-op.
 +            hir::ExprKind::Path(QPath::Resolved(_, path)) if Self::is_variant(cx, path.res) => {
 +                if let Some(msg) = Self::no_op_msg(cx) {
 +                    span_lint_and_sugg(
 +                        cx,
 +                        BIND_INSTEAD_OF_MAP,
 +                        expr.span,
 +                        &msg,
 +                        "use the expression directly",
 +                        snippet(cx, recv.span, "..").into(),
 +                        Applicability::MachineApplicable,
 +                    );
 +                }
 +                true
 +            },
 +            _ => false,
 +        }
 +    }
 +
 +    fn is_variant(cx: &LateContext<'_>, res: Res) -> bool {
 +        if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
 +            if let Ok(variant_id) = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM) {
 +                return cx.tcx.parent(id) == variant_id;
 +            }
 +        }
 +        false
 +    }
 +}
index 51aec21527a7116e8d253911e12611ba829af055,0000000000000000000000000000000000000000..b2bc1ad5c9ed0f33cc1ce5d69af3485e58bc94fd
mode 100644,000000..100644
--- /dev/null
@@@ -1,51 -1,0 +1,51 @@@
-             let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0].0).peel_refs();
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::{method_chain_args, path_def_id};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_lint::Lint;
 +use rustc_middle::ty::{self, DefIdTree};
 +
 +/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
 +pub(super) fn check(
 +    cx: &LateContext<'_>,
 +    info: &crate::methods::BinaryExprInfo<'_>,
 +    chain_methods: &[&str],
 +    lint: &'static Lint,
 +    suggest: &str,
 +) -> bool {
 +    if_chain! {
 +        if let Some(args) = method_chain_args(info.chain, chain_methods);
 +        if let hir::ExprKind::Call(fun, [arg_char]) = info.other.kind;
 +        if let Some(id) = path_def_id(cx, fun).map(|ctor_id| cx.tcx.parent(ctor_id));
 +        if Some(id) == cx.tcx.lang_items().option_some_variant();
 +        then {
 +            let mut applicability = Applicability::MachineApplicable;
++            let self_ty = cx.typeck_results().expr_ty_adjusted(args[0].0).peel_refs();
 +
 +            if *self_ty.kind() != ty::Str {
 +                return false;
 +            }
 +
 +            span_lint_and_sugg(
 +                cx,
 +                lint,
 +                info.expr.span,
 +                &format!("you should use the `{}` method", suggest),
 +                "like this",
 +                format!("{}{}.{}({})",
 +                        if info.eq { "" } else { "!" },
 +                        snippet_with_applicability(cx, args[0].0.span, "..", &mut applicability),
 +                        suggest,
 +                        snippet_with_applicability(cx, arg_char.span, "..", &mut applicability)),
 +                applicability,
 +            );
 +
 +            return true;
 +        }
 +    }
 +
 +    false
 +}
index f5bead387d7bd170b17aec80361b8bb04ffc8560,0000000000000000000000000000000000000000..7ab6b84c2074918f39b8f0d5aa67de94d23a914c
mode 100644,000000..100644
--- /dev/null
@@@ -1,135 -1,0 +1,139 @@@
-     let arg = if method_name == sym::clone && args.is_empty() { receiver } else { return };
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::get_parent_node;
 +use clippy_utils::source::snippet_with_context;
 +use clippy_utils::sugg;
 +use clippy_utils::ty::is_copy;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BindingAnnotation, ByRef, Expr, ExprKind, MatchSource, Node, PatKind, QPath};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, adjustment::Adjust};
 +use rustc_span::symbol::{sym, Symbol};
 +
 +use super::CLONE_DOUBLE_REF;
 +use super::CLONE_ON_COPY;
 +
 +/// Checks for the `CLONE_ON_COPY` lint.
 +#[allow(clippy::too_many_lines)]
 +pub(super) fn check(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    method_name: Symbol,
 +    receiver: &Expr<'_>,
 +    args: &[Expr<'_>],
 +) {
++    let arg = if method_name == sym::clone && args.is_empty() {
++        receiver
++    } else {
++        return;
++    };
 +    if cx
 +        .typeck_results()
 +        .type_dependent_def_id(expr.hir_id)
 +        .and_then(|id| cx.tcx.trait_of_item(id))
 +        .zip(cx.tcx.lang_items().clone_trait())
 +        .map_or(true, |(x, y)| x != y)
 +    {
 +        return;
 +    }
 +    let arg_adjustments = cx.typeck_results().expr_adjustments(arg);
 +    let arg_ty = arg_adjustments
 +        .last()
 +        .map_or_else(|| cx.typeck_results().expr_ty(arg), |a| a.target);
 +
 +    let ty = cx.typeck_results().expr_ty(expr);
 +    if let ty::Ref(_, inner, _) = arg_ty.kind() {
 +        if let ty::Ref(_, innermost, _) = inner.kind() {
 +            span_lint_and_then(
 +                cx,
 +                CLONE_DOUBLE_REF,
 +                expr.span,
 +                &format!(
 +                    "using `clone` on a double-reference; \
 +                    this will copy the reference of type `{}` instead of cloning the inner type",
 +                    ty
 +                ),
 +                |diag| {
 +                    if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) {
 +                        let mut ty = innermost;
 +                        let mut n = 0;
 +                        while let ty::Ref(_, inner, _) = ty.kind() {
 +                            ty = inner;
 +                            n += 1;
 +                        }
 +                        let refs = "&".repeat(n + 1);
 +                        let derefs = "*".repeat(n);
 +                        let explicit = format!("<{}{}>::clone({})", refs, ty, snip);
 +                        diag.span_suggestion(
 +                            expr.span,
 +                            "try dereferencing it",
 +                            format!("{}({}{}).clone()", refs, derefs, snip.deref()),
 +                            Applicability::MaybeIncorrect,
 +                        );
 +                        diag.span_suggestion(
 +                            expr.span,
 +                            "or try being explicit if you are sure, that you want to clone a reference",
 +                            explicit,
 +                            Applicability::MaybeIncorrect,
 +                        );
 +                    }
 +                },
 +            );
 +            return; // don't report clone_on_copy
 +        }
 +    }
 +
 +    if is_copy(cx, ty) {
 +        let parent_is_suffix_expr = match get_parent_node(cx.tcx, expr.hir_id) {
 +            Some(Node::Expr(parent)) => match parent.kind {
 +                // &*x is a nop, &x.clone() is not
 +                ExprKind::AddrOf(..) => return,
 +                // (*x).func() is useless, x.clone().func() can work in case func borrows self
 +                ExprKind::MethodCall(_, self_arg, ..)
 +                    if expr.hir_id == self_arg.hir_id && ty != cx.typeck_results().expr_ty_adjusted(expr) =>
 +                {
 +                    return;
 +                },
 +                // ? is a Call, makes sure not to rec *x?, but rather (*x)?
 +                ExprKind::Call(hir_callee, _) => matches!(
 +                    hir_callee.kind,
 +                    ExprKind::Path(QPath::LangItem(rustc_hir::LangItem::TryTraitBranch, _, _))
 +                ),
 +                ExprKind::MethodCall(_, self_arg, ..) if expr.hir_id == self_arg.hir_id => true,
 +                ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
 +                | ExprKind::Field(..)
 +                | ExprKind::Index(..) => true,
 +                _ => false,
 +            },
 +            // local binding capturing a reference
 +            Some(Node::Local(l)) if matches!(l.pat.kind, PatKind::Binding(BindingAnnotation(ByRef::Yes, _), ..)) => {
 +                return;
 +            },
 +            _ => false,
 +        };
 +
 +        let mut app = Applicability::MachineApplicable;
 +        let snip = snippet_with_context(cx, arg.span, expr.span.ctxt(), "_", &mut app).0;
 +
 +        let deref_count = arg_adjustments
 +            .iter()
 +            .take_while(|adj| matches!(adj.kind, Adjust::Deref(_)))
 +            .count();
 +        let (help, sugg) = if deref_count == 0 {
 +            ("try removing the `clone` call", snip.into())
 +        } else if parent_is_suffix_expr {
 +            ("try dereferencing it", format!("({}{})", "*".repeat(deref_count), snip))
 +        } else {
 +            ("try dereferencing it", format!("{}{}", "*".repeat(deref_count), snip))
 +        };
 +
 +        span_lint_and_sugg(
 +            cx,
 +            CLONE_ON_COPY,
 +            expr.span,
 +            &format!("using `clone` on type `{}` which implements the `Copy` trait", ty),
 +            help,
 +            sugg,
 +            app,
 +        );
 +    }
 +}
index 9dc839afc6256e8c9b89d8d51d3e8fd1c74f9543,0000000000000000000000000000000000000000..9719b2f1c5125ff78f97934aeed8078405aa8878
mode 100644,000000..100644
--- /dev/null
@@@ -1,197 -1,0 +1,197 @@@
-             let closure_expr = peel_blocks(&body.value);
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::{indent_of, reindent_multiline, snippet};
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{is_trait_method, path_to_local_id, peel_blocks, SpanlessEq};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::def::Res;
 +use rustc_hir::{Closure, Expr, ExprKind, PatKind, PathSegment, QPath, UnOp};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::adjustment::Adjust;
 +use rustc_span::source_map::Span;
 +use rustc_span::symbol::{sym, Symbol};
 +use std::borrow::Cow;
 +
 +use super::MANUAL_FILTER_MAP;
 +use super::MANUAL_FIND_MAP;
 +use super::OPTION_FILTER_MAP;
 +
 +fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol) -> bool {
 +    match &expr.kind {
 +        hir::ExprKind::Path(QPath::TypeRelative(_, mname)) => mname.ident.name == method_name,
 +        hir::ExprKind::Path(QPath::Resolved(_, segments)) => {
 +            segments.segments.last().unwrap().ident.name == method_name
 +        },
 +        hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
 +            let body = cx.tcx.hir().body(body);
++            let closure_expr = peel_blocks(body.value);
 +            let arg_id = body.params[0].pat.hir_id;
 +            match closure_expr.kind {
 +                hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, receiver, ..) => {
 +                    if_chain! {
 +                    if ident.name == method_name;
 +                    if let hir::ExprKind::Path(path) = &receiver.kind;
 +                    if let Res::Local(ref local) = cx.qpath_res(path, receiver.hir_id);
 +                    then {
 +                        return arg_id == *local
 +                    }
 +                    }
 +                    false
 +                },
 +                _ => false,
 +            }
 +        },
 +        _ => false,
 +    }
 +}
 +
 +fn is_option_filter_map<'tcx>(cx: &LateContext<'tcx>, filter_arg: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) -> bool {
 +    is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_some))
 +}
 +
 +/// is `filter(|x| x.is_some()).map(|x| x.unwrap())`
 +fn is_filter_some_map_unwrap(
 +    cx: &LateContext<'_>,
 +    expr: &hir::Expr<'_>,
 +    filter_recv: &hir::Expr<'_>,
 +    filter_arg: &hir::Expr<'_>,
 +    map_arg: &hir::Expr<'_>,
 +) -> bool {
 +    let iterator = is_trait_method(cx, expr, sym::Iterator);
 +    let option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(filter_recv), sym::Option);
 +
 +    (iterator || option) && is_option_filter_map(cx, filter_arg, map_arg)
 +}
 +
 +/// lint use of `filter().map()` or `find().map()` for `Iterators`
 +#[allow(clippy::too_many_arguments)]
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &hir::Expr<'_>,
 +    filter_recv: &hir::Expr<'_>,
 +    filter_arg: &hir::Expr<'_>,
 +    filter_span: Span,
 +    map_recv: &hir::Expr<'_>,
 +    map_arg: &hir::Expr<'_>,
 +    map_span: Span,
 +    is_find: bool,
 +) {
 +    if is_filter_some_map_unwrap(cx, expr, filter_recv, filter_arg, map_arg) {
 +        span_lint_and_sugg(
 +            cx,
 +            OPTION_FILTER_MAP,
 +            filter_span.with_hi(expr.span.hi()),
 +            "`filter` for `Some` followed by `unwrap`",
 +            "consider using `flatten` instead",
 +            reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, map_span)).into_owned(),
 +            Applicability::MachineApplicable,
 +        );
 +
 +        return;
 +    }
 +
 +    if_chain! {
 +            if is_trait_method(cx, map_recv, sym::Iterator);
 +
 +            // filter(|x| ...is_some())...
 +            if let ExprKind::Closure(&Closure { body: filter_body_id, .. }) = filter_arg.kind;
 +            let filter_body = cx.tcx.hir().body(filter_body_id);
 +            if let [filter_param] = filter_body.params;
 +            // optional ref pattern: `filter(|&x| ..)`
 +            let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind {
 +                (ref_pat, true)
 +            } else {
 +                (filter_param.pat, false)
 +            };
 +            // closure ends with is_some() or is_ok()
 +            if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind;
 +            if let ExprKind::MethodCall(path, filter_arg, [], _) = filter_body.value.kind;
 +            if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).peel_refs().ty_adt_def();
 +            if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::Option, opt_ty.did()) {
 +                Some(false)
 +            } else if cx.tcx.is_diagnostic_item(sym::Result, opt_ty.did()) {
 +                Some(true)
 +            } else {
 +                None
 +            };
 +            if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" };
 +
 +            // ...map(|x| ...unwrap())
 +            if let ExprKind::Closure(&Closure { body: map_body_id, .. }) = map_arg.kind;
 +            let map_body = cx.tcx.hir().body(map_body_id);
 +            if let [map_param] = map_body.params;
 +            if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind;
 +            // closure ends with expect() or unwrap()
 +            if let ExprKind::MethodCall(seg, map_arg, ..) = map_body.value.kind;
 +            if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or);
 +
 +            // .filter(..).map(|y| f(y).copied().unwrap())
 +            //                     ~~~~
 +            let map_arg_peeled = match map_arg.kind {
 +                ExprKind::MethodCall(method, original_arg, [], _) if acceptable_methods(method) => {
 +                    original_arg
 +                },
 +                _ => map_arg,
 +            };
 +
 +            // .filter(|x| x.is_some()).map(|y| y[.acceptable_method()].unwrap())
 +            let simple_equal = path_to_local_id(filter_arg, filter_param_id)
 +                && path_to_local_id(map_arg_peeled, map_param_id);
 +
 +            let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
 +                // in `filter(|x| ..)`, replace `*x` with `x`
 +                let a_path = if_chain! {
 +                    if !is_filter_param_ref;
 +                    if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind;
 +                    then { expr_path } else { a }
 +                };
 +                // let the filter closure arg and the map closure arg be equal
 +                path_to_local_id(a_path, filter_param_id)
 +                    && path_to_local_id(b, map_param_id)
 +                    && cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b)
 +            };
 +
 +            if simple_equal || SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg_peeled);
 +            then {
 +                let span = filter_span.with_hi(expr.span.hi());
 +                let (filter_name, lint) = if is_find {
 +                    ("find", MANUAL_FIND_MAP)
 +                } else {
 +                    ("filter", MANUAL_FILTER_MAP)
 +                };
 +                let msg = format!("`{filter_name}(..).map(..)` can be simplified as `{filter_name}_map(..)`");
 +                let (to_opt, deref) = if is_result {
 +                    (".ok()", String::new())
 +                } else {
 +                    let derefs = cx.typeck_results()
 +                        .expr_adjustments(map_arg)
 +                        .iter()
 +                        .filter(|adj| matches!(adj.kind, Adjust::Deref(_)))
 +                        .count();
 +
 +                    ("", "*".repeat(derefs))
 +                };
 +                let sugg = format!(
 +                    "{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})",
 +                    snippet(cx, map_arg.span, ".."),
 +                );
 +                span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable);
 +            }
 +    }
 +}
 +
 +fn acceptable_methods(method: &PathSegment<'_>) -> bool {
 +    let methods: [Symbol; 8] = [
 +        sym::clone,
 +        sym::as_ref,
 +        sym!(copied),
 +        sym!(cloned),
 +        sym!(as_deref),
 +        sym!(as_mut),
 +        sym!(as_deref_mut),
 +        sym!(to_owned),
 +    ];
 +
 +    methods.contains(&method.ident.name)
 +}
index e8442091fd3067fd3717c397856770c6055d17de,0000000000000000000000000000000000000000..8261ef5e1ce345819266a83a37f3905a5b5f47f8
mode 100644,000000..100644
--- /dev/null
@@@ -1,122 -1,0 +1,122 @@@
-             let closure_expr = peel_blocks(&closure_body.value);
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
 +use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs, peel_blocks};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_middle::mir::Mutability;
 +use rustc_middle::ty;
 +use rustc_middle::ty::adjustment::Adjust;
 +use rustc_semver::RustcVersion;
 +use rustc_span::symbol::Ident;
 +use rustc_span::{sym, Span};
 +
 +use super::MAP_CLONE;
 +
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'_>,
 +    e: &hir::Expr<'_>,
 +    recv: &hir::Expr<'_>,
 +    arg: &'tcx hir::Expr<'_>,
 +    msrv: Option<RustcVersion>,
 +) {
 +    if_chain! {
 +        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id);
 +        if cx.tcx.impl_of_method(method_id)
 +            .map_or(false, |id| is_type_diagnostic_item(cx, cx.tcx.type_of(id), sym::Option))
 +            || is_diag_trait_item(cx, method_id, sym::Iterator);
 +        if let hir::ExprKind::Closure(&hir::Closure{ body, .. }) = arg.kind;
 +        then {
 +            let closure_body = cx.tcx.hir().body(body);
++            let closure_expr = peel_blocks(closure_body.value);
 +            match closure_body.params[0].pat.kind {
 +                hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
 +                    hir::BindingAnnotation::NONE, .., name, None
 +                ) = inner.kind {
 +                    if ident_eq(name, closure_expr) {
 +                        lint_explicit_closure(cx, e.span, recv.span, true, msrv);
 +                    }
 +                },
 +                hir::PatKind::Binding(hir::BindingAnnotation::NONE, .., name, None) => {
 +                    match closure_expr.kind {
 +                        hir::ExprKind::Unary(hir::UnOp::Deref, inner) => {
 +                            if ident_eq(name, inner) {
 +                                if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() {
 +                                    lint_explicit_closure(cx, e.span, recv.span, true, msrv);
 +                                }
 +                            }
 +                        },
 +                        hir::ExprKind::MethodCall(method, obj, [], _) => if_chain! {
 +                            if ident_eq(name, obj) && method.ident.name == sym::clone;
 +                            if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id);
 +                            if let Some(trait_id) = cx.tcx.trait_of_item(fn_id);
 +                            if cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id);
 +                            // no autoderefs
 +                            if !cx.typeck_results().expr_adjustments(obj).iter()
 +                                .any(|a| matches!(a.kind, Adjust::Deref(Some(..))));
 +                            then {
 +                                let obj_ty = cx.typeck_results().expr_ty(obj);
 +                                if let ty::Ref(_, ty, mutability) = obj_ty.kind() {
 +                                    if matches!(mutability, Mutability::Not) {
 +                                        let copy = is_copy(cx, *ty);
 +                                        lint_explicit_closure(cx, e.span, recv.span, copy, msrv);
 +                                    }
 +                                } else {
 +                                    lint_needless_cloning(cx, e.span, recv.span);
 +                                }
 +                            }
 +                        },
 +                        _ => {},
 +                    }
 +                },
 +                _ => {},
 +            }
 +        }
 +    }
 +}
 +
 +fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool {
 +    if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind {
 +        path.segments.len() == 1 && path.segments[0].ident == name
 +    } else {
 +        false
 +    }
 +}
 +
 +fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
 +    span_lint_and_sugg(
 +        cx,
 +        MAP_CLONE,
 +        root.trim_start(receiver).unwrap(),
 +        "you are needlessly cloning iterator elements",
 +        "remove the `map` call",
 +        String::new(),
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
 +fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: Option<RustcVersion>) {
 +    let mut applicability = Applicability::MachineApplicable;
 +
 +    let (message, sugg_method) = if is_copy && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
 +        ("you are using an explicit closure for copying elements", "copied")
 +    } else {
 +        ("you are using an explicit closure for cloning elements", "cloned")
 +    };
 +
 +    span_lint_and_sugg(
 +        cx,
 +        MAP_CLONE,
 +        replace,
 +        message,
 +        &format!("consider calling the dedicated `{}` method", sugg_method),
 +        format!(
 +            "{}.{}()",
 +            snippet_with_applicability(cx, root, "..", &mut applicability),
 +            sugg_method,
 +        ),
 +        applicability,
 +    );
 +}
index 48a9d6e7c329e806ec571ebde331b7e0dbc63c0d,0000000000000000000000000000000000000000..41942b20ea163f34e0e60d47b489b19870b145aa
mode 100644,000000..100644
--- /dev/null
@@@ -1,3890 -1,0 +1,3891 @@@
-     /// etc., and suggests to use `or_else`, `unwrap_or_else`, etc., or
-     /// `unwrap_or_default` instead.
 +mod bind_instead_of_map;
 +mod bytecount;
 +mod bytes_count_to_len;
 +mod bytes_nth;
 +mod case_sensitive_file_extension_comparisons;
 +mod chars_cmp;
 +mod chars_cmp_with_unwrap;
 +mod chars_last_cmp;
 +mod chars_last_cmp_with_unwrap;
 +mod chars_next_cmp;
 +mod chars_next_cmp_with_unwrap;
 +mod clone_on_copy;
 +mod clone_on_ref_ptr;
 +mod cloned_instead_of_copied;
 +mod collapsible_str_replace;
 +mod err_expect;
 +mod expect_fun_call;
 +mod expect_used;
 +mod extend_with_drain;
 +mod filetype_is_file;
 +mod filter_map;
 +mod filter_map_identity;
 +mod filter_map_next;
 +mod filter_next;
 +mod flat_map_identity;
 +mod flat_map_option;
 +mod from_iter_instead_of_collect;
 +mod get_first;
 +mod get_last_with_len;
 +mod get_unwrap;
 +mod implicit_clone;
 +mod inefficient_to_string;
 +mod inspect_for_each;
 +mod into_iter_on_ref;
 +mod is_digit_ascii_radix;
 +mod iter_cloned_collect;
 +mod iter_count;
 +mod iter_next_slice;
 +mod iter_nth;
 +mod iter_nth_zero;
 +mod iter_on_single_or_empty_collections;
 +mod iter_overeager_cloned;
 +mod iter_skip_next;
 +mod iter_with_drain;
 +mod iterator_step_by_zero;
 +mod manual_ok_or;
 +mod manual_saturating_arithmetic;
 +mod manual_str_repeat;
 +mod map_clone;
 +mod map_collect_result_unit;
 +mod map_err_ignore;
 +mod map_flatten;
 +mod map_identity;
 +mod map_unwrap_or;
 +mod mut_mutex_lock;
 +mod needless_option_as_deref;
 +mod needless_option_take;
 +mod no_effect_replace;
 +mod obfuscated_if_else;
 +mod ok_expect;
 +mod open_options;
 +mod option_as_ref_deref;
 +mod option_map_or_none;
 +mod option_map_unwrap_or;
 +mod or_fun_call;
 +mod or_then_unwrap;
 +mod path_buf_push_overwrite;
 +mod range_zip_with_len;
 +mod repeat_once;
 +mod search_is_some;
 +mod single_char_add_str;
 +mod single_char_insert_string;
 +mod single_char_pattern;
 +mod single_char_push_string;
 +mod skip_while_next;
 +mod stable_sort_primitive;
 +mod str_splitn;
 +mod string_extend_chars;
 +mod suspicious_map;
 +mod suspicious_splitn;
 +mod suspicious_to_owned;
 +mod uninit_assumed_init;
 +mod unit_hash;
 +mod unnecessary_filter_map;
 +mod unnecessary_fold;
 +mod unnecessary_iter_cloned;
 +mod unnecessary_join;
 +mod unnecessary_lazy_eval;
 +mod unnecessary_sort_by;
 +mod unnecessary_to_owned;
 +mod unwrap_or_else_default;
 +mod unwrap_used;
 +mod useless_asref;
 +mod utils;
 +mod vec_resize_to_zero;
 +mod verbose_file_reads;
 +mod wrong_self_convention;
 +mod zst_offset;
 +
 +use bind_instead_of_map::BindInsteadOfMap;
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 +use clippy_utils::ty::{contains_adt_constructor, implements_trait, is_copy, is_type_diagnostic_item};
 +use clippy_utils::{
 +    contains_return, get_trait_def_id, is_trait_method, iter_input_pats, meets_msrv, msrvs, paths, return_ty,
 +};
 +use if_chain::if_chain;
 +use rustc_hir as hir;
 +use rustc_hir::def::Res;
 +use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::{self, TraitRef, Ty};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{sym, Span};
 +use rustc_typeck::hir_ty_to_ty;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `cloned()` on an `Iterator` or `Option` where
 +    /// `copied()` could be used instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// `copied()` is better because it guarantees that the type being cloned
 +    /// implements `Copy`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// [1, 2, 3].iter().cloned();
 +    /// ```
 +    /// Use instead:
 +    /// ```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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for consecutive calls to `str::replace` (2 or more)
 +    /// that can be collapsed into a single call.
 +    ///
 +    /// ### Why is this bad?
 +    /// Consecutive `str::replace` calls scan the string multiple times
 +    /// with repetitive code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let hello = "hesuo worpd"
 +    ///     .replace('s', "l")
 +    ///     .replace("u", "l")
 +    ///     .replace('p', "l");
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let hello = "hesuo worpd".replace(&['s', 'u', 'p'], "l");
 +    /// ```
 +    #[clippy::version = "1.64.0"]
 +    pub COLLAPSIBLE_STR_REPLACE,
 +    perf,
 +    "collapse consecutive calls to str::replace (2 or more) into a single call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.cloned().<func>()` where call to `.cloned()` can be postponed.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's often inefficient to clone all elements of an iterator, when eventually, only some
 +    /// of them will be consumed.
 +    ///
 +    /// ### Known Problems
 +    /// This `lint` removes the side of effect of cloning items in the iterator.
 +    /// A code that relies on that side-effect could fail.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let vec = vec!["string".to_string()];
 +    /// vec.iter().cloned().take(10);
 +    /// vec.iter().cloned().last();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let vec = vec!["string".to_string()];
 +    /// vec.iter().take(10).cloned();
 +    /// vec.iter().last().cloned();
 +    /// ```
 +    #[clippy::version = "1.60.0"]
 +    pub ITER_OVEREAGER_CLONED,
 +    perf,
 +    "using `cloned()` early with `Iterator::iter()` can lead to some performance inefficiencies"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `Iterator::flat_map()` where `filter_map()` could be
 +    /// used instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// When applicable, `filter_map()` is more clear since it shows that
 +    /// `Option` is used to produce 0 or 1 items.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect();
 +    /// ```
 +    /// Use instead:
 +    /// ```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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.unwrap()` or `.unwrap_err()` calls on `Result`s and `.unwrap()` call on `Option`s.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is better to handle the `None` or `Err` case,
 +    /// or at least call `.expect(_)` with a more helpful message. Still, for a lot of
 +    /// quick-and-dirty code, `unwrap` is a good choice, which is why this lint is
 +    /// `Allow` by default.
 +    ///
 +    /// `result.unwrap()` will let the thread panic on `Err` values.
 +    /// Normally, you want to implement more sophisticated error handling,
 +    /// and propagate errors upwards with `?` operator.
 +    ///
 +    /// Even if you want to panic on errors, not all `Error`s implement good
 +    /// messages on display. Therefore, it may be beneficial to look at the places
 +    /// where they may get displayed. Activate this lint to do just that.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// option.unwrap();
 +    /// result.unwrap();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// option.expect("more helpful message");
 +    /// result.expect("more helpful message");
 +    /// ```
 +    ///
 +    /// If [expect_used](#expect_used) is enabled, instead:
 +    /// ```rust,ignore
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// option?;
 +    ///
 +    /// // or
 +    ///
 +    /// result?;
 +    /// ```
 +    #[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()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.expect()` or `.expect_err()` calls on `Result`s and `.expect()` call on `Option`s.
 +    ///
 +    /// ### Why is this bad?
 +    /// Usually it is better to handle the `None` or `Err` case.
 +    /// Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why
 +    /// this lint is `Allow` by default.
 +    ///
 +    /// `result.expect()` will let the thread panic on `Err`
 +    /// values. Normally, you want to implement more sophisticated error handling,
 +    /// and propagate errors upwards with `?` operator.
 +    ///
 +    /// ### Examples
 +    /// ```rust,ignore
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// option.expect("one");
 +    /// result.expect("one");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// option?;
 +    ///
 +    /// // or
 +    ///
 +    /// result?;
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub EXPECT_USED,
 +    restriction,
 +    "using `.expect()` on `Result` or `Option`, which might be better handled"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for methods that should live in a trait
 +    /// implementation of a `std` trait (see [llogiq's blog
 +    /// post](http://llogiq.github.io/2015/07/30/traits.html) for further
 +    /// information) instead of an inherent implementation.
 +    ///
 +    /// ### Why is this bad?
 +    /// Implementing the traits improve ergonomics for users of
 +    /// the code, often with very little cost. Also people seeing a `mul(...)`
 +    /// method
 +    /// may expect `*` to work equally, so you should have good reason to disappoint
 +    /// them.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct X;
 +    /// impl X {
 +    ///     fn add(&self, other: &X) -> X {
 +    ///         // ..
 +    /// # X
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SHOULD_IMPLEMENT_TRAIT,
 +    style,
 +    "defining a method that should be implementing a std trait"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for methods with certain name prefixes and which
 +    /// doesn't match how self is taken. The actual rules are:
 +    ///
 +    /// |Prefix |Postfix     |`self` taken                   | `self` type  |
 +    /// |-------|------------|-------------------------------|--------------|
 +    /// |`as_`  | none       |`&self` or `&mut self`         | any          |
 +    /// |`from_`| none       | none                          | any          |
 +    /// |`into_`| none       |`self`                         | any          |
 +    /// |`is_`  | none       |`&mut self` or `&self` or none | any          |
 +    /// |`to_`  | `_mut`     |`&mut self`                    | any          |
 +    /// |`to_`  | not `_mut` |`self`                         | `Copy`       |
 +    /// |`to_`  | not `_mut` |`&self`                        | not `Copy`   |
 +    ///
 +    /// Note: Clippy doesn't trigger methods with `to_` prefix in:
 +    /// - Traits definition.
 +    /// Clippy can not tell if a type that implements a trait is `Copy` or not.
 +    /// - Traits implementation, when `&self` is taken.
 +    /// The method signature is controlled by the trait and often `&self` is required for all types that implement the trait
 +    /// (see e.g. the `std::string::ToString` trait).
 +    ///
 +    /// Clippy allows `Pin<&Self>` and `Pin<&mut Self>` if `&self` and `&mut self` is required.
 +    ///
 +    /// Please find more info here:
 +    /// https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv
 +    ///
 +    /// ### Why is this bad?
 +    /// Consistency breeds readability. If you follow the
 +    /// conventions, your users won't be surprised that they, e.g., need to supply a
 +    /// mutable reference to a `as_..` function.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # struct X;
 +    /// impl X {
 +    ///     fn as_str(self) -> &'static str {
 +    ///         // ..
 +    /// # ""
 +    ///     }
 +    /// }
 +    /// ```
 +    #[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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `ok().expect(..)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Because you usually call `expect()` on the `Result`
 +    /// directly to get a better error message.
 +    ///
 +    /// ### Known problems
 +    /// The error type needs to implement `Debug`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = Ok::<_, ()>(());
 +    /// x.ok().expect("why did I do this again?");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x = Ok::<_, ()>(());
 +    /// 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.err().expect()` calls on the `Result` type.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.expect_err()` can be called directly to avoid the extra type conversion from `err()`.
 +    ///
 +    /// ### Example
 +    /// ```should_panic
 +    /// let x: Result<u32, &str> = Ok(10);
 +    /// x.err().expect("Testing err().expect()");
 +    /// ```
 +    /// Use instead:
 +    /// ```should_panic
 +    /// let x: Result<u32, &str> = Ok(10);
 +    /// x.expect_err("Testing expect_err");
 +    /// ```
 +    #[clippy::version = "1.62.0"]
 +    pub ERR_EXPECT,
 +    style,
 +    r#"using `.err().expect("")` when `.expect_err("")` can be used"#
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `_.unwrap_or_else(Default::default)` on `Option` and
 +    /// `Result` values.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, these can be written as `_.unwrap_or_default`, which is
 +    /// simpler and more concise.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let x = Some(1);
 +    /// x.unwrap_or_else(Default::default);
 +    /// x.unwrap_or_else(u32::default);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x = Some(1);
 +    /// 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()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or
 +    /// `result.map(_).unwrap_or_else(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, these can be written more concisely (resp.) as
 +    /// `option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`.
 +    ///
 +    /// ### Known problems
 +    /// The order of the arguments is not in execution order
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// # fn some_function(foo: ()) -> usize { 1 }
 +    /// option.map(|a| a + 1).unwrap_or(0);
 +    /// result.map(|a| a + 1).unwrap_or_else(some_function);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// # fn some_function(foo: ()) -> usize { 1 }
 +    /// option.map_or(0, |a| a + 1);
 +    /// result.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)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map_or(None, _)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.and_then(_)`.
 +    ///
 +    /// ### Known problems
 +    /// The order of the arguments is not in execution order.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let opt = Some(1);
 +    /// opt.map_or(None, |a| Some(a + 1));
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let opt = Some(1);
 +    /// 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)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map_or(None, Some)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.ok()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let r: Result<u32, &str> = Ok(1);
 +    /// assert_eq!(Some(1), r.map_or(None, Some));
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # 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()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or
 +    /// `_.or_else(|x| Err(y))`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.map(|x| y)` or `_.map_err(|x| y)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn opt() -> Option<&'static str> { Some("42") }
 +    /// # fn res() -> Result<&'static str, &'static str> { Ok("42") }
 +    /// let _ = opt().and_then(|s| Some(s.len()));
 +    /// let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) });
 +    /// let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) });
 +    /// ```
 +    ///
 +    /// The correct use would be:
 +    ///
 +    /// ```rust
 +    /// # fn opt() -> Option<&'static str> { Some("42") }
 +    /// # fn res() -> Result<&'static str, &'static str> { Ok("42") }
 +    /// let _ = opt().map(|s| s.len());
 +    /// 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)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.filter(_).next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.find(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().filter(|x| **x == 0).next();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # 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)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.skip_while(condition).next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.find(!condition)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().skip_while(|x| **x == 0).next();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # 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)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map(_).flatten(_)` on `Iterator` and `Option`
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.flat_map(_)` for `Iterator` or `_.and_then(_)` for `Option`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let vec = vec![vec![1]];
 +    /// let opt = Some(5);
 +    ///
 +    /// vec.iter().map(|x| x.iter()).flatten();
 +    /// opt.map(|x| Some(x * 2)).flatten();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let vec = vec![vec![1]];
 +    /// # let opt = Some(5);
 +    /// vec.iter().flat_map(|x| x.iter());
 +    /// opt.and_then(|x| Some(x * 2));
 +    /// ```
 +    #[clippy::version = "1.31.0"]
 +    pub MAP_FLATTEN,
 +    complexity,
 +    "using combinations of `flatten` and `map` which can usually be written as a single method call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.filter(_).map(_)` that can be written more simply
 +    /// as `filter_map(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code in the `filter` and `map` operations is poor style and
 +    /// less performant.
 +    ///
 +     /// ### Example
 +    /// ```rust
 +    /// # #![allow(unused)]
 +    /// (0_i32..10)
 +    ///     .filter(|n| n.checked_add(1).is_some())
 +    ///     .map(|n| n.checked_add(1).unwrap());
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # #[allow(unused)]
 +    /// (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(_)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.find(_).map(_)` that can be written more simply
 +    /// as `find_map(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code in the `find` and `map` operations is poor style and
 +    /// less performant.
 +    ///
 +     /// ### Example
 +    /// ```rust
 +    /// (0_i32..10)
 +    ///     .find(|n| n.checked_add(1).is_some())
 +    ///     .map(|n| n.checked_add(1).unwrap());
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```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(_)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.filter_map(_).next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.find_map(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    ///  (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next();
 +    /// ```
 +    /// Can be written as
 +    ///
 +    /// ```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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `flat_map(|x| x)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely by using `flatten`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let iter = vec![vec![0]].into_iter();
 +    /// iter.flat_map(|x| x);
 +    /// ```
 +    /// Can be written as
 +    /// ```rust
 +    /// # 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for an iterator or string search (such as `find()`,
 +    /// `position()`, or `rposition()`) followed by a call to `is_some()` or `is_none()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as:
 +    /// * `_.any(_)`, or `_.contains(_)` for `is_some()`,
 +    /// * `!_.any(_)`, or `!_.contains(_)` for `is_none()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # #![allow(unused)]
 +    /// let vec = vec![1];
 +    /// vec.iter().find(|x| **x == 0).is_some();
 +    ///
 +    /// "hello world".find("world").is_none();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let vec = vec![1];
 +    /// vec.iter().any(|x| *x == 0);
 +    ///
 +    /// # #[allow(unused)]
 +    /// !"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()`)"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.chars().next()` on a `str` to check
 +    /// if it starts with a given char.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.starts_with(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let name = "foo";
 +    /// if name.chars().next() == Some('_') {};
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`,
++    /// `.or_insert(foo(..))` etc., and suggests to use `.or_else(|| foo(..))`,
++    /// `.unwrap_or_else(|| foo(..))`, `.unwrap_or_default()` or `.or_default()`
++    /// etc. instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// The function will always be called and potentially
 +    /// allocate an object acting as the default.
 +    ///
 +    /// ### Known problems
 +    /// If the function has side-effects, not calling it will
 +    /// change the semantic of the program, but you shouldn't rely on that anyway.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// foo.unwrap_or(String::new());
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// foo.unwrap_or_else(String::new);
 +    ///
 +    /// // or
 +    ///
 +    /// # 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`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.or(…).unwrap()` calls to Options and Results.
 +    ///
 +    /// ### Why is this bad?
 +    /// You should use `.unwrap_or(…)` instead for clarity.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let fallback = "fallback";
 +    /// // Result
 +    /// # type Error = &'static str;
 +    /// # let result: Result<&str, Error> = Err("error");
 +    /// let value = result.or::<Error>(Ok(fallback)).unwrap();
 +    ///
 +    /// // Option
 +    /// # let option: Option<&str> = None;
 +    /// let value = option.or(Some(fallback)).unwrap();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # let fallback = "fallback";
 +    /// // Result
 +    /// # let result: Result<&str, &str> = Err("error");
 +    /// let value = result.unwrap_or(fallback);
 +    ///
 +    /// // Option
 +    /// # let option: Option<&str> = None;
 +    /// let value = option.unwrap_or(fallback);
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub OR_THEN_UNWRAP,
 +    complexity,
 +    "checks for `.or(…).unwrap()` calls to Options and Results."
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`,
 +    /// etc., and suggests to use `unwrap_or_else` instead
 +    ///
 +    /// ### Why is this bad?
 +    /// The function will always be called.
 +    ///
 +    /// ### Known problems
 +    /// If the function has side-effects, not calling it will
 +    /// change the semantics of the program, but you shouldn't rely on that anyway.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// # let err_code = "418";
 +    /// # let err_msg = "I'm a teapot";
 +    /// foo.expect(&format!("Err {}: {}", err_code, err_msg));
 +    ///
 +    /// // or
 +    ///
 +    /// # let foo = Some(String::new());
 +    /// foo.expect(format!("Err {}: {}", err_code, err_msg).as_str());
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// # let err_code = "418";
 +    /// # 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.clone()` on a `Copy` type.
 +    ///
 +    /// ### Why is this bad?
 +    /// The only reason `Copy` types implement `Clone` is for
 +    /// generics, not for using the `clone` method on a concrete type.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// 42u64.clone();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CLONE_ON_COPY,
 +    complexity,
 +    "using `clone` on a `Copy` type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.clone()` on a ref-counted pointer,
 +    /// (`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified
 +    /// function syntax instead (e.g., `Rc::clone(foo)`).
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling '.clone()' on an Rc, Arc, or Weak
 +    /// can obscure the fact that only the pointer is being cloned, not the underlying
 +    /// data.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::rc::Rc;
 +    /// let x = Rc::new(1);
 +    ///
 +    /// x.clone();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::rc::Rc;
 +    /// # let x = Rc::new(1);
 +    /// Rc::clone(&x);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CLONE_ON_REF_PTR,
 +    restriction,
 +    "using 'clone' on a ref-counted pointer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.clone()` on an `&&T`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Cloning an `&&T` copies the inner `&T`, instead of
 +    /// cloning the underlying `T`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn main() {
 +    ///     let x = vec![1];
 +    ///     let y = &&x;
 +    ///     let z = y.clone();
 +    ///     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`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.to_string()` on an `&&T` where
 +    /// `T` implements `ToString` directly (like `&&str` or `&&String`).
 +    ///
 +    /// ### Why is this bad?
 +    /// This bypasses the specialized implementation of
 +    /// `ToString` and instead goes through the more expensive string formatting
 +    /// facilities.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Generic implementation for `T: Display` is used (slow)
 +    /// ["foo", "bar"].iter().map(|s| s.to_string());
 +    ///
 +    /// // 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`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `new` not returning a type that contains `Self`.
 +    ///
 +    /// ### Why is this bad?
 +    /// As a convention, `new` methods are used to make a new
 +    /// instance of a type.
 +    ///
 +    /// ### Example
 +    /// In an impl block:
 +    /// ```rust
 +    /// # struct Foo;
 +    /// # struct NotAFoo;
 +    /// impl Foo {
 +    ///     fn new() -> NotAFoo {
 +    /// # NotAFoo
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// # struct Foo;
 +    /// struct Bar(Foo);
 +    /// impl Foo {
 +    ///     // Bad. The type name must contain `Self`
 +    ///     fn new() -> Bar {
 +    /// # Bar(Foo)
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// # struct Foo;
 +    /// # struct FooError;
 +    /// impl Foo {
 +    ///     // Good. Return type contains `Self`
 +    ///     fn new() -> Result<Foo, FooError> {
 +    /// # Ok(Foo)
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Or in a trait definition:
 +    /// ```rust
 +    /// pub trait Trait {
 +    ///     // Bad. The type name must contain `Self`
 +    ///     fn new();
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// pub trait Trait {
 +    ///     // Good. Return type contains `Self`
 +    ///     fn new() -> Self;
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEW_RET_NO_SELF,
 +    style,
 +    "not returning type containing `Self` in a `new` method"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for string methods that receive a single-character
 +    /// `str` as an argument, e.g., `_.split("x")`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Performing these methods using a `char` is faster than
 +    /// using a `str`.
 +    ///
 +    /// ### Known problems
 +    /// Does not catch multi-byte unicode characters.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// _.split("x");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// _.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\")`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calling `.step_by(0)` on iterators which panics.
 +    ///
 +    /// ### Why is this bad?
 +    /// This very much looks like an oversight. Use `panic!()` instead if you
 +    /// actually intend to panic.
 +    ///
 +    /// ### Example
 +    /// ```rust,should_panic
 +    /// for x in (0..100).step_by(0) {
 +    ///     //..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ITERATOR_STEP_BY_ZERO,
 +    correctness,
 +    "using `Iterator::step_by(0)`, which will panic at runtime"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for indirect collection of populated `Option`
 +    ///
 +    /// ### Why is this bad?
 +    /// `Option` is like a collection of 0-1 things, so `flatten`
 +    /// automatically does this without suspicious-looking `unwrap` calls.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = std::iter::empty::<Option<i32>>().filter(Option::is_some).map(Option::unwrap);
 +    /// ```
 +    /// Use instead:
 +    /// ```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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `iter.nth(0)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `iter.next()` is equivalent to
 +    /// `iter.nth(0)`, as they both consume the next element,
 +    ///  but is more readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// # let mut s = HashSet::new();
 +    /// # s.insert(1);
 +    /// let x = s.iter().nth(0);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// # let mut s = HashSet::new();
 +    /// # 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()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.iter().nth()` (and the related
 +    /// `.iter_mut().nth()`) on standard library types with *O*(1) element access.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.get()` and `.get_mut()` are more efficient and more
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.iter().nth(3);
 +    /// let bad_slice = &some_vec[..].iter().nth(3);
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.skip(x).next()` on iterators.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.nth(x)` is cleaner
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.iter().skip(3).next();
 +    /// let bad_slice = &some_vec[..].iter().skip(3).next();
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.drain(..)` on `Vec` and `VecDeque` for iteration.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.into_iter()` is simpler with better performance.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// let mut foo = vec![0, 1, 2, 3];
 +    /// let bar: HashSet<usize> = foo.drain(..).collect();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// let foo = vec![0, 1, 2, 3];
 +    /// let bar: HashSet<usize> = foo.into_iter().collect();
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub ITER_WITH_DRAIN,
 +    nursery,
 +    "replace `.drain(..)` with `.into_iter()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for using `x.get(x.len() - 1)` instead of
 +    /// `x.last()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `x.last()` is easier to read and has the same
 +    /// result.
 +    ///
 +    /// Note that using `x[x.len() - 1]` is semantically different from
 +    /// `x.last()`.  Indexing into the array will panic on out-of-bounds
 +    /// accesses, while `x.get()` and `x.last()` will return `None`.
 +    ///
 +    /// There is another lint (get_unwrap) that covers the case of using
 +    /// `x.get(index).unwrap()` instead of `x[index]`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = vec![2, 3, 5];
 +    /// let last_element = x.get(x.len() - 1);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.get().unwrap()` (or
 +    /// `.get_mut().unwrap`) on a standard library type which implements `Index`
 +    ///
 +    /// ### Why is this bad?
 +    /// Using the Index trait (`[]`) is more clear and more
 +    /// concise.
 +    ///
 +    /// ### Known problems
 +    /// Not a replacement for error handling: Using either
 +    /// `.unwrap()` or the Index trait (`[]`) carries the risk of causing a `panic`
 +    /// if the value being accessed is `None`. If the use of `.get().unwrap()` is a
 +    /// temporary placeholder for dealing with the `Option` type, then this does
 +    /// not mitigate the need for error handling. If there is a chance that `.get()`
 +    /// will be `None` in your program, then it is advisable that the `None` case
 +    /// is handled in a future refactor instead of using `.unwrap()` or the Index
 +    /// trait.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut some_vec = vec![0, 1, 2, 3];
 +    /// let last = some_vec.get(3).unwrap();
 +    /// *some_vec.get_mut(0).unwrap() = 1;
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let mut some_vec = vec![0, 1, 2, 3];
 +    /// 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for occurrences where one vector gets extended instead of append
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `append` instead of `extend` is more concise and faster
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut a = vec![1, 2, 3];
 +    /// let mut b = vec![4, 5, 6];
 +    ///
 +    /// a.extend(b.drain(..));
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let mut a = vec![1, 2, 3];
 +    /// let mut b = vec![4, 5, 6];
 +    ///
 +    /// 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 vector to another"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.extend(s.chars())` where s is a
 +    /// `&str` or `String`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.push_str(s)` is clearer
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let abc = "abc";
 +    /// let def = String::from("def");
 +    /// let mut s = String::new();
 +    /// s.extend(abc.chars());
 +    /// s.extend(def.chars());
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let abc = "abc";
 +    /// let def = String::from("def");
 +    /// let mut s = String::new();
 +    /// 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`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.cloned().collect()` on slice to
 +    /// create a `Vec`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.to_vec()` is clearer
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let s = [1, 2, 3, 4, 5];
 +    /// let s2: Vec<isize> = s[..].iter().cloned().collect();
 +    /// ```
 +    /// The better use would be:
 +    /// ```rust
 +    /// 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`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.chars().last()` or
 +    /// `_.chars().next_back()` on a `str` to check if it ends with a given char.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.ends_with(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let name = "_";
 +    /// name.chars().last() == Some('_') || name.chars().next_back() == Some('-');
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let name = "_";
 +    /// 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.as_ref()` or `.as_mut()` where the
 +    /// types before and after the call are the same.
 +    ///
 +    /// ### Why is this bad?
 +    /// The call is unnecessary.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn do_stuff(x: &[i32]) {}
 +    /// let x: &[i32] = &[1, 2, 3, 4, 5];
 +    /// do_stuff(x.as_ref());
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// # fn do_stuff(x: &[i32]) {}
 +    /// 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for using `fold` when a more succinct alternative exists.
 +    /// Specifically, this checks for `fold`s which could be replaced by `any`, `all`,
 +    /// `sum` or `product`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # #[allow(unused)]
 +    /// (0..3).fold(false, |acc, x| acc || x > 2);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// (0..3).any(|x| x > 2);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub UNNECESSARY_FOLD,
 +    style,
 +    "using `fold` when a more succinct alternative exists"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `filter_map` calls that could be replaced by `filter` or `map`.
 +    /// More specifically it checks if the closure provided is only performing one of the
 +    /// filter or map operations and suggests the appropriate option.
 +    ///
 +    /// ### Why is this bad?
 +    /// Complexity. The intent is also clearer if only a single
 +    /// operation is being performed.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None });
 +    ///
 +    /// // As there is no transformation of the argument this could be written as:
 +    /// let _ = (0..3).filter(|&x| x > 2);
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// let _ = (0..4).filter_map(|x| Some(x + 1));
 +    ///
 +    /// // 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `find_map` calls that could be replaced by `find` or `map`. More
 +    /// specifically it checks if the closure provided is only performing one of the
 +    /// find or map operations and suggests the appropriate option.
 +    ///
 +    /// ### Why is this bad?
 +    /// Complexity. The intent is also clearer if only a single
 +    /// operation is being performed.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).find_map(|x| if x > 2 { Some(x) } else { None });
 +    ///
 +    /// // As there is no transformation of the argument this could be written as:
 +    /// let _ = (0..3).find(|&x| x > 2);
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// let _ = (0..4).find_map(|x| Some(x + 1));
 +    ///
 +    /// // As there is no conditional check on the argument this could be written as:
 +    /// let _ = (0..4).map(|x| x + 1).next();
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub UNNECESSARY_FIND_MAP,
 +    complexity,
 +    "using `find_map` when a more succinct alternative exists"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `into_iter` calls on references which should be replaced by `iter`
 +    /// or `iter_mut`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability. Calling `into_iter` on a reference will not move out its
 +    /// content into the resulting iterator, which is confusing. It is better just call `iter` or
 +    /// `iter_mut` directly.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![3, 4, 5];
 +    /// (&vec).into_iter();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let vec = vec![3, 4, 5];
 +    /// (&vec).iter();
 +    /// ```
 +    #[clippy::version = "1.32.0"]
 +    pub INTO_ITER_ON_REF,
 +    style,
 +    "using `.into_iter()` on a reference"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `map` followed by a `count`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It looks suspicious. Maybe `map` was confused with `filter`.
 +    /// If the `map` call is intentional, this should be rewritten
 +    /// using `inspect`. Or, if you intend to drive the iterator to
 +    /// completion, you can just use `for_each` instead.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).map(|x| x + 2).count();
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub SUSPICIOUS_MAP,
 +    suspicious,
 +    "suspicious usage of map"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `MaybeUninit::uninit().assume_init()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// For most types, this is undefined behavior.
 +    ///
 +    /// ### Known problems
 +    /// For now, we accept empty tuples and tuples / arrays
 +    /// of `MaybeUninit`. There may be other types that allow uninitialized
 +    /// data, but those are not yet rigorously defined.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Beware the UB
 +    /// use std::mem::MaybeUninit;
 +    ///
 +    /// let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
 +    /// ```
 +    ///
 +    /// Note that the following is OK:
 +    ///
 +    /// ```rust
 +    /// use std::mem::MaybeUninit;
 +    ///
 +    /// let _: [MaybeUninit<bool>; 5] = unsafe {
 +    ///     MaybeUninit::uninit().assume_init()
 +    /// };
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub UNINIT_ASSUMED_INIT,
 +    correctness,
 +    "`MaybeUninit::uninit().assume_init()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// These can be written simply with `saturating_add/sub` methods.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let y: u32 = 0;
 +    /// # let x: u32 = 100;
 +    /// let add = x.checked_add(y).unwrap_or(u32::MAX);
 +    /// let sub = x.checked_sub(y).unwrap_or(u32::MIN);
 +    /// ```
 +    ///
 +    /// can be written using dedicated methods for saturating addition/subtraction as:
 +    ///
 +    /// ```rust
 +    /// # let y: u32 = 0;
 +    /// # let x: u32 = 100;
 +    /// let add = x.saturating_add(y);
 +    /// let sub = x.saturating_sub(y);
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub MANUAL_SATURATING_ARITHMETIC,
 +    style,
 +    "`.checked_add/sub(x).unwrap_or(MAX/MIN)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to
 +    /// zero-sized types
 +    ///
 +    /// ### Why is this bad?
 +    /// This is a no-op, and likely unintended
 +    ///
 +    /// ### Example
 +    /// ```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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `FileType::is_file()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// When people testing a file type with `FileType::is_file`
 +    /// they are testing whether a path is something they can get bytes from. But
 +    /// `is_file` doesn't cover special file types in unix-like systems, and doesn't cover
 +    /// symlink in windows. Using `!FileType::is_dir()` is a better way to that intention.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # || {
 +    /// let metadata = std::fs::metadata("foo.txt")?;
 +    /// let filetype = metadata.file_type();
 +    ///
 +    /// if filetype.is_file() {
 +    ///     // read file
 +    /// }
 +    /// # Ok::<_, std::io::Error>(())
 +    /// # };
 +    /// ```
 +    ///
 +    /// should be written as:
 +    ///
 +    /// ```rust
 +    /// # || {
 +    /// let metadata = std::fs::metadata("foo.txt")?;
 +    /// let filetype = metadata.file_type();
 +    ///
 +    /// if !filetype.is_dir() {
 +    ///     // read file
 +    /// }
 +    /// # 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str).
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.as_deref()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let opt = Some("".to_string());
 +    /// opt.as_ref().map(String::as_str)
 +    /// # ;
 +    /// ```
 +    /// Can be written as
 +    /// ```rust
 +    /// # let opt = Some("".to_string());
 +    /// 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()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `iter().next()` on a Slice or an Array
 +    ///
 +    /// ### Why is this bad?
 +    /// These can be shortened into `.get()`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let a = [1, 2, 3];
 +    /// # let b = vec![1, 2, 3];
 +    /// a[2..].iter().next();
 +    /// b.iter().next();
 +    /// ```
 +    /// should be written as:
 +    /// ```rust
 +    /// # let a = [1, 2, 3];
 +    /// # let b = vec![1, 2, 3];
 +    /// 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()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Warns when using `push_str`/`insert_str` with a single-character string literal
 +    /// where `push`/`insert` with a `char` would work fine.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's less clear that we are pushing a single character.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let mut string = String::new();
 +    /// string.insert_str(0, "R");
 +    /// string.push_str("R");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let mut string = String::new();
 +    /// 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// As the counterpart to `or_fun_call`, this lint looks for unnecessary
 +    /// lazily evaluated closures on `Option` and `Result`.
 +    ///
 +    /// This lint suggests changing the following functions, when eager evaluation results in
 +    /// simpler code:
 +    ///  - `unwrap_or_else` to `unwrap_or`
 +    ///  - `and_then` to `and`
 +    ///  - `or_else` to `or`
 +    ///  - `get_or_insert_with` to `get_or_insert`
 +    ///  - `ok_or_else` to `ok_or`
 +    ///
 +    /// ### Why is this bad?
 +    /// Using eager evaluation is shorter and simpler in some cases.
 +    ///
 +    /// ### Known problems
 +    /// It is possible, but not recommended for `Deref` and `Index` to have
 +    /// side effects. Eagerly evaluating them can change the semantics of the program.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // example code where clippy issues a warning
 +    /// let opt: Option<u32> = None;
 +    ///
 +    /// opt.unwrap_or_else(|| 42);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let opt: Option<u32> = None;
 +    ///
 +    /// 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map(_).collect::<Result<(), _>()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `try_for_each` instead is more readable and idiomatic.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// (0..3).map(|t| Err(t)).collect::<Result<(), _>>();
 +    /// ```
 +    /// Use instead:
 +    /// ```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`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `from_iter()` function calls on types that implement the `FromIterator`
 +    /// trait.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is recommended style to use collect. See
 +    /// [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let five_fives = std::iter::repeat(5).take(5);
 +    ///
 +    /// let v = Vec::from_iter(five_fives);
 +    ///
 +    /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let five_fives = std::iter::repeat(5).take(5);
 +    ///
 +    /// let v: Vec<i32> = five_fives.collect();
 +    ///
 +    /// 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()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `inspect().for_each()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is the same as performing the computation
 +    /// inside `inspect` at the beginning of the closure in `for_each`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// [1,2,3,4,5].iter()
 +    /// .inspect(|&x| println!("inspect the number: {}", x))
 +    /// .for_each(|&x| {
 +    ///     assert!(x >= 0);
 +    /// });
 +    /// ```
 +    /// Can be written as
 +    /// ```rust
 +    /// [1,2,3,4,5].iter()
 +    /// .for_each(|&x| {
 +    ///     println!("inspect the number: {}", x);
 +    ///     assert!(x >= 0);
 +    /// });
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub INSPECT_FOR_EACH,
 +    complexity,
 +    "using `.inspect().for_each()`, which can be replaced with `.for_each()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `filter_map(|x| x)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely by using `flatten`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let iter = vec![Some(1)].into_iter();
 +    /// iter.filter_map(|x| x);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for instances of `map(f)` where `f` is the identity function.
 +    ///
 +    /// ### Why is this bad?
 +    /// It can be written more concisely without the call to `map`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = [1, 2, 3];
 +    /// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = [1, 2, 3];
 +    /// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub MAP_IDENTITY,
 +    complexity,
 +    "using iterator.map(|x| x)"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.bytes().nth()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.as_bytes().get()` is more efficient and more
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # #[allow(unused)]
 +    /// "Hello".bytes().nth(3);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # #[allow(unused)]
 +    /// "Hello".as_bytes().get(3);
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub BYTES_NTH,
 +    style,
 +    "replace `.bytes().nth()` with `.as_bytes().get()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer.
 +    ///
 +    /// ### Why is this bad?
 +    /// These methods do the same thing as `_.clone()` but may be confusing as
 +    /// to why we are calling `to_vec` on something that is already a `Vec` or calling `to_owned` on something that is already owned.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let a = vec![1, 2, 3];
 +    /// let b = a.to_vec();
 +    /// let c = a.to_owned();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let a = vec![1, 2, 3];
 +    /// 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.iter().count()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.len()` is more efficient and more
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # #![allow(unused)]
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    ///
 +    /// some_vec.iter().count();
 +    /// &some_vec[..].iter().count();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    ///
 +    /// some_vec.len();
 +    /// &some_vec[..].len();
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub ITER_COUNT,
 +    complexity,
 +    "replace `.iter().count()` with `.len()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the usage of `_.to_owned()`, on a `Cow<'_, _>`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `to_owned()` on a `Cow` creates a clone of the `Cow`
 +    /// itself, without taking ownership of the `Cow` contents (i.e.
 +    /// it's equivalent to calling `Cow::clone`).
 +    /// The similarly named `into_owned` method, on the other hand,
 +    /// clones the `Cow` contents, effectively turning any `Cow::Borrowed`
 +    /// into a `Cow::Owned`.
 +    ///
 +    /// Given the potential ambiguity, consider replacing `to_owned`
 +    /// with `clone` for better readability or, if getting a `Cow::Owned`
 +    /// was the original intent, using `into_owned` instead.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::borrow::Cow;
 +    /// let s = "Hello world!";
 +    /// let cow = Cow::Borrowed(s);
 +    ///
 +    /// let data = cow.to_owned();
 +    /// assert!(matches!(data, Cow::Borrowed(_)))
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::borrow::Cow;
 +    /// let s = "Hello world!";
 +    /// let cow = Cow::Borrowed(s);
 +    ///
 +    /// let data = cow.clone();
 +    /// assert!(matches!(data, Cow::Borrowed(_)))
 +    /// ```
 +    /// or
 +    /// ```rust
 +    /// # use std::borrow::Cow;
 +    /// let s = "Hello world!";
 +    /// let cow = Cow::Borrowed(s);
 +    ///
 +    /// let data = cow.into_owned();
 +    /// assert!(matches!(data, String))
 +    /// ```
 +    #[clippy::version = "1.65.0"]
 +    pub SUSPICIOUS_TO_OWNED,
 +    suspicious,
 +    "calls to `to_owned` on a `Cow<'_, _>` might not do what they are expected"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to [`splitn`]
 +    /// (https://doc.rust-lang.org/std/primitive.str.html#method.splitn) and
 +    /// related functions with either zero or one splits.
 +    ///
 +    /// ### Why is this bad?
 +    /// These calls don't actually split the value and are
 +    /// likely to be intended as a different number.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let s = "";
 +    /// for x in s.splitn(1, ":") {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let s = "";
 +    /// for x in s.splitn(2, ":") {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.54.0"]
 +    pub SUSPICIOUS_SPLITN,
 +    correctness,
 +    "checks for `.splitn(0, ..)` and `.splitn(1, ..)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for manual implementations of `str::repeat`
 +    ///
 +    /// ### Why is this bad?
 +    /// These are both harder to read, as well as less performant.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: String = std::iter::repeat('x').take(10).collect();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let x: String = "x".repeat(10);
 +    /// ```
 +    #[clippy::version = "1.54.0"]
 +    pub MANUAL_STR_REPEAT,
 +    perf,
 +    "manual implementation of `str::repeat`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `str::splitn(2, _)`
 +    ///
 +    /// ### Why is this bad?
 +    /// `split_once` is both clearer in intent and slightly more efficient.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// let s = "key=value=add";
 +    /// let (key, value) = s.splitn(2, '=').next_tuple()?;
 +    /// let value = s.splitn(2, '=').nth(1)?;
 +    ///
 +    /// let mut parts = s.splitn(2, '=');
 +    /// let key = parts.next()?;
 +    /// let value = parts.next()?;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// let s = "key=value=add";
 +    /// let (key, value) = s.split_once('=')?;
 +    /// let value = s.split_once('=')?.1;
 +    ///
 +    /// let (key, value) = s.split_once('=')?;
 +    /// ```
 +    ///
 +    /// ### Limitations
 +    /// The multiple statement variant currently only detects `iter.next()?`/`iter.next().unwrap()`
 +    /// in two separate `let` statements that immediately follow the `splitn()`
 +    #[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
 +    /// let str = "key=value=add";
 +    /// let _ = str.splitn(3, '=').next().unwrap();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let str = "key=value=add";
 +    /// let _ = str.split('=').next().unwrap();
 +    /// ```
 +    #[clippy::version = "1.59.0"]
 +    pub NEEDLESS_SPLITN,
 +    complexity,
 +    "usages of `str::splitn` that can be replaced with `str::split`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnecessary calls to [`ToOwned::to_owned`](https://doc.rust-lang.org/std/borrow/trait.ToOwned.html#tymethod.to_owned)
 +    /// and other `to_owned`-like functions.
 +    ///
 +    /// ### Why is this bad?
 +    /// The unnecessary calls result in useless allocations.
 +    ///
 +    /// ### Known problems
 +    /// `unnecessary_to_owned` can falsely trigger if `IntoIterator::into_iter` is applied to an
 +    /// owned copy of a resource and the resource is later used mutably. See
 +    /// [#8148](https://github.com/rust-lang/rust-clippy/issues/8148).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let path = std::path::Path::new("x");
 +    /// foo(&path.to_string_lossy().to_string());
 +    /// fn foo(s: &str) {}
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let path = std::path::Path::new("x");
 +    /// foo(&path.to_string_lossy());
 +    /// fn foo(s: &str) {}
 +    /// ```
 +    #[clippy::version = "1.59.0"]
 +    pub UNNECESSARY_TO_OWNED,
 +    perf,
 +    "unnecessary calls to `to_owned`-like functions"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.collect::<Vec<String>>().join("")` on iterators.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.collect::<String>()` is more concise and might be more performant
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let vector = vec!["hello",  "world"];
 +    /// let output = vector.iter().map(|item| item.to_uppercase()).collect::<Vec<String>>().join("");
 +    /// println!("{}", output);
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let vector = vec!["hello",  "world"];
 +    /// let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
 +    /// println!("{}", output);
 +    /// ```
 +    /// ### Known problems
 +    /// While `.collect::<String>()` is sometimes more performant, there are cases where
 +    /// using `.collect::<String>()` over `.collect::<Vec<String>>().join("")`
 +    /// will prevent loop unrolling and will result in a negative performance impact.
 +    ///
 +    /// Additionally, differences have been observed between aarch64 and x86_64 assembly output,
 +    /// with aarch64 tending to producing faster assembly in more cases when using `.collect::<String>()`
 +    #[clippy::version = "1.61.0"]
 +    pub UNNECESSARY_JOIN,
 +    pedantic,
 +    "using `.collect::<Vec<String>>().join(\"\")` on an iterator"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for no-op uses of `Option::{as_deref, as_deref_mut}`,
 +    /// for example, `Option<&T>::as_deref()` returns the same type.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code and improving readability.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let a = Some(&1);
 +    /// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32>
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// 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`."
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Finds usages of [`char::is_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) that
 +    /// can be replaced with [`is_ascii_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_digit) or
 +    /// [`is_ascii_hexdigit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_hexdigit).
 +    ///
 +    /// ### Why is this bad?
 +    /// `is_digit(..)` is slower and requires specifying the radix.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let c: char = '6';
 +    /// c.is_digit(10);
 +    /// c.is_digit(16);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let c: char = '6';
 +    /// c.is_ascii_digit();
 +    /// c.is_ascii_hexdigit();
 +    /// ```
 +    #[clippy::version = "1.62.0"]
 +    pub IS_DIGIT_ASCII_RADIX,
 +    style,
 +    "use of `char::is_digit(..)` with literal radix of 10 or 16"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calling `take` function after `as_ref`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code. `take` writes `None` to its argument.
 +    /// In this case the modification is useless as it's a temporary that cannot be read from afterwards.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = Some(3);
 +    /// x.as_ref().take();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = Some(3);
 +    /// x.as_ref();
 +    /// ```
 +    #[clippy::version = "1.62.0"]
 +    pub NEEDLESS_OPTION_TAKE,
 +    complexity,
 +    "using `.as_ref().take()` on a temporary value"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `replace` statements which have no effect.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's either a mistake or confusing.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// "1234".replace("12", "12");
 +    /// "1234".replacen("12", "12", 1);
 +    /// ```
 +    #[clippy::version = "1.63.0"]
 +    pub NO_EFFECT_REPLACE,
 +    suspicious,
 +    "replace with no effect"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `.then_some(..).unwrap_or(..)`
 +    ///
 +    /// ### Why is this bad?
 +    /// This can be written more clearly with `if .. else ..`
 +    ///
 +    /// ### Limitations
 +    /// This lint currently only looks for usages of
 +    /// `.then_some(..).unwrap_or(..)`, but will be expanded
 +    /// to account for similar patterns.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = true;
 +    /// x.then_some("a").unwrap_or("b");
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = true;
 +    /// if x { "a" } else { "b" };
 +    /// ```
 +    #[clippy::version = "1.64.0"]
 +    pub OBFUSCATED_IF_ELSE,
 +    style,
 +    "use of `.then_some(..).unwrap_or(..)` can be written \
 +    more clearly with `if .. else ..`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///
 +    /// Checks for calls to `iter`, `iter_mut` or `into_iter` on collections containing a single item
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// It is simpler to use the once function from the standard library:
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```rust
 +    /// let a = [123].iter();
 +    /// let b = Some(123).into_iter();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// use std::iter;
 +    /// let a = iter::once(&123);
 +    /// let b = iter::once(123);
 +    /// ```
 +    ///
 +    /// ### Known problems
 +    ///
 +    /// The type of the resulting iterator might become incompatible with its usage
 +    #[clippy::version = "1.64.0"]
 +    pub ITER_ON_SINGLE_ITEMS,
 +    nursery,
 +    "Iterator for array of length 1"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///
 +    /// Checks for calls to `iter`, `iter_mut` or `into_iter` on empty collections
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// It is simpler to use the empty function from the standard library:
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```rust
 +    /// use std::{slice, option};
 +    /// let a: slice::Iter<i32> = [].iter();
 +    /// let f: option::IntoIter<i32> = None.into_iter();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// use std::iter;
 +    /// let a: iter::Empty<i32> = iter::empty();
 +    /// let b: iter::Empty<i32> = iter::empty();
 +    /// ```
 +    ///
 +    /// ### Known problems
 +    ///
 +    /// The type of the resulting iterator might become incompatible with its usage
 +    #[clippy::version = "1.64.0"]
 +    pub ITER_ON_EMPTY_COLLECTIONS,
 +    nursery,
 +    "Iterator for empty array"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for naive byte counts
 +    ///
 +    /// ### Why is this bad?
 +    /// The [`bytecount`](https://crates.io/crates/bytecount)
 +    /// crate has methods to count your bytes faster, especially for large slices.
 +    ///
 +    /// ### Known problems
 +    /// If you have predominantly small slices, the
 +    /// `bytecount::count(..)` method may actually be slower. However, if you can
 +    /// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be
 +    /// faster in those cases.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![1_u8];
 +    /// let count = vec.iter().filter(|x| **x == 0u8).count();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// # let vec = vec![1_u8];
 +    /// let count = bytecount::count(&vec, 0u8);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NAIVE_BYTECOUNT,
 +    pedantic,
 +    "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// It checks for `str::bytes().count()` and suggests replacing it with
 +    /// `str::len()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `str::bytes().count()` is longer and may not be as performant as using
 +    /// `str::len()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// "hello".bytes().count();
 +    /// String::from("hello").bytes().count();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// "hello".len();
 +    /// String::from("hello").len();
 +    /// ```
 +    #[clippy::version = "1.62.0"]
 +    pub BYTES_COUNT_TO_LEN,
 +    complexity,
 +    "Using `bytes().count()` when `len()` performs the same functionality"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `ends_with` with possible file extensions
 +    /// and suggests to use a case-insensitive approach instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// `ends_with` is case-sensitive and may not detect files with a valid extension.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn is_rust_file(filename: &str) -> bool {
 +    ///     filename.ends_with(".rs")
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn is_rust_file(filename: &str) -> bool {
 +    ///     let filename = std::path::Path::new(filename);
 +    ///     filename.extension()
 +    ///         .map_or(false, |ext| ext.eq_ignore_ascii_case("rs"))
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
 +    pedantic,
 +    "Checks for calls to ends_with with case-sensitive file extensions"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for using `x.get(0)` instead of
 +    /// `x.first()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `x.first()` is easier to read and has the same
 +    /// result.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = vec![2, 3, 5];
 +    /// let first_element = x.get(0);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = vec![2, 3, 5];
 +    /// let first_element = x.first();
 +    /// ```
 +    #[clippy::version = "1.63.0"]
 +    pub GET_FIRST,
 +    style,
 +    "Using `x.get(0)` when `x.first()` is simpler"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///
 +    /// Finds patterns that reimplement `Option::ok_or`.
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// Concise code helps focusing on behavior instead of boilerplate.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// let foo: Option<i32> = None;
 +    /// foo.map_or(Err("error"), |v| Ok(v));
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// 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`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `map(|x| x.clone())` or
 +    /// dereferencing closures for `Copy` types, on `Iterator` or `Option`,
 +    /// and suggests `cloned()` or `copied()` instead
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = vec![42, 43];
 +    /// let y = x.iter();
 +    /// let z = y.map(|i| *i);
 +    /// ```
 +    ///
 +    /// The correct use would be:
 +    ///
 +    /// ```rust
 +    /// let x = vec![42, 43];
 +    /// 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for instances of `map_err(|_| Some::Enum)`
 +    ///
 +    /// ### Why is this bad?
 +    /// This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error
 +    ///
 +    /// ### Example
 +    /// Before:
 +    /// ```rust
 +    /// use std::fmt;
 +    ///
 +    /// #[derive(Debug)]
 +    /// enum Error {
 +    ///     Indivisible,
 +    ///     Remainder(u8),
 +    /// }
 +    ///
 +    /// impl fmt::Display for Error {
 +    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +    ///         match self {
 +    ///             Error::Indivisible => write!(f, "could not divide input by three"),
 +    ///             Error::Remainder(remainder) => write!(
 +    ///                 f,
 +    ///                 "input is not divisible by three, remainder = {}",
 +    ///                 remainder
 +    ///             ),
 +    ///         }
 +    ///     }
 +    /// }
 +    ///
 +    /// impl std::error::Error for Error {}
 +    ///
 +    /// fn divisible_by_3(input: &str) -> Result<(), Error> {
 +    ///     input
 +    ///         .parse::<i32>()
 +    ///         .map_err(|_| Error::Indivisible)
 +    ///         .map(|v| v % 3)
 +    ///         .and_then(|remainder| {
 +    ///             if remainder == 0 {
 +    ///                 Ok(())
 +    ///             } else {
 +    ///                 Err(Error::Remainder(remainder as u8))
 +    ///             }
 +    ///         })
 +    /// }
 +    ///  ```
 +    ///
 +    ///  After:
 +    ///  ```rust
 +    /// use std::{fmt, num::ParseIntError};
 +    ///
 +    /// #[derive(Debug)]
 +    /// enum Error {
 +    ///     Indivisible(ParseIntError),
 +    ///     Remainder(u8),
 +    /// }
 +    ///
 +    /// impl fmt::Display for Error {
 +    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +    ///         match self {
 +    ///             Error::Indivisible(_) => write!(f, "could not divide input by three"),
 +    ///             Error::Remainder(remainder) => write!(
 +    ///                 f,
 +    ///                 "input is not divisible by three, remainder = {}",
 +    ///                 remainder
 +    ///             ),
 +    ///         }
 +    ///     }
 +    /// }
 +    ///
 +    /// impl std::error::Error for Error {
 +    ///     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
 +    ///         match self {
 +    ///             Error::Indivisible(source) => Some(source),
 +    ///             _ => None,
 +    ///         }
 +    ///     }
 +    /// }
 +    ///
 +    /// fn divisible_by_3(input: &str) -> Result<(), Error> {
 +    ///     input
 +    ///         .parse::<i32>()
 +    ///         .map_err(Error::Indivisible)
 +    ///         .map(|v| v % 3)
 +    ///         .and_then(|remainder| {
 +    ///             if remainder == 0 {
 +    ///                 Ok(())
 +    ///             } else {
 +    ///                 Err(Error::Remainder(remainder as u8))
 +    ///             }
 +    ///         })
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.48.0"]
 +    pub MAP_ERR_IGNORE,
 +    restriction,
 +    "`map_err` should not ignore the original error"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `&mut Mutex::lock` calls
 +    ///
 +    /// ### Why is this bad?
 +    /// `Mutex::lock` is less efficient than
 +    /// calling `Mutex::get_mut`. In addition you also have a statically
 +    /// guarantee that the mutex isn't locked, instead of just a runtime
 +    /// guarantee.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::sync::{Arc, Mutex};
 +    ///
 +    /// let mut value_rc = Arc::new(Mutex::new(42_u8));
 +    /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
 +    ///
 +    /// let mut value = value_mutex.lock().unwrap();
 +    /// *value += 1;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// use std::sync::{Arc, Mutex};
 +    ///
 +    /// let mut value_rc = Arc::new(Mutex::new(42_u8));
 +    /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
 +    ///
 +    /// 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for duplicate open options as well as combinations
 +    /// that make no sense.
 +    ///
 +    /// ### Why is this bad?
 +    /// In the best case, the code will be harder to read than
 +    /// necessary. I don't know the worst case.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::fs::OpenOptions;
 +    ///
 +    /// 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push)
 +    /// calls on `PathBuf` that can cause overwrites.
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `push` with a root path at the start can overwrite the
 +    /// previous defined path.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::path::PathBuf;
 +    ///
 +    /// let mut x = PathBuf::from("/foo");
 +    /// x.push("/bar");
 +    /// assert_eq!(x, PathBuf::from("/bar"));
 +    /// ```
 +    /// Could be written:
 +    ///
 +    /// ```rust
 +    /// use std::path::PathBuf;
 +    ///
 +    /// let mut x = PathBuf::from("/foo");
 +    /// 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for zipping a collection with the range of
 +    /// `0.._.len()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The code is better expressed with `.enumerate()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = vec![1];
 +    /// let _ = x.iter().zip(0..x.len());
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x = vec![1];
 +    /// let _ = x.iter().enumerate();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub RANGE_ZIP_WITH_LEN,
 +    complexity,
 +    "zipping iterator with a range when `enumerate()` would do"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.repeat(1)` and suggest the following method for each types.
 +    /// - `.to_string()` for `str`
 +    /// - `.clone()` for `String`
 +    /// - `.to_vec()` for `slice`
 +    ///
 +    /// The lint will evaluate constant expressions and values as arguments of `.repeat(..)` and emit a message if
 +    /// they are equivalent to `1`. (Related discussion in [rust-clippy#7306](https://github.com/rust-lang/rust-clippy/issues/7306))
 +    ///
 +    /// ### Why is this bad?
 +    /// For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning
 +    /// the string is the intention behind this, `clone()` should be used.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn main() {
 +    ///     let x = String::from("hello world").repeat(1);
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn main() {
 +    ///     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()` "
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// When sorting primitive values (integers, bools, chars, as well
 +    /// as arrays, slices, and tuples of such items), it is typically better to
 +    /// use an unstable sort than a stable sort.
 +    ///
 +    /// ### Why is this bad?
 +    /// Typically, using a stable sort consumes more memory and cpu cycles.
 +    /// Because values which compare equal are identical, preserving their
 +    /// relative order (the guarantee that a stable sort provides) means
 +    /// nothing, while the extra costs still apply.
 +    ///
 +    /// ### Known problems
 +    ///
 +    /// As pointed out in
 +    /// [issue #8241](https://github.com/rust-lang/rust-clippy/issues/8241),
 +    /// a stable sort can instead be significantly faster for certain scenarios
 +    /// (eg. when a sorted vector is extended with new data and resorted).
 +    ///
 +    /// For more information and benchmarking results, please refer to the
 +    /// issue linked above.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut vec = vec![2, 1, 3];
 +    /// vec.sort();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let mut vec = vec![2, 1, 3];
 +    /// vec.sort_unstable();
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub STABLE_SORT_PRIMITIVE,
 +    pedantic,
 +    "use of sort() when sort_unstable() is equivalent"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects `().hash(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Hashing a unit value doesn't do anything as the implementation of `Hash` for `()` is a no-op.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::hash::Hash;
 +    /// # use std::collections::hash_map::DefaultHasher;
 +    /// # enum Foo { Empty, WithValue(u8) }
 +    /// # use Foo::*;
 +    /// # let mut state = DefaultHasher::new();
 +    /// # let my_enum = Foo::Empty;
 +    /// match my_enum {
 +    ///       Empty => ().hash(&mut state),
 +    ///       WithValue(x) => x.hash(&mut state),
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::hash::Hash;
 +    /// # use std::collections::hash_map::DefaultHasher;
 +    /// # enum Foo { Empty, WithValue(u8) }
 +    /// # use Foo::*;
 +    /// # let mut state = DefaultHasher::new();
 +    /// # let my_enum = Foo::Empty;
 +    /// match my_enum {
 +    ///       Empty => 0_u8.hash(&mut state),
 +    ///       WithValue(x) => x.hash(&mut state),
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub UNIT_HASH,
 +    correctness,
 +    "hashing a unit value, which does nothing"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects uses of `Vec::sort_by` passing in a closure
 +    /// which compares the two arguments, either directly or indirectly.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
 +    /// possible) than to use `Vec::sort_by` and a more complicated
 +    /// closure.
 +    ///
 +    /// ### Known problems
 +    /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already
 +    /// imported by a use statement, then it will need to be added manually.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # struct A;
 +    /// # impl A { fn foo(&self) {} }
 +    /// # let mut vec: Vec<A> = Vec::new();
 +    /// vec.sort_by(|a, b| a.foo().cmp(&b.foo()));
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # struct A;
 +    /// # impl A { fn foo(&self) {} }
 +    /// # 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Finds occurrences of `Vec::resize(0, an_int)`
 +    ///
 +    /// ### Why is this bad?
 +    /// This is probably an argument inversion mistake.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// vec!(1, 2, 3, 4, 5).resize(0, 5)
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// vec!(1, 2, 3, 4, 5).clear()
 +    /// ```
 +    #[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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of File::read_to_end and File::read_to_string.
 +    ///
 +    /// ### Why is this bad?
 +    /// `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values.
 +    /// See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html)
 +    ///
 +    /// ### Example
 +    /// ```rust,no_run
 +    /// # use std::io::Read;
 +    /// # use std::fs::File;
 +    /// let mut f = File::open("foo.txt").unwrap();
 +    /// let mut bytes = Vec::new();
 +    /// f.read_to_end(&mut bytes).unwrap();
 +    /// ```
 +    /// Can be written more concisely as
 +    /// ```rust,no_run
 +    /// # 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`"
 +}
 +
 +pub struct Methods {
 +    avoid_breaking_exported_api: bool,
 +    msrv: Option<RustcVersion>,
 +    allow_expect_in_tests: bool,
 +    allow_unwrap_in_tests: bool,
 +}
 +
 +impl Methods {
 +    #[must_use]
 +    pub fn new(
 +        avoid_breaking_exported_api: bool,
 +        msrv: Option<RustcVersion>,
 +        allow_expect_in_tests: bool,
 +        allow_unwrap_in_tests: bool,
 +    ) -> Self {
 +        Self {
 +            avoid_breaking_exported_api,
 +            msrv,
 +            allow_expect_in_tests,
 +            allow_unwrap_in_tests,
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(Methods => [
 +    UNWRAP_USED,
 +    EXPECT_USED,
 +    SHOULD_IMPLEMENT_TRAIT,
 +    WRONG_SELF_CONVENTION,
 +    OK_EXPECT,
 +    UNWRAP_OR_ELSE_DEFAULT,
 +    MAP_UNWRAP_OR,
 +    RESULT_MAP_OR_INTO_OPTION,
 +    OPTION_MAP_OR_NONE,
 +    BIND_INSTEAD_OF_MAP,
 +    OR_FUN_CALL,
 +    OR_THEN_UNWRAP,
 +    EXPECT_FUN_CALL,
 +    CHARS_NEXT_CMP,
 +    CHARS_LAST_CMP,
 +    CLONE_ON_COPY,
 +    CLONE_ON_REF_PTR,
 +    CLONE_DOUBLE_REF,
 +    COLLAPSIBLE_STR_REPLACE,
 +    ITER_OVEREAGER_CLONED,
 +    CLONED_INSTEAD_OF_COPIED,
 +    FLAT_MAP_OPTION,
 +    INEFFICIENT_TO_STRING,
 +    NEW_RET_NO_SELF,
 +    SINGLE_CHAR_PATTERN,
 +    SINGLE_CHAR_ADD_STR,
 +    SEARCH_IS_SOME,
 +    FILTER_NEXT,
 +    SKIP_WHILE_NEXT,
 +    FILTER_MAP_IDENTITY,
 +    MAP_IDENTITY,
 +    MANUAL_FILTER_MAP,
 +    MANUAL_FIND_MAP,
 +    OPTION_FILTER_MAP,
 +    FILTER_MAP_NEXT,
 +    FLAT_MAP_IDENTITY,
 +    MAP_FLATTEN,
 +    ITERATOR_STEP_BY_ZERO,
 +    ITER_NEXT_SLICE,
 +    ITER_COUNT,
 +    ITER_NTH,
 +    ITER_NTH_ZERO,
 +    BYTES_NTH,
 +    ITER_SKIP_NEXT,
 +    GET_UNWRAP,
 +    GET_LAST_WITH_LEN,
 +    STRING_EXTEND_CHARS,
 +    ITER_CLONED_COLLECT,
 +    ITER_WITH_DRAIN,
 +    USELESS_ASREF,
 +    UNNECESSARY_FOLD,
 +    UNNECESSARY_FILTER_MAP,
 +    UNNECESSARY_FIND_MAP,
 +    INTO_ITER_ON_REF,
 +    SUSPICIOUS_MAP,
 +    UNINIT_ASSUMED_INIT,
 +    MANUAL_SATURATING_ARITHMETIC,
 +    ZST_OFFSET,
 +    FILETYPE_IS_FILE,
 +    OPTION_AS_REF_DEREF,
 +    UNNECESSARY_LAZY_EVALUATIONS,
 +    MAP_COLLECT_RESULT_UNIT,
 +    FROM_ITER_INSTEAD_OF_COLLECT,
 +    INSPECT_FOR_EACH,
 +    IMPLICIT_CLONE,
 +    SUSPICIOUS_TO_OWNED,
 +    SUSPICIOUS_SPLITN,
 +    MANUAL_STR_REPEAT,
 +    EXTEND_WITH_DRAIN,
 +    MANUAL_SPLIT_ONCE,
 +    NEEDLESS_SPLITN,
 +    UNNECESSARY_TO_OWNED,
 +    UNNECESSARY_JOIN,
 +    ERR_EXPECT,
 +    NEEDLESS_OPTION_AS_DEREF,
 +    IS_DIGIT_ASCII_RADIX,
 +    NEEDLESS_OPTION_TAKE,
 +    NO_EFFECT_REPLACE,
 +    OBFUSCATED_IF_ELSE,
 +    ITER_ON_SINGLE_ITEMS,
 +    ITER_ON_EMPTY_COLLECTIONS,
 +    NAIVE_BYTECOUNT,
 +    BYTES_COUNT_TO_LEN,
 +    CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
 +    GET_FIRST,
 +    MANUAL_OK_OR,
 +    MAP_CLONE,
 +    MAP_ERR_IGNORE,
 +    MUT_MUTEX_LOCK,
 +    NONSENSICAL_OPEN_OPTIONS,
 +    PATH_BUF_PUSH_OVERWRITE,
 +    RANGE_ZIP_WITH_LEN,
 +    REPEAT_ONCE,
 +    STABLE_SORT_PRIMITIVE,
 +    UNIT_HASH,
 +    UNNECESSARY_SORT_BY,
 +    VEC_RESIZE_TO_ZERO,
 +    VERBOSE_FILE_READS,
 +]);
 +
 +/// Extracts a method call name, args, and `Span` of the method name.
 +fn method_call<'tcx>(
 +    recv: &'tcx hir::Expr<'tcx>,
 +) -> Option<(&'tcx str, &'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], Span)> {
 +    if let ExprKind::MethodCall(path, receiver, args, _) = recv.kind {
 +        if !args.iter().any(|e| e.span.from_expansion()) && !receiver.span.from_expansion() {
 +            let name = path.ident.name.as_str();
 +            return Some((name, receiver, args, path.ident.span));
 +        }
 +    }
 +    None
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for Methods {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        self.check_methods(cx, expr);
 +
 +        match expr.kind {
 +            hir::ExprKind::Call(func, args) => {
 +                from_iter_instead_of_collect::check(cx, expr, args, func);
 +            },
 +            hir::ExprKind::MethodCall(method_call, receiver, args, _) => {
 +                let method_span = method_call.ident.span;
 +                or_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args);
 +                expect_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args);
 +                clone_on_copy::check(cx, expr, method_call.ident.name, receiver, args);
 +                clone_on_ref_ptr::check(cx, expr, method_call.ident.name, receiver, args);
 +                inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args);
 +                single_char_add_str::check(cx, expr, receiver, args);
 +                into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver);
 +                single_char_pattern::check(cx, expr, method_call.ident.name, receiver, args);
 +                unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, self.msrv);
 +            },
 +            hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
 +                let mut info = BinaryExprInfo {
 +                    expr,
 +                    chain: lhs,
 +                    other: rhs,
 +                    eq: op.node == hir::BinOpKind::Eq,
 +                };
 +                lint_binary_expr_with_method_call(cx, &mut info);
 +            },
 +            _ => (),
 +        }
 +    }
 +
 +    #[allow(clippy::too_many_lines)]
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
 +        if in_external_macro(cx.sess(), impl_item.span) {
 +            return;
 +        }
 +        let name = impl_item.ident.name.as_str();
 +        let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id());
 +        let item = cx.tcx.hir().expect_item(parent);
 +        let self_ty = cx.tcx.type_of(item.def_id);
 +
 +        let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }));
 +        if_chain! {
 +            if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind;
 +            if let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next();
 +
 +            let method_sig = cx.tcx.fn_sig(impl_item.def_id);
 +            let method_sig = cx.tcx.erase_late_bound_regions(method_sig);
 +
 +            let first_arg_ty = method_sig.inputs().iter().next();
 +
 +            // check conventions w.r.t. conversion method names and predicates
 +            if let Some(first_arg_ty) = first_arg_ty;
 +
 +            then {
 +                // if this impl block implements a trait, lint in trait definition instead
 +                if !implements_trait && cx.access_levels.is_exported(impl_item.def_id) {
 +                    // check missing trait implementations
 +                    for method_config in &TRAIT_METHODS {
 +                        if name == method_config.method_name &&
 +                            sig.decl.inputs.len() == method_config.param_count &&
 +                            method_config.output_type.matches(&sig.decl.output) &&
 +                            method_config.self_kind.matches(cx, self_ty, *first_arg_ty) &&
 +                            fn_header_equals(method_config.fn_header, sig.header) &&
 +                            method_config.lifetime_param_cond(impl_item)
 +                        {
 +                            span_lint_and_help(
 +                                cx,
 +                                SHOULD_IMPLEMENT_TRAIT,
 +                                impl_item.span,
 +                                &format!(
 +                                    "method `{}` can be confused for the standard trait method `{}::{}`",
 +                                    method_config.method_name,
 +                                    method_config.trait_name,
 +                                    method_config.method_name
 +                                ),
 +                                None,
 +                                &format!(
 +                                    "consider implementing the trait `{}` or choosing a less ambiguous method name",
 +                                    method_config.trait_name
 +                                )
 +                            );
 +                        }
 +                    }
 +                }
 +
 +                if sig.decl.implicit_self.has_implicit_self()
 +                    && !(self.avoid_breaking_exported_api
 +                        && cx.access_levels.is_exported(impl_item.def_id))
 +                {
 +                    wrong_self_convention::check(
 +                        cx,
 +                        name,
 +                        self_ty,
 +                        *first_arg_ty,
 +                        first_arg.pat.span,
 +                        implements_trait,
 +                        false
 +                    );
 +                }
 +            }
 +        }
 +
 +        // if this impl block implements a trait, lint in trait definition instead
 +        if implements_trait {
 +            return;
 +        }
 +
 +        if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
 +            let ret_ty = return_ty(cx, impl_item.hir_id());
 +
 +            // walk the return type and check for Self (this does not check associated types)
 +            if let Some(self_adt) = self_ty.ty_adt_def() {
 +                if contains_adt_constructor(ret_ty, self_adt) {
 +                    return;
 +                }
 +            } else if ret_ty.contains(self_ty) {
 +                return;
 +            }
 +
 +            // if return type is impl trait, check the associated types
 +            if let ty::Opaque(def_id, _) = *ret_ty.kind() {
 +                // one of the associated types must be Self
 +                for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
 +                    if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
 +                        let assoc_ty = match projection_predicate.term.unpack() {
 +                            ty::TermKind::Ty(ty) => ty,
 +                            ty::TermKind::Const(_c) => continue,
 +                        };
 +                        // walk the associated type and check for Self
 +                        if let Some(self_adt) = self_ty.ty_adt_def() {
 +                            if contains_adt_constructor(assoc_ty, self_adt) {
 +                                return;
 +                            }
 +                        } else if assoc_ty.contains(self_ty) {
 +                            return;
 +                        }
 +                    }
 +                }
 +            }
 +
 +            if name == "new" && ret_ty != self_ty {
 +                span_lint(
 +                    cx,
 +                    NEW_RET_NO_SELF,
 +                    impl_item.span,
 +                    "methods called `new` usually return `Self`",
 +                );
 +            }
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
 +        if in_external_macro(cx.tcx.sess, item.span) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let TraitItemKind::Fn(ref sig, _) = item.kind;
 +            if sig.decl.implicit_self.has_implicit_self();
 +            if let Some(first_arg_ty) = sig.decl.inputs.iter().next();
 +
 +            then {
 +                let first_arg_span = first_arg_ty.span;
 +                let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty);
 +                let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder();
 +                wrong_self_convention::check(
 +                    cx,
 +                    item.ident.name.as_str(),
 +                    self_ty,
 +                    first_arg_ty,
 +                    first_arg_span,
 +                    false,
 +                    true
 +                );
 +            }
 +        }
 +
 +        if_chain! {
 +            if item.ident.name == sym::new;
 +            if let TraitItemKind::Fn(_, _) = item.kind;
 +            let ret_ty = return_ty(cx, item.hir_id());
 +            let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder();
 +            if !ret_ty.contains(self_ty);
 +
 +            then {
 +                span_lint(
 +                    cx,
 +                    NEW_RET_NO_SELF,
 +                    item.span,
 +                    "methods called `new` usually return `Self`",
 +                );
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +impl Methods {
 +    #[allow(clippy::too_many_lines)]
 +    fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let Some((name, recv, args, span)) = method_call(expr) {
 +            match (name, args) {
 +                ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
 +                    zst_offset::check(cx, expr, recv);
 +                },
 +                ("and_then", [arg]) => {
 +                    let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg);
 +                    let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg);
 +                    if !biom_option_linted && !biom_result_linted {
 +                        unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
 +                    }
 +                },
 +                ("as_deref" | "as_deref_mut", []) => {
 +                    needless_option_as_deref::check(cx, expr, recv, name);
 +                },
 +                ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
 +                ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
 +                ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
 +                ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv),
 +                ("collect", []) => match method_call(recv) {
 +                    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);
 +                    },
 +                    Some(("take", take_self_arg, [take_arg], _)) => {
 +                        if meets_msrv(self.msrv, msrvs::STR_REPEAT) {
 +                            manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg);
 +                        }
 +                    },
 +                    _ => {},
 +                },
 +                ("count", []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) {
 +                    Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false),
 +                    Some((name2 @ ("into_iter" | "iter" | "iter_mut"), recv2, [], _)) => {
 +                        iter_count::check(cx, expr, recv2, name2);
 +                    },
 +                    Some(("map", _, [arg], _)) => suspicious_map::check(cx, expr, recv, arg),
 +                    Some(("filter", recv2, [arg], _)) => bytecount::check(cx, expr, recv2, arg),
 +                    Some(("bytes", recv2, [], _)) => bytes_count_to_len::check(cx, expr, recv, recv2),
 +                    _ => {},
 +                },
 +                ("drain", [arg]) => {
 +                    iter_with_drain::check(cx, expr, recv, span, arg);
 +                },
 +                ("ends_with", [arg]) => {
 +                    if let ExprKind::MethodCall(.., span) = expr.kind {
 +                        case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg);
 +                    }
 +                },
 +                ("expect", [_]) => match method_call(recv) {
 +                    Some(("ok", recv, [], _)) => ok_expect::check(cx, expr, recv),
 +                    Some(("err", recv, [], err_span)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span),
 +                    _ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests),
 +                },
 +                ("expect_err", [_]) => expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests),
 +                ("extend", [arg]) => {
 +                    string_extend_chars::check(cx, expr, recv, arg);
 +                    extend_with_drain::check(cx, expr, recv, arg);
 +                },
 +                ("filter_map", [arg]) => {
 +                    unnecessary_filter_map::check(cx, expr, arg, name);
 +                    filter_map_identity::check(cx, expr, arg, span);
 +                },
 +                ("find_map", [arg]) => {
 +                    unnecessary_filter_map::check(cx, expr, arg, name);
 +                },
 +                ("flat_map", [arg]) => {
 +                    flat_map_identity::check(cx, expr, arg, span);
 +                    flat_map_option::check(cx, expr, arg, span);
 +                },
 +                ("flatten", []) => match method_call(recv) {
 +                    Some(("map", recv, [map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span),
 +                    Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, true),
 +                    _ => {},
 +                },
 +                ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
 +                ("for_each", [_]) => {
 +                    if let Some(("inspect", _, [_], span2)) = method_call(recv) {
 +                        inspect_for_each::check(cx, expr, span2);
 +                    }
 +                },
 +                ("get", [arg]) => {
 +                    get_first::check(cx, expr, recv, arg);
 +                    get_last_with_len::check(cx, expr, recv, arg);
 +                },
 +                ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
 +                ("hash", [arg]) => {
 +                    unit_hash::check(cx, expr, recv, arg);
 +                },
 +                ("is_file", []) => filetype_is_file::check(cx, expr, recv),
 +                ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv),
 +                ("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
 +                ("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
 +                ("iter" | "iter_mut" | "into_iter", []) => {
 +                    iter_on_single_or_empty_collections::check(cx, expr, name, recv);
 +                },
 +                ("join", [join_arg]) => {
 +                    if let Some(("collect", _, _, span)) = method_call(recv) {
 +                        unnecessary_join::check(cx, expr, recv, join_arg, span);
 +                    }
 +                },
 +                ("last", []) | ("skip", [_]) => {
 +                    if let Some((name2, recv2, args2, _span2)) = method_call(recv) {
 +                        if let ("cloned", []) = (name2, args2) {
 +                            iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
 +                        }
 +                    }
 +                },
 +                ("lock", []) => {
 +                    mut_mutex_lock::check(cx, expr, recv, span);
 +                },
 +                (name @ ("map" | "map_err"), [m_arg]) => {
 +                    if name == "map" {
 +                        map_clone::check(cx, expr, recv, m_arg, self.msrv);
 +                    } else {
 +                        map_err_ignore::check(cx, expr, m_arg);
 +                    }
 +                    if let Some((name, recv2, args, span2)) = method_call(recv) {
 +                        match (name, args) {
 +                            ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv),
 +                            ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv),
 +                            ("filter", [f_arg]) => {
 +                                filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false);
 +                            },
 +                            ("find", [f_arg]) => {
 +                                filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true);
 +                            },
 +                            _ => {},
 +                        }
 +                    }
 +                    map_identity::check(cx, expr, recv, m_arg, name, span);
 +                },
 +                ("map_or", [def, map]) => {
 +                    option_map_or_none::check(cx, expr, recv, def, map);
 +                    manual_ok_or::check(cx, expr, recv, def, map);
 +                },
 +                ("next", []) => {
 +                    if let Some((name2, recv2, args2, _)) = method_call(recv) {
 +                        match (name2, args2) {
 +                            ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
 +                            ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg),
 +                            ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv),
 +                            ("iter", []) => iter_next_slice::check(cx, expr, recv2),
 +                            ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg),
 +                            ("skip_while", [_]) => skip_while_next::check(cx, expr),
 +                            _ => {},
 +                        }
 +                    }
 +                },
 +                ("nth", [n_arg]) => match method_call(recv) {
 +                    Some(("bytes", recv2, [], _)) => bytes_nth::check(cx, expr, recv2, n_arg),
 +                    Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
 +                    Some(("iter", recv2, [], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
 +                    Some(("iter_mut", recv2, [], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
 +                    _ => iter_nth_zero::check(cx, expr, recv, n_arg),
 +                },
 +                ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"),
 +                ("open", [_]) => {
 +                    open_options::check(cx, expr, recv);
 +                },
 +                ("or_else", [arg]) => {
 +                    if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) {
 +                        unnecessary_lazy_eval::check(cx, expr, recv, arg, "or");
 +                    }
 +                },
 +                ("push", [arg]) => {
 +                    path_buf_push_overwrite::check(cx, expr, arg);
 +                },
 +                ("read_to_end", [_]) => {
 +                    verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_END_MSG);
 +                },
 +                ("read_to_string", [_]) => {
 +                    verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_STRING_MSG);
 +                },
 +                ("repeat", [arg]) => {
 +                    repeat_once::check(cx, expr, recv, arg);
 +                },
 +                (name @ ("replace" | "replacen"), [arg1, arg2] | [arg1, arg2, _]) => {
 +                    no_effect_replace::check(cx, expr, arg1, arg2);
 +
 +                    // Check for repeated `str::replace` calls to perform `collapsible_str_replace` lint
 +                    if name == "replace" && let Some(("replace", ..)) = method_call(recv) {
 +                        collapsible_str_replace::check(cx, expr, arg1, arg2);
 +                    }
 +                },
 +                ("resize", [count_arg, default_arg]) => {
 +                    vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span);
 +                },
 +                ("sort", []) => {
 +                    stable_sort_primitive::check(cx, expr, recv);
 +                },
 +                ("sort_by", [arg]) => {
 +                    unnecessary_sort_by::check(cx, expr, recv, arg, false);
 +                },
 +                ("sort_unstable_by", [arg]) => {
 +                    unnecessary_sort_by::check(cx, expr, recv, arg, true);
 +                },
 +                ("splitn" | "rsplitn", [count_arg, pat_arg]) => {
 +                    if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
 +                        suspicious_splitn::check(cx, name, expr, recv, count);
 +                        str_splitn::check(cx, name, expr, recv, pat_arg, count, self.msrv);
 +                    }
 +                },
 +                ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => {
 +                    if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
 +                        suspicious_splitn::check(cx, name, expr, recv, count);
 +                    }
 +                },
 +                ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
 +                ("take", [_arg]) => {
 +                    if let Some((name2, recv2, args2, _span2)) = method_call(recv) {
 +                        if let ("cloned", []) = (name2, args2) {
 +                            iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
 +                        }
 +                    }
 +                },
 +                ("take", []) => needless_option_take::check(cx, expr, recv),
 +                ("then", [arg]) => {
 +                    if !meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
 +                        return;
 +                    }
 +                    unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some");
 +                },
 +                ("to_owned", []) => {
 +                    if !suspicious_to_owned::check(cx, expr, recv) {
 +                        implicit_clone::check(cx, name, expr, recv);
 +                    }
 +                },
 +                ("to_os_string" | "to_path_buf" | "to_vec", []) => {
 +                    implicit_clone::check(cx, name, expr, recv);
 +                },
 +                ("unwrap", []) => {
 +                    match method_call(recv) {
 +                        Some(("get", recv, [get_arg], _)) => {
 +                            get_unwrap::check(cx, expr, recv, get_arg, false);
 +                        },
 +                        Some(("get_mut", recv, [get_arg], _)) => {
 +                            get_unwrap::check(cx, expr, recv, get_arg, true);
 +                        },
 +                        Some(("or", recv, [or_arg], or_span)) => {
 +                            or_then_unwrap::check(cx, expr, recv, or_arg, or_span);
 +                        },
 +                        _ => {},
 +                    }
 +                    unwrap_used::check(cx, expr, recv, false, self.allow_unwrap_in_tests);
 +                },
 +                ("unwrap_err", []) => unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests),
 +                ("unwrap_or", [u_arg]) => match method_call(recv) {
 +                    Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _)) => {
 +                        manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
 +                    },
 +                    Some(("map", m_recv, [m_arg], span)) => {
 +                        option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span);
 +                    },
 +                    Some(("then_some", t_recv, [t_arg], _)) => {
 +                        obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg);
 +                    },
 +                    _ => {},
 +                },
 +                ("unwrap_or_else", [u_arg]) => match method_call(recv) {
 +                    Some(("map", recv, [map_arg], _))
 +                        if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {},
 +                    _ => {
 +                        unwrap_or_else_default::check(cx, expr, recv, u_arg);
 +                        unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
 +                    },
 +                },
 +                ("zip", [arg]) => {
 +                    if let ExprKind::MethodCall(name, iter_recv, [], _) = recv.kind
 +                        && name.ident.name == sym::iter
 +                    {
 +                        range_zip_with_len::check(cx, expr, iter_recv, arg);
 +                    }
 +                },
 +                _ => {},
 +            }
 +        }
 +    }
 +}
 +
 +fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) {
 +    if let Some((name @ ("find" | "position" | "rposition"), f_recv, [arg], span)) = method_call(recv) {
 +        search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span);
 +    }
 +}
 +
 +/// Used for `lint_binary_expr_with_method_call`.
 +#[derive(Copy, Clone)]
 +struct BinaryExprInfo<'a> {
 +    expr: &'a hir::Expr<'a>,
 +    chain: &'a hir::Expr<'a>,
 +    other: &'a hir::Expr<'a>,
 +    eq: bool,
 +}
 +
 +/// Checks for the `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
 +fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExprInfo<'_>) {
 +    macro_rules! lint_with_both_lhs_and_rhs {
 +        ($func:expr, $cx:expr, $info:ident) => {
 +            if !$func($cx, $info) {
 +                ::std::mem::swap(&mut $info.chain, &mut $info.other);
 +                if $func($cx, $info) {
 +                    return;
 +                }
 +            }
 +        };
 +    }
 +
 +    lint_with_both_lhs_and_rhs!(chars_next_cmp::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_last_cmp::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_next_cmp_with_unwrap::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_last_cmp_with_unwrap::check, cx, info);
 +}
 +
 +const FN_HEADER: hir::FnHeader = hir::FnHeader {
 +    unsafety: hir::Unsafety::Normal,
 +    constness: hir::Constness::NotConst,
 +    asyncness: hir::IsAsync::NotAsync,
 +    abi: rustc_target::spec::abi::Abi::Rust,
 +};
 +
 +struct ShouldImplTraitCase {
 +    trait_name: &'static str,
 +    method_name: &'static str,
 +    param_count: usize,
 +    fn_header: hir::FnHeader,
 +    // implicit self kind expected (none, self, &self, ...)
 +    self_kind: SelfKind,
 +    // checks against the output type
 +    output_type: OutType,
 +    // certain methods with explicit lifetimes can't implement the equivalent trait method
 +    lint_explicit_lifetime: bool,
 +}
 +impl ShouldImplTraitCase {
 +    const fn new(
 +        trait_name: &'static str,
 +        method_name: &'static str,
 +        param_count: usize,
 +        fn_header: hir::FnHeader,
 +        self_kind: SelfKind,
 +        output_type: OutType,
 +        lint_explicit_lifetime: bool,
 +    ) -> ShouldImplTraitCase {
 +        ShouldImplTraitCase {
 +            trait_name,
 +            method_name,
 +            param_count,
 +            fn_header,
 +            self_kind,
 +            output_type,
 +            lint_explicit_lifetime,
 +        }
 +    }
 +
 +    fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool {
 +        self.lint_explicit_lifetime
 +            || !impl_item.generics.params.iter().any(|p| {
 +                matches!(
 +                    p.kind,
 +                    hir::GenericParamKind::Lifetime {
 +                        kind: hir::LifetimeParamKind::Explicit
 +                    }
 +                )
 +            })
 +    }
 +}
 +
 +#[rustfmt::skip]
 +const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [
 +    ShouldImplTraitCase::new("std::ops::Add", "add",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::convert::AsMut", "as_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::convert::AsRef", "as_ref",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::BitAnd", "bitand",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::BitOr", "bitor",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::BitXor", "bitxor",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::borrow::Borrow", "borrow",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::clone::Clone", "clone",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::cmp::Ord", "cmp",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Any, true),
 +    // FIXME: default doesn't work
 +    ShouldImplTraitCase::new("std::default::Default", "default",  0,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Deref", "deref",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::Div", "div",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Drop", "drop",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Unit, true),
 +    ShouldImplTraitCase::new("std::cmp::PartialEq", "eq",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Bool, true),
 +    ShouldImplTraitCase::new("std::iter::FromIterator", "from_iter",  1,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::str::FromStr", "from_str",  1,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::hash::Hash", "hash",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Unit, true),
 +    ShouldImplTraitCase::new("std::ops::Index", "index",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::IndexMut", "index_mut",  2,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::iter::IntoIterator", "into_iter",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Mul", "mul",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Neg", "neg",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::iter::Iterator", "next",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Any, false),
 +    ShouldImplTraitCase::new("std::ops::Not", "not",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Rem", "rem",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Shl", "shl",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Shr", "shr",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Sub", "sub",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +];
 +
 +#[derive(Clone, Copy, PartialEq, Eq, Debug)]
 +enum SelfKind {
 +    Value,
 +    Ref,
 +    RefMut,
 +    No,
 +}
 +
 +impl SelfKind {
 +    fn matches<'a>(self, cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +        fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +            if ty == parent_ty {
 +                true
 +            } else if ty.is_box() {
 +                ty.boxed_ty() == parent_ty
 +            } else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) {
 +                if let ty::Adt(_, substs) = ty.kind() {
 +                    substs.types().next().map_or(false, |t| t == parent_ty)
 +                } else {
 +                    false
 +                }
 +            } else {
 +                false
 +            }
 +        }
 +
 +        fn matches_ref<'a>(cx: &LateContext<'a>, mutability: hir::Mutability, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +            if let ty::Ref(_, t, m) = *ty.kind() {
 +                return m == mutability && t == parent_ty;
 +            }
 +
 +            let trait_path = match mutability {
 +                hir::Mutability::Not => &paths::ASREF_TRAIT,
 +                hir::Mutability::Mut => &paths::ASMUT_TRAIT,
 +            };
 +
 +            let trait_def_id = match get_trait_def_id(cx, trait_path) {
 +                Some(did) => did,
 +                None => return false,
 +            };
 +            implements_trait(cx, ty, trait_def_id, &[parent_ty.into()])
 +        }
 +
 +        fn matches_none<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +            !matches_value(cx, parent_ty, ty)
 +                && !matches_ref(cx, hir::Mutability::Not, parent_ty, ty)
 +                && !matches_ref(cx, hir::Mutability::Mut, parent_ty, ty)
 +        }
 +
 +        match self {
 +            Self::Value => matches_value(cx, parent_ty, ty),
 +            Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty),
 +            Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty),
 +            Self::No => matches_none(cx, parent_ty, ty),
 +        }
 +    }
 +
 +    #[must_use]
 +    fn description(self) -> &'static str {
 +        match self {
 +            Self::Value => "`self` by value",
 +            Self::Ref => "`self` by reference",
 +            Self::RefMut => "`self` by mutable reference",
 +            Self::No => "no `self`",
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Copy)]
 +enum OutType {
 +    Unit,
 +    Bool,
 +    Any,
 +    Ref,
 +}
 +
 +impl OutType {
 +    fn matches(self, ty: &hir::FnRetTy<'_>) -> bool {
 +        let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[]));
 +        match (self, ty) {
 +            (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true,
 +            (Self::Unit, &hir::FnRetTy::Return(ty)) if is_unit(ty) => true,
 +            (Self::Bool, &hir::FnRetTy::Return(ty)) if is_bool(ty) => true,
 +            (Self::Any, &hir::FnRetTy::Return(ty)) if !is_unit(ty) => true,
 +            (Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Rptr(_, _)),
 +            _ => false,
 +        }
 +    }
 +}
 +
 +fn is_bool(ty: &hir::Ty<'_>) -> bool {
 +    if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
 +        matches!(path.res, Res::PrimTy(PrimTy::Bool))
 +    } else {
 +        false
 +    }
 +}
 +
 +fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool {
 +    expected.constness == actual.constness
 +        && expected.unsafety == actual.unsafety
 +        && expected.asyncness == actual.asyncness
 +}
index bd8458a222e2922e1d68c3d26e9b37fc0f084d20,0000000000000000000000000000000000000000..b9593b3687d9cf3633d93dc397eda0893dd729c3
mode 100644,000000..100644
--- /dev/null
@@@ -1,30 -1,0 +1,31 @@@
- use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::{expr_custom_deref_adjustment, ty::is_type_diagnostic_item};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, Mutability};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty;
 +use rustc_span::{sym, Span};
 +
 +use super::MUT_MUTEX_LOCK;
 +
 +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &'tcx Expr<'tcx>, name_span: Span) {
 +    if_chain! {
++        if matches!(expr_custom_deref_adjustment(cx, recv), None | Some(Mutability::Mut));
 +        if let ty::Ref(_, _, Mutability::Mut) = cx.typeck_results().expr_ty(recv).kind();
 +        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id);
 +        if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
 +        if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Mutex);
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                MUT_MUTEX_LOCK,
 +                name_span,
 +                "calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference",
 +                "change this to",
 +                "get_mut".to_owned(),
 +                Applicability::MaybeIncorrect,
 +            );
 +        }
 +    }
 +}
index 903fa306f935a8e6b005c7c67e6323b6ad706a27,0000000000000000000000000000000000000000..597af853dc681a57d44f973f8a4fbe83a9d14977
mode 100644,000000..100644
--- /dev/null
@@@ -1,178 -1,0 +1,178 @@@
-         if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 1 {
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::paths;
 +use clippy_utils::ty::match_type;
 +use rustc_ast::ast::LitKind;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::LateContext;
 +use rustc_span::source_map::{Span, Spanned};
 +
 +use super::NONSENSICAL_OPEN_OPTIONS;
 +
 +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
 +    if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
 +        && let Some(impl_id) = cx.tcx.impl_of_method(method_id)
 +        && match_type(cx, cx.tcx.type_of(impl_id), &paths::OPEN_OPTIONS)
 +    {
 +        let mut options = Vec::new();
 +        get_open_options(cx, recv, &mut options);
 +        check_open_options(cx, &options, e.span);
 +    }
 +}
 +
 +#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 +enum Argument {
 +    True,
 +    False,
 +    Unknown,
 +}
 +
 +#[derive(Debug)]
 +enum OpenOption {
 +    Write,
 +    Read,
 +    Truncate,
 +    Create,
 +    Append,
 +}
 +
 +fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec<(OpenOption, Argument)>) {
 +    if let ExprKind::MethodCall(path, receiver, arguments, _) = argument.kind {
 +        let obj_ty = cx.typeck_results().expr_ty(receiver).peel_refs();
 +
 +        // Only proceed if this is a call on some object of type std::fs::OpenOptions
++        if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && !arguments.is_empty() {
 +            let argument_option = match arguments[0].kind {
 +                ExprKind::Lit(ref span) => {
 +                    if let Spanned {
 +                        node: LitKind::Bool(lit),
 +                        ..
 +                    } = *span
 +                    {
 +                        if lit { Argument::True } else { Argument::False }
 +                    } else {
 +                        // The function is called with a literal which is not a boolean literal.
 +                        // This is theoretically possible, but not very likely.
 +                        return;
 +                    }
 +                },
 +                _ => Argument::Unknown,
 +            };
 +
 +            match path.ident.as_str() {
 +                "create" => {
 +                    options.push((OpenOption::Create, argument_option));
 +                },
 +                "append" => {
 +                    options.push((OpenOption::Append, argument_option));
 +                },
 +                "truncate" => {
 +                    options.push((OpenOption::Truncate, argument_option));
 +                },
 +                "read" => {
 +                    options.push((OpenOption::Read, argument_option));
 +                },
 +                "write" => {
 +                    options.push((OpenOption::Write, argument_option));
 +                },
 +                _ => (),
 +            }
 +
 +            get_open_options(cx, receiver, options);
 +        }
 +    }
 +}
 +
 +fn check_open_options(cx: &LateContext<'_>, options: &[(OpenOption, Argument)], span: Span) {
 +    let (mut create, mut append, mut truncate, mut read, mut write) = (false, false, false, false, false);
 +    let (mut create_arg, mut append_arg, mut truncate_arg, mut read_arg, mut write_arg) =
 +        (false, false, false, false, false);
 +    // This code is almost duplicated (oh, the irony), but I haven't found a way to
 +    // unify it.
 +
 +    for option in options {
 +        match *option {
 +            (OpenOption::Create, arg) => {
 +                if create {
 +                    span_lint(
 +                        cx,
 +                        NONSENSICAL_OPEN_OPTIONS,
 +                        span,
 +                        "the method `create` is called more than once",
 +                    );
 +                } else {
 +                    create = true;
 +                }
 +                create_arg = create_arg || (arg == Argument::True);
 +            },
 +            (OpenOption::Append, arg) => {
 +                if append {
 +                    span_lint(
 +                        cx,
 +                        NONSENSICAL_OPEN_OPTIONS,
 +                        span,
 +                        "the method `append` is called more than once",
 +                    );
 +                } else {
 +                    append = true;
 +                }
 +                append_arg = append_arg || (arg == Argument::True);
 +            },
 +            (OpenOption::Truncate, arg) => {
 +                if truncate {
 +                    span_lint(
 +                        cx,
 +                        NONSENSICAL_OPEN_OPTIONS,
 +                        span,
 +                        "the method `truncate` is called more than once",
 +                    );
 +                } else {
 +                    truncate = true;
 +                }
 +                truncate_arg = truncate_arg || (arg == Argument::True);
 +            },
 +            (OpenOption::Read, arg) => {
 +                if read {
 +                    span_lint(
 +                        cx,
 +                        NONSENSICAL_OPEN_OPTIONS,
 +                        span,
 +                        "the method `read` is called more than once",
 +                    );
 +                } else {
 +                    read = true;
 +                }
 +                read_arg = read_arg || (arg == Argument::True);
 +            },
 +            (OpenOption::Write, arg) => {
 +                if write {
 +                    span_lint(
 +                        cx,
 +                        NONSENSICAL_OPEN_OPTIONS,
 +                        span,
 +                        "the method `write` is called more than once",
 +                    );
 +                } else {
 +                    write = true;
 +                }
 +                write_arg = write_arg || (arg == Argument::True);
 +            },
 +        }
 +    }
 +
 +    if read && truncate && read_arg && truncate_arg && !(write && write_arg) {
 +        span_lint(
 +            cx,
 +            NONSENSICAL_OPEN_OPTIONS,
 +            span,
 +            "file opened with `truncate` and `read`",
 +        );
 +    }
 +    if append && truncate && append_arg && truncate_arg {
 +        span_lint(
 +            cx,
 +            NONSENSICAL_OPEN_OPTIONS,
 +            span,
 +            "file opened with `append` and `truncate`",
 +        );
 +    }
 +}
index 81c67b4ca6a59d0816908e88e600eeff66e13e38,0000000000000000000000000000000000000000..c409268de769d6616a1555071d5a7d1f75a07cdb
mode 100644,000000..100644
--- /dev/null
@@@ -1,119 -1,0 +1,119 @@@
-             let closure_expr = peel_blocks(&closure_body.value);
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, peel_blocks};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_middle::ty;
 +use rustc_semver::RustcVersion;
 +use rustc_span::sym;
 +
 +use super::OPTION_AS_REF_DEREF;
 +
 +/// lint use of `_.as_ref().map(Deref::deref)` for `Option`s
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &hir::Expr<'_>,
 +    as_ref_recv: &hir::Expr<'_>,
 +    map_arg: &hir::Expr<'_>,
 +    is_mut: bool,
 +    msrv: Option<RustcVersion>,
 +) {
 +    if !meets_msrv(msrv, msrvs::OPTION_AS_DEREF) {
 +        return;
 +    }
 +
 +    let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not);
 +
 +    let option_ty = cx.typeck_results().expr_ty(as_ref_recv);
 +    if !is_type_diagnostic_item(cx, option_ty, sym::Option) {
 +        return;
 +    }
 +
 +    let deref_aliases: [&[&str]; 9] = [
 +        &paths::DEREF_TRAIT_METHOD,
 +        &paths::DEREF_MUT_TRAIT_METHOD,
 +        &paths::CSTRING_AS_C_STR,
 +        &paths::OS_STRING_AS_OS_STR,
 +        &paths::PATH_BUF_AS_PATH,
 +        &paths::STRING_AS_STR,
 +        &paths::STRING_AS_MUT_STR,
 +        &paths::VEC_AS_SLICE,
 +        &paths::VEC_AS_MUT_SLICE,
 +    ];
 +
 +    let is_deref = match map_arg.kind {
 +        hir::ExprKind::Path(ref expr_qpath) => cx
 +            .qpath_res(expr_qpath, map_arg.hir_id)
 +            .opt_def_id()
 +            .map_or(false, |fun_def_id| {
 +                deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path))
 +            }),
 +        hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
 +            let closure_body = cx.tcx.hir().body(body);
++            let closure_expr = peel_blocks(closure_body.value);
 +
 +            match &closure_expr.kind {
 +                hir::ExprKind::MethodCall(_, receiver, [], _) => {
 +                    if_chain! {
 +                        if path_to_local_id(receiver, closure_body.params[0].pat.hir_id);
 +                        let adj = cx
 +                            .typeck_results()
 +                            .expr_adjustments(receiver)
 +                            .iter()
 +                            .map(|x| &x.kind)
 +                            .collect::<Box<[_]>>();
 +                        if let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj;
 +                        then {
 +                            let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap();
 +                            deref_aliases.iter().any(|path| match_def_path(cx, method_did, path))
 +                        } else {
 +                            false
 +                        }
 +                    }
 +                },
 +                hir::ExprKind::AddrOf(hir::BorrowKind::Ref, m, inner) if same_mutability(m) => {
 +                    if_chain! {
 +                        if let hir::ExprKind::Unary(hir::UnOp::Deref, inner1) = inner.kind;
 +                        if let hir::ExprKind::Unary(hir::UnOp::Deref, inner2) = inner1.kind;
 +                        then {
 +                            path_to_local_id(inner2, closure_body.params[0].pat.hir_id)
 +                        } else {
 +                            false
 +                        }
 +                    }
 +                },
 +                _ => false,
 +            }
 +        },
 +        _ => false,
 +    };
 +
 +    if is_deref {
 +        let current_method = if is_mut {
 +            format!(".as_mut().map({})", snippet(cx, map_arg.span, ".."))
 +        } else {
 +            format!(".as_ref().map({})", snippet(cx, map_arg.span, ".."))
 +        };
 +        let method_hint = if is_mut { "as_deref_mut" } else { "as_deref" };
 +        let hint = format!("{}.{}()", snippet(cx, as_ref_recv.span, ".."), method_hint);
 +        let suggestion = format!("try using {} instead", method_hint);
 +
 +        let msg = format!(
 +            "called `{0}` on an Option value. This can be done more directly \
 +            by calling `{1}` instead",
 +            current_method, hint
 +        );
 +        span_lint_and_sugg(
 +            cx,
 +            OPTION_AS_REF_DEREF,
 +            expr.span,
 +            &msg,
 +            &suggestion,
 +            hint,
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
index 5a39b82b027d1643536ec0c4f2fd9237fad57ccf,0000000000000000000000000000000000000000..6657cdccd010c4398a7d0b3dbfd9d8a0e8ca6e91
mode 100644,000000..100644
--- /dev/null
@@@ -1,122 -1,0 +1,122 @@@
-             if let Some((func, [arg_char])) = reduce_unit_expression(&body.value);
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{is_lang_ctor, path_def_id};
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::LangItem::{OptionNone, OptionSome};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::DefIdTree;
 +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>(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(inner_expr)
 +                },
 +                _ => None,
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +/// lint use of `_.map_or(None, _)` for `Option`s and `Result`s
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx hir::Expr<'_>,
 +    recv: &'tcx hir::Expr<'_>,
 +    def_arg: &'tcx hir::Expr<'_>,
 +    map_arg: &'tcx hir::Expr<'_>,
 +) {
 +    let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option);
 +    let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
 +
 +    // There are two variants of this `map_or` lint:
 +    // (1) using `map_or` as an adapter from `Result<T,E>` to `Option<T>`
 +    // (2) using `map_or` as a combinator instead of `and_then`
 +    //
 +    // (For this lint) we don't care if any other type calls `map_or`
 +    if !is_option && !is_result {
 +        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;
 +    }
 +
 +    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(&hir::Closure { body, fn_decl_span, .. }) = map_arg.kind;
 +            let arg_snippet = snippet(cx, fn_decl_span, "..");
 +            let body = cx.tcx.hir().body(body);
++            if let Some((func, [arg_char])) = reduce_unit_expression(body.value);
 +            if let Some(id) = path_def_id(cx, func).map(|ctor_id| cx.tcx.parent(ctor_id));
 +            if Some(id) == cx.tcx.lang_items().option_some_variant();
 +            then {
 +                let func_snippet = snippet(cx, arg_char.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,
 +                );
 +            }
 +        }
 +
 +        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";
 +        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, "..");
 +        span_lint_and_sugg(
 +            cx,
 +            RESULT_MAP_OR_INTO_OPTION,
 +            expr.span,
 +            msg,
 +            "try using `ok` instead",
 +            format!("{0}.ok()", self_snippet),
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
index 76876d8662936846bb7766db36547df3903900a5,0000000000000000000000000000000000000000..b43b9258c471da6ec44eae9a9204d0b4c6327a2d
mode 100644,000000..100644
--- /dev/null
@@@ -1,176 -1,0 +1,181 @@@
-     /// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`.
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::eager_or_lazy::switch_to_lazy_eval;
 +use clippy_utils::source::{snippet, snippet_with_macro_callsite};
 +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_lint::LateContext;
 +use rustc_span::source_map::Span;
 +use rustc_span::symbol::{kw, sym};
 +use std::borrow::Cow;
 +
 +use super::OR_FUN_CALL;
 +
 +/// Checks for the `OR_FUN_CALL` lint.
 +#[allow(clippy::too_many_lines)]
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &hir::Expr<'_>,
 +    method_span: Span,
 +    name: &str,
 +    receiver: &'tcx hir::Expr<'_>,
 +    args: &'tcx [hir::Expr<'_>],
 +) {
-             if name == "unwrap_or";
++    /// Checks for `unwrap_or(T::new())`, `unwrap_or(T::default())`,
++    /// `or_insert(T::new())` or `or_insert(T::default())`.
 +    #[allow(clippy::too_many_arguments)]
 +    fn check_unwrap_or_default(
 +        cx: &LateContext<'_>,
 +        name: &str,
 +        fun: &hir::Expr<'_>,
 +        arg: &hir::Expr<'_>,
 +        or_has_args: bool,
 +        span: Span,
 +        method_span: Span,
 +    ) -> bool {
 +        let is_default_default = || is_trait_item(cx, fun, sym::Default);
 +
 +        let implements_default = |arg, default_trait_id| {
 +            let arg_ty = cx.typeck_results().expr_ty(arg);
 +            implements_trait(cx, arg_ty, default_trait_id, &[])
 +        };
 +
 +        if_chain! {
 +            if !or_has_args;
-                     "unwrap_or_default()".to_string(),
++            if let Some(sugg) = match name {
++                "unwrap_or" => Some("unwrap_or_default"),
++                "or_insert" => Some("or_default"),
++                _ => None,
++            };
 +            if let hir::ExprKind::Path(ref qpath) = fun.kind;
 +            if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default);
 +            let path = last_path_segment(qpath).ident.name;
 +            // needs to target Default::default in particular or be *::new and have a Default impl
 +            // available
 +            if (matches!(path, kw::Default) && is_default_default())
 +                || (matches!(path, sym::new) && implements_default(arg, default_trait_id));
 +
 +            then {
 +                span_lint_and_sugg(
 +                    cx,
 +                    OR_FUN_CALL,
 +                    method_span.with_hi(span.hi()),
 +                    &format!("use of `{}` followed by a call to `{}`", name, path),
 +                    "try this",
-         static KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [
++                    format!("{}()", sugg),
 +                    Applicability::MachineApplicable,
 +                );
 +
 +                true
 +            } else {
 +                false
 +            }
 +        }
 +    }
 +
 +    /// Checks for `*or(foo())`.
 +    #[allow(clippy::too_many_arguments)]
 +    fn check_general_case<'tcx>(
 +        cx: &LateContext<'tcx>,
 +        name: &str,
 +        method_span: Span,
 +        self_expr: &hir::Expr<'_>,
 +        arg: &'tcx hir::Expr<'_>,
 +        span: Span,
 +        // None if lambda is required
 +        fun_span: Option<Span>,
 +    ) {
 +        // (path, fn_has_argument, methods, suffix)
++        const KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [
 +            (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"),
 +            (&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"),
 +            (&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"),
 +            (&paths::RESULT, true, &["or", "unwrap_or"], "else"),
 +        ];
 +
 +        if_chain! {
 +            if KNOW_TYPES.iter().any(|k| k.2.contains(&name));
 +
 +            if switch_to_lazy_eval(cx, arg);
 +            if !contains_return(arg);
 +
 +            let self_ty = cx.typeck_results().expr_ty(self_expr);
 +
 +            if let Some(&(_, fn_has_arguments, poss, suffix)) =
 +                KNOW_TYPES.iter().find(|&&i| match_type(cx, self_ty, i.0));
 +
 +            if poss.contains(&name);
 +
 +            then {
 +                let macro_expanded_snipped;
 +                let sugg: Cow<'_, str> = {
 +                    let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) {
 +                        (false, Some(fun_span)) => (fun_span, false),
 +                        _ => (arg.span, true),
 +                    };
 +                    let snippet = {
 +                        let not_macro_argument_snippet = snippet_with_macro_callsite(cx, snippet_span, "..");
 +                        if not_macro_argument_snippet == "vec![]" {
 +                            macro_expanded_snipped = snippet(cx, snippet_span, "..");
 +                            match macro_expanded_snipped.strip_prefix("$crate::vec::") {
 +                                Some(stripped) => Cow::from(stripped),
 +                                None => macro_expanded_snipped
 +                            }
 +                        }
 +                        else {
 +                            not_macro_argument_snippet
 +                        }
 +                    };
 +
 +                    if use_lambda {
 +                        let l_arg = if fn_has_arguments { "_" } else { "" };
 +                        format!("|{}| {}", l_arg, snippet).into()
 +                    } else {
 +                        snippet
 +                    }
 +                };
 +                let span_replace_word = method_span.with_hi(span.hi());
 +                span_lint_and_sugg(
 +                    cx,
 +                    OR_FUN_CALL,
 +                    span_replace_word,
 +                    &format!("use of `{}` followed by a function call", name),
 +                    "try this",
 +                    format!("{}_{}({})", name, suffix, sugg),
 +                    Applicability::HasPlaceholders,
 +                );
 +            }
 +        }
 +    }
 +
 +    if let [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, arg, or_has_args, expr.span, method_span) {
 +                    let fun_span = if or_has_args { None } else { Some(fun.span) };
 +                    check_general_case(cx, name, method_span, receiver, arg, expr.span, fun_span);
 +                }
 +            },
 +            hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => {
 +                check_general_case(cx, name, method_span, receiver, arg, expr.span, None);
 +            },
 +            _ => (),
 +        }
 +    }
 +}
index 9c3375bf35e7dc523ce4cd89b4b7435354873a8d,0000000000000000000000000000000000000000..851cdf544550f6caf077890c8c3d9f1293058bbd
mode 100644,000000..100644
--- /dev/null
@@@ -1,36 -1,0 +1,36 @@@
-         if !cx.typeck_results().expr_ty(&closure_body.value).is_unit();
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::usage::mutated_variables;
 +use clippy_utils::{expr_or_init, is_trait_method};
 +use if_chain::if_chain;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_span::sym;
 +
 +use super::SUSPICIOUS_MAP;
 +
 +pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) {
 +    if_chain! {
 +        if is_trait_method(cx, count_recv, sym::Iterator);
 +        let closure = expr_or_init(cx, map_arg);
 +        if let Some(def_id) = cx.tcx.hir().opt_local_def_id(closure.hir_id);
 +        if let Some(body_id) = cx.tcx.hir().maybe_body_owned_by(def_id);
 +        let closure_body = cx.tcx.hir().body(body_id);
-             if let Some(map_mutated_vars) = mutated_variables(&closure_body.value, cx) {
++        if !cx.typeck_results().expr_ty(closure_body.value).is_unit();
 +        then {
++            if let Some(map_mutated_vars) = mutated_variables(closure_body.value, cx) {
 +                // A variable is used mutably inside of the closure. Suppress the lint.
 +                if !map_mutated_vars.is_empty() {
 +                    return;
 +                }
 +            }
 +            span_lint_and_help(
 +                cx,
 +                SUSPICIOUS_MAP,
 +                expr.span,
 +                "this call to `map()` won't have an effect on the call to `count()`",
 +                None,
 +                "make sure you did not confuse `map` with `filter`, `for_each` or `inspect`",
 +            );
 +        }
 +    }
 +}
index bafa6fc584d48548e595d218fac04d29d129a54b,0000000000000000000000000000000000000000..4e8c201f470bab9b86fbb2b130ca8af2969f03ef
mode 100644,000000..100644
--- /dev/null
@@@ -1,132 -1,0 +1,131 @@@
-         let mutates_arg =
-             mutated_variables(&body.value, cx).map_or(true, |used_mutably| used_mutably.contains(&arg_id));
-         let (clone_or_copy_needed, _) = clone_or_copy_needed(cx, body.params[0].pat, &body.value);
 +use super::utils::clone_or_copy_needed;
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::ty::is_copy;
 +use clippy_utils::usage::mutated_variables;
 +use clippy_utils::{is_lang_ctor, is_trait_method, path_to_local_id};
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::{walk_expr, Visitor};
 +use rustc_hir::LangItem::{OptionNone, OptionSome};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty;
 +use rustc_span::sym;
 +
 +use super::UNNECESSARY_FILTER_MAP;
 +use super::UNNECESSARY_FIND_MAP;
 +
 +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, name: &str) {
 +    if !is_trait_method(cx, expr, sym::Iterator) {
 +        return;
 +    }
 +
 +    if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = arg.kind {
 +        let body = cx.tcx.hir().body(body);
 +        let arg_id = body.params[0].pat.hir_id;
-         let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, &body.value);
++        let mutates_arg = mutated_variables(body.value, cx).map_or(true, |used_mutably| used_mutably.contains(&arg_id));
++        let (clone_or_copy_needed, _) = clone_or_copy_needed(cx, body.params[0].pat, body.value);
 +
-         return_visitor.visit_expr(&body.value);
++        let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, body.value);
 +
 +        let mut return_visitor = ReturnVisitor::new(cx, arg_id);
-             match cx.typeck_results().expr_ty(&body.value).kind() {
++        return_visitor.visit_expr(body.value);
 +        found_mapping |= return_visitor.found_mapping;
 +        found_filtering |= return_visitor.found_filtering;
 +
 +        let in_ty = cx.typeck_results().node_type(body.params[0].hir_id);
 +        let sugg = if !found_filtering {
 +            if name == "filter_map" { "map" } else { "map(..).next()" }
 +        } else if !found_mapping && !mutates_arg && (!clone_or_copy_needed || is_copy(cx, in_ty)) {
++            match cx.typeck_results().expr_ty(body.value).kind() {
 +                ty::Adt(adt, subst)
 +                    if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && in_ty == subst.type_at(0) =>
 +                {
 +                    if name == "filter_map" { "filter" } else { "find" }
 +                },
 +                _ => return,
 +            }
 +        } else {
 +            return;
 +        };
 +        span_lint(
 +            cx,
 +            if name == "filter_map" {
 +                UNNECESSARY_FILTER_MAP
 +            } else {
 +                UNNECESSARY_FIND_MAP
 +            },
 +            expr.span,
 +            &format!("this `.{}` can be written more simply using `.{}`", name, sugg),
 +        );
 +    }
 +}
 +
 +// returns (found_mapping, found_filtering)
 +fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> (bool, bool) {
 +    match &expr.kind {
 +        hir::ExprKind::Call(func, args) => {
 +            if let hir::ExprKind::Path(ref path) = func.kind {
 +                if is_lang_ctor(cx, path, OptionSome) {
 +                    if path_to_local_id(&args[0], arg_id) {
 +                        return (false, false);
 +                    }
 +                    return (true, false);
 +                }
 +            }
 +            (true, true)
 +        },
 +        hir::ExprKind::Block(block, _) => block
 +            .expr
 +            .as_ref()
 +            .map_or((false, false), |expr| check_expression(cx, arg_id, expr)),
 +        hir::ExprKind::Match(_, arms, _) => {
 +            let mut found_mapping = false;
 +            let mut found_filtering = false;
 +            for arm in *arms {
 +                let (m, f) = check_expression(cx, arg_id, arm.body);
 +                found_mapping |= m;
 +                found_filtering |= f;
 +            }
 +            (found_mapping, found_filtering)
 +        },
 +        // There must be an else_arm or there will be a type error
 +        hir::ExprKind::If(_, if_arm, Some(else_arm)) => {
 +            let if_check = check_expression(cx, arg_id, if_arm);
 +            let else_check = check_expression(cx, arg_id, else_arm);
 +            (if_check.0 | else_check.0, if_check.1 | else_check.1)
 +        },
 +        hir::ExprKind::Path(path) if is_lang_ctor(cx, path, OptionNone) => (false, true),
 +        _ => (true, true),
 +    }
 +}
 +
 +struct ReturnVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    arg_id: hir::HirId,
 +    // Found a non-None return that isn't Some(input)
 +    found_mapping: bool,
 +    // Found a return that isn't Some
 +    found_filtering: bool,
 +}
 +
 +impl<'a, 'tcx> ReturnVisitor<'a, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>, arg_id: hir::HirId) -> ReturnVisitor<'a, 'tcx> {
 +        ReturnVisitor {
 +            cx,
 +            arg_id,
 +            found_mapping: false,
 +            found_filtering: false,
 +        }
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for ReturnVisitor<'a, 'tcx> {
 +    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
 +        if let hir::ExprKind::Ret(Some(expr)) = &expr.kind {
 +            let (found_mapping, found_filtering) = check_expression(self.cx, self.arg_id, expr);
 +            self.found_mapping |= found_mapping;
 +            self.found_filtering |= found_filtering;
 +        } else {
 +            walk_expr(self, expr);
 +        }
 +    }
 +}
index c3531d4d0511e9e8f730dbc64e35190fc2228f53,0000000000000000000000000000000000000000..c17ef6809f91281aa7ab9490d0092a1f077d8a0f
mode 100644,000000..100644
--- /dev/null
@@@ -1,95 -1,0 +1,95 @@@
-             let closure_expr = peel_blocks(&closure_body.value);
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::{is_trait_method, path_to_local_id, peel_blocks, strip_pat_refs};
 +use if_chain::if_chain;
 +use rustc_ast::ast;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::PatKind;
 +use rustc_lint::LateContext;
 +use rustc_span::{source_map::Span, sym};
 +
 +use super::UNNECESSARY_FOLD;
 +
 +pub(super) fn check(
 +    cx: &LateContext<'_>,
 +    expr: &hir::Expr<'_>,
 +    init: &hir::Expr<'_>,
 +    acc: &hir::Expr<'_>,
 +    fold_span: Span,
 +) {
 +    fn check_fold_with_op(
 +        cx: &LateContext<'_>,
 +        expr: &hir::Expr<'_>,
 +        acc: &hir::Expr<'_>,
 +        fold_span: Span,
 +        op: hir::BinOpKind,
 +        replacement_method_name: &str,
 +        replacement_has_args: bool,
 +    ) {
 +        if_chain! {
 +            // Extract the body of the closure passed to fold
 +            if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = acc.kind;
 +            let closure_body = cx.tcx.hir().body(body);
++            let closure_expr = peel_blocks(closure_body.value);
 +
 +            // Check if the closure body is of the form `acc <op> some_expr(x)`
 +            if let hir::ExprKind::Binary(ref bin_op, left_expr, right_expr) = closure_expr.kind;
 +            if bin_op.node == op;
 +
 +            // Extract the names of the two arguments to the closure
 +            if let [param_a, param_b] = closure_body.params;
 +            if let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(param_a.pat).kind;
 +            if let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(param_b.pat).kind;
 +
 +            if path_to_local_id(left_expr, first_arg_id);
 +            if replacement_has_args || path_to_local_id(right_expr, second_arg_id);
 +
 +            then {
 +                let mut applicability = Applicability::MachineApplicable;
 +                let sugg = if replacement_has_args {
 +                    format!(
 +                        "{replacement}(|{s}| {r})",
 +                        replacement = replacement_method_name,
 +                        s = second_arg_ident,
 +                        r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability),
 +                    )
 +                } else {
 +                    format!(
 +                        "{replacement}()",
 +                        replacement = replacement_method_name,
 +                    )
 +                };
 +
 +                span_lint_and_sugg(
 +                    cx,
 +                    UNNECESSARY_FOLD,
 +                    fold_span.with_hi(expr.span.hi()),
 +                    // TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f)
 +                    "this `.fold` can be written more succinctly using another method",
 +                    "try",
 +                    sugg,
 +                    applicability,
 +                );
 +            }
 +        }
 +    }
 +
 +    // Check that this is a call to Iterator::fold rather than just some function called fold
 +    if !is_trait_method(cx, expr, sym::Iterator) {
 +        return;
 +    }
 +
 +    // Check if the first argument to .fold is a suitable literal
 +    if let hir::ExprKind::Lit(ref lit) = init.kind {
 +        match lit.node {
 +            ast::LitKind::Bool(false) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Or, "any", true),
 +            ast::LitKind::Bool(true) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::And, "all", true),
 +            ast::LitKind::Int(0, _) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Add, "sum", false),
 +            ast::LitKind::Int(1, _) => {
 +                check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Mul, "product", false);
 +            },
 +            _ => (),
 +        }
 +    }
 +}
index 6f25acca1de6123e1a08586d8edd57f17fe192d8,0000000000000000000000000000000000000000..ed5a75b0f3ce574ee50790436ef6704d6323bbe0
mode 100644,000000..100644
--- /dev/null
@@@ -1,228 -1,0 +1,228 @@@
-         if is_trait_method(cx, &closure_body.value, sym::Ord);
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::is_trait_method;
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::ty::implements_trait;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, subst::GenericArgKind};
 +use rustc_span::sym;
 +use rustc_span::symbol::Ident;
 +use std::iter;
 +
 +use super::UNNECESSARY_SORT_BY;
 +
 +enum LintTrigger {
 +    Sort(SortDetection),
 +    SortByKey(SortByKeyDetection),
 +}
 +
 +struct SortDetection {
 +    vec_name: String,
 +}
 +
 +struct SortByKeyDetection {
 +    vec_name: String,
 +    closure_arg: String,
 +    closure_body: String,
 +    reverse: bool,
 +}
 +
 +/// Detect if the two expressions are mirrored (identical, except one
 +/// contains a and the other replaces it with b)
 +fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident: &Ident) -> bool {
 +    match (&a_expr.kind, &b_expr.kind) {
 +        // Two boxes with mirrored contents
 +        (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => {
 +            mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
 +        },
 +        // Two arrays with mirrored contents
 +        (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => {
 +            iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
 +        },
 +        // The two exprs are function calls.
 +        // Check to see that the function itself and its arguments are mirrored
 +        (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) => {
 +            mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
 +                && iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
 +        },
 +        // The two exprs are method calls.
 +        // Check to see that the function is the same and the arguments are mirrored
 +        // This is enough because the receiver of the method is listed in the arguments
 +        (
 +            ExprKind::MethodCall(left_segment, left_receiver, left_args, _),
 +            ExprKind::MethodCall(right_segment, right_receiver, right_args, _),
 +        ) => {
 +            left_segment.ident == right_segment.ident
 +                && iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
 +                && mirrored_exprs(left_receiver, a_ident, right_receiver, b_ident)
 +        },
 +        // Two tuples with mirrored contents
 +        (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => {
 +            iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
 +        },
 +        // Two binary ops, which are the same operation and which have mirrored arguments
 +        (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) => {
 +            left_op.node == right_op.node
 +                && mirrored_exprs(left_left, a_ident, right_left, b_ident)
 +                && mirrored_exprs(left_right, a_ident, right_right, b_ident)
 +        },
 +        // Two unary ops, which are the same operation and which have the same argument
 +        (ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) => {
 +            left_op == right_op && mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
 +        },
 +        // The two exprs are literals of some kind
 +        (ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node,
 +        (ExprKind::Cast(left, _), ExprKind::Cast(right, _)) => mirrored_exprs(left, a_ident, right, b_ident),
 +        (ExprKind::DropTemps(left_block), ExprKind::DropTemps(right_block)) => {
 +            mirrored_exprs(left_block, a_ident, right_block, b_ident)
 +        },
 +        (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => {
 +            left_ident.name == right_ident.name && mirrored_exprs(left_expr, a_ident, right_expr, right_ident)
 +        },
 +        // Two paths: either one is a and the other is b, or they're identical to each other
 +        (
 +            ExprKind::Path(QPath::Resolved(
 +                _,
 +                Path {
 +                    segments: left_segments,
 +                    ..
 +                },
 +            )),
 +            ExprKind::Path(QPath::Resolved(
 +                _,
 +                Path {
 +                    segments: right_segments,
 +                    ..
 +                },
 +            )),
 +        ) => {
 +            (iter::zip(*left_segments, *right_segments).all(|(left, right)| left.ident == right.ident)
 +                && left_segments
 +                    .iter()
 +                    .all(|seg| &seg.ident != a_ident && &seg.ident != b_ident))
 +                || (left_segments.len() == 1
 +                    && &left_segments[0].ident == a_ident
 +                    && right_segments.len() == 1
 +                    && &right_segments[0].ident == b_ident)
 +        },
 +        // Matching expressions, but one or both is borrowed
 +        (
 +            ExprKind::AddrOf(left_kind, Mutability::Not, left_expr),
 +            ExprKind::AddrOf(right_kind, Mutability::Not, right_expr),
 +        ) => left_kind == right_kind && mirrored_exprs(left_expr, a_ident, right_expr, b_ident),
 +        (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) => mirrored_exprs(a_expr, a_ident, right_expr, b_ident),
 +        (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(left_expr, a_ident, b_expr, b_ident),
 +        _ => false,
 +    }
 +}
 +
 +fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) -> Option<LintTrigger> {
 +    if_chain! {
 +        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
 +        if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
 +        if cx.tcx.type_of(impl_id).is_slice();
 +        if let ExprKind::Closure(&Closure { body, .. }) = arg.kind;
 +        if let closure_body = cx.tcx.hir().body(body);
 +        if let &[
 +            Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..},
 +            Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. }
 +        ] = &closure_body.params;
 +        if let ExprKind::MethodCall(method_path, left_expr, [right_expr], _) = closure_body.value.kind;
 +        if method_path.ident.name == sym::cmp;
++        if is_trait_method(cx, closure_body.value, sym::Ord);
 +        then {
 +            let (closure_body, closure_arg, reverse) = if mirrored_exprs(
 +                left_expr,
 +                left_ident,
 +                right_expr,
 +                right_ident
 +            ) {
 +                (Sugg::hir(cx, left_expr, "..").to_string(), left_ident.name.to_string(), false)
 +            } else if mirrored_exprs(left_expr, right_ident, right_expr, left_ident) {
 +                (Sugg::hir(cx, left_expr, "..").to_string(), right_ident.name.to_string(), true)
 +            } else {
 +                return None;
 +            };
 +            let vec_name = Sugg::hir(cx, recv, "..").to_string();
 +
 +            if_chain! {
 +                if let ExprKind::Path(QPath::Resolved(_, Path {
 +                    segments: [PathSegment { ident: left_name, .. }], ..
 +                })) = &left_expr.kind;
 +                if left_name == left_ident;
 +                if cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| {
 +                    implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[])
 +                });
 +                then {
 +                    return Some(LintTrigger::Sort(SortDetection { vec_name }));
 +                }
 +            }
 +
 +            if !expr_borrows(cx, left_expr) {
 +                return Some(LintTrigger::SortByKey(SortByKeyDetection {
 +                    vec_name,
 +                    closure_arg,
 +                    closure_body,
 +                    reverse,
 +                }));
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    let ty = cx.typeck_results().expr_ty(expr);
 +    matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
 +}
 +
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    recv: &'tcx Expr<'_>,
 +    arg: &'tcx Expr<'_>,
 +    is_unstable: bool,
 +) {
 +    match detect_lint(cx, expr, recv, arg) {
 +        Some(LintTrigger::SortByKey(trigger)) => span_lint_and_sugg(
 +            cx,
 +            UNNECESSARY_SORT_BY,
 +            expr.span,
 +            "use Vec::sort_by_key here instead",
 +            "try",
 +            format!(
 +                "{}.sort{}_by_key(|{}| {})",
 +                trigger.vec_name,
 +                if is_unstable { "_unstable" } else { "" },
 +                trigger.closure_arg,
 +                if trigger.reverse {
 +                    format!("std::cmp::Reverse({})", trigger.closure_body)
 +                } else {
 +                    trigger.closure_body.to_string()
 +                },
 +            ),
 +            if trigger.reverse {
 +                Applicability::MaybeIncorrect
 +            } else {
 +                Applicability::MachineApplicable
 +            },
 +        ),
 +        Some(LintTrigger::Sort(trigger)) => span_lint_and_sugg(
 +            cx,
 +            UNNECESSARY_SORT_BY,
 +            expr.span,
 +            "use Vec::sort here instead",
 +            "try",
 +            format!(
 +                "{}.sort{}()",
 +                trigger.vec_name,
 +                if is_unstable { "_unstable" } else { "" },
 +            ),
 +            Applicability::MachineApplicable,
 +        ),
 +        None => {},
 +    }
 +}
index 85da97a39f9a1539435c7f4d202b2dd5eb06fda9,0000000000000000000000000000000000000000..763bfafecef14916ebbccec2203d4c569eabed9a
mode 100644,000000..100644
--- /dev/null
@@@ -1,438 -1,0 +1,499 @@@
- use clippy_utils::ty::{
-     get_associated_type, get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, peel_mid_ty_refs,
- };
- use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item};
 +use super::implicit_clone::is_clone_like;
 +use super::unnecessary_iter_cloned::{self, is_into_iter};
++use crate::rustc_middle::ty::Subst;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_opt;
- use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind};
++use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs};
++use clippy_utils::visitors::find_all_ret_expressions;
++use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty};
 +use clippy_utils::{meets_msrv, msrvs};
 +use rustc_errors::Applicability;
- use rustc_middle::ty::{self, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
++use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, Node};
++use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_lint::LateContext;
 +use rustc_middle::mir::Mutability;
 +use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
 +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
-             } else if is_to_owned_like(cx, method_name, method_def_id) {
++use rustc_middle::ty::EarlyBinder;
++use rustc_middle::ty::{self, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
 +use rustc_semver::RustcVersion;
 +use rustc_span::{sym, Symbol};
++use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
++use rustc_typeck::check::{FnCtxt, Inherited};
 +use std::cmp::max;
 +
 +use super::UNNECESSARY_TO_OWNED;
 +
 +pub fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +    method_name: Symbol,
 +    receiver: &'tcx Expr<'_>,
 +    args: &'tcx [Expr<'_>],
 +    msrv: Option<RustcVersion>,
 +) {
 +    if_chain! {
 +        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
 +        if args.is_empty();
 +        then {
 +            if is_cloned_or_copied(cx, method_name, method_def_id) {
 +                unnecessary_iter_cloned::check(cx, expr, method_name, receiver);
-         if let Some((callee_def_id, call_substs, call_receiver, call_args)) = get_callee_substs_and_args(cx, maybe_call);
++            } else if is_to_owned_like(cx, expr, method_name, method_def_id) {
 +                // At this point, we know the call is of a `to_owned`-like function. The functions
 +                // `check_addr_of_expr` and `check_call_arg` determine whether the call is unnecessary
 +                // based on its context, that is, whether it is a referent in an `AddrOf` expression, an
 +                // argument in a `into_iter` call, or an argument in the call of some other function.
 +                if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) {
 +                    return;
 +                }
 +                if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) {
 +                    return;
 +                }
 +                check_other_call_arg(cx, expr, method_name, receiver);
 +            }
 +        }
 +    }
 +}
 +
 +/// Checks whether `expr` is a referent in an `AddrOf` expression and, if so, determines whether its
 +/// call of a `to_owned`-like function is unnecessary.
 +#[allow(clippy::too_many_lines)]
 +fn check_addr_of_expr(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    method_name: Symbol,
 +    method_def_id: DefId,
 +    receiver: &Expr<'_>,
 +) -> bool {
 +    if_chain! {
 +        if let Some(parent) = get_parent_expr(cx, expr);
 +        if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind;
 +        let adjustments = cx.typeck_results().expr_adjustments(parent).iter().collect::<Vec<_>>();
 +        if let
 +            // For matching uses of `Cow::from`
 +            [
 +                Adjustment {
 +                    kind: Adjust::Deref(None),
 +                    target: referent_ty,
 +                },
 +                Adjustment {
 +                    kind: Adjust::Borrow(_),
 +                    target: target_ty,
 +                },
 +            ]
 +            // For matching uses of arrays
 +            | [
 +                Adjustment {
 +                    kind: Adjust::Deref(None),
 +                    target: referent_ty,
 +                },
 +                Adjustment {
 +                    kind: Adjust::Borrow(_),
 +                    ..
 +                },
 +                Adjustment {
 +                    kind: Adjust::Pointer(_),
 +                    target: target_ty,
 +                },
 +            ]
 +            // For matching everything else
 +            | [
 +                Adjustment {
 +                    kind: Adjust::Deref(None),
 +                    target: referent_ty,
 +                },
 +                Adjustment {
 +                    kind: Adjust::Deref(Some(OverloadedDeref { .. })),
 +                    ..
 +                },
 +                Adjustment {
 +                    kind: Adjust::Borrow(_),
 +                    target: target_ty,
 +                },
 +            ] = adjustments[..];
 +        let receiver_ty = cx.typeck_results().expr_ty(receiver);
 +        let (target_ty, n_target_refs) = peel_mid_ty_refs(*target_ty);
 +        let (receiver_ty, n_receiver_refs) = peel_mid_ty_refs(receiver_ty);
 +        // Only flag cases satisfying at least one of the following three conditions:
 +        // * the referent and receiver types are distinct
 +        // * the referent/receiver type is a copyable array
 +        // * the method is `Cow::into_owned`
 +        // This restriction is to ensure there is no overlap between `redundant_clone` and this
 +        // lint. It also avoids the following false positive:
 +        //  https://github.com/rust-lang/rust-clippy/issues/8759
 +        //   Arrays are a bit of a corner case. Non-copyable arrays are handled by
 +        // `redundant_clone`, but copyable arrays are not.
 +        if *referent_ty != receiver_ty
 +            || (matches!(referent_ty.kind(), ty::Array(..)) && is_copy(cx, *referent_ty))
 +            || is_cow_into_owned(cx, method_name, method_def_id);
 +        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
 +        then {
 +            if receiver_ty == target_ty && n_target_refs >= n_receiver_refs {
 +                span_lint_and_sugg(
 +                    cx,
 +                    UNNECESSARY_TO_OWNED,
 +                    parent.span,
 +                    &format!("unnecessary use of `{}`", method_name),
 +                    "use",
 +                    format!(
 +                        "{:&>width$}{}",
 +                        "",
 +                        receiver_snippet,
 +                        width = n_target_refs - n_receiver_refs
 +                    ),
 +                    Applicability::MachineApplicable,
 +                );
 +                return true;
 +            }
 +            if_chain! {
 +                if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref);
 +                if implements_trait(cx, receiver_ty, deref_trait_id, &[]);
 +                if get_associated_type(cx, receiver_ty, deref_trait_id, "Target") == Some(target_ty);
 +                then {
 +                    if n_receiver_refs > 0 {
 +                        span_lint_and_sugg(
 +                            cx,
 +                            UNNECESSARY_TO_OWNED,
 +                            parent.span,
 +                            &format!("unnecessary use of `{}`", method_name),
 +                            "use",
 +                            receiver_snippet,
 +                            Applicability::MachineApplicable,
 +                        );
 +                    } else {
 +                        span_lint_and_sugg(
 +                            cx,
 +                            UNNECESSARY_TO_OWNED,
 +                            expr.span.with_lo(receiver.span.hi()),
 +                            &format!("unnecessary use of `{}`", method_name),
 +                            "remove this",
 +                            String::new(),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    }
 +                    return true;
 +                }
 +            }
 +            if_chain! {
 +                if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef);
 +                if implements_trait(cx, receiver_ty, as_ref_trait_id, &[GenericArg::from(target_ty)]);
 +                then {
 +                    span_lint_and_sugg(
 +                        cx,
 +                        UNNECESSARY_TO_OWNED,
 +                        parent.span,
 +                        &format!("unnecessary use of `{}`", method_name),
 +                        "use",
 +                        format!("{}.as_ref()", receiver_snippet),
 +                        Applicability::MachineApplicable,
 +                    );
 +                    return true;
 +                }
 +            }
 +        }
 +    }
 +    false
 +}
 +
 +/// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its
 +/// call of a `to_owned`-like function is unnecessary.
 +fn check_into_iter_call_arg(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    method_name: Symbol,
 +    receiver: &Expr<'_>,
 +    msrv: Option<RustcVersion>,
 +) -> bool {
 +    if_chain! {
 +        if let Some(parent) = get_parent_expr(cx, expr);
 +        if let Some(callee_def_id) = fn_def_id(cx, parent);
 +        if is_into_iter(cx, callee_def_id);
 +        if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
 +        let parent_ty = cx.typeck_results().expr_ty(parent);
 +        if implements_trait(cx, parent_ty, iterator_trait_id, &[]);
 +        if let Some(item_ty) = get_iterator_item_ty(cx, parent_ty);
 +        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
 +        then {
 +            if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
 +                return true;
 +            }
 +            let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
 +                "copied"
 +            } else {
 +                "cloned"
 +            };
 +            // The next suggestion may be incorrect because the removal of the `to_owned`-like
 +            // function could cause the iterator to hold a reference to a resource that is used
 +            // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148.
 +            span_lint_and_sugg(
 +                cx,
 +                UNNECESSARY_TO_OWNED,
 +                parent.span,
 +                &format!("unnecessary use of `{}`", method_name),
 +                "use",
 +                format!("{}.iter().{}()", receiver_snippet, cloned_or_copied),
 +                Applicability::MaybeIncorrect,
 +            );
 +            return true;
 +        }
 +    }
 +    false
 +}
 +
 +/// Checks whether `expr` is an argument in a function call and, if so, determines whether its call
 +/// of a `to_owned`-like function is unnecessary.
 +fn check_other_call_arg<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +    method_name: Symbol,
 +    receiver: &'tcx Expr<'tcx>,
 +) -> bool {
 +    if_chain! {
 +        if let Some((maybe_call, maybe_arg)) = skip_addr_of_ancestors(cx, expr);
-         let index = if let Some(call_receiver) = call_receiver {
-             std::iter::once(call_receiver).chain(call_args.iter()).position(|arg| arg.hir_id == maybe_arg.hir_id)
-         } else {
-             call_args.iter().position(|arg| arg.hir_id == maybe_arg.hir_id)
-         };
-         if let Some(i) = index;
++        if let Some((callee_def_id, _, recv, call_args)) = get_callee_substs_and_args(cx, maybe_call);
 +        let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
-         if let (trait_predicates, projection_predicates) = get_input_traits_and_projections(cx, callee_def_id, input);
++        if let Some(i) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == maybe_arg.hir_id);
 +        if let Some(input) = fn_sig.inputs().get(i);
 +        let (input, n_refs) = peel_mid_ty_refs(*input);
-         // If the callee has type parameters, they could appear in `projection_predicate.ty` or the
-         // types of `trait_predicate.trait_ref.substs`.
-         if if trait_predicate.def_id() == deref_trait_id {
-             if let [projection_predicate] = projection_predicates[..] {
-                 let normalized_ty =
-                     cx.tcx
-                         .subst_and_normalize_erasing_regions(call_substs, cx.param_env, projection_predicate.term);
-                 implements_trait(cx, receiver_ty, deref_trait_id, &[])
-                     && get_associated_type(cx, receiver_ty, deref_trait_id, "Target")
-                         .map_or(false, |ty| ty::TermKind::Ty(ty) == normalized_ty.unpack())
-             } else {
-                 false
-             }
-         } else if trait_predicate.def_id() == as_ref_trait_id {
-             let composed_substs = compose_substs(
-                 cx,
-                 &trait_predicate.trait_ref.substs.iter().skip(1).collect::<Vec<_>>()[..],
-                 call_substs,
-             );
-             // if `expr` is a `String` and generic target is [u8], skip
-             // (https://github.com/rust-lang/rust-clippy/issues/9317).
-             if let [subst] = composed_substs[..]
-                 && let GenericArgKind::Type(arg_ty) = subst.unpack()
-                 && arg_ty.is_slice()
-                 && let inner_ty = arg_ty.builtin_index().unwrap()
-                 && let ty::Uint(ty::UintTy::U8) = inner_ty.kind()
-                 && let self_ty = cx.typeck_results().expr_ty(expr).peel_refs()
-                 && is_type_diagnostic_item(cx, self_ty, sym::String) {
-                 false
-             } else {
-                 implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs)
-             }
-         } else {
-             false
-         };
++        if let (trait_predicates, _) = get_input_traits_and_projections(cx, callee_def_id, input);
 +        if let Some(sized_def_id) = cx.tcx.lang_items().sized_trait();
 +        if let [trait_predicate] = trait_predicates
 +            .iter()
 +            .filter(|trait_predicate| trait_predicate.def_id() != sized_def_id)
 +            .collect::<Vec<_>>()[..];
 +        if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref);
 +        if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef);
++        if trait_predicate.def_id() == deref_trait_id || trait_predicate.def_id() == as_ref_trait_id;
 +        let receiver_ty = cx.typeck_results().expr_ty(receiver);
-         // If the trait is `AsRef` and the input type variable `T` occurs in the output type, then
-         // `T` must not be instantiated with a reference
-         // (https://github.com/rust-lang/rust-clippy/issues/8507).
-         if (n_refs == 0 && !receiver_ty.is_ref())
-             || trait_predicate.def_id() != as_ref_trait_id
-             || !fn_sig.output().contains(input);
++        if can_change_type(cx, maybe_arg, receiver_ty);
 +        // We can't add an `&` when the trait is `Deref` because `Target = &T` won't match
 +        // `Target = T`.
 +        if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id;
 +        let n_refs = max(n_refs, if is_copy(cx, receiver_ty) { 0 } else { 1 });
-         if let ExprKind::MethodCall(_, receiver, args, _) = expr.kind;
 +        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                UNNECESSARY_TO_OWNED,
 +                maybe_arg.span,
 +                &format!("unnecessary use of `{}`", method_name),
 +                "use",
 +                format!("{:&>width$}{}", "", receiver_snippet, width = n_refs),
 +                Applicability::MachineApplicable,
 +            );
 +            return true;
 +        }
 +    }
 +    false
 +}
 +
 +/// Walks an expression's ancestors until it finds a non-`AddrOf` expression. Returns the first such
 +/// expression found (if any) along with the immediately prior expression.
 +fn skip_addr_of_ancestors<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    mut expr: &'tcx Expr<'tcx>,
 +) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
 +    while let Some(parent) = get_parent_expr(cx, expr) {
 +        if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind {
 +            expr = parent;
 +        } else {
 +            return Some((parent, expr));
 +        }
 +    }
 +    None
 +}
 +
 +/// Checks whether an expression is a function or method call and, if so, returns its `DefId`,
 +/// `Substs`, and arguments.
 +fn get_callee_substs_and_args<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +) -> Option<(DefId, SubstsRef<'tcx>, Option<&'tcx Expr<'tcx>>, &'tcx [Expr<'tcx>])> {
 +    if_chain! {
 +        if let ExprKind::Call(callee, args) = expr.kind;
 +        let callee_ty = cx.typeck_results().expr_ty(callee);
 +        if let ty::FnDef(callee_def_id, _) = callee_ty.kind();
 +        then {
 +            let substs = cx.typeck_results().node_substs(callee.hir_id);
 +            return Some((*callee_def_id, substs, None, args));
 +        }
 +    }
 +    if_chain! {
-             return Some((method_def_id, substs, Some(receiver), args));
++        if let ExprKind::MethodCall(_, recv, args, _) = expr.kind;
 +        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
 +        then {
 +            let substs = cx.typeck_results().node_substs(expr.hir_id);
- /// Composes two substitutions by applying the latter to the types of the former.
- fn compose_substs<'tcx>(
-     cx: &LateContext<'tcx>,
-     left: &[GenericArg<'tcx>],
-     right: SubstsRef<'tcx>,
- ) -> Vec<GenericArg<'tcx>> {
-     left.iter()
-         .map(|arg| {
-             if let GenericArgKind::Type(arg_ty) = arg.unpack() {
-                 let normalized_ty = cx.tcx.subst_and_normalize_erasing_regions(right, cx.param_env, arg_ty);
-                 GenericArg::from(normalized_ty)
-             } else {
-                 *arg
++            return Some((method_def_id, substs, Some(recv), args));
 +        }
 +    }
 +    None
 +}
 +
 +/// Returns the `TraitPredicate`s and `ProjectionPredicate`s for a function's input type.
 +fn get_input_traits_and_projections<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    callee_def_id: DefId,
 +    input: Ty<'tcx>,
 +) -> (Vec<TraitPredicate<'tcx>>, Vec<ProjectionPredicate<'tcx>>) {
 +    let mut trait_predicates = Vec::new();
 +    let mut projection_predicates = Vec::new();
 +    for predicate in cx.tcx.param_env(callee_def_id).caller_bounds() {
 +        match predicate.kind().skip_binder() {
 +            PredicateKind::Trait(trait_predicate) => {
 +                if trait_predicate.trait_ref.self_ty() == input {
 +                    trait_predicates.push(trait_predicate);
 +                }
 +            },
 +            PredicateKind::Projection(projection_predicate) => {
 +                if projection_predicate.projection_ty.self_ty() == input {
 +                    projection_predicates.push(projection_predicate);
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +    (trait_predicates, projection_predicates)
 +}
 +
-         })
-         .collect()
++fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<'a>) -> bool {
++    for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
++        match node {
++            Node::Stmt(_) => return true,
++            Node::Block(..) => continue,
++            Node::Item(item) => {
++                if let ItemKind::Fn(_, _, body_id) = &item.kind
++                && let output_ty = return_ty(cx, item.hir_id())
++                && let local_def_id = cx.tcx.hir().local_def_id(item.hir_id())
++                && Inherited::build(cx.tcx, local_def_id).enter(|inherited| {
++                    let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, item.hir_id());
++                    fn_ctxt.can_coerce(ty, output_ty)
++                }) {
++                    if has_lifetime(output_ty) && has_lifetime(ty) {
++                        return false;
++                    }
++                    let body = cx.tcx.hir().body(*body_id);
++                    let body_expr = &body.value;
++                    let mut count = 0;
++                    return find_all_ret_expressions(cx, body_expr, |_| { count += 1; count <= 1 });
++                }
 +            }
- fn is_to_owned_like(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
++            Node::Expr(parent_expr) => {
++                if let Some((callee_def_id, call_substs, recv, call_args)) = get_callee_substs_and_args(cx, parent_expr)
++                {
++                    let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
++                    if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id)
++                        && let Some(param_ty) = fn_sig.inputs().get(arg_index)
++                        && let ty::Param(ParamTy { index: param_index , ..}) = param_ty.kind()
++                    {
++                        if fn_sig
++                            .inputs()
++                            .iter()
++                            .enumerate()
++                            .filter(|(i, _)| *i != arg_index)
++                            .any(|(_, ty)| ty.contains(*param_ty))
++                        {
++                            return false;
++                        }
++
++                        let mut trait_predicates = cx.tcx.param_env(callee_def_id)
++                            .caller_bounds().iter().filter(|predicate| {
++                            if let PredicateKind::Trait(trait_predicate) =  predicate.kind().skip_binder()
++                                && trait_predicate.trait_ref.self_ty() == *param_ty {
++                                    true
++                                } else {
++                                false
++                            }
++                        });
++
++                        let new_subst = cx.tcx.mk_substs(
++                            call_substs.iter()
++                                .enumerate()
++                                .map(|(i, t)|
++                                     if i == (*param_index as usize) {
++                                         GenericArg::from(ty)
++                                     } else {
++                                         t
++                                     }));
++
++                        if trait_predicates.any(|predicate| {
++                            let predicate = EarlyBinder(predicate).subst(cx.tcx, new_subst);
++                            let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate);
++                            !cx.tcx
++                                .infer_ctxt()
++                                .enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
++                        }) {
++                            return false;
++                        }
++
++                        let output_ty = fn_sig.output();
++                        if output_ty.contains(*param_ty) {
++                            if let Ok(new_ty)  = cx.tcx.try_subst_and_normalize_erasing_regions(
++                                new_subst, cx.param_env, output_ty) {
++                                expr = parent_expr;
++                                ty = new_ty;
++                                continue;
++                            }
++                            return false;
++                        }
++
++                        return true;
++                    }
++                } else if let ExprKind::Block(..) = parent_expr.kind {
++                    continue;
++                }
++                return false;
++            },
++            _ => return false,
++        }
++    }
++
++    false
++}
++
++fn has_lifetime(ty: Ty<'_>) -> bool {
++    ty.walk().any(|t| matches!(t.unpack(), GenericArgKind::Lifetime(_)))
 +}
 +
 +/// Returns true if the named method is `Iterator::cloned` or `Iterator::copied`.
 +fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
 +    (method_name.as_str() == "cloned" || method_name.as_str() == "copied")
 +        && is_diag_trait_item(cx, method_def_id, sym::Iterator)
 +}
 +
 +/// Returns true if the named method can be used to convert the receiver to its "owned"
 +/// representation.
-         || is_to_string(cx, method_name, method_def_id)
++fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: Symbol, method_def_id: DefId) -> bool {
 +    is_clone_like(cx, method_name.as_str(), method_def_id)
 +        || is_cow_into_owned(cx, method_name, method_def_id)
- /// Returns true if the named method is `ToString::to_string`.
- fn is_to_string(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
-     method_name == sym::to_string && is_diag_trait_item(cx, method_def_id, sym::ToString)
++        || is_to_string_on_string_like(cx, call_expr, method_name, method_def_id)
 +}
 +
 +/// Returns true if the named method is `Cow::into_owned`.
 +fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
 +    method_name.as_str() == "into_owned" && is_diag_item_method(cx, method_def_id, sym::Cow)
 +}
 +
++/// Returns true if the named method is `ToString::to_string` and it's called on a type that
++/// is string-like i.e. implements `AsRef<str>` or `Deref<str>`.
++fn is_to_string_on_string_like<'a>(
++    cx: &LateContext<'_>,
++    call_expr: &'a Expr<'a>,
++    method_name: Symbol,
++    method_def_id: DefId,
++) -> bool {
++    if method_name != sym::to_string || !is_diag_trait_item(cx, method_def_id, sym::ToString) {
++        return false;
++    }
++
++    if let Some(substs) = cx.typeck_results().node_substs_opt(call_expr.hir_id)
++        && let [generic_arg] = substs.as_slice()
++        && let GenericArgKind::Type(ty) = generic_arg.unpack()
++        && let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref)
++        && let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef)
++        && (implements_trait(cx, ty, deref_trait_id, &[cx.tcx.types.str_.into()]) ||
++            implements_trait(cx, ty, as_ref_trait_id, &[cx.tcx.types.str_.into()])) {
++            true
++        } else {
++            false
++        }
 +}
index f3af281d6cacc289838d278b25d9d3ed144d4494,0000000000000000000000000000000000000000..045f739e64ded7db0b2a352287eb394ca45d8956
mode 100644,000000..100644
--- /dev/null
@@@ -1,46 -1,0 +1,66 @@@
- use rustc_span::sym;
 +//! Lint for `some_result_or_option.unwrap_or_else(Default::default)`
 +
 +use super::UNWRAP_OR_ELSE_DEFAULT;
 +use clippy_utils::{
 +    diagnostics::span_lint_and_sugg, is_default_equivalent_call, source::snippet_with_applicability,
 +    ty::is_type_diagnostic_item,
 +};
++use rustc_ast::ast::LitKind;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
-         if is_default_equivalent_call(cx, u_arg);
++use rustc_span::{sym, symbol};
 +
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx hir::Expr<'_>,
 +    recv: &'tcx hir::Expr<'_>,
 +    u_arg: &'tcx hir::Expr<'_>,
 +) {
 +    // something.unwrap_or_else(Default::default)
 +    // ^^^^^^^^^- recv          ^^^^^^^^^^^^^^^^- u_arg
 +    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- expr
 +    let recv_ty = cx.typeck_results().expr_ty(recv);
 +    let is_option = is_type_diagnostic_item(cx, recv_ty, sym::Option);
 +    let is_result = is_type_diagnostic_item(cx, recv_ty, sym::Result);
 +
 +    if_chain! {
 +        if is_option || is_result;
++        if closure_body_returns_empty_to_string(cx, u_arg) || is_default_equivalent_call(cx, u_arg);
 +        then {
 +            let mut applicability = Applicability::MachineApplicable;
 +
 +            span_lint_and_sugg(
 +                cx,
 +                UNWRAP_OR_ELSE_DEFAULT,
 +                expr.span,
 +                "use of `.unwrap_or_else(..)` to construct default value",
 +                "try",
 +                format!(
 +                    "{}.unwrap_or_default()",
 +                    snippet_with_applicability(cx, recv.span, "..", &mut applicability)
 +                ),
 +                applicability,
 +            );
 +        }
 +    }
 +}
++
++fn closure_body_returns_empty_to_string(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> bool {
++    if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = e.kind {
++        let body = cx.tcx.hir().body(body);
++
++        if body.params.is_empty()
++            && let hir::Expr{ kind, .. } = &body.value
++            && let hir::ExprKind::MethodCall(hir::PathSegment {ident, ..}, self_arg, _, _) = kind
++            && ident == &symbol::Ident::from_str("to_string")
++            && let hir::Expr{ kind, .. } = self_arg
++            && let hir::ExprKind::Lit(lit) = kind
++            && let LitKind::Str(symbol::kw::Empty, _) = lit.node
++        {
++            return true;
++        }
++    }
++
++    false
++}
index 44b21e7b080d22c6aa8c8654ceb76b226ccba6d2,0000000000000000000000000000000000000000..4d8579135fc061279efa57bdf4b9e83c8adf1568
mode 100644,000000..100644
--- /dev/null
@@@ -1,129 -1,0 +1,125 @@@
- use if_chain::if_chain;
 +use clippy_utils::consts::{constant_simple, Constant};
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::{match_trait_method, paths};
-             if_chain! {
-                 if cx.typeck_results().expr_ty(receiver).is_floating_point() || match_trait_method(cx, expr, &paths::ORD);
-                 then {
-                     if path.ident.name == sym!(max) {
-                         fetch_const(cx, Some(receiver), args, MinMax::Max)
-                     } else if path.ident.name == sym!(min) {
-                         fetch_const(cx, Some(receiver), args, MinMax::Min)
-                     } else {
-                         None
-                     }
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +use std::cmp::Ordering;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for expressions where `std::cmp::min` and `max` are
 +    /// used to clamp values, but switched so that the result is constant.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is in all probability not the intended outcome. At
 +    /// the least it hurts readability of the code.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// min(0, max(100, x))
 +    ///
 +    /// // or
 +    ///
 +    /// x.max(100).min(0)
 +    /// ```
 +    /// 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"
 +}
 +
 +declare_lint_pass!(MinMaxPass => [MIN_MAX]);
 +
 +impl<'tcx> LateLintPass<'tcx> for MinMaxPass {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let Some((outer_max, outer_c, oe)) = min_max(cx, expr) {
 +            if let Some((inner_max, inner_c, ie)) = min_max(cx, oe) {
 +                if outer_max == inner_max {
 +                    return;
 +                }
 +                match (
 +                    outer_max,
 +                    Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(ie), &outer_c, &inner_c),
 +                ) {
 +                    (_, None) | (MinMax::Max, Some(Ordering::Less)) | (MinMax::Min, Some(Ordering::Greater)) => (),
 +                    _ => {
 +                        span_lint(
 +                            cx,
 +                            MIN_MAX,
 +                            expr.span,
 +                            "this `min`/`max` combination leads to constant result",
 +                        );
 +                    },
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +#[derive(PartialEq, Eq, Debug, Clone, Copy)]
 +enum MinMax {
 +    Min,
 +    Max,
 +}
 +
 +fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> {
 +    match expr.kind {
 +        ExprKind::Call(path, args) => {
 +            if let ExprKind::Path(ref qpath) = path.kind {
 +                cx.typeck_results()
 +                    .qpath_res(qpath, path.hir_id)
 +                    .opt_def_id()
 +                    .and_then(|def_id| match cx.tcx.get_diagnostic_name(def_id) {
 +                        Some(sym::cmp_min) => fetch_const(cx, None, args, MinMax::Min),
 +                        Some(sym::cmp_max) => fetch_const(cx, None, args, MinMax::Max),
 +                        _ => None,
 +                    })
 +            } else {
 +                None
 +            }
 +        },
 +        ExprKind::MethodCall(path, receiver, args @ [_], _) => {
-     let mut args = receiver.into_iter().chain(args.into_iter());
-     let arg0 = args.next()?;
-     let arg1 = args.next()?;
++            if cx.typeck_results().expr_ty(receiver).is_floating_point() || match_trait_method(cx, expr, &paths::ORD) {
++                if path.ident.name == sym!(max) {
++                    fetch_const(cx, Some(receiver), args, MinMax::Max)
++                } else if path.ident.name == sym!(min) {
++                    fetch_const(cx, Some(receiver), args, MinMax::Min)
 +                } else {
 +                    None
 +                }
++            } else {
++                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +fn fetch_const<'a>(
 +    cx: &LateContext<'_>,
 +    receiver: Option<&'a Expr<'a>>,
 +    args: &'a [Expr<'a>],
 +    m: MinMax,
 +) -> Option<(MinMax, Constant, &'a Expr<'a>)> {
-     constant_simple(cx, cx.typeck_results(), arg0).map_or_else(
-         || constant_simple(cx, cx.typeck_results(), arg1).map(|c| (m, c, arg0)),
++    let mut args = receiver.into_iter().chain(args);
++    let first_arg = args.next()?;
++    let second_arg = args.next()?;
 +    if args.next().is_some() {
 +        return None;
 +    }
-             if constant_simple(cx, cx.typeck_results(), arg1).is_none() {
++    constant_simple(cx, cx.typeck_results(), first_arg).map_or_else(
++        || constant_simple(cx, cx.typeck_results(), second_arg).map(|c| (m, c, first_arg)),
 +        |c| {
-                 Some((m, c, arg1))
++            if constant_simple(cx, cx.typeck_results(), second_arg).is_none() {
 +                // otherwise ignore
++                Some((m, c, second_arg))
 +            } else {
 +                None
 +            }
 +        },
 +    )
 +}
index f8cc3fbb3cdfaeacdcc0332fb432c05586d767eb,0000000000000000000000000000000000000000..3233d87c073193f99a0046745ff49c603f7576a5
mode 100644,000000..100644
--- /dev/null
@@@ -1,160 -1,0 +1,160 @@@
-                 ret_collector.visit_expr(&body.value);
 +use rustc_errors::Applicability;
 +use rustc_hir::{
 +    intravisit::{walk_expr, Visitor},
 +    Closure, Expr, ExprKind, Stmt, StmtKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{source_map::Span, sym, Symbol};
 +
 +use if_chain::if_chain;
 +
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::is_trait_method;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::ty::has_iter_method;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `for_each` that would be more simply written as a
 +    /// `for` loop.
 +    ///
 +    /// ### Why is this bad?
 +    /// `for_each` may be used after applying iterator transformers like
 +    /// `filter` for better readability and performance. It may also be used to fit a simple
 +    /// operation on one line.
 +    /// But when none of these apply, a simple `for` loop is more idiomatic.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let v = vec![0, 1, 2];
 +    /// v.iter().for_each(|elem| {
 +    ///     println!("{}", elem);
 +    /// })
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let v = vec![0, 1, 2];
 +    /// for elem in v.iter() {
 +    ///     println!("{}", elem);
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub NEEDLESS_FOR_EACH,
 +    pedantic,
 +    "using `for_each` where a `for` loop would be simpler"
 +}
 +
 +declare_lint_pass!(NeedlessForEach => [NEEDLESS_FOR_EACH]);
 +
 +impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
 +    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
 +        let expr = match stmt.kind {
 +            StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr,
 +            _ => return,
 +        };
 +
 +        if_chain! {
 +            // Check the method name is `for_each`.
 +            if let ExprKind::MethodCall(method_name, for_each_recv, [for_each_arg], _) = expr.kind;
 +            if method_name.ident.name == Symbol::intern("for_each");
 +            // Check `for_each` is an associated function of `Iterator`.
 +            if is_trait_method(cx, expr, sym::Iterator);
 +            // Checks the receiver of `for_each` is also a method call.
 +            if let ExprKind::MethodCall(_, iter_recv, [], _) = for_each_recv.kind;
 +            // Skip the lint if the call chain is too long. e.g. `v.field.iter().for_each()` or
 +            // `v.foo().iter().for_each()` must be skipped.
 +            if matches!(
 +                iter_recv.kind,
 +                ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Path(..)
 +            );
 +            // Checks the type of the `iter` method receiver is NOT a user defined type.
 +            if has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some();
 +            // Skip the lint if the body is not block because this is simpler than `for` loop.
 +            // e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop.
 +            if let ExprKind::Closure(&Closure { body, .. }) = for_each_arg.kind;
 +            let body = cx.tcx.hir().body(body);
 +            if let ExprKind::Block(..) = body.value.kind;
 +            then {
 +                let mut ret_collector = RetCollector::default();
++                ret_collector.visit_expr(body.value);
 +
 +                // Skip the lint if `return` is used in `Loop` in order not to suggest using `'label`.
 +                if ret_collector.ret_in_loop {
 +                    return;
 +                }
 +
 +                let (mut applicability, ret_suggs) = if ret_collector.spans.is_empty() {
 +                    (Applicability::MachineApplicable, None)
 +                } else {
 +                    (
 +                        Applicability::MaybeIncorrect,
 +                        Some(
 +                            ret_collector
 +                                .spans
 +                                .into_iter()
 +                                .map(|span| (span, "continue".to_string()))
 +                                .collect(),
 +                        ),
 +                    )
 +                };
 +
 +                let sugg = format!(
 +                    "for {} in {} {}",
 +                    snippet_with_applicability(cx, body.params[0].pat.span, "..", &mut applicability),
 +                    snippet_with_applicability(cx, for_each_recv.span, "..", &mut applicability),
 +                    snippet_with_applicability(cx, body.value.span, "..", &mut applicability),
 +                );
 +
 +                span_lint_and_then(cx, NEEDLESS_FOR_EACH, stmt.span, "needless use of `for_each`", |diag| {
 +                    diag.span_suggestion(stmt.span, "try", sugg, applicability);
 +                    if let Some(ret_suggs) = ret_suggs {
 +                        diag.multipart_suggestion("...and replace `return` with `continue`", ret_suggs, applicability);
 +                    }
 +                })
 +            }
 +        }
 +    }
 +}
 +
 +/// This type plays two roles.
 +/// 1. Collect spans of `return` in the closure body.
 +/// 2. Detect use of `return` in `Loop` in the closure body.
 +///
 +/// NOTE: The functionality of this type is similar to
 +/// [`clippy_utils::visitors::find_all_ret_expressions`], but we can't use
 +/// `find_all_ret_expressions` instead of this type. The reasons are:
 +/// 1. `find_all_ret_expressions` passes the argument of `ExprKind::Ret` to a callback, but what we
 +///    need here is `ExprKind::Ret` itself.
 +/// 2. We can't trace current loop depth with `find_all_ret_expressions`.
 +#[derive(Default)]
 +struct RetCollector {
 +    spans: Vec<Span>,
 +    ret_in_loop: bool,
 +    loop_depth: u16,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for RetCollector {
 +    fn visit_expr(&mut self, expr: &Expr<'_>) {
 +        match expr.kind {
 +            ExprKind::Ret(..) => {
 +                if self.loop_depth > 0 && !self.ret_in_loop {
 +                    self.ret_in_loop = true;
 +                }
 +
 +                self.spans.push(expr.span);
 +            },
 +
 +            ExprKind::Loop(..) => {
 +                self.loop_depth += 1;
 +                walk_expr(self, expr);
 +                self.loop_depth -= 1;
 +                return;
 +            },
 +
 +            _ => {},
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +}
index 17d5fa2152bbffe438f627e0d41103f9c063df95,0000000000000000000000000000000000000000..6217110a1f3aee8253704a06c8894757fd54ed51
mode 100644,000000..100644
--- /dev/null
@@@ -1,402 -1,0 +1,395 @@@
-                 if sig.decl.implicit_self.has_implicit_self() {
-                     1
-                 } else {
-                     0
-                 },
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::{get_expr_use_or_unification_node, get_parent_node, path_def_id, path_to_local, path_to_local_id};
 +use core::cell::Cell;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_errors::Applicability;
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::hir_id::HirIdMap;
 +use rustc_hir::{Body, Expr, ExprKind, HirId, ImplItem, ImplItemKind, Node, PatKind, TraitItem, TraitItemKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
 +use rustc_middle::ty::{self, ConstKind};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::{kw, Ident};
 +use rustc_span::Span;
++use std::iter;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for arguments that are only used in recursion with no side-effects.
 +    ///
 +    /// ### Why is this bad?
 +    /// It could contain a useless calculation and can make function simpler.
 +    ///
 +    /// The arguments can be involved in calculations and assignments but as long as
 +    /// the calculations have no side-effects (function calls or mutating dereference)
 +    /// and the assigned variables are also only in recursion, it is useless.
 +    ///
 +    /// ### Known problems
 +    /// Too many code paths in the linting code are currently untested and prone to produce false
 +    /// positives or are prone to have performance implications.
 +    ///
 +    /// In some cases, this would not catch all useless arguments.
 +    ///
 +    /// ```rust
 +    /// fn foo(a: usize, b: usize) -> usize {
 +    ///     let f = |x| x + 1;
 +    ///
 +    ///     if a == 0 {
 +    ///         1
 +    ///     } else {
 +    ///         foo(a - 1, f(b))
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// For example, the argument `b` is only used in recursion, but the lint would not catch it.
 +    ///
 +    /// List of some examples that can not be caught:
 +    /// - binary operation of non-primitive types
 +    /// - closure usage
 +    /// - some `break` relative operations
 +    /// - struct pattern binding
 +    ///
 +    /// Also, when you recurse the function name with path segments, it is not possible to detect.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn f(a: usize, b: usize) -> usize {
 +    ///     if a == 0 {
 +    ///         1
 +    ///     } else {
 +    ///         f(a - 1, b + 1)
 +    ///     }
 +    /// }
 +    /// # fn main() {
 +    /// #     print!("{}", f(1, 1));
 +    /// # }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn f(a: usize) -> usize {
 +    ///     if a == 0 {
 +    ///         1
 +    ///     } else {
 +    ///         f(a - 1)
 +    ///     }
 +    /// }
 +    /// # fn main() {
 +    /// #     print!("{}", f(1));
 +    /// # }
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub ONLY_USED_IN_RECURSION,
 +    complexity,
 +    "arguments that is only used in recursion can be removed"
 +}
 +impl_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]);
 +
 +#[derive(Clone, Copy)]
 +enum FnKind {
 +    Fn,
 +    TraitFn,
 +    // This is a hack. Ideally we would store a `SubstsRef<'tcx>` type here, but a lint pass must be `'static`.
 +    // Substitutions are, however, interned. This allows us to store the pointer as a `usize` when comparing for
 +    // equality.
 +    ImplTraitFn(usize),
 +}
 +
 +struct Param {
 +    /// The function this is a parameter for.
 +    fn_id: DefId,
 +    fn_kind: FnKind,
 +    /// The index of this parameter.
 +    idx: usize,
 +    ident: Ident,
 +    /// Whether this parameter should be linted. Set by `Params::flag_for_linting`.
 +    apply_lint: Cell<bool>,
 +    /// All the uses of this parameter.
 +    uses: Vec<Usage>,
 +}
 +impl Param {
 +    fn new(fn_id: DefId, fn_kind: FnKind, idx: usize, ident: Ident) -> Self {
 +        Self {
 +            fn_id,
 +            fn_kind,
 +            idx,
 +            ident,
 +            apply_lint: Cell::new(true),
 +            uses: Vec::new(),
 +        }
 +    }
 +}
 +
 +#[derive(Debug)]
 +struct Usage {
 +    span: Span,
 +    idx: usize,
 +}
 +impl Usage {
 +    fn new(span: Span, idx: usize) -> Self {
 +        Self { span, idx }
 +    }
 +}
 +
 +/// The parameters being checked by the lint, indexed by both the parameter's `HirId` and the
 +/// `DefId` of the function paired with the parameter's index.
 +#[derive(Default)]
 +struct Params {
 +    params: Vec<Param>,
 +    by_id: HirIdMap<usize>,
 +    by_fn: FxHashMap<(DefId, usize), usize>,
 +}
 +impl Params {
 +    fn insert(&mut self, param: Param, id: HirId) {
 +        let idx = self.params.len();
 +        self.by_id.insert(id, idx);
 +        self.by_fn.insert((param.fn_id, param.idx), idx);
 +        self.params.push(param);
 +    }
 +
 +    fn remove_by_id(&mut self, id: HirId) {
 +        if let Some(param) = self.get_by_id_mut(id) {
 +            param.uses = Vec::new();
 +            let key = (param.fn_id, param.idx);
 +            self.by_fn.remove(&key);
 +            self.by_id.remove(&id);
 +        }
 +    }
 +
 +    fn get_by_id_mut(&mut self, id: HirId) -> Option<&mut Param> {
 +        self.params.get_mut(*self.by_id.get(&id)?)
 +    }
 +
 +    fn get_by_fn(&self, id: DefId, idx: usize) -> Option<&Param> {
 +        self.params.get(*self.by_fn.get(&(id, idx))?)
 +    }
 +
 +    fn clear(&mut self) {
 +        self.params.clear();
 +        self.by_id.clear();
 +        self.by_fn.clear();
 +    }
 +
 +    /// Sets the `apply_lint` flag on each parameter.
 +    fn flag_for_linting(&mut self) {
 +        // Stores the list of parameters currently being resolved. Needed to avoid cycles.
 +        let mut eval_stack = Vec::new();
 +        for param in &self.params {
 +            self.try_disable_lint_for_param(param, &mut eval_stack);
 +        }
 +    }
 +
 +    // Use by calling `flag_for_linting`.
 +    fn try_disable_lint_for_param(&self, param: &Param, eval_stack: &mut Vec<usize>) -> bool {
 +        if !param.apply_lint.get() {
 +            true
 +        } else if param.uses.is_empty() {
 +            // Don't lint on unused parameters.
 +            param.apply_lint.set(false);
 +            true
 +        } else if eval_stack.contains(&param.idx) {
 +            // Already on the evaluation stack. Returning false will continue to evaluate other dependencies.
 +            false
 +        } else {
 +            eval_stack.push(param.idx);
 +            // Check all cases when used at a different parameter index.
 +            // Needed to catch cases like: `fn f(x: u32, y: u32) { f(y, x) }`
 +            for usage in param.uses.iter().filter(|u| u.idx != param.idx) {
 +                if self
 +                    .get_by_fn(param.fn_id, usage.idx)
 +                    // If the parameter can't be found, then it's used for more than just recursion.
 +                    .map_or(true, |p| self.try_disable_lint_for_param(p, eval_stack))
 +                {
 +                    param.apply_lint.set(false);
 +                    eval_stack.pop();
 +                    return true;
 +                }
 +            }
 +            eval_stack.pop();
 +            false
 +        }
 +    }
 +}
 +
 +#[derive(Default)]
 +pub struct OnlyUsedInRecursion {
 +    /// Track the top-level body entered. Needed to delay reporting when entering nested bodies.
 +    entered_body: Option<HirId>,
 +    params: Params,
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
 +    fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'tcx>) {
 +        if body.value.span.from_expansion() {
 +            return;
 +        }
 +        // `skip_params` is either `0` or `1` to skip the `self` parameter in trait functions.
 +        // It can't be renamed, and it can't be removed without removing it from multiple functions.
 +        let (fn_id, fn_kind, skip_params) = match get_parent_node(cx.tcx, body.value.hir_id) {
 +            Some(Node::Item(i)) => (i.def_id.to_def_id(), FnKind::Fn, 0),
 +            Some(Node::TraitItem(&TraitItem {
 +                kind: TraitItemKind::Fn(ref sig, _),
 +                def_id,
 +                ..
 +            })) => (
 +                def_id.to_def_id(),
 +                FnKind::TraitFn,
-                         if sig.decl.implicit_self.has_implicit_self() {
-                             1
-                         } else {
-                             0
-                         },
++                usize::from(sig.decl.implicit_self.has_implicit_self()),
 +            ),
 +            Some(Node::ImplItem(&ImplItem {
 +                kind: ImplItemKind::Fn(ref sig, _),
 +                def_id,
 +                ..
 +            })) => {
 +                #[allow(trivial_casts)]
 +                if let Some(Node::Item(item)) = get_parent_node(cx.tcx, cx.tcx.hir().local_def_id_to_hir_id(def_id))
 +                    && let Some(trait_ref) = cx.tcx.impl_trait_ref(item.def_id)
 +                    && let Some(trait_item_id) = cx.tcx.associated_item(def_id).trait_item_def_id
 +                {
 +                    (
 +                        trait_item_id,
 +                        FnKind::ImplTraitFn(cx.tcx.erase_regions(trait_ref.substs) as *const _ as usize),
-                             if let Some(idx) = std::iter::once(receiver).chain(args.iter()).position(|arg| arg.hir_id == child_id) {
++                        usize::from(sig.decl.implicit_self.has_implicit_self()),
 +                    )
 +                } else {
 +                    (def_id.to_def_id(), FnKind::Fn, 0)
 +                }
 +            },
 +            _ => return,
 +        };
 +        body.params
 +            .iter()
 +            .enumerate()
 +            .skip(skip_params)
 +            .filter_map(|(idx, p)| match p.pat.kind {
 +                PatKind::Binding(_, id, ident, None) if !ident.as_str().starts_with('_') => {
 +                    Some((id, Param::new(fn_id, fn_kind, idx, ident)))
 +                },
 +                _ => None,
 +            })
 +            .for_each(|(id, param)| self.params.insert(param, id));
 +        if self.entered_body.is_none() {
 +            self.entered_body = Some(body.value.hir_id);
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) {
 +        if let Some(id) = path_to_local(e)
 +            && let Some(param) = self.params.get_by_id_mut(id)
 +        {
 +            let typeck = cx.typeck_results();
 +            let span = e.span;
 +            let mut e = e;
 +            loop {
 +                match get_expr_use_or_unification_node(cx.tcx, e) {
 +                    None | Some((Node::Stmt(_), _)) => return,
 +                    Some((Node::Expr(parent), child_id)) => match parent.kind {
 +                        // Recursive call. Track which index the parameter is used in.
 +                        ExprKind::Call(callee, args)
 +                            if path_def_id(cx, callee).map_or(false, |id| {
 +                                id == param.fn_id
 +                                    && has_matching_substs(param.fn_kind, typeck.node_substs(callee.hir_id))
 +                            }) =>
 +                        {
 +                            if let Some(idx) = args.iter().position(|arg| arg.hir_id == child_id) {
 +                                param.uses.push(Usage::new(span, idx));
 +                            }
 +                            return;
 +                        },
 +                        ExprKind::MethodCall(_, receiver, args, _)
 +                            if typeck.type_dependent_def_id(parent.hir_id).map_or(false, |id| {
 +                                id == param.fn_id
 +                                    && has_matching_substs(param.fn_kind, typeck.node_substs(parent.hir_id))
 +                            }) =>
 +                        {
++                            if let Some(idx) = iter::once(receiver).chain(args).position(|arg| arg.hir_id == child_id) {
 +                                param.uses.push(Usage::new(span, idx));
 +                            }
 +                            return;
 +                        },
 +                        // Assignment to a parameter is fine.
 +                        ExprKind::Assign(lhs, _, _) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
 +                            return;
 +                        },
 +                        // Parameter update e.g. `x = x + 1`
 +                        ExprKind::Assign(lhs, rhs, _) | ExprKind::AssignOp(_, lhs, rhs)
 +                            if rhs.hir_id == child_id && path_to_local_id(lhs, id) =>
 +                        {
 +                            return;
 +                        },
 +                        // Side-effect free expressions. Walk to the parent expression.
 +                        ExprKind::Binary(_, lhs, rhs)
 +                            if typeck.expr_ty(lhs).is_primitive() && typeck.expr_ty(rhs).is_primitive() =>
 +                        {
 +                            e = parent;
 +                            continue;
 +                        },
 +                        ExprKind::Unary(_, arg) if typeck.expr_ty(arg).is_primitive() => {
 +                            e = parent;
 +                            continue;
 +                        },
 +                        ExprKind::AddrOf(..) | ExprKind::Cast(..) => {
 +                            e = parent;
 +                            continue;
 +                        },
 +                        // Only allow field accesses without auto-deref
 +                        ExprKind::Field(..) if typeck.adjustments().get(child_id).is_none() => {
 +                            e = parent;
 +                            continue
 +                        }
 +                        _ => (),
 +                    },
 +                    _ => (),
 +                }
 +                self.params.remove_by_id(id);
 +                return;
 +            }
 +        }
 +    }
 +
 +    fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'tcx>) {
 +        if self.entered_body == Some(body.value.hir_id) {
 +            self.entered_body = None;
 +            self.params.flag_for_linting();
 +            for param in &self.params.params {
 +                if param.apply_lint.get() {
 +                    span_lint_and_then(
 +                        cx,
 +                        ONLY_USED_IN_RECURSION,
 +                        param.ident.span,
 +                        "parameter is only used in recursion",
 +                        |diag| {
 +                            if param.ident.name != kw::SelfLower {
 +                                diag.span_suggestion(
 +                                    param.ident.span,
 +                                    "if this is intentional, prefix it with an underscore",
 +                                    format!("_{}", param.ident.name),
 +                                    Applicability::MaybeIncorrect,
 +                                );
 +                            }
 +                            diag.span_note(
 +                                param.uses.iter().map(|x| x.span).collect::<Vec<_>>(),
 +                                "parameter used here",
 +                            );
 +                        },
 +                    );
 +                }
 +            }
 +            self.params.clear();
 +        }
 +    }
 +}
 +
 +fn has_matching_substs(kind: FnKind, substs: SubstsRef<'_>) -> bool {
 +    match kind {
 +        FnKind::Fn => true,
 +        FnKind::TraitFn => substs.iter().enumerate().all(|(idx, subst)| match subst.unpack() {
 +            GenericArgKind::Lifetime(_) => true,
 +            GenericArgKind::Type(ty) => matches!(*ty.kind(), ty::Param(ty) if ty.index as usize == idx),
 +            GenericArgKind::Const(c) => matches!(c.kind(), ConstKind::Param(c) if c.index as usize == idx),
 +        }),
 +        #[allow(trivial_casts)]
 +        FnKind::ImplTraitFn(expected_substs) => substs as *const _ as usize == expected_substs,
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..83b69fbb3116466f3799a5f9b3d81bb3bb8ae000
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,173 @@@
++#![allow(
++    // False positive
++    clippy::match_same_arms
++)]
++
++use super::ARITHMETIC_SIDE_EFFECTS;
++use clippy_utils::{consts::constant_simple, diagnostics::span_lint};
++use rustc_ast as ast;
++use rustc_data_structures::fx::FxHashSet;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::Ty;
++use rustc_session::impl_lint_pass;
++use rustc_span::source_map::{Span, Spanned};
++
++const HARD_CODED_ALLOWED: &[&str] = &[
++    "f32",
++    "f64",
++    "std::num::Saturating",
++    "std::string::String",
++    "std::num::Wrapping",
++];
++
++#[derive(Debug)]
++pub struct ArithmeticSideEffects {
++    allowed: FxHashSet<String>,
++    // Used to check whether expressions are constants, such as in enum discriminants and consts
++    const_span: Option<Span>,
++    expr_span: Option<Span>,
++}
++
++impl_lint_pass!(ArithmeticSideEffects => [ARITHMETIC_SIDE_EFFECTS]);
++
++impl ArithmeticSideEffects {
++    #[must_use]
++    pub fn new(mut allowed: FxHashSet<String>) -> Self {
++        allowed.extend(HARD_CODED_ALLOWED.iter().copied().map(String::from));
++        Self {
++            allowed,
++            const_span: None,
++            expr_span: None,
++        }
++    }
++
++    /// Checks assign operators (+=, -=, *=, /=) of integers in a non-constant environment that
++    /// won't overflow.
++    fn has_valid_assign_op(op: &Spanned<hir::BinOpKind>, rhs: &hir::Expr<'_>, rhs_refs: Ty<'_>) -> bool {
++        if !Self::is_literal_integer(rhs, rhs_refs) {
++            return false;
++        }
++        if let hir::BinOpKind::Div | hir::BinOpKind::Mul = op.node
++            && let hir::ExprKind::Lit(ref lit) = rhs.kind
++            && let ast::LitKind::Int(1, _) = lit.node
++        {
++            return true;
++        }
++        false
++    }
++
++    /// Checks "raw" binary operators (+, -, *, /) of integers in a non-constant environment
++    /// already handled by the CTFE.
++    fn has_valid_bin_op(lhs: &hir::Expr<'_>, lhs_refs: Ty<'_>, rhs: &hir::Expr<'_>, rhs_refs: Ty<'_>) -> bool {
++        Self::is_literal_integer(lhs, lhs_refs) && Self::is_literal_integer(rhs, rhs_refs)
++    }
++
++    /// Checks if the given `expr` has any of the inner `allowed` elements.
++    fn is_allowed_ty(&self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
++        self.allowed.contains(
++            cx.typeck_results()
++                .expr_ty(expr)
++                .to_string()
++                .split('<')
++                .next()
++                .unwrap_or_default(),
++        )
++    }
++
++    /// Explicit integers like `1` or `i32::MAX`. Does not take into consideration references.
++    fn is_literal_integer(expr: &hir::Expr<'_>, expr_refs: Ty<'_>) -> bool {
++        let is_integral = expr_refs.is_integral();
++        let is_literal = matches!(expr.kind, hir::ExprKind::Lit(_));
++        is_integral && is_literal
++    }
++
++    fn issue_lint(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
++        span_lint(cx, ARITHMETIC_SIDE_EFFECTS, expr.span, "arithmetic detected");
++        self.expr_span = Some(expr.span);
++    }
++
++    /// Manages when the lint should be triggered. Operations in constant environments, hard coded
++    /// types, custom allowed types and non-constant operations that won't overflow are ignored.
++    fn manage_bin_ops(
++        &mut self,
++        cx: &LateContext<'_>,
++        expr: &hir::Expr<'_>,
++        op: &Spanned<hir::BinOpKind>,
++        lhs: &hir::Expr<'_>,
++        rhs: &hir::Expr<'_>,
++    ) {
++        if constant_simple(cx, cx.typeck_results(), expr).is_some() {
++            return;
++        }
++        if !matches!(
++            op.node,
++            hir::BinOpKind::Add
++                | hir::BinOpKind::Sub
++                | hir::BinOpKind::Mul
++                | hir::BinOpKind::Div
++                | hir::BinOpKind::Rem
++                | hir::BinOpKind::Shl
++                | hir::BinOpKind::Shr
++        ) {
++            return;
++        };
++        if self.is_allowed_ty(cx, lhs) || self.is_allowed_ty(cx, rhs) {
++            return;
++        }
++        let lhs_refs = cx.typeck_results().expr_ty(lhs).peel_refs();
++        let rhs_refs = cx.typeck_results().expr_ty(rhs).peel_refs();
++        let has_valid_assign_op = Self::has_valid_assign_op(op, rhs, rhs_refs);
++        if has_valid_assign_op || Self::has_valid_bin_op(lhs, lhs_refs, rhs, rhs_refs) {
++            return;
++        }
++        self.issue_lint(cx, expr);
++    }
++}
++
++impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
++    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
++        if self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span)) {
++            return;
++        }
++        match &expr.kind {
++            hir::ExprKind::Binary(op, lhs, rhs) | hir::ExprKind::AssignOp(op, lhs, rhs) => {
++                self.manage_bin_ops(cx, expr, op, lhs, rhs);
++            },
++            hir::ExprKind::Unary(hir::UnOp::Neg, _) => {
++                if constant_simple(cx, cx.typeck_results(), expr).is_none() {
++                    self.issue_lint(cx, expr);
++                }
++            },
++            _ => {},
++        }
++    }
++
++    fn check_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
++        let body_owner = cx.tcx.hir().body_owner(body.id());
++        let body_owner_def_id = cx.tcx.hir().local_def_id(body_owner);
++        let body_owner_kind = cx.tcx.hir().body_owner_kind(body_owner_def_id);
++        if let hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) = body_owner_kind {
++            let body_span = cx.tcx.hir().span_with_body(body_owner);
++            if let Some(span) = self.const_span && span.contains(body_span) {
++                return;
++            }
++            self.const_span = Some(body_span);
++        }
++    }
++
++    fn check_body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
++        let body_owner = cx.tcx.hir().body_owner(body.id());
++        let body_span = cx.tcx.hir().span(body_owner);
++        if let Some(span) = self.const_span && span.contains(body_span) {
++            return;
++        }
++        self.const_span = None;
++    }
++
++    fn check_expr_post(&mut self, _: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
++        if Some(expr.span) == self.expr_span {
++            self.expr_span = None;
++        }
++    }
++}
index bb6d99406b49376309e8e40d2f938686bbd0421f,0000000000000000000000000000000000000000..c32b4df4f75c1f3daebf4911ac70332160aa6935
mode 100644,000000..100644
--- /dev/null
@@@ -1,888 -1,0 +1,892 @@@
- pub(crate) mod arithmetic;
 +mod absurd_extreme_comparisons;
 +mod assign_op_pattern;
 +mod bit_mask;
 +mod cmp_nan;
 +mod cmp_owned;
 +mod double_comparison;
 +mod duration_subsec;
 +mod eq_op;
 +mod erasing_op;
 +mod float_cmp;
 +mod float_equality_without_abs;
 +mod identity_op;
 +mod integer_division;
 +mod misrefactored_assign_op;
 +mod modulo_arithmetic;
 +mod modulo_one;
 +mod needless_bitwise_bool;
 +mod numeric_arithmetic;
 +mod op_ref;
 +mod ptr_eq;
 +mod self_assignment;
 +mod verbose_bit_mask;
 +
-     /// Checks for any kind of arithmetic operation of any type.
++pub(crate) mod arithmetic_side_effects;
 +
 +use rustc_hir::{Body, Expr, ExprKind, UnOp};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for comparisons where one side of the relation is
 +    /// either the minimum or maximum value for its type and warns if it involves a
 +    /// case that is always true or always false. Only integer and boolean types are
 +    /// checked.
 +    ///
 +    /// ### Why is this bad?
 +    /// An expression like `min <= x` may misleadingly imply
 +    /// that it is possible for `x` to be less than the minimum. Expressions like
 +    /// `max < x` are probably mistakes.
 +    ///
 +    /// ### Known problems
 +    /// For `usize` the size of the current compile target will
 +    /// be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such
 +    /// a comparison to detect target pointer width will trigger this lint. One can
 +    /// use `mem::sizeof` and compare its value or conditional compilation
 +    /// attributes
 +    /// like `#[cfg(target_pointer_width = "64")] ..` instead.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let vec: Vec<isize> = Vec::new();
 +    /// 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
-     /// or can panic (`/`, `%`). Known safe built-in types like `Wrapping` or `Saturing` are filtered
-     /// away.
++    /// Checks any kind of arithmetic operation of any type.
 +    ///
 +    /// Operators like `+`, `-`, `*` or `<<` are usually capable of overflowing according to the [Rust
 +    /// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),
-     /// Integer overflow will trigger a panic in debug builds or will wrap in
-     /// release mode. Division by zero will cause a panic in either mode. In some applications one
-     /// wants explicitly checked, wrapping or saturating arithmetic.
++    /// or can panic (`/`, `%`).
++    ///
++    /// Known safe built-in types like `Wrapping` or `Saturing`, floats, operations in constant
++    /// environments, allowed types and non-constant operations that won't overflow are ignored.
 +    ///
 +    /// ### Why is this bad?
-     /// # let a = 0;
-     /// a + 1;
++    /// For integers, overflow will trigger a panic in debug builds or wrap the result in
++    /// release mode; division by zero will cause a panic in either mode. As a result, it is
++    /// desirable to explicitly call checked, wrapping or saturating arithmetic methods.
 +    ///
 +    /// #### Example
 +    /// ```rust
-     /// Third-party types also tend to overflow.
++    /// // `n` can be any number, including `i32::MAX`.
++    /// fn foo(n: i32) -> i32 {
++    ///   n + 1
++    /// }
 +    /// ```
 +    ///
-     /// Custom allowed types can be specified through the "arithmetic-allowed" filter.
++    /// Third-party types can also overflow or present unwanted side-effects.
 +    ///
 +    /// #### Example
 +    /// ```ignore,rust
 +    /// use rust_decimal::Decimal;
 +    /// let _n = Decimal::MAX + Decimal::MAX;
 +    /// ```
 +    ///
 +    /// ### Allowed types
-     pub ARITHMETIC,
++    /// Custom allowed types can be specified through the "arithmetic-side-effects-allowed" filter.
 +    #[clippy::version = "1.64.0"]
-     "any arithmetic expression that could overflow or panic"
++    pub ARITHMETIC_SIDE_EFFECTS,
 +    restriction,
-     ARITHMETIC,
++    "any arithmetic expression that can cause side effects like overflows or panics"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for integer arithmetic operations which could overflow or panic.
 +    ///
 +    /// Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable
 +    /// of overflowing according to the [Rust
 +    /// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),
 +    /// or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is
 +    /// attempted.
 +    ///
 +    /// ### Why is this bad?
 +    /// Integer overflow will trigger a panic in debug builds or will wrap in
 +    /// release mode. Division by zero will cause a panic in either mode. In some applications one
 +    /// wants explicitly checked, wrapping or saturating arithmetic.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let a = 0;
 +    /// a + 1;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub INTEGER_ARITHMETIC,
 +    restriction,
 +    "any integer arithmetic expression which could overflow or panic"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for float arithmetic.
 +    ///
 +    /// ### Why is this bad?
 +    /// For some embedded systems or kernel development, it
 +    /// can be useful to rule out floating-point numbers.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let a = 0.0;
 +    /// a + 1.0;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FLOAT_ARITHMETIC,
 +    restriction,
 +    "any floating-point arithmetic statement"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `a = a op b` or `a = b commutative_op a`
 +    /// patterns.
 +    ///
 +    /// ### Why is this bad?
 +    /// These can be written as the shorter `a op= b`.
 +    ///
 +    /// ### Known problems
 +    /// While forbidden by the spec, `OpAssign` traits may have
 +    /// implementations that differ from the regular `Op` impl.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut a = 5;
 +    /// let b = 0;
 +    /// // ...
 +    ///
 +    /// a = a + b;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let mut a = 5;
 +    /// let b = 0;
 +    /// // ...
 +    ///
 +    /// 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `a op= a op b` or `a op= b op a` patterns.
 +    ///
 +    /// ### Why is this bad?
 +    /// Most likely these are bugs where one meant to write `a
 +    /// op= b`.
 +    ///
 +    /// ### Known problems
 +    /// Clippy cannot know for sure if `a op= a op b` should have
 +    /// been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore, it suggests both.
 +    /// If `a op= a op b` is really the correct behavior it should be
 +    /// written as `a = a op a op b` as it's less confusing.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut a = 5;
 +    /// let b = 2;
 +    /// // ...
 +    /// a += a + b;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MISREFACTORED_ASSIGN_OP,
 +    suspicious,
 +    "having a variable on both sides of an assign op"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for incompatible bit masks in comparisons.
 +    ///
 +    /// The formula for detecting if an expression of the type `_ <bit_op> m
 +    /// <cmp_op> c` (where `<bit_op>` is one of {`&`, `|`} and `<cmp_op>` is one of
 +    /// {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following
 +    /// table:
 +    ///
 +    /// |Comparison  |Bit Op|Example      |is always|Formula               |
 +    /// |------------|------|-------------|---------|----------------------|
 +    /// |`==` or `!=`| `&`  |`x & 2 == 3` |`false`  |`c & m != c`          |
 +    /// |`<`  or `>=`| `&`  |`x & 2 < 3`  |`true`   |`m < c`               |
 +    /// |`>`  or `<=`| `&`  |`x & 1 > 1`  |`false`  |`m <= c`              |
 +    /// |`==` or `!=`| `\|` |`x \| 1 == 0`|`false`  |`c \| m != c`         |
 +    /// |`<`  or `>=`| `\|` |`x \| 1 < 1` |`false`  |`m >= c`              |
 +    /// |`<=` or `>` | `\|` |`x \| 1 > 0` |`true`   |`m > c`               |
 +    ///
 +    /// ### Why is this bad?
 +    /// If the bits that the comparison cares about are always
 +    /// set to zero or one by the bit mask, the comparison is constant `true` or
 +    /// `false` (depending on mask, compared value, and operators).
 +    ///
 +    /// So the code is actively misleading, and the only reason someone would write
 +    /// this intentionally is to win an underhanded Rust contest or create a
 +    /// test-case for this lint.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # 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`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for bit masks in comparisons which can be removed
 +    /// without changing the outcome. The basic structure can be seen in the
 +    /// following table:
 +    ///
 +    /// |Comparison| Bit Op   |Example     |equals |
 +    /// |----------|----------|------------|-------|
 +    /// |`>` / `<=`|`\|` / `^`|`x \| 2 > 3`|`x > 3`|
 +    /// |`<` / `>=`|`\|` / `^`|`x ^ 1 < 4` |`x < 4`|
 +    ///
 +    /// ### Why is this bad?
 +    /// Not equally evil as [`bad_bit_mask`](#bad_bit_mask),
 +    /// but still a bit misleading, because the bit mask is ineffective.
 +    ///
 +    /// ### Known problems
 +    /// False negatives: This lint will only match instances
 +    /// where we have figured out the math (which is for a power-of-two compared
 +    /// value). This means things like `x | 1 >= 7` (which would be better written
 +    /// as `x >= 6`) will not be reported (but bit masks like this are fairly
 +    /// uncommon).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # 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`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for bit masks that can be replaced by a call
 +    /// to `trailing_zeros`
 +    ///
 +    /// ### Why is this bad?
 +    /// `x.trailing_zeros() > 4` is much clearer than `x & 15
 +    /// == 0`
 +    ///
 +    /// ### Known problems
 +    /// llvm generates better code for `x & 15 == 0` on x86
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for double comparisons that could be simplified to a single expression.
 +    ///
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = 1;
 +    /// # let y = 2;
 +    /// if x == y || x < y {}
 +    /// ```
 +    ///
 +    /// Use instead:
 +    ///
 +    /// ```rust
 +    /// # let x = 1;
 +    /// # let y = 2;
 +    /// if x <= y {}
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DOUBLE_COMPARISONS,
 +    complexity,
 +    "unnecessary double comparisons that can be simplified"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calculation of subsecond microseconds or milliseconds
 +    /// from other `Duration` methods.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's more concise to call `Duration::subsec_micros()` or
 +    /// `Duration::subsec_millis()` than to calculate them.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::time::Duration;
 +    /// # let duration = Duration::new(5, 0);
 +    /// let micros = duration.subsec_nanos() / 1_000;
 +    /// let millis = duration.subsec_nanos() / 1_000_000;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::time::Duration;
 +    /// # let duration = Duration::new(5, 0);
 +    /// let micros = duration.subsec_micros();
 +    /// let millis = duration.subsec_millis();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DURATION_SUBSEC,
 +    complexity,
 +    "checks for calculation of subsecond microseconds or milliseconds"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for equal operands to comparison, logical and
 +    /// bitwise, difference and division binary operators (`==`, `>`, etc., `&&`,
 +    /// `||`, `&`, `|`, `^`, `-` and `/`).
 +    ///
 +    /// ### Why is this bad?
 +    /// This is usually just a typo or a copy and paste error.
 +    ///
 +    /// ### Known problems
 +    /// False negatives: We had some false positives regarding
 +    /// calls (notably [racer](https://github.com/phildawes/racer) had one instance
 +    /// of `x.pop() && x.pop()`), so we removed matching any function or method
 +    /// calls. We may introduce a list of known pure functions in the future.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = 1;
 +    /// if x + 1 == x + 1 {}
 +    ///
 +    /// // or
 +    ///
 +    /// # let a = 3;
 +    /// # 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`)"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for arguments to `==` which have their address
 +    /// taken to satisfy a bound
 +    /// and suggests to dereference the other argument instead
 +    ///
 +    /// ### Why is this bad?
 +    /// It is more idiomatic to dereference the other argument.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// &x == y
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// x == *y
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub OP_REF,
 +    style,
 +    "taking a reference to satisfy the type constraints on `==`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for erasing operations, e.g., `x * 0`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The whole expression can be replaced by zero.
 +    /// This is most likely not the intended outcome and should probably be
 +    /// corrected
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = 1;
 +    /// 0 / x;
 +    /// 0 * x;
 +    /// x & 0;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ERASING_OP,
 +    correctness,
 +    "using erasing operations, e.g., `x * 0` or `y & 0`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for statements of the form `(a - b) < f32::EPSILON` or
 +    /// `(a - b) < f64::EPSILON`. Notes the missing `.abs()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The code without `.abs()` is more likely to have a bug.
 +    ///
 +    /// ### Known problems
 +    /// If the user can ensure that b is larger than a, the `.abs()` is
 +    /// technically unnecessary. However, it will make the code more robust and doesn't have any
 +    /// large performance implications. If the abs call was deliberately left out for performance
 +    /// reasons, it is probably better to state this explicitly in the code, which then can be done
 +    /// with an allow.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// pub fn is_roughly_equal(a: f32, b: f32) -> bool {
 +    ///     (a - b) < f32::EPSILON
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// pub fn is_roughly_equal(a: f32, b: f32) -> bool {
 +    ///     (a - b).abs() < f32::EPSILON
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.48.0"]
 +    pub FLOAT_EQUALITY_WITHOUT_ABS,
 +    suspicious,
 +    "float equality check without `.abs()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for identity operations, e.g., `x + 0`.
 +    ///
 +    /// ### Why is this bad?
 +    /// This code can be removed without changing the
 +    /// meaning. So it just obscures what's going on. Delete it mercilessly.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # 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`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for division of integers
 +    ///
 +    /// ### Why is this bad?
 +    /// When outside of some very specific algorithms,
 +    /// integer division is very often a mistake because it discards the
 +    /// remainder.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = 3 / 2;
 +    /// println!("{}", x);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = 3f32 / 2f32;
 +    /// println!("{}", x);
 +    /// ```
 +    #[clippy::version = "1.37.0"]
 +    pub INTEGER_DIVISION,
 +    restriction,
 +    "integer division may cause loss of precision"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for comparisons to NaN.
 +    ///
 +    /// ### Why is this bad?
 +    /// NaN does not compare meaningfully to anything – not
 +    /// even itself – so those comparisons are simply wrong.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = 1.0;
 +    /// if x == f32::NAN { }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x = 1.0f32;
 +    /// 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for conversions to owned values just for the sake
 +    /// of a comparison.
 +    ///
 +    /// ### Why is this bad?
 +    /// The comparison can operate on a reference, so creating
 +    /// an owned value effectively throws it away directly afterwards, which is
 +    /// needlessly consuming code and heap space.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = "foo";
 +    /// # let y = String::from("foo");
 +    /// if x.to_owned() == y {}
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x = "foo";
 +    /// # 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()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for (in-)equality comparisons on floating-point
 +    /// values (apart from zero), except in functions called `*eq*` (which probably
 +    /// implement equality for a type involving floats).
 +    ///
 +    /// ### Why is this bad?
 +    /// Floating point calculations are usually imprecise, so
 +    /// asking if two values are *exactly* equal is asking for trouble. For a good
 +    /// guide on what to do, see [the floating point
 +    /// guide](http://www.floating-point-gui.de/errors/comparison).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = 1.2331f64;
 +    /// let y = 1.2332f64;
 +    ///
 +    /// if y == 1.23f64 { }
 +    /// if y != x {} // where both are floats
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x = 1.2331f64;
 +    /// # let y = 1.2332f64;
 +    /// let error_margin = f64::EPSILON; // Use an epsilon for comparison
 +    /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
 +    /// // let error_margin = std::f64::EPSILON;
 +    /// 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for (in-)equality comparisons on floating-point
 +    /// value and constant, except in functions called `*eq*` (which probably
 +    /// implement equality for a type involving floats).
 +    ///
 +    /// ### Why is this bad?
 +    /// Floating point calculations are usually imprecise, so
 +    /// asking if two values are *exactly* equal is asking for trouble. For a good
 +    /// guide on what to do, see [the floating point
 +    /// guide](http://www.floating-point-gui.de/errors/comparison).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: f64 = 1.0;
 +    /// const ONE: f64 = 1.00;
 +    ///
 +    /// if x == ONE { } // where both are floats
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x: f64 = 1.0;
 +    /// # const ONE: f64 = 1.00;
 +    /// let error_margin = f64::EPSILON; // Use an epsilon for comparison
 +    /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
 +    /// // 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for getting the remainder of a division by one or minus
 +    /// one.
 +    ///
 +    /// ### Why is this bad?
 +    /// The result for a divisor of one can only ever be zero; for
 +    /// minus one it can cause panic/overflow (if the left operand is the minimal value of
 +    /// the respective integer type) or results in zero. No one will write such code
 +    /// deliberately, unless trying to win an Underhanded Rust Contest. Even for that
 +    /// contest, it's probably a bad idea. Use something more underhanded.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = 1;
 +    /// 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for modulo arithmetic.
 +    ///
 +    /// ### Why is this bad?
 +    /// The results of modulo (%) operation might differ
 +    /// depending on the language, when negative numbers are involved.
 +    /// If you interop with different languages it might be beneficial
 +    /// to double check all places that use modulo arithmetic.
 +    ///
 +    /// For example, in Rust `17 % -3 = 2`, but in Python `17 % -3 = -1`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = -17 % 3;
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub MODULO_ARITHMETIC,
 +    restriction,
 +    "any modulo arithmetic statement"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for uses of bitwise and/or operators between booleans, where performance may be improved by using
 +    /// a lazy and.
 +    ///
 +    /// ### Why is this bad?
 +    /// The bitwise operators do not support short-circuiting, so it may hinder code performance.
 +    /// Additionally, boolean logic "masked" as bitwise logic is not caught by lints like `unnecessary_fold`
 +    ///
 +    /// ### Known problems
 +    /// This lint evaluates only when the right side is determined to have no side effects. At this time, that
 +    /// determination is quite conservative.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let (x,y) = (true, false);
 +    /// if x & !y {} // where both x and y are booleans
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Use `std::ptr::eq` when applicable
 +    ///
 +    /// ### Why is this bad?
 +    /// `ptr::eq` can be used to compare `&T` references
 +    /// (which coerce to `*const T` implicitly) by their address rather than
 +    /// comparing the values they point to.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let a = &[1, 2, 3];
 +    /// let b = &[1, 2, 3];
 +    ///
 +    /// assert!(a as *const _ as usize == b as *const _ as usize);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let a = &[1, 2, 3];
 +    /// let b = &[1, 2, 3];
 +    ///
 +    /// assert!(std::ptr::eq(a, b));
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub PTR_EQ,
 +    style,
 +    "use `std::ptr::eq` when comparing raw pointers"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for explicit self-assignments.
 +    ///
 +    /// ### Why is this bad?
 +    /// Self-assignments are redundant and unlikely to be
 +    /// intentional.
 +    ///
 +    /// ### Known problems
 +    /// If expression contains any deref coercions or
 +    /// indexing operations they are assumed not to have any side effects.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct Event {
 +    ///     x: i32,
 +    /// }
 +    ///
 +    /// fn copy_position(a: &mut Event, b: &Event) {
 +    ///     a.x = a.x;
 +    /// }
 +    /// ```
 +    ///
 +    /// Should be:
 +    /// ```rust
 +    /// struct Event {
 +    ///     x: i32,
 +    /// }
 +    ///
 +    /// fn copy_position(a: &mut Event, b: &Event) {
 +    ///     a.x = b.x;
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.48.0"]
 +    pub SELF_ASSIGNMENT,
 +    correctness,
 +    "explicit self-assignment"
 +}
 +
 +pub struct Operators {
 +    arithmetic_context: numeric_arithmetic::Context,
 +    verbose_bit_mask_threshold: u64,
 +}
 +impl_lint_pass!(Operators => [
 +    ABSURD_EXTREME_COMPARISONS,
++    ARITHMETIC_SIDE_EFFECTS,
 +    INTEGER_ARITHMETIC,
 +    FLOAT_ARITHMETIC,
 +    ASSIGN_OP_PATTERN,
 +    MISREFACTORED_ASSIGN_OP,
 +    BAD_BIT_MASK,
 +    INEFFECTIVE_BIT_MASK,
 +    VERBOSE_BIT_MASK,
 +    DOUBLE_COMPARISONS,
 +    DURATION_SUBSEC,
 +    EQ_OP,
 +    OP_REF,
 +    ERASING_OP,
 +    FLOAT_EQUALITY_WITHOUT_ABS,
 +    IDENTITY_OP,
 +    INTEGER_DIVISION,
 +    CMP_NAN,
 +    CMP_OWNED,
 +    FLOAT_CMP,
 +    FLOAT_CMP_CONST,
 +    MODULO_ONE,
 +    MODULO_ARITHMETIC,
 +    NEEDLESS_BITWISE_BOOL,
 +    PTR_EQ,
 +    SELF_ASSIGNMENT,
 +]);
 +impl Operators {
 +    pub fn new(verbose_bit_mask_threshold: u64) -> Self {
 +        Self {
 +            arithmetic_context: numeric_arithmetic::Context::default(),
 +            verbose_bit_mask_threshold,
 +        }
 +    }
 +}
 +impl<'tcx> LateLintPass<'tcx> for Operators {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        eq_op::check_assert(cx, e);
 +        match e.kind {
 +            ExprKind::Binary(op, lhs, rhs) => {
 +                if !e.span.from_expansion() {
 +                    absurd_extreme_comparisons::check(cx, e, op.node, lhs, rhs);
 +                    if !(macro_with_not_op(lhs) || macro_with_not_op(rhs)) {
 +                        eq_op::check(cx, e, op.node, lhs, rhs);
 +                        op_ref::check(cx, e, op.node, lhs, rhs);
 +                    }
 +                    erasing_op::check(cx, e, op.node, lhs, rhs);
 +                    identity_op::check(cx, e, op.node, lhs, rhs);
 +                    needless_bitwise_bool::check(cx, e, op.node, lhs, rhs);
 +                    ptr_eq::check(cx, e, op.node, lhs, rhs);
 +                }
 +                self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs);
 +                bit_mask::check(cx, e, op.node, lhs, rhs);
 +                verbose_bit_mask::check(cx, e, op.node, lhs, rhs, self.verbose_bit_mask_threshold);
 +                double_comparison::check(cx, op.node, lhs, rhs, e.span);
 +                duration_subsec::check(cx, e, op.node, lhs, rhs);
 +                float_equality_without_abs::check(cx, e, op.node, lhs, rhs);
 +                integer_division::check(cx, e, op.node, lhs, rhs);
 +                cmp_nan::check(cx, e, op.node, lhs, rhs);
 +                cmp_owned::check(cx, op.node, lhs, rhs);
 +                float_cmp::check(cx, e, op.node, lhs, rhs);
 +                modulo_one::check(cx, e, op.node, rhs);
 +                modulo_arithmetic::check(cx, e, op.node, lhs, rhs);
 +            },
 +            ExprKind::AssignOp(op, lhs, rhs) => {
 +                self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs);
 +                misrefactored_assign_op::check(cx, e, op.node, lhs, rhs);
 +                modulo_arithmetic::check(cx, e, op.node, lhs, rhs);
 +            },
 +            ExprKind::Assign(lhs, rhs, _) => {
 +                assign_op_pattern::check(cx, e, lhs, rhs);
 +                self_assignment::check(cx, e, lhs, rhs);
 +            },
 +            ExprKind::Unary(op, arg) => {
 +                if op == UnOp::Neg {
 +                    self.arithmetic_context.check_negate(cx, e, arg);
 +                }
 +            },
 +            _ => (),
 +        }
 +    }
 +
 +    fn check_expr_post(&mut self, _: &LateContext<'_>, e: &Expr<'_>) {
 +        self.arithmetic_context.expr_post(e.hir_id);
 +    }
 +
 +    fn check_body(&mut self, cx: &LateContext<'tcx>, b: &'tcx Body<'_>) {
 +        self.arithmetic_context.enter_body(cx, b);
 +    }
 +
 +    fn check_body_post(&mut self, cx: &LateContext<'tcx>, b: &'tcx Body<'_>) {
 +        self.arithmetic_context.body_post(cx, b);
 +    }
 +}
 +
 +fn macro_with_not_op(e: &Expr<'_>) -> bool {
 +    if let ExprKind::Unary(_, e) = e.kind {
 +        e.span.from_expansion()
 +    } else {
 +        false
 +    }
 +}
index 21acf003d92b2a775d4068bcbcde535de7e7b531,0000000000000000000000000000000000000000..4aa0d9227abadd80aba8b0811f8f893e0a7965ac
mode 100644,000000..100644
--- /dev/null
@@@ -1,87 -1,0 +1,87 @@@
-     .visit_expr(&body.value);
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::macros::root_macro_call_first_node;
 +use clippy_utils::return_ty;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::visitors::expr_visitor_no_bodies;
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::{FnKind, Visitor};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{sym, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result.
 +    ///
 +    /// ### Why is this bad?
 +    /// For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence panicking macros should be avoided.
 +    ///
 +    /// ### Known problems
 +    /// Functions called from a function returning a `Result` may invoke a panicking macro. This is not checked.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn result_with_panic() -> Result<bool, String>
 +    /// {
 +    ///     panic!("error");
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn result_without_panic() -> Result<bool, String> {
 +    ///     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"
 +}
 +
 +declare_lint_pass!(PanicInResultFn  => [PANIC_IN_RESULT_FN]);
 +
 +impl<'tcx> LateLintPass<'tcx> for PanicInResultFn {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        fn_kind: FnKind<'tcx>,
 +        _: &'tcx hir::FnDecl<'tcx>,
 +        body: &'tcx hir::Body<'tcx>,
 +        span: Span,
 +        hir_id: hir::HirId,
 +    ) {
 +        if !matches!(fn_kind, FnKind::Closure) && is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::Result) {
 +            lint_impl_body(cx, span, body);
 +        }
 +    }
 +}
 +
 +fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) {
 +    let mut panics = Vec::new();
 +    expr_visitor_no_bodies(|expr| {
 +        let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return true };
 +        if matches!(
 +            cx.tcx.item_name(macro_call.def_id).as_str(),
 +            "unimplemented" | "unreachable" | "panic" | "todo" | "assert" | "assert_eq" | "assert_ne"
 +        ) {
 +            panics.push(macro_call.span);
 +            return false;
 +        }
 +        true
 +    })
++    .visit_expr(body.value);
 +    if !panics.is_empty() {
 +        span_lint_and_then(
 +            cx,
 +            PANIC_IN_RESULT_FN,
 +            impl_span,
 +            "used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`",
 +            move |diag| {
 +                diag.help(
 +                    "`unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing",
 +                );
 +                diag.span_note(panics, "return Err() instead of panicking");
 +            },
 +        );
 +    }
 +}
index 0028e0bc6c517e79e13f3b57452972f013da44c0,0000000000000000000000000000000000000000..41d1baba64f851ffa6409bef6e1c6f3f62575ca4
mode 100644,000000..100644
--- /dev/null
@@@ -1,687 -1,0 +1,687 @@@
-                 sig.header.unsafety == Unsafety::Unsafe || contains_unsafe_block(cx, &body.value)
 +//! Checks for usage of  `&Vec[_]` and `&String`.
 +
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::expr_sig;
 +use clippy_utils::visitors::contains_unsafe_block;
 +use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, paths};
 +use if_chain::if_chain;
 +use rustc_errors::{Applicability, MultiSpan};
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::hir_id::HirIdMap;
 +use rustc_hir::intravisit::{walk_expr, Visitor};
 +use rustc_hir::{
 +    self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg,
 +    ImplItemKind, ItemKind, Lifetime, LifetimeName, Mutability, Node, Param, ParamName, PatKind, QPath, TraitFn,
 +    TraitItem, TraitItemKind, TyKind, Unsafety,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use rustc_span::sym;
 +use rustc_span::symbol::Symbol;
 +use std::fmt;
 +use std::iter;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint checks for function arguments of type `&String`, `&Vec`,
 +    /// `&PathBuf`, and `Cow<_>`. It will also suggest you replace `.clone()` calls
 +    /// with the appropriate `.to_owned()`/`to_string()` calls.
 +    ///
 +    /// ### Why is this bad?
 +    /// Requiring the argument to be of the specific size
 +    /// makes the function less useful for no benefit; slices in the form of `&[T]`
 +    /// or `&str` usually suffice and can be obtained from other types, too.
 +    ///
 +    /// ### Known problems
 +    /// There may be `fn(&Vec)`-typed references pointing to your function.
 +    /// If you have them, you will get a compiler error after applying this lint's
 +    /// suggestions. You then have the choice to undo your changes or change the
 +    /// type of the reference.
 +    ///
 +    /// Note that if the function is part of your public interface, there may be
 +    /// other crates referencing it, of which you may not be aware. Carefully
 +    /// deprecate the function before applying the lint suggestions in this case.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// fn foo(&Vec<u32>) { .. }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```ignore
 +    /// 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint checks for equality comparisons with `ptr::null`
 +    ///
 +    /// ### Why is this bad?
 +    /// It's easier and more readable to use the inherent
 +    /// `.is_null()`
 +    /// method instead
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// use std::ptr;
 +    ///
 +    /// if x == ptr::null {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// if x.is_null() {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CMP_NULL,
 +    style,
 +    "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint checks for functions that take immutable references and return
 +    /// mutable ones. This will not trigger if no unsafe code exists as there
 +    /// are multiple safe functions which will do this transformation
 +    ///
 +    /// To be on the conservative side, if there's at least one mutable
 +    /// reference with the output lifetime, this lint will not trigger.
 +    ///
 +    /// ### Why is this bad?
 +    /// Creating a mutable reference which can be repeatably derived from an
 +    /// immutable reference is unsound as it allows creating multiple live
 +    /// mutable references to the same object.
 +    ///
 +    /// This [error](https://github.com/rust-lang/rust/issues/39465) actually
 +    /// lead to an interim Rust release 1.15.1.
 +    ///
 +    /// ### Known problems
 +    /// This pattern is used by memory allocators to allow allocating multiple
 +    /// objects while returning mutable references to each one. So long as
 +    /// different mutable references are returned each time such a function may
 +    /// be safe.
 +    ///
 +    /// ### Example
 +    /// ```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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint checks for invalid usages of `ptr::null`.
 +    ///
 +    /// ### Why is this bad?
 +    /// This causes undefined behavior.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// // Undefined behavior
 +    /// unsafe { std::slice::from_raw_parts(ptr::null(), 0); }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```ignore
 +    /// 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"
 +}
 +
 +declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Ptr {
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
 +        if let TraitItemKind::Fn(sig, trait_method) = &item.kind {
 +            if matches!(trait_method, TraitFn::Provided(_)) {
 +                // Handled by check body.
 +                return;
 +            }
 +
 +            check_mut_from_ref(cx, sig, None);
 +            for arg in check_fn_args(
 +                cx,
 +                cx.tcx.fn_sig(item.def_id).skip_binder().inputs(),
 +                sig.decl.inputs,
 +                &[],
 +            )
 +            .filter(|arg| arg.mutability() == Mutability::Not)
 +            {
 +                span_lint_hir_and_then(cx, PTR_ARG, arg.emission_id, arg.span, &arg.build_msg(), |diag| {
 +                    diag.span_suggestion(
 +                        arg.span,
 +                        "change this to",
 +                        format!("{}{}", arg.ref_prefix, arg.deref_ty.display(cx)),
 +                        Applicability::Unspecified,
 +                    );
 +                });
 +            }
 +        }
 +    }
 +
 +    fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
 +        let hir = cx.tcx.hir();
 +        let mut parents = hir.parent_iter(body.value.hir_id);
 +        let (item_id, sig, is_trait_item) = match parents.next() {
 +            Some((_, Node::Item(i))) => {
 +                if let ItemKind::Fn(sig, ..) = &i.kind {
 +                    (i.def_id, sig, false)
 +                } else {
 +                    return;
 +                }
 +            },
 +            Some((_, Node::ImplItem(i))) => {
 +                if !matches!(parents.next(),
 +                    Some((_, Node::Item(i))) if matches!(&i.kind, ItemKind::Impl(i) if i.of_trait.is_none())
 +                ) {
 +                    return;
 +                }
 +                if let ImplItemKind::Fn(sig, _) = &i.kind {
 +                    (i.def_id, sig, false)
 +                } else {
 +                    return;
 +                }
 +            },
 +            Some((_, Node::TraitItem(i))) => {
 +                if let TraitItemKind::Fn(sig, _) = &i.kind {
 +                    (i.def_id, sig, true)
 +                } else {
 +                    return;
 +                }
 +            },
 +            _ => return,
 +        };
 +
 +        check_mut_from_ref(cx, sig, Some(body));
 +        let decl = sig.decl;
 +        let sig = cx.tcx.fn_sig(item_id).skip_binder();
 +        let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, body.params)
 +            .filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not)
 +            .collect();
 +        let results = check_ptr_arg_usage(cx, body, &lint_args);
 +
 +        for (result, args) in results.iter().zip(lint_args.iter()).filter(|(r, _)| !r.skip) {
 +            span_lint_hir_and_then(cx, PTR_ARG, args.emission_id, args.span, &args.build_msg(), |diag| {
 +                diag.multipart_suggestion(
 +                    "change this to",
 +                    iter::once((args.span, format!("{}{}", args.ref_prefix, args.deref_ty.display(cx))))
 +                        .chain(result.replacements.iter().map(|r| {
 +                            (
 +                                r.expr_span,
 +                                format!("{}{}", snippet_opt(cx, r.self_span).unwrap(), r.replacement),
 +                            )
 +                        }))
 +                        .collect(),
 +                    Applicability::Unspecified,
 +                );
 +            });
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let ExprKind::Binary(ref op, l, r) = expr.kind {
 +            if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(cx, l) || is_null_path(cx, r)) {
 +                span_lint(
 +                    cx,
 +                    CMP_NULL,
 +                    expr.span,
 +                    "comparing with null is better expressed by the `.is_null()` method",
 +                );
 +            }
 +        } else {
 +            check_invalid_ptr_usage(cx, expr);
 +        }
 +    }
 +}
 +
 +fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +    // (fn_path, arg_indices) - `arg_indices` are the `arg` positions where null would cause U.B.
 +    const INVALID_NULL_PTR_USAGE_TABLE: [(&[&str], &[usize]); 16] = [
 +        (&paths::SLICE_FROM_RAW_PARTS, &[0]),
 +        (&paths::SLICE_FROM_RAW_PARTS_MUT, &[0]),
 +        (&paths::PTR_COPY, &[0, 1]),
 +        (&paths::PTR_COPY_NONOVERLAPPING, &[0, 1]),
 +        (&paths::PTR_READ, &[0]),
 +        (&paths::PTR_READ_UNALIGNED, &[0]),
 +        (&paths::PTR_READ_VOLATILE, &[0]),
 +        (&paths::PTR_REPLACE, &[0]),
 +        (&paths::PTR_SLICE_FROM_RAW_PARTS, &[0]),
 +        (&paths::PTR_SLICE_FROM_RAW_PARTS_MUT, &[0]),
 +        (&paths::PTR_SWAP, &[0, 1]),
 +        (&paths::PTR_SWAP_NONOVERLAPPING, &[0, 1]),
 +        (&paths::PTR_WRITE, &[0]),
 +        (&paths::PTR_WRITE_UNALIGNED, &[0]),
 +        (&paths::PTR_WRITE_VOLATILE, &[0]),
 +        (&paths::PTR_WRITE_BYTES, &[0]),
 +    ];
 +
 +    if_chain! {
 +        if let ExprKind::Call(fun, args) = expr.kind;
 +        if let ExprKind::Path(ref qpath) = fun.kind;
 +        if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
 +        let fun_def_path = cx.get_def_path(fun_def_id).into_iter().map(Symbol::to_ident_string).collect::<Vec<_>>();
 +        if let Some(&(_, arg_indices)) = INVALID_NULL_PTR_USAGE_TABLE
 +            .iter()
 +            .find(|&&(fn_path, _)| fn_path == fun_def_path);
 +        then {
 +            for &arg_idx in arg_indices {
 +                if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) {
 +                    span_lint_and_sugg(
 +                        cx,
 +                        INVALID_NULL_PTR_USAGE,
 +                        arg.span,
 +                        "pointer must be non-null",
 +                        "change this to",
 +                        "core::ptr::NonNull::dangling().as_ptr()".to_string(),
 +                        Applicability::MachineApplicable,
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +#[derive(Default)]
 +struct PtrArgResult {
 +    skip: bool,
 +    replacements: Vec<PtrArgReplacement>,
 +}
 +
 +struct PtrArgReplacement {
 +    expr_span: Span,
 +    self_span: Span,
 +    replacement: &'static str,
 +}
 +
 +struct PtrArg<'tcx> {
 +    idx: usize,
 +    emission_id: hir::HirId,
 +    span: Span,
 +    ty_did: DefId,
 +    ty_name: Symbol,
 +    method_renames: &'static [(&'static str, &'static str)],
 +    ref_prefix: RefPrefix,
 +    deref_ty: DerefTy<'tcx>,
 +}
 +impl PtrArg<'_> {
 +    fn build_msg(&self) -> String {
 +        format!(
 +            "writing `&{}{}` instead of `&{}{}` involves a new object where a slice will do",
 +            self.ref_prefix.mutability.prefix_str(),
 +            self.ty_name,
 +            self.ref_prefix.mutability.prefix_str(),
 +            self.deref_ty.argless_str(),
 +        )
 +    }
 +
 +    fn mutability(&self) -> Mutability {
 +        self.ref_prefix.mutability
 +    }
 +}
 +
 +struct RefPrefix {
 +    lt: LifetimeName,
 +    mutability: Mutability,
 +}
 +impl fmt::Display for RefPrefix {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        use fmt::Write;
 +        f.write_char('&')?;
 +        match self.lt {
 +            LifetimeName::Param(_, ParamName::Plain(name)) => {
 +                name.fmt(f)?;
 +                f.write_char(' ')?;
 +            },
 +            LifetimeName::Infer => f.write_str("'_ ")?,
 +            LifetimeName::Static => f.write_str("'static ")?,
 +            _ => (),
 +        }
 +        f.write_str(self.mutability.prefix_str())
 +    }
 +}
 +
 +struct DerefTyDisplay<'a, 'tcx>(&'a LateContext<'tcx>, &'a DerefTy<'tcx>);
 +impl fmt::Display for DerefTyDisplay<'_, '_> {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        use std::fmt::Write;
 +        match self.1 {
 +            DerefTy::Str => f.write_str("str"),
 +            DerefTy::Path => f.write_str("Path"),
 +            DerefTy::Slice(hir_ty, ty) => {
 +                f.write_char('[')?;
 +                match hir_ty.and_then(|s| snippet_opt(self.0, s)) {
 +                    Some(s) => f.write_str(&s)?,
 +                    None => ty.fmt(f)?,
 +                }
 +                f.write_char(']')
 +            },
 +        }
 +    }
 +}
 +
 +enum DerefTy<'tcx> {
 +    Str,
 +    Path,
 +    Slice(Option<Span>, Ty<'tcx>),
 +}
 +impl<'tcx> DerefTy<'tcx> {
 +    fn argless_str(&self) -> &'static str {
 +        match *self {
 +            Self::Str => "str",
 +            Self::Path => "Path",
 +            Self::Slice(..) => "[_]",
 +        }
 +    }
 +
 +    fn display<'a>(&'a self, cx: &'a LateContext<'tcx>) -> DerefTyDisplay<'a, 'tcx> {
 +        DerefTyDisplay(cx, self)
 +    }
 +}
 +
 +fn check_fn_args<'cx, 'tcx: 'cx>(
 +    cx: &'cx LateContext<'tcx>,
 +    tys: &'tcx [Ty<'tcx>],
 +    hir_tys: &'tcx [hir::Ty<'tcx>],
 +    params: &'tcx [Param<'tcx>],
 +) -> impl Iterator<Item = PtrArg<'tcx>> + 'cx {
 +    tys.iter()
 +        .zip(hir_tys.iter())
 +        .enumerate()
 +        .filter_map(|(i, (ty, hir_ty))| {
 +            if_chain! {
 +                if let ty::Ref(_, ty, mutability) = *ty.kind();
 +                if let ty::Adt(adt, substs) = *ty.kind();
 +
 +                if let TyKind::Rptr(lt, ref ty) = hir_ty.kind;
 +                if let TyKind::Path(QPath::Resolved(None, path)) = ty.ty.kind;
 +
 +                // Check that the name as typed matches the actual name of the type.
 +                // e.g. `fn foo(_: &Foo)` shouldn't trigger the lint when `Foo` is an alias for `Vec`
 +                if let [.., name] = path.segments;
 +                if cx.tcx.item_name(adt.did()) == name.ident.name;
 +
 +                then {
 +                    let emission_id = params.get(i).map_or(hir_ty.hir_id, |param| param.hir_id);
 +                    let (method_renames, deref_ty) = match cx.tcx.get_diagnostic_name(adt.did()) {
 +                        Some(sym::Vec) => (
 +                            [("clone", ".to_owned()")].as_slice(),
 +                            DerefTy::Slice(
 +                                name.args
 +                                    .and_then(|args| args.args.first())
 +                                    .and_then(|arg| if let GenericArg::Type(ty) = arg {
 +                                        Some(ty.span)
 +                                    } else {
 +                                        None
 +                                    }),
 +                                substs.type_at(0),
 +                            ),
 +                        ),
 +                        Some(sym::String) => (
 +                            [("clone", ".to_owned()"), ("as_str", "")].as_slice(),
 +                            DerefTy::Str,
 +                        ),
 +                        Some(sym::PathBuf) => (
 +                            [("clone", ".to_path_buf()"), ("as_path", "")].as_slice(),
 +                            DerefTy::Path,
 +                        ),
 +                        Some(sym::Cow) if mutability == Mutability::Not => {
 +                            let ty_name = name.args
 +                                .and_then(|args| {
 +                                    args.args.iter().find_map(|a| match a {
 +                                        GenericArg::Type(x) => Some(x),
 +                                        _ => None,
 +                                    })
 +                                })
 +                                .and_then(|arg| snippet_opt(cx, arg.span))
 +                                .unwrap_or_else(|| substs.type_at(1).to_string());
 +                            span_lint_hir_and_then(
 +                                cx,
 +                                PTR_ARG,
 +                                emission_id,
 +                                hir_ty.span,
 +                                "using a reference to `Cow` is not recommended",
 +                                |diag| {
 +                                    diag.span_suggestion(
 +                                        hir_ty.span,
 +                                        "change this to",
 +                                        format!("&{}{}", mutability.prefix_str(), ty_name),
 +                                        Applicability::Unspecified,
 +                                    );
 +                                }
 +                            );
 +                            return None;
 +                        },
 +                        _ => return None,
 +                    };
 +                    return Some(PtrArg {
 +                        idx: i,
 +                        emission_id,
 +                        span: hir_ty.span,
 +                        ty_did: adt.did(),
 +                        ty_name: name.ident.name,
 +                        method_renames,
 +                        ref_prefix: RefPrefix {
 +                            lt: lt.name,
 +                            mutability,
 +                        },
 +                        deref_ty,
 +                    });
 +                }
 +            }
 +            None
 +        })
 +}
 +
 +fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&'tcx Body<'_>>) {
 +    if let FnRetTy::Return(ty) = sig.decl.output
 +        && let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty)
 +    {
 +        let out_region = cx.tcx.named_region(out.hir_id);
 +        let args: Option<Vec<_>> = sig
 +            .decl
 +            .inputs
 +            .iter()
 +            .filter_map(get_rptr_lm)
 +            .filter(|&(lt, _, _)| cx.tcx.named_region(lt.hir_id) == out_region)
 +            .map(|(_, mutability, span)| (mutability == Mutability::Not).then_some(span))
 +            .collect();
 +        if let Some(args) = args
 +            && !args.is_empty()
 +            && body.map_or(true, |body| {
-     v.visit_expr(&body.value);
++                sig.header.unsafety == Unsafety::Unsafe || contains_unsafe_block(cx, body.value)
 +            })
 +        {
 +            span_lint_and_then(
 +                cx,
 +                MUT_FROM_REF,
 +                ty.span,
 +                "mutable borrow from immutable input(s)",
 +                |diag| {
 +                    let ms = MultiSpan::from_spans(args);
 +                    diag.span_note(ms, "immutable borrow here");
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +#[expect(clippy::too_many_lines)]
 +fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: &[PtrArg<'tcx>]) -> Vec<PtrArgResult> {
 +    struct V<'cx, 'tcx> {
 +        cx: &'cx LateContext<'tcx>,
 +        /// Map from a local id to which argument it came from (index into `Self::args` and
 +        /// `Self::results`)
 +        bindings: HirIdMap<usize>,
 +        /// The arguments being checked.
 +        args: &'cx [PtrArg<'tcx>],
 +        /// The results for each argument (len should match args.len)
 +        results: Vec<PtrArgResult>,
 +        /// The number of arguments which can't be linted. Used to return early.
 +        skip_count: usize,
 +    }
 +    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
 +        type NestedFilter = nested_filter::OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.cx.tcx.hir()
 +        }
 +
 +        fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if self.skip_count == self.args.len() {
 +                return;
 +            }
 +
 +            // Check if this is local we care about
 +            let args_idx = match path_to_local(e).and_then(|id| self.bindings.get(&id)) {
 +                Some(&i) => i,
 +                None => return walk_expr(self, e),
 +            };
 +            let args = &self.args[args_idx];
 +            let result = &mut self.results[args_idx];
 +
 +            // Helper function to handle early returns.
 +            let mut set_skip_flag = || {
 +                if !result.skip {
 +                    self.skip_count += 1;
 +                }
 +                result.skip = true;
 +            };
 +
 +            match get_expr_use_or_unification_node(self.cx.tcx, e) {
 +                Some((Node::Stmt(_), _)) => (),
 +                Some((Node::Local(l), _)) => {
 +                    // Only trace simple bindings. e.g `let x = y;`
 +                    if let PatKind::Binding(BindingAnnotation::NONE, id, _, None) = l.pat.kind {
 +                        self.bindings.insert(id, args_idx);
 +                    } else {
 +                        set_skip_flag();
 +                    }
 +                },
 +                Some((Node::Expr(e), child_id)) => match e.kind {
 +                    ExprKind::Call(f, expr_args) => {
 +                        let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0);
 +                        if expr_sig(self.cx, f).and_then(|sig| sig.input(i)).map_or(true, |ty| {
 +                            match *ty.skip_binder().peel_refs().kind() {
 +                                ty::Param(_) => true,
 +                                ty::Adt(def, _) => def.did() == args.ty_did,
 +                                _ => false,
 +                            }
 +                        }) {
 +                            // Passed to a function taking the non-dereferenced type.
 +                            set_skip_flag();
 +                        }
 +                    },
 +                    ExprKind::MethodCall(name, self_arg, expr_args, _) => {
 +                        let i = std::iter::once(self_arg)
 +                            .chain(expr_args.iter())
 +                            .position(|arg| arg.hir_id == child_id)
 +                            .unwrap_or(0);
 +                        if i == 0 {
 +                            // Check if the method can be renamed.
 +                            let name = name.ident.as_str();
 +                            if let Some((_, replacement)) = args.method_renames.iter().find(|&&(x, _)| x == name) {
 +                                result.replacements.push(PtrArgReplacement {
 +                                    expr_span: e.span,
 +                                    self_span: self_arg.span,
 +                                    replacement,
 +                                });
 +                                return;
 +                            }
 +                        }
 +
 +                        let id = if let Some(x) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) {
 +                            x
 +                        } else {
 +                            set_skip_flag();
 +                            return;
 +                        };
 +
 +                        match *self.cx.tcx.fn_sig(id).skip_binder().inputs()[i].peel_refs().kind() {
 +                            ty::Param(_) => {
 +                                set_skip_flag();
 +                            },
 +                            // If the types match check for methods which exist on both types. e.g. `Vec::len` and
 +                            // `slice::len`
 +                            ty::Adt(def, _) if def.did() == args.ty_did => {
 +                                set_skip_flag();
 +                            },
 +                            _ => (),
 +                        }
 +                    },
 +                    // Indexing is fine for currently supported types.
 +                    ExprKind::Index(e, _) if e.hir_id == child_id => (),
 +                    _ => set_skip_flag(),
 +                },
 +                _ => set_skip_flag(),
 +            }
 +        }
 +    }
 +
 +    let mut skip_count = 0;
 +    let mut results = args.iter().map(|_| PtrArgResult::default()).collect::<Vec<_>>();
 +    let mut v = V {
 +        cx,
 +        bindings: args
 +            .iter()
 +            .enumerate()
 +            .filter_map(|(i, arg)| {
 +                let param = &body.params[arg.idx];
 +                match param.pat.kind {
 +                    PatKind::Binding(BindingAnnotation::NONE, id, _, None)
 +                        if !is_lint_allowed(cx, PTR_ARG, param.hir_id) =>
 +                    {
 +                        Some((id, i))
 +                    },
 +                    _ => {
 +                        skip_count += 1;
 +                        results[i].skip = true;
 +                        None
 +                    },
 +                }
 +            })
 +            .collect(),
 +        args,
 +        results,
 +        skip_count,
 +    };
++    v.visit_expr(body.value);
 +    v.results
 +}
 +
 +fn get_rptr_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> {
 +    if let TyKind::Rptr(ref lt, ref m) = ty.kind {
 +        Some((lt, m.mutbl, ty.span))
 +    } else {
 +        None
 +    }
 +}
 +
 +fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    if let ExprKind::Call(pathexp, []) = expr.kind {
 +        path_def_id(cx, pathexp).map_or(false, |id| {
 +            matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ptr_null | sym::ptr_null_mut))
 +        })
 +    } else {
 +        false
 +    }
 +}
index 490f345d2970777634c6554ec54cbb5fd5c040cb,0000000000000000000000000000000000000000..918d624eec6fa852da2d6767b4c2eef29f5cb7ed
mode 100644,000000..100644
--- /dev/null
@@@ -1,537 -1,0 +1,532 @@@
-             let span = if expr.span.from_expansion() {
-                 expr.span
-                     .ctxt()
-                     .outer_expn_data()
-                     .call_site
-             } else {
-                 expr.span
-             };
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::higher;
 +use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, path_to_local};
 +use if_chain::if_chain;
 +use rustc_ast::ast::RangeLimits;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BinOpKind, Expr, ExprKind, HirId};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::{Span, Spanned};
 +use std::cmp::Ordering;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for exclusive ranges where 1 is added to the
 +    /// upper bound, e.g., `x..(y+1)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The code is more readable with an inclusive range
 +    /// like `x..=y`.
 +    ///
 +    /// ### Known problems
 +    /// Will add unnecessary pair of parentheses when the
 +    /// expression is not wrapped in a pair but starts with an opening parenthesis
 +    /// and ends with a closing one.
 +    /// I.e., `let _ = (f()+1)..(f()+1)` results in `let _ = ((f()+1)..=f())`.
 +    ///
 +    /// Also in many cases, inclusive ranges are still slower to run than
 +    /// exclusive ranges, because they essentially add an extra branch that
 +    /// LLVM may fail to hoist out of the loop.
 +    ///
 +    /// This will cause a warning that cannot be fixed if the consumer of the
 +    /// range only accepts a specific range type, instead of the generic
 +    /// `RangeBounds` trait
 +    /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = 0;
 +    /// # let y = 1;
 +    /// for i in x..(y+1) {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x = 0;
 +    /// # let y = 1;
 +    /// for i in x..=y {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub RANGE_PLUS_ONE,
 +    pedantic,
 +    "`x..(y+1)` reads better as `x..=y`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for inclusive ranges where 1 is subtracted from
 +    /// the upper bound, e.g., `x..=(y-1)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The code is more readable with an exclusive range
 +    /// like `x..y`.
 +    ///
 +    /// ### Known problems
 +    /// This will cause a warning that cannot be fixed if
 +    /// the consumer of the range only accepts a specific range type, instead of
 +    /// the generic `RangeBounds` trait
 +    /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = 0;
 +    /// # let y = 1;
 +    /// for i in x..=(y-1) {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x = 0;
 +    /// # let y = 1;
 +    /// for i in x..y {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub RANGE_MINUS_ONE,
 +    pedantic,
 +    "`x..=(y-1)` reads better as `x..y`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for range expressions `x..y` where both `x` and `y`
 +    /// are constant and `x` is greater or equal to `y`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Empty ranges yield no values so iterating them is a no-op.
 +    /// Moreover, trying to use a reversed range to index a slice will panic at run-time.
 +    ///
 +    /// ### Example
 +    /// ```rust,no_run
 +    /// fn main() {
 +    ///     (10..=0).for_each(|x| println!("{}", x));
 +    ///
 +    ///     let arr = [1, 2, 3, 4, 5];
 +    ///     let sub = &arr[3..1];
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn main() {
 +    ///     (0..=10).rev().for_each(|x| println!("{}", x));
 +    ///
 +    ///     let arr = [1, 2, 3, 4, 5];
 +    ///     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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for expressions like `x >= 3 && x < 8` that could
 +    /// be more readably expressed as `(3..8).contains(x)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `contains` expresses the intent better and has less
 +    /// failure modes (such as fencepost errors or using `||` instead of `&&`).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // given
 +    /// let x = 6;
 +    ///
 +    /// assert!(x >= 3 && x < 8);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    ///# let x = 6;
 +    /// assert!((3..8).contains(&x));
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub MANUAL_RANGE_CONTAINS,
 +    style,
 +    "manually reimplementing {`Range`, `RangeInclusive`}`::contains`"
 +}
 +
 +pub struct Ranges {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl Ranges {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(Ranges => [
 +    RANGE_PLUS_ONE,
 +    RANGE_MINUS_ONE,
 +    REVERSED_EMPTY_RANGES,
 +    MANUAL_RANGE_CONTAINS,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Ranges {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let ExprKind::Binary(ref op, l, r) = expr.kind {
 +            if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) {
 +                check_possible_range_contains(cx, op.node, l, r, expr, expr.span);
 +            }
 +        }
 +
 +        check_exclusive_range_plus_one(cx, expr);
 +        check_inclusive_range_minus_one(cx, expr);
 +        check_reversed_empty_range(cx, expr);
 +    }
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +fn check_possible_range_contains(
 +    cx: &LateContext<'_>,
 +    op: BinOpKind,
 +    left: &Expr<'_>,
 +    right: &Expr<'_>,
 +    expr: &Expr<'_>,
 +    span: Span,
 +) {
 +    if in_constant(cx, expr.hir_id) {
 +        return;
 +    }
 +
 +    let combine_and = match op {
 +        BinOpKind::And | BinOpKind::BitAnd => true,
 +        BinOpKind::Or | BinOpKind::BitOr => false,
 +        _ => return,
 +    };
 +    // value, name, order (higher/lower), inclusiveness
 +    if let (Some(l), Some(r)) = (check_range_bounds(cx, left), check_range_bounds(cx, right)) {
 +        // we only lint comparisons on the same name and with different
 +        // direction
 +        if l.id != r.id || l.ord == r.ord {
 +            return;
 +        }
 +        let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(l.expr), &l.val, &r.val);
 +        if combine_and && ord == Some(r.ord) {
 +            // order lower bound and upper bound
 +            let (l_span, u_span, l_inc, u_inc) = if r.ord == Ordering::Less {
 +                (l.val_span, r.val_span, l.inc, r.inc)
 +            } else {
 +                (r.val_span, l.val_span, r.inc, l.inc)
 +            };
 +            // we only lint inclusive lower bounds
 +            if !l_inc {
 +                return;
 +            }
 +            let (range_type, range_op) = if u_inc {
 +                ("RangeInclusive", "..=")
 +            } else {
 +                ("Range", "..")
 +            };
 +            let mut applicability = Applicability::MachineApplicable;
 +            let name = snippet_with_applicability(cx, l.name_span, "_", &mut applicability);
 +            let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability);
 +            let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability);
 +            let space = if lo.ends_with('.') { " " } else { "" };
 +            span_lint_and_sugg(
 +                cx,
 +                MANUAL_RANGE_CONTAINS,
 +                span,
 +                &format!("manual `{}::contains` implementation", range_type),
 +                "use",
 +                format!("({}{}{}{}).contains(&{})", lo, space, range_op, hi, name),
 +                applicability,
 +            );
 +        } else if !combine_and && ord == Some(l.ord) {
 +            // `!_.contains(_)`
 +            // order lower bound and upper bound
 +            let (l_span, u_span, l_inc, u_inc) = if l.ord == Ordering::Less {
 +                (l.val_span, r.val_span, l.inc, r.inc)
 +            } else {
 +                (r.val_span, l.val_span, r.inc, l.inc)
 +            };
 +            if l_inc {
 +                return;
 +            }
 +            let (range_type, range_op) = if u_inc {
 +                ("Range", "..")
 +            } else {
 +                ("RangeInclusive", "..=")
 +            };
 +            let mut applicability = Applicability::MachineApplicable;
 +            let name = snippet_with_applicability(cx, l.name_span, "_", &mut applicability);
 +            let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability);
 +            let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability);
 +            let space = if lo.ends_with('.') { " " } else { "" };
 +            span_lint_and_sugg(
 +                cx,
 +                MANUAL_RANGE_CONTAINS,
 +                span,
 +                &format!("manual `!{}::contains` implementation", range_type),
 +                "use",
 +                format!("!({}{}{}{}).contains(&{})", lo, space, range_op, hi, name),
 +                applicability,
 +            );
 +        }
 +    }
 +
 +    // If the LHS is the same operator, we have to recurse to get the "real" RHS, since they have
 +    // the same operator precedence
 +    if_chain! {
 +        if let ExprKind::Binary(ref lhs_op, _left, new_lhs) = left.kind;
 +        if op == lhs_op.node;
 +        let new_span = Span::new(new_lhs.span.lo(), right.span.hi(), expr.span.ctxt(), expr.span.parent());
 +        if let Some(snip) = &snippet_opt(cx, new_span);
 +        // Do not continue if we have mismatched number of parens, otherwise the suggestion is wrong
 +        if snip.matches('(').count() == snip.matches(')').count();
 +        then {
 +            check_possible_range_contains(cx, op, new_lhs, right, expr, new_span);
 +        }
 +    }
 +}
 +
 +struct RangeBounds<'a> {
 +    val: Constant,
 +    expr: &'a Expr<'a>,
 +    id: HirId,
 +    name_span: Span,
 +    val_span: Span,
 +    ord: Ordering,
 +    inc: bool,
 +}
 +
 +// Takes a binary expression such as x <= 2 as input
 +// Breaks apart into various pieces, such as the value of the number,
 +// hir id of the variable, and direction/inclusiveness of the operator
 +fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option<RangeBounds<'a>> {
 +    if let ExprKind::Binary(ref op, l, r) = ex.kind {
 +        let (inclusive, ordering) = match op.node {
 +            BinOpKind::Gt => (false, Ordering::Greater),
 +            BinOpKind::Ge => (true, Ordering::Greater),
 +            BinOpKind::Lt => (false, Ordering::Less),
 +            BinOpKind::Le => (true, Ordering::Less),
 +            _ => return None,
 +        };
 +        if let Some(id) = path_to_local(l) {
 +            if let Some((c, _)) = constant(cx, cx.typeck_results(), r) {
 +                return Some(RangeBounds {
 +                    val: c,
 +                    expr: r,
 +                    id,
 +                    name_span: l.span,
 +                    val_span: r.span,
 +                    ord: ordering,
 +                    inc: inclusive,
 +                });
 +            }
 +        } else if let Some(id) = path_to_local(r) {
 +            if let Some((c, _)) = constant(cx, cx.typeck_results(), l) {
 +                return Some(RangeBounds {
 +                    val: c,
 +                    expr: l,
 +                    id,
 +                    name_span: r.span,
 +                    val_span: l.span,
 +                    ord: ordering.reverse(),
 +                    inc: inclusive,
 +                });
 +            }
 +        }
 +    }
 +    None
 +}
 +
 +// exclusive range plus one: `x..(y+1)`
 +fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if_chain! {
++        if expr.span.can_be_used_for_suggestions();
 +        if let Some(higher::Range {
 +            start,
 +            end: Some(end),
 +            limits: RangeLimits::HalfOpen
 +        }) = higher::Range::hir(expr);
 +        if let Some(y) = y_plus_one(cx, end);
 +        then {
++            let span = expr.span;
 +            span_lint_and_then(
 +                cx,
 +                RANGE_PLUS_ONE,
 +                span,
 +                "an inclusive range would be more readable",
 +                |diag| {
 +                    let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string());
 +                    let end = Sugg::hir(cx, y, "y").maybe_par();
 +                    if let Some(is_wrapped) = &snippet_opt(cx, span) {
 +                        if is_wrapped.starts_with('(') && is_wrapped.ends_with(')') {
 +                            diag.span_suggestion(
 +                                span,
 +                                "use",
 +                                format!("({}..={})", start, end),
 +                                Applicability::MaybeIncorrect,
 +                            );
 +                        } else {
 +                            diag.span_suggestion(
 +                                span,
 +                                "use",
 +                                format!("{}..={}", start, end),
 +                                Applicability::MachineApplicable, // snippet
 +                            );
 +                        }
 +                    }
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +// inclusive range minus one: `x..=(y-1)`
 +fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if_chain! {
++        if expr.span.can_be_used_for_suggestions();
 +        if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::Range::hir(expr);
 +        if let Some(y) = y_minus_one(cx, end);
 +        then {
 +            span_lint_and_then(
 +                cx,
 +                RANGE_MINUS_ONE,
 +                expr.span,
 +                "an exclusive range would be more readable",
 +                |diag| {
 +                    let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string());
 +                    let end = Sugg::hir(cx, y, "y").maybe_par();
 +                    diag.span_suggestion(
 +                        expr.span,
 +                        "use",
 +                        format!("{}..{}", start, end),
 +                        Applicability::MachineApplicable, // snippet
 +                    );
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    fn inside_indexing_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +        matches!(
 +            get_parent_expr(cx, expr),
 +            Some(Expr {
 +                kind: ExprKind::Index(..),
 +                ..
 +            })
 +        )
 +    }
 +
 +    fn is_for_loop_arg(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +        let mut cur_expr = expr;
 +        while let Some(parent_expr) = get_parent_expr(cx, cur_expr) {
 +            match higher::ForLoop::hir(parent_expr) {
 +                Some(higher::ForLoop { arg, .. }) if arg.hir_id == expr.hir_id => return true,
 +                _ => cur_expr = parent_expr,
 +            }
 +        }
 +
 +        false
 +    }
 +
 +    fn is_empty_range(limits: RangeLimits, ordering: Ordering) -> bool {
 +        match limits {
 +            RangeLimits::HalfOpen => ordering != Ordering::Less,
 +            RangeLimits::Closed => ordering == Ordering::Greater,
 +        }
 +    }
 +
 +    if_chain! {
 +        if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::Range::hir(expr);
 +        let ty = cx.typeck_results().expr_ty(start);
 +        if let ty::Int(_) | ty::Uint(_) = ty.kind();
 +        if let Some((start_idx, _)) = constant(cx, cx.typeck_results(), start);
 +        if let Some((end_idx, _)) = constant(cx, cx.typeck_results(), end);
 +        if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx);
 +        if is_empty_range(limits, ordering);
 +        then {
 +            if inside_indexing_expr(cx, expr) {
 +                // Avoid linting `N..N` as it has proven to be useful, see #5689 and #5628 ...
 +                if ordering != Ordering::Equal {
 +                    span_lint(
 +                        cx,
 +                        REVERSED_EMPTY_RANGES,
 +                        expr.span,
 +                        "this range is reversed and using it to index a slice will panic at run-time",
 +                    );
 +                }
 +            // ... except in for loop arguments for backwards compatibility with `reverse_range_loop`
 +            } else if ordering != Ordering::Equal || is_for_loop_arg(cx, expr) {
 +                span_lint_and_then(
 +                    cx,
 +                    REVERSED_EMPTY_RANGES,
 +                    expr.span,
 +                    "this range is empty so it will yield no values",
 +                    |diag| {
 +                        if ordering != Ordering::Equal {
 +                            let start_snippet = snippet(cx, start.span, "_");
 +                            let end_snippet = snippet(cx, end.span, "_");
 +                            let dots = match limits {
 +                                RangeLimits::HalfOpen => "..",
 +                                RangeLimits::Closed => "..="
 +                            };
 +
 +                            diag.span_suggestion(
 +                                expr.span,
 +                                "consider using the following if you are attempting to iterate over this \
 +                                 range in reverse",
 +                                format!("({}{}{}).rev()", end_snippet, dots, start_snippet),
 +                                Applicability::MaybeIncorrect,
 +                            );
 +                        }
 +                    },
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn y_plus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> {
 +    match expr.kind {
 +        ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Add, ..
 +            },
 +            lhs,
 +            rhs,
 +        ) => {
 +            if is_integer_const(cx, lhs, 1) {
 +                Some(rhs)
 +            } else if is_integer_const(cx, rhs, 1) {
 +                Some(lhs)
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +fn y_minus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> {
 +    match expr.kind {
 +        ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Sub, ..
 +            },
 +            lhs,
 +            rhs,
 +        ) if is_integer_const(cx, rhs, 1) => Some(lhs),
 +        _ => None,
 +    }
 +}
index 1926661c59603a886b5bfe369532d42df86a497e,0000000000000000000000000000000000000000..91553240e3c910fa4a25d4222a9c315a6ca0dad3
mode 100644,000000..100644
--- /dev/null
@@@ -1,325 -1,0 +1,325 @@@
-                 check_final_expr(cx, &body.value, Some(body.value.span), replacement);
 +use clippy_utils::diagnostics::span_lint_hir_and_then;
 +use clippy_utils::source::{snippet_opt, snippet_with_context};
 +use clippy_utils::{fn_def_id, path_to_local_id};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
 +use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::subst::GenericArgKind;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `let`-bindings, which are subsequently
 +    /// returned.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is just extraneous code. Remove it to make your code
 +    /// more rusty.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn foo() -> String {
 +    ///     let x = String::new();
 +    ///     x
 +    /// }
 +    /// ```
 +    /// instead, use
 +    /// ```
 +    /// fn foo() -> String {
 +    ///     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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for return statements at the end of a block.
 +    ///
 +    /// ### Why is this bad?
 +    /// Removing the `return` and semicolon will make the code
 +    /// more rusty.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn foo(x: usize) -> usize {
 +    ///     return x;
 +    /// }
 +    /// ```
 +    /// simplify to
 +    /// ```rust
 +    /// fn foo(x: usize) -> usize {
 +    ///     x
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEEDLESS_RETURN,
 +    style,
 +    "using a return statement like `return expr;` where an expression would suffice"
 +}
 +
 +#[derive(PartialEq, Eq, Copy, Clone)]
 +enum RetReplacement {
 +    Empty,
 +    Block,
 +    Unit,
 +}
 +
 +declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Return {
 +    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
 +        // we need both a let-binding stmt and an expr
 +        if_chain! {
 +            if let Some(retexpr) = block.expr;
 +            if let Some(stmt) = block.stmts.iter().last();
 +            if let StmtKind::Local(local) = &stmt.kind;
 +            if local.ty.is_none();
 +            if cx.tcx.hir().attrs(local.hir_id).is_empty();
 +            if let Some(initexpr) = &local.init;
 +            if let PatKind::Binding(_, local_id, _, _) = local.pat.kind;
 +            if path_to_local_id(retexpr, local_id);
 +            if !last_statement_borrows(cx, initexpr);
 +            if !in_external_macro(cx.sess(), initexpr.span);
 +            if !in_external_macro(cx.sess(), retexpr.span);
 +            if !local.span.from_expansion();
 +            then {
 +                span_lint_hir_and_then(
 +                    cx,
 +                    LET_AND_RETURN,
 +                    retexpr.hir_id,
 +                    retexpr.span,
 +                    "returning the result of a `let` binding from a block",
 +                    |err| {
 +                        err.span_label(local.span, "unnecessary `let` binding");
 +
 +                        if let Some(mut snippet) = snippet_opt(cx, initexpr.span) {
 +                            if !cx.typeck_results().expr_adjustments(retexpr).is_empty() {
 +                                snippet.push_str(" as _");
 +                            }
 +                            err.multipart_suggestion(
 +                                "return the expression directly",
 +                                vec![
 +                                    (local.span, String::new()),
 +                                    (retexpr.span, snippet),
 +                                ],
 +                                Applicability::MachineApplicable,
 +                            );
 +                        } else {
 +                            err.span_help(initexpr.span, "this expression can be directly returned");
 +                        }
 +                    },
 +                );
 +            }
 +        }
 +    }
 +
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        kind: FnKind<'tcx>,
 +        _: &'tcx FnDecl<'tcx>,
 +        body: &'tcx Body<'tcx>,
 +        _: Span,
 +        _: HirId,
 +    ) {
 +        match kind {
 +            FnKind::Closure => {
 +                // when returning without value in closure, replace this `return`
 +                // with an empty block to prevent invalid suggestion (see #6501)
 +                let replacement = if let ExprKind::Ret(None) = &body.value.kind {
 +                    RetReplacement::Block
 +                } else {
 +                    RetReplacement::Empty
 +                };
++                check_final_expr(cx, body.value, Some(body.value.span), replacement);
 +            },
 +            FnKind::ItemFn(..) | FnKind::Method(..) => {
 +                if let ExprKind::Block(block, _) = body.value.kind {
 +                    check_block_return(cx, block);
 +                }
 +            },
 +        }
 +    }
 +}
 +
 +fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) {
 +    if let Some(expr) = block.expr {
 +        check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty);
 +    } else if let Some(stmt) = block.stmts.iter().last() {
 +        match stmt.kind {
 +            StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
 +                check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty);
 +            },
 +            _ => (),
 +        }
 +    }
 +}
 +
 +fn check_final_expr<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +    span: Option<Span>,
 +    replacement: RetReplacement,
 +) {
 +    match expr.kind {
 +        // simple return is always "bad"
 +        ExprKind::Ret(ref inner) => {
 +            if cx.tcx.hir().attrs(expr.hir_id).is_empty() {
 +                let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
 +                if !borrows {
 +                    emit_return_lint(
 +                        cx,
 +                        inner.map_or(expr.hir_id, |inner| inner.hir_id),
 +                        span.expect("`else return` is not possible"),
 +                        inner.as_ref().map(|i| i.span),
 +                        replacement,
 +                    );
 +                }
 +            }
 +        },
 +        // a whole block? check it!
 +        ExprKind::Block(block, _) => {
 +            check_block_return(cx, block);
 +        },
 +        ExprKind::If(_, then, else_clause_opt) => {
 +            if let ExprKind::Block(ifblock, _) = then.kind {
 +                check_block_return(cx, ifblock);
 +            }
 +            if let Some(else_clause) = else_clause_opt {
 +                check_final_expr(cx, else_clause, None, RetReplacement::Empty);
 +            }
 +        },
 +        // a match expr, check all arms
 +        // an if/if let expr, check both exprs
 +        // note, if without else is going to be a type checking error anyways
 +        // (except for unit type functions) so we don't match it
 +        ExprKind::Match(_, arms, MatchSource::Normal) => {
 +            for arm in arms.iter() {
 +                check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Unit);
 +            }
 +        },
 +        ExprKind::DropTemps(expr) => check_final_expr(cx, expr, None, RetReplacement::Empty),
 +        _ => (),
 +    }
 +}
 +
 +fn emit_return_lint(
 +    cx: &LateContext<'_>,
 +    emission_place: HirId,
 +    ret_span: Span,
 +    inner_span: Option<Span>,
 +    replacement: RetReplacement,
 +) {
 +    if ret_span.from_expansion() {
 +        return;
 +    }
 +    match inner_span {
 +        Some(inner_span) => {
 +            let mut applicability = Applicability::MachineApplicable;
 +            span_lint_hir_and_then(
 +                cx,
 +                NEEDLESS_RETURN,
 +                emission_place,
 +                ret_span,
 +                "unneeded `return` statement",
 +                |diag| {
 +                    let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability);
 +                    diag.span_suggestion(ret_span, "remove `return`", snippet, applicability);
 +                },
 +            );
 +        },
 +        None => match replacement {
 +            RetReplacement::Empty => {
 +                span_lint_hir_and_then(
 +                    cx,
 +                    NEEDLESS_RETURN,
 +                    emission_place,
 +                    ret_span,
 +                    "unneeded `return` statement",
 +                    |diag| {
 +                        diag.span_suggestion(
 +                            ret_span,
 +                            "remove `return`",
 +                            String::new(),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    },
 +                );
 +            },
 +            RetReplacement::Block => {
 +                span_lint_hir_and_then(
 +                    cx,
 +                    NEEDLESS_RETURN,
 +                    emission_place,
 +                    ret_span,
 +                    "unneeded `return` statement",
 +                    |diag| {
 +                        diag.span_suggestion(
 +                            ret_span,
 +                            "replace `return` with an empty block",
 +                            "{}".to_string(),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    },
 +                );
 +            },
 +            RetReplacement::Unit => {
 +                span_lint_hir_and_then(
 +                    cx,
 +                    NEEDLESS_RETURN,
 +                    emission_place,
 +                    ret_span,
 +                    "unneeded `return` statement",
 +                    |diag| {
 +                        diag.span_suggestion(
 +                            ret_span,
 +                            "replace `return` with a unit value",
 +                            "()".to_string(),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    },
 +                );
 +            },
 +        },
 +    }
 +}
 +
 +fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
 +    let mut visitor = BorrowVisitor { cx, borrows: false };
 +    walk_expr(&mut visitor, expr);
 +    visitor.borrows
 +}
 +
 +struct BorrowVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    borrows: bool,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> {
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if self.borrows || expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        if let Some(def_id) = fn_def_id(self.cx, expr) {
 +            self.borrows = self
 +                .cx
 +                .tcx
 +                .fn_sig(def_id)
 +                .output()
 +                .skip_binder()
 +                .walk()
 +                .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +}
index 4294464dbf61a5fffbda1a1d728a9c3a71608f9d,0000000000000000000000000000000000000000..6add20c1fb712dcad9575e7b2b278bc4125f1412
mode 100644,000000..100644
--- /dev/null
@@@ -1,116 -1,0 +1,116 @@@
-             if count_binops(&body.value) == 1;
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::{binop_traits, trait_ref_of_method, BINOP_TRAITS, OP_ASSIGN_TRAITS};
 +use if_chain::if_chain;
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::{walk_expr, Visitor};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Lints for suspicious operations in impls of arithmetic operators, e.g.
 +    /// subtracting elements in an Add impl.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is probably a typo or copy-and-paste error and not intended.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// impl Add for Foo {
 +    ///     type Output = Foo;
 +    ///
 +    ///     fn add(self, other: Foo) -> Foo {
 +    ///         Foo(self.0 - other.0)
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SUSPICIOUS_ARITHMETIC_IMPL,
 +    suspicious,
 +    "suspicious use of operators in impl of arithmetic trait"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Lints for suspicious operations in impls of OpAssign, e.g.
 +    /// subtracting elements in an AddAssign impl.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is probably a typo or copy-and-paste error and not intended.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// impl AddAssign for Foo {
 +    ///     fn add_assign(&mut self, other: Foo) {
 +    ///         *self = *self - other;
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SUSPICIOUS_OP_ASSIGN_IMPL,
 +    suspicious,
 +    "suspicious use of operators in impl of OpAssign trait"
 +}
 +
 +declare_lint_pass!(SuspiciousImpl => [SUSPICIOUS_ARITHMETIC_IMPL, SUSPICIOUS_OP_ASSIGN_IMPL]);
 +
 +impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if_chain! {
 +            if let hir::ExprKind::Binary(binop, _, _) | hir::ExprKind::AssignOp(binop, ..) = expr.kind;
 +            if let Some((binop_trait_lang, op_assign_trait_lang)) = binop_traits(binop.node);
 +            if let Ok(binop_trait_id) = cx.tcx.lang_items().require(binop_trait_lang);
 +            if let Ok(op_assign_trait_id) = cx.tcx.lang_items().require(op_assign_trait_lang);
 +
 +            // Check for more than one binary operation in the implemented function
 +            // Linting when multiple operations are involved can result in false positives
 +            let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id);
 +            if let hir::Node::ImplItem(impl_item) = cx.tcx.hir().get_by_def_id(parent_fn);
 +            if let hir::ImplItemKind::Fn(_, body_id) = impl_item.kind;
 +            let body = cx.tcx.hir().body(body_id);
 +            let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id);
 +            if let Some(trait_ref) = trait_ref_of_method(cx, parent_fn);
 +            let trait_id = trait_ref.path.res.def_id();
 +            if ![binop_trait_id, op_assign_trait_id].contains(&trait_id);
 +            if let Some(&(_, lint)) = [
 +                (&BINOP_TRAITS, SUSPICIOUS_ARITHMETIC_IMPL),
 +                (&OP_ASSIGN_TRAITS, SUSPICIOUS_OP_ASSIGN_IMPL),
 +            ]
 +                .iter()
 +                .find(|&(ts, _)| ts.iter().any(|&t| Ok(trait_id) == cx.tcx.lang_items().require(t)));
++            if count_binops(body.value) == 1;
 +            then {
 +                span_lint(
 +                    cx,
 +                    lint,
 +                    binop.span,
 +                    &format!("suspicious use of `{}` in `{}` impl", binop.node.as_str(), cx.tcx.item_name(trait_id)),
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn count_binops(expr: &hir::Expr<'_>) -> u32 {
 +    let mut visitor = BinaryExprVisitor::default();
 +    visitor.visit_expr(expr);
 +    visitor.nb_binops
 +}
 +
 +#[derive(Default)]
 +struct BinaryExprVisitor {
 +    nb_binops: u32,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for BinaryExprVisitor {
 +    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
 +        match expr.kind {
 +            hir::ExprKind::Binary(..)
 +            | hir::ExprKind::Unary(hir::UnOp::Not | hir::UnOp::Neg, _)
 +            | hir::ExprKind::AssignOp(..) => self.nb_binops += 1,
 +            _ => {},
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +}
index 851eef7b332417658b090489fa968aa0850923f4,0000000000000000000000000000000000000000..c0a4f3fbacd6487521ae0b9ca0777545176e1e51
mode 100644,000000..100644
--- /dev/null
@@@ -1,185 -1,0 +1,185 @@@
-                     match check_arg(cx, &args[i]) {
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 +use clippy_utils::{get_trait_def_id, paths};
 +use if_chain::if_chain;
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::{Closure, Expr, ExprKind, StmtKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_middle::ty::{GenericPredicates, PredicateKind, ProjectionPredicate, TraitPredicate};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{BytePos, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for functions that expect closures of type
 +    /// Fn(...) -> Ord where the implemented closure returns the unit type.
 +    /// The lint also suggests to remove the semi-colon at the end of the statement if present.
 +    ///
 +    /// ### Why is this bad?
 +    /// Likely, returning the unit type is unintentional, and
 +    /// could simply be caused by an extra semi-colon. Since () implements Ord
 +    /// it doesn't cause a compilation error.
 +    /// This is the same reasoning behind the unit_cmp lint.
 +    ///
 +    /// ### Known problems
 +    /// If returning unit is intentional, then there is no
 +    /// way of specifying this without triggering needless_return lint
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// 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 ()."
 +}
 +
 +declare_lint_pass!(UnitReturnExpectingOrd => [UNIT_RETURN_EXPECTING_ORD]);
 +
 +fn get_trait_predicates_for_trait_id<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    generics: GenericPredicates<'tcx>,
 +    trait_id: Option<DefId>,
 +) -> Vec<TraitPredicate<'tcx>> {
 +    let mut preds = Vec::new();
 +    for (pred, _) in generics.predicates {
 +        if_chain! {
 +            if let PredicateKind::Trait(poly_trait_pred) = pred.kind().skip_binder();
 +            let trait_pred = cx.tcx.erase_late_bound_regions(pred.kind().rebind(poly_trait_pred));
 +            if let Some(trait_def_id) = trait_id;
 +            if trait_def_id == trait_pred.trait_ref.def_id;
 +            then {
 +                preds.push(trait_pred);
 +            }
 +        }
 +    }
 +    preds
 +}
 +
 +fn get_projection_pred<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    generics: GenericPredicates<'tcx>,
 +    trait_pred: TraitPredicate<'tcx>,
 +) -> Option<ProjectionPredicate<'tcx>> {
 +    generics.predicates.iter().find_map(|(proj_pred, _)| {
 +        if let ty::PredicateKind::Projection(pred) = proj_pred.kind().skip_binder() {
 +            let projection_pred = cx.tcx.erase_late_bound_regions(proj_pred.kind().rebind(pred));
 +            if projection_pred.projection_ty.substs == trait_pred.trait_ref.substs {
 +                return Some(projection_pred);
 +            }
 +        }
 +        None
 +    })
 +}
 +
 +fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Vec<(usize, String)> {
 +    let mut args_to_check = Vec::new();
 +    if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
 +        let fn_sig = cx.tcx.fn_sig(def_id);
 +        let generics = cx.tcx.predicates_of(def_id);
 +        let fn_mut_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().fn_mut_trait());
 +        let ord_preds = get_trait_predicates_for_trait_id(cx, generics, get_trait_def_id(cx, &paths::ORD));
 +        let partial_ord_preds =
 +            get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().partial_ord_trait());
 +        // Trying to call erase_late_bound_regions on fn_sig.inputs() gives the following error
 +        // The trait `rustc::ty::TypeFoldable<'_>` is not implemented for
 +        // `&[rustc_middle::ty::Ty<'_>]`
 +        let inputs_output = cx.tcx.erase_late_bound_regions(fn_sig.inputs_and_output());
 +        inputs_output
 +            .iter()
 +            .rev()
 +            .skip(1)
 +            .rev()
 +            .enumerate()
 +            .for_each(|(i, inp)| {
 +                for trait_pred in &fn_mut_preds {
 +                    if_chain! {
 +                        if trait_pred.self_ty() == inp;
 +                        if let Some(return_ty_pred) = get_projection_pred(cx, generics, *trait_pred);
 +                        then {
 +                            if ord_preds.iter().any(|ord| Some(ord.self_ty()) == return_ty_pred.term.ty()) {
 +                                args_to_check.push((i, "Ord".to_string()));
 +                            } else if partial_ord_preds.iter().any(|pord| {
 +                                pord.self_ty() == return_ty_pred.term.ty().unwrap()
 +                            }) {
 +                                args_to_check.push((i, "PartialOrd".to_string()));
 +                            }
 +                        }
 +                    }
 +                }
 +            });
 +    }
 +    args_to_check
 +}
 +
 +fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Span, Option<Span>)> {
 +    if_chain! {
 +        if let ExprKind::Closure(&Closure { body, fn_decl_span, .. }) = arg.kind;
 +        if let ty::Closure(_def_id, substs) = &cx.typeck_results().node_type(arg.hir_id).kind();
 +        let ret_ty = substs.as_closure().sig().output();
 +        let ty = cx.tcx.erase_late_bound_regions(ret_ty);
 +        if ty.is_unit();
 +        then {
 +            let body = cx.tcx.hir().body(body);
 +            if_chain! {
 +                if let ExprKind::Block(block, _) = body.value.kind;
 +                if block.expr.is_none();
 +                if let Some(stmt) = block.stmts.last();
 +                if let StmtKind::Semi(_) = stmt.kind;
 +                then {
 +                    let data = stmt.span.data();
 +                    // Make a span out of the semicolon for the help message
 +                    Some((fn_decl_span, Some(data.with_lo(data.hi-BytePos(1)))))
 +                } else {
 +                    Some((fn_decl_span, None))
 +                }
 +            }
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
 +        if let ExprKind::MethodCall(_, receiver, args, _) = expr.kind {
 +            let arg_indices = get_args_to_check(cx, expr);
 +            let args = std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>();
 +            for (i, trait_name) in arg_indices {
 +                if i < args.len() {
++                    match check_arg(cx, args[i]) {
 +                        Some((span, None)) => {
 +                            span_lint(
 +                                cx,
 +                                UNIT_RETURN_EXPECTING_ORD,
 +                                span,
 +                                &format!(
 +                                    "this closure returns \
 +                                   the unit type which also implements {}",
 +                                    trait_name
 +                                ),
 +                            );
 +                        },
 +                        Some((span, Some(last_semi))) => {
 +                            span_lint_and_help(
 +                                cx,
 +                                UNIT_RETURN_EXPECTING_ORD,
 +                                span,
 +                                &format!(
 +                                    "this closure returns \
 +                                   the unit type which also implements {}",
 +                                    trait_name
 +                                ),
 +                                Some(last_semi),
 +                                "probably caused by this trailing semicolon",
 +                            );
 +                        },
 +                        None => {},
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
index 7ffb53dcf455f371e313cade841426bdeae0fd92,0000000000000000000000000000000000000000..a6f777abc6e942aaf0259f83314b859ba569efb0
mode 100644,000000..100644
--- /dev/null
@@@ -1,209 -1,0 +1,209 @@@
-         lint_unit_args(cx, expr, &args_to_recover.as_slice());
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::is_from_proc_macro;
 +use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{self as hir, Block, Expr, ExprKind, MatchSource, Node, StmtKind};
 +use rustc_lint::LateContext;
 +
 +use super::{utils, UNIT_ARG};
 +
 +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
 +    if expr.span.from_expansion() {
 +        return;
 +    }
 +
 +    // apparently stuff in the desugaring of `?` can trigger this
 +    // so check for that here
 +    // only the calls to `Try::from_error` is marked as desugared,
 +    // so we need to check both the current Expr and its parent.
 +    if is_questionmark_desugar_marked_call(expr) {
 +        return;
 +    }
 +    let map = &cx.tcx.hir();
 +    let opt_parent_node = map.find(map.get_parent_node(expr.hir_id));
 +    if_chain! {
 +        if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node;
 +        if is_questionmark_desugar_marked_call(parent_expr);
 +        then {
 +            return;
 +        }
 +    }
 +
 +    let args: Vec<_> = match expr.kind {
 +        ExprKind::Call(_, args) => args.iter().collect(),
 +        ExprKind::MethodCall(_, receiver, args, _) => std::iter::once(receiver).chain(args.iter()).collect(),
 +        _ => return,
 +    };
 +
 +    let args_to_recover = args
 +        .into_iter()
 +        .filter(|arg| {
 +            if cx.typeck_results().expr_ty(arg).is_unit() && !utils::is_unit_literal(arg) {
 +                !matches!(
 +                    &arg.kind,
 +                    ExprKind::Match(.., MatchSource::TryDesugar) | ExprKind::Path(..)
 +                )
 +            } else {
 +                false
 +            }
 +        })
 +        .collect::<Vec<_>>();
 +    if !args_to_recover.is_empty() && !is_from_proc_macro(cx, expr) {
++        lint_unit_args(cx, expr, args_to_recover.as_slice());
 +    }
 +}
 +
 +fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool {
 +    use rustc_span::hygiene::DesugaringKind;
 +    if let ExprKind::Call(callee, _) = expr.kind {
 +        callee.span.is_desugaring(DesugaringKind::QuestionMark)
 +    } else {
 +        false
 +    }
 +}
 +
 +fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Expr<'_>]) {
 +    let mut applicability = Applicability::MachineApplicable;
 +    let (singular, plural) = if args_to_recover.len() > 1 {
 +        ("", "s")
 +    } else {
 +        ("a ", "")
 +    };
 +    span_lint_and_then(
 +        cx,
 +        UNIT_ARG,
 +        expr.span,
 +        &format!("passing {}unit value{} to a function", singular, plural),
 +        |db| {
 +            let mut or = "";
 +            args_to_recover
 +                .iter()
 +                .filter_map(|arg| {
 +                    if_chain! {
 +                        if let ExprKind::Block(block, _) = arg.kind;
 +                        if block.expr.is_none();
 +                        if let Some(last_stmt) = block.stmts.iter().last();
 +                        if let StmtKind::Semi(last_expr) = last_stmt.kind;
 +                        if let Some(snip) = snippet_opt(cx, last_expr.span);
 +                        then {
 +                            Some((
 +                                last_stmt.span,
 +                                snip,
 +                            ))
 +                        }
 +                        else {
 +                            None
 +                        }
 +                    }
 +                })
 +                .for_each(|(span, sugg)| {
 +                    db.span_suggestion(
 +                        span,
 +                        "remove the semicolon from the last statement in the block",
 +                        sugg,
 +                        Applicability::MaybeIncorrect,
 +                    );
 +                    or = "or ";
 +                    applicability = Applicability::MaybeIncorrect;
 +                });
 +
 +            let arg_snippets: Vec<String> = args_to_recover
 +                .iter()
 +                .filter_map(|arg| snippet_opt(cx, arg.span))
 +                .collect();
 +            let arg_snippets_without_empty_blocks: Vec<String> = args_to_recover
 +                .iter()
 +                .filter(|arg| !is_empty_block(arg))
 +                .filter_map(|arg| snippet_opt(cx, arg.span))
 +                .collect();
 +
 +            if let Some(call_snippet) = snippet_opt(cx, expr.span) {
 +                let sugg = fmt_stmts_and_call(
 +                    cx,
 +                    expr,
 +                    &call_snippet,
 +                    &arg_snippets,
 +                    &arg_snippets_without_empty_blocks,
 +                );
 +
 +                if arg_snippets_without_empty_blocks.is_empty() {
 +                    db.multipart_suggestion(
 +                        &format!("use {}unit literal{} instead", singular, plural),
 +                        args_to_recover
 +                            .iter()
 +                            .map(|arg| (arg.span, "()".to_string()))
 +                            .collect::<Vec<_>>(),
 +                        applicability,
 +                    );
 +                } else {
 +                    let plural = arg_snippets_without_empty_blocks.len() > 1;
 +                    let empty_or_s = if plural { "s" } else { "" };
 +                    let it_or_them = if plural { "them" } else { "it" };
 +                    db.span_suggestion(
 +                        expr.span,
 +                        &format!(
 +                            "{}move the expression{} in front of the call and replace {} with the unit literal `()`",
 +                            or, empty_or_s, it_or_them
 +                        ),
 +                        sugg,
 +                        applicability,
 +                    );
 +                }
 +            }
 +        },
 +    );
 +}
 +
 +fn is_empty_block(expr: &Expr<'_>) -> bool {
 +    matches!(
 +        expr.kind,
 +        ExprKind::Block(
 +            Block {
 +                stmts: &[],
 +                expr: None,
 +                ..
 +            },
 +            _,
 +        )
 +    )
 +}
 +
 +fn fmt_stmts_and_call(
 +    cx: &LateContext<'_>,
 +    call_expr: &Expr<'_>,
 +    call_snippet: &str,
 +    args_snippets: &[impl AsRef<str>],
 +    non_empty_block_args_snippets: &[impl AsRef<str>],
 +) -> String {
 +    let call_expr_indent = indent_of(cx, call_expr.span).unwrap_or(0);
 +    let call_snippet_with_replacements = args_snippets
 +        .iter()
 +        .fold(call_snippet.to_owned(), |acc, arg| acc.replacen(arg.as_ref(), "()", 1));
 +
 +    let mut stmts_and_call = non_empty_block_args_snippets
 +        .iter()
 +        .map(|it| it.as_ref().to_owned())
 +        .collect::<Vec<_>>();
 +    stmts_and_call.push(call_snippet_with_replacements);
 +    stmts_and_call = stmts_and_call
 +        .into_iter()
 +        .map(|v| reindent_multiline(v.into(), true, Some(call_expr_indent)).into_owned())
 +        .collect();
 +
 +    let mut stmts_and_call_snippet = stmts_and_call.join(&format!("{}{}", ";\n", " ".repeat(call_expr_indent)));
 +    // expr is not in a block statement or result expression position, wrap in a block
 +    let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(call_expr.hir_id));
 +    if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) {
 +        let block_indent = call_expr_indent + 4;
 +        stmts_and_call_snippet =
 +            reindent_multiline(stmts_and_call_snippet.into(), true, Some(block_indent)).into_owned();
 +        stmts_and_call_snippet = format!(
 +            "{{\n{}{}\n{}}}",
 +            " ".repeat(block_indent),
 +            &stmts_and_call_snippet,
 +            " ".repeat(call_expr_indent)
 +        );
 +    }
 +    stmts_and_call_snippet
 +}
index a5afbb8ff9da49272efd9887b20732ed221c0049,0000000000000000000000000000000000000000..2c40827db0e75677800317cf9af9e397028e8b35
mode 100644,000000..100644
--- /dev/null
@@@ -1,177 -1,0 +1,177 @@@
-         let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| {
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet;
 +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;
 +use rustc_hir::LangItem::{OptionSome, ResultOk};
 +use rustc_hir::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::sym;
 +use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for private functions that only return `Ok` or `Some`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is not meaningful to wrap values when no `None` or `Err` is returned.
 +    ///
 +    /// ### Known problems
 +    /// There can be false positives if the function signature is designed to
 +    /// fit some external requirement.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn get_cool_number(a: bool, b: bool) -> Option<i32> {
 +    ///     if a && b {
 +    ///         return Some(50);
 +    ///     }
 +    ///     if a {
 +    ///         Some(0)
 +    ///     } else {
 +    ///         Some(10)
 +    ///     }
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn get_cool_number(a: bool, b: bool) -> i32 {
 +    ///     if a && b {
 +    ///         return 50;
 +    ///     }
 +    ///     if a {
 +    ///         0
 +    ///     } else {
 +    ///         10
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.50.0"]
 +    pub UNNECESSARY_WRAPS,
 +    pedantic,
 +    "functions that only return `Ok` or `Some`"
 +}
 +
 +pub struct UnnecessaryWraps {
 +    avoid_breaking_exported_api: bool,
 +}
 +
 +impl_lint_pass!(UnnecessaryWraps => [UNNECESSARY_WRAPS]);
 +
 +impl UnnecessaryWraps {
 +    pub fn new(avoid_breaking_exported_api: bool) -> Self {
 +        Self {
 +            avoid_breaking_exported_api,
 +        }
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        fn_kind: FnKind<'tcx>,
 +        fn_decl: &FnDecl<'tcx>,
 +        body: &Body<'tcx>,
 +        span: Span,
 +        hir_id: HirId,
 +    ) {
 +        // Abort if public function/method or closure.
 +        match fn_kind {
 +            FnKind::ItemFn(..) | FnKind::Method(..) => {
 +                let def_id = cx.tcx.hir().local_def_id(hir_id);
 +                if self.avoid_breaking_exported_api && cx.access_levels.is_exported(def_id) {
 +                    return;
 +                }
 +            },
 +            FnKind::Closure => return,
 +        }
 +
 +        // Abort if the method is implementing a trait or of it a trait method.
 +        if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
 +            if matches!(
 +                item.kind,
 +                ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..)
 +            ) {
 +                return;
 +            }
 +        }
 +
 +        // Get the wrapper and inner types, if can't, abort.
 +        let (return_type_label, lang_item, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id).kind() {
 +            if cx.tcx.is_diagnostic_item(sym::Option, adt_def.did()) {
 +                ("Option", OptionSome, subst.type_at(0))
 +            } else if cx.tcx.is_diagnostic_item(sym::Result, adt_def.did()) {
 +                ("Result", ResultOk, subst.type_at(0))
 +            } else {
 +                return;
 +            }
 +        } else {
 +            return;
 +        };
 +
 +        // Check if all return expression respect the following condition and collect them.
 +        let mut suggs = Vec::new();
++        let can_sugg = find_all_ret_expressions(cx, body.value, |ret_expr| {
 +            if_chain! {
 +                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.
 +                if let ExprKind::Path(qpath) = &func.kind;
 +                if is_lang_ctor(cx, qpath, lang_item);
 +                // Make sure the function argument does not contain a return expression.
 +                if !contains_return(arg);
 +                then {
 +                    suggs.push(
 +                        (
 +                            ret_expr.span,
 +                            if inner_type.is_unit() {
 +                                String::new()
 +                            } else {
 +                                snippet(cx, arg.span.source_callsite(), "..").to_string()
 +                            }
 +                        )
 +                    );
 +                    true
 +                } else {
 +                    false
 +                }
 +            }
 +        });
 +
 +        if can_sugg && !suggs.is_empty() {
 +            let (lint_msg, return_type_sugg_msg, return_type_sugg, body_sugg_msg) = if inner_type.is_unit() {
 +                (
 +                    "this function's return value is unnecessary".to_string(),
 +                    "remove the return type...".to_string(),
 +                    snippet(cx, fn_decl.output.span(), "..").to_string(),
 +                    "...and then remove returned values",
 +                )
 +            } else {
 +                (
 +                    format!(
 +                        "this function's return value is unnecessarily wrapped by `{}`",
 +                        return_type_label
 +                    ),
 +                    format!("remove `{}` from the return type...", return_type_label),
 +                    inner_type.to_string(),
 +                    "...and then change returning expressions",
 +                )
 +            };
 +
 +            span_lint_and_then(cx, UNNECESSARY_WRAPS, span, lint_msg.as_str(), |diag| {
 +                diag.span_suggestion(
 +                    fn_decl.output.span(),
 +                    return_type_sugg_msg.as_str(),
 +                    return_type_sugg,
 +                    Applicability::MaybeIncorrect,
 +                );
 +                diag.multipart_suggestion(body_sugg_msg, suggs, Applicability::MaybeIncorrect);
 +            });
 +        }
 +    }
 +}
index 9092156be150a30634c382add42dd2fcc6a5e1e1,0000000000000000000000000000000000000000..7e451b7b7a419239c5c136541ab74222e54fce8e
mode 100644,000000..100644
--- /dev/null
@@@ -1,331 -1,0 +1,331 @@@
-                 assert!(args.len() == 0);
 +use clippy_utils::diagnostics::span_lint_hir_and_then;
 +use clippy_utils::higher;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{path_to_local, usage::is_potentially_mutated};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor};
 +use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, PathSegment, UnOp};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::Ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls of `unwrap[_err]()` that cannot fail.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `if let` or `match` is more idiomatic.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let option = Some(0);
 +    /// # fn do_something_with(_x: usize) {}
 +    /// if option.is_some() {
 +    ///     do_something_with(option.unwrap())
 +    /// }
 +    /// ```
 +    ///
 +    /// Could be written:
 +    ///
 +    /// ```rust
 +    /// # let option = Some(0);
 +    /// # fn do_something_with(_x: usize) {}
 +    /// if let Some(value) = option {
 +    ///     do_something_with(value)
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub UNNECESSARY_UNWRAP,
 +    complexity,
 +    "checks for calls of `unwrap[_err]()` that cannot fail"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls of `unwrap[_err]()` that will always fail.
 +    ///
 +    /// ### Why is this bad?
 +    /// If panicking is desired, an explicit `panic!()` should be used.
 +    ///
 +    /// ### Known problems
 +    /// This lint only checks `if` conditions not assignments.
 +    /// So something like `let x: Option<()> = None; x.unwrap();` will not be recognized.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let option = Some(0);
 +    /// # fn do_something_with(_x: usize) {}
 +    /// if option.is_none() {
 +    ///     do_something_with(option.unwrap())
 +    /// }
 +    /// ```
 +    ///
 +    /// 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"
 +}
 +
 +/// Visitor that keeps track of which variables are unwrappable.
 +struct UnwrappableVariablesVisitor<'a, 'tcx> {
 +    unwrappables: Vec<UnwrapInfo<'tcx>>,
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +/// What kind of unwrappable this is.
 +#[derive(Copy, Clone, Debug)]
 +enum UnwrappableKind {
 +    Option,
 +    Result,
 +}
 +
 +impl UnwrappableKind {
 +    fn success_variant_pattern(self) -> &'static str {
 +        match self {
 +            UnwrappableKind::Option => "Some(..)",
 +            UnwrappableKind::Result => "Ok(..)",
 +        }
 +    }
 +
 +    fn error_variant_pattern(self) -> &'static str {
 +        match self {
 +            UnwrappableKind::Option => "None",
 +            UnwrappableKind::Result => "Err(..)",
 +        }
 +    }
 +}
 +
 +/// Contains information about whether a variable can be unwrapped.
 +#[derive(Copy, Clone, Debug)]
 +struct UnwrapInfo<'tcx> {
 +    /// The variable that is checked
 +    local_id: HirId,
 +    /// The if itself
 +    if_expr: &'tcx Expr<'tcx>,
 +    /// The check, like `x.is_ok()`
 +    check: &'tcx Expr<'tcx>,
 +    /// The check's name, like `is_ok`
 +    check_name: &'tcx PathSegment<'tcx>,
 +    /// The branch where the check takes place, like `if x.is_ok() { .. }`
 +    branch: &'tcx Expr<'tcx>,
 +    /// Whether `is_some()` or `is_ok()` was called (as opposed to `is_err()` or `is_none()`).
 +    safe_to_unwrap: bool,
 +    /// What kind of unwrappable this is.
 +    kind: UnwrappableKind,
 +    /// If the check is the entire condition (`if x.is_ok()`) or only a part of it (`foo() &&
 +    /// x.is_ok()`)
 +    is_entire_condition: bool,
 +}
 +
 +/// Collects the information about unwrappable variables from an if condition
 +/// The `invert` argument tells us whether the condition is negated.
 +fn collect_unwrap_info<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    if_expr: &'tcx Expr<'_>,
 +    expr: &'tcx Expr<'_>,
 +    branch: &'tcx Expr<'_>,
 +    invert: bool,
 +    is_entire_condition: bool,
 +) -> Vec<UnwrapInfo<'tcx>> {
 +    fn is_relevant_option_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool {
 +        is_type_diagnostic_item(cx, ty, sym::Option) && ["is_some", "is_none"].contains(&method_name)
 +    }
 +
 +    fn is_relevant_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool {
 +        is_type_diagnostic_item(cx, ty, sym::Result) && ["is_ok", "is_err"].contains(&method_name)
 +    }
 +
 +    if let ExprKind::Binary(op, left, right) = &expr.kind {
 +        match (invert, op.node) {
 +            (false, BinOpKind::And | BinOpKind::BitAnd) | (true, BinOpKind::Or | BinOpKind::BitOr) => {
 +                let mut unwrap_info = collect_unwrap_info(cx, if_expr, left, branch, invert, false);
 +                unwrap_info.append(&mut collect_unwrap_info(cx, if_expr, right, branch, invert, false));
 +                return unwrap_info;
 +            },
 +            _ => (),
 +        }
 +    } else if let ExprKind::Unary(UnOp::Not, expr) = &expr.kind {
 +        return collect_unwrap_info(cx, if_expr, expr, branch, !invert, false);
 +    } else {
 +        if_chain! {
 +            if let ExprKind::MethodCall(method_name, receiver, args, _) = &expr.kind;
 +            if let Some(local_id) = path_to_local(receiver);
 +            let ty = cx.typeck_results().expr_ty(receiver);
 +            let name = method_name.ident.as_str();
 +            if is_relevant_option_call(cx, ty, name) || is_relevant_result_call(cx, ty, name);
 +            then {
++                assert!(args.is_empty());
 +                let unwrappable = match name {
 +                    "is_some" | "is_ok" => true,
 +                    "is_err" | "is_none" => false,
 +                    _ => unreachable!(),
 +                };
 +                let safe_to_unwrap = unwrappable != invert;
 +                let kind = if is_type_diagnostic_item(cx, ty, sym::Option) {
 +                    UnwrappableKind::Option
 +                } else {
 +                    UnwrappableKind::Result
 +                };
 +
 +                return vec![
 +                    UnwrapInfo {
 +                        local_id,
 +                        if_expr,
 +                        check: expr,
 +                        check_name: method_name,
 +                        branch,
 +                        safe_to_unwrap,
 +                        kind,
 +                        is_entire_condition,
 +                    }
 +                ]
 +            }
 +        }
 +    }
 +    Vec::new()
 +}
 +
 +impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> {
 +    fn visit_branch(
 +        &mut self,
 +        if_expr: &'tcx Expr<'_>,
 +        cond: &'tcx Expr<'_>,
 +        branch: &'tcx Expr<'_>,
 +        else_branch: bool,
 +    ) {
 +        let prev_len = self.unwrappables.len();
 +        for unwrap_info in collect_unwrap_info(self.cx, if_expr, cond, branch, else_branch, true) {
 +            if is_potentially_mutated(unwrap_info.local_id, cond, self.cx)
 +                || is_potentially_mutated(unwrap_info.local_id, branch, self.cx)
 +            {
 +                // if the variable is mutated, we don't know whether it can be unwrapped:
 +                continue;
 +            }
 +            self.unwrappables.push(unwrap_info);
 +        }
 +        walk_expr(self, branch);
 +        self.unwrappables.truncate(prev_len);
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
 +    type NestedFilter = nested_filter::OnlyBodies;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        // Shouldn't lint when `expr` is in macro.
 +        if in_external_macro(self.cx.tcx.sess, expr.span) {
 +            return;
 +        }
 +        if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr) {
 +            walk_expr(self, cond);
 +            self.visit_branch(expr, cond, then, false);
 +            if let Some(else_inner) = r#else {
 +                self.visit_branch(expr, cond, else_inner, true);
 +            }
 +        } else {
 +            // find `unwrap[_err]()` calls:
 +            if_chain! {
 +                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()
 +                    .find(|u| u.local_id == id);
 +                // Span contexts should not differ with the conditional branch
 +                let span_ctxt = expr.span.ctxt();
 +                if unwrappable.branch.span.ctxt() == span_ctxt;
 +                if unwrappable.check.span.ctxt() == span_ctxt;
 +                then {
 +                    if call_to_unwrap == unwrappable.safe_to_unwrap {
 +                        let is_entire_condition = unwrappable.is_entire_condition;
 +                        let unwrappable_variable_name = self.cx.tcx.hir().name(unwrappable.local_id);
 +                        let suggested_pattern = if call_to_unwrap {
 +                            unwrappable.kind.success_variant_pattern()
 +                        } else {
 +                            unwrappable.kind.error_variant_pattern()
 +                        };
 +
 +                        span_lint_hir_and_then(
 +                            self.cx,
 +                            UNNECESSARY_UNWRAP,
 +                            expr.hir_id,
 +                            expr.span,
 +                            &format!(
 +                                "called `{}` on `{}` after checking its variant with `{}`",
 +                                method_name.ident.name,
 +                                unwrappable_variable_name,
 +                                unwrappable.check_name.ident.as_str(),
 +                            ),
 +                            |diag| {
 +                                if is_entire_condition {
 +                                    diag.span_suggestion(
 +                                        unwrappable.check.span.with_lo(unwrappable.if_expr.span.lo()),
 +                                        "try",
 +                                        format!(
 +                                            "if let {} = {}",
 +                                            suggested_pattern,
 +                                            unwrappable_variable_name,
 +                                        ),
 +                                        // We don't track how the unwrapped value is used inside the
 +                                        // block or suggest deleting the unwrap, so we can't offer a
 +                                        // fixable solution.
 +                                        Applicability::Unspecified,
 +                                    );
 +                                } else {
 +                                    diag.span_label(unwrappable.check.span, "the check is happening here");
 +                                    diag.help("try using `if let` or `match`");
 +                                }
 +                            },
 +                        );
 +                    } else {
 +                        span_lint_hir_and_then(
 +                            self.cx,
 +                            PANICKING_UNWRAP,
 +                            expr.hir_id,
 +                            expr.span,
 +                            &format!("this call to `{}()` will always panic",
 +                            method_name.ident.name),
 +                            |diag| { diag.span_label(unwrappable.check.span, "because of this check"); },
 +                        );
 +                    }
 +                }
 +            }
 +            walk_expr(self, expr);
 +        }
 +    }
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +}
 +
 +declare_lint_pass!(Unwrap => [PANICKING_UNWRAP, UNNECESSARY_UNWRAP]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Unwrap {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        kind: FnKind<'tcx>,
 +        decl: &'tcx FnDecl<'_>,
 +        body: &'tcx Body<'_>,
 +        span: Span,
 +        fn_id: HirId,
 +    ) {
 +        if span.from_expansion() {
 +            return;
 +        }
 +
 +        let mut v = UnwrappableVariablesVisitor {
 +            cx,
 +            unwrappables: Vec::new(),
 +        };
 +
 +        walk_fn(&mut v, kind, decl, body.id(), span, fn_id);
 +    }
 +}
index b3ca15f7648bc082bdb8ea71694d65631c9c6200,0000000000000000000000000000000000000000..46020adcaa2caae66e67974e86f0f17f08543c44
mode 100644,000000..100644
--- /dev/null
@@@ -1,133 -1,0 +1,133 @@@
-             let receiver_ty = self.typeck_results.expr_ty(&arglists[0].0).peel_refs();
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{method_chain_args, return_ty};
 +use if_chain::if_chain;
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::{self, Visitor};
 +use rustc_hir::{Expr, ImplItemKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{sym, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for functions of type `Result` that contain `expect()` or `unwrap()`
 +    ///
 +    /// ### Why is this bad?
 +    /// These functions promote recoverable errors to non-recoverable errors which may be undesirable in code bases which wish to avoid panics.
 +    ///
 +    /// ### Known problems
 +    /// This can cause false positives in functions that handle both recoverable and non recoverable errors.
 +    ///
 +    /// ### Example
 +    /// Before:
 +    /// ```rust
 +    /// fn divisible_by_3(i_str: String) -> Result<(), String> {
 +    ///     let i = i_str
 +    ///         .parse::<i32>()
 +    ///         .expect("cannot divide the input by three");
 +    ///
 +    ///     if i % 3 != 0 {
 +    ///         Err("Number is not divisible by 3")?
 +    ///     }
 +    ///
 +    ///     Ok(())
 +    /// }
 +    /// ```
 +    ///
 +    /// After:
 +    /// ```rust
 +    /// fn divisible_by_3(i_str: String) -> Result<(), String> {
 +    ///     let i = i_str
 +    ///         .parse::<i32>()
 +    ///         .map_err(|e| format!("cannot divide the input by three: {}", e))?;
 +    ///
 +    ///     if i % 3 != 0 {
 +    ///         Err("Number is not divisible by 3")?
 +    ///     }
 +    ///
 +    ///     Ok(())
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.48.0"]
 +    pub UNWRAP_IN_RESULT,
 +    restriction,
 +    "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`"
 +}
 +
 +declare_lint_pass!(UnwrapInResult=> [UNWRAP_IN_RESULT]);
 +
 +impl<'tcx> LateLintPass<'tcx> for UnwrapInResult {
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
 +        if_chain! {
 +            // first check if it's a method or function
 +            if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind;
 +            // checking if its return type is `result` or `option`
 +            if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id()), sym::Result)
 +                || is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id()), sym::Option);
 +            then {
 +                lint_impl_body(cx, impl_item.span, impl_item);
 +            }
 +        }
 +    }
 +}
 +
 +struct FindExpectUnwrap<'a, 'tcx> {
 +    lcx: &'a LateContext<'tcx>,
 +    typeck_results: &'tcx ty::TypeckResults<'tcx>,
 +    result: Vec<Span>,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> {
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        // check for `expect`
 +        if let Some(arglists) = method_chain_args(expr, &["expect"]) {
-             let receiver_ty = self.typeck_results.expr_ty(&arglists[0].0).peel_refs();
++            let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs();
 +            if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option)
 +                || is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result)
 +            {
 +                self.result.push(expr.span);
 +            }
 +        }
 +
 +        // check for `unwrap`
 +        if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
-         fpu.visit_expr(&body.value);
++            let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs();
 +            if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option)
 +                || is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result)
 +            {
 +                self.result.push(expr.span);
 +            }
 +        }
 +
 +        // and check sub-expressions
 +        intravisit::walk_expr(self, expr);
 +    }
 +}
 +
 +fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) {
 +    if let ImplItemKind::Fn(_, body_id) = impl_item.kind {
 +        let body = cx.tcx.hir().body(body_id);
 +        let mut fpu = FindExpectUnwrap {
 +            lcx: cx,
 +            typeck_results: cx.tcx.typeck(impl_item.def_id),
 +            result: Vec::new(),
 +        };
++        fpu.visit_expr(body.value);
 +
 +        // if we've found one, lint
 +        if !fpu.result.is_empty() {
 +            span_lint_and_then(
 +                cx,
 +                UNWRAP_IN_RESULT,
 +                impl_span,
 +                "used unwrap or expect in a function that returns result or option",
 +                move |diag| {
 +                    diag.help("unwrap and expect should not be used in a function that returns result or option");
 +                    diag.span_note(fpu.result, "potential non-recoverable error(s)");
 +                },
 +            );
 +        }
 +    }
 +}
index 1489c96d9e9b03bcb61a8bb8cd84fb7a0977be97,0000000000000000000000000000000000000000..4003fff27c006a7d5956aa6094406fc4f1f735eb
mode 100644,000000..100644
--- /dev/null
@@@ -1,750 -1,0 +1,750 @@@
-             v.expr(&v.bind("expr", &hir.body(body_id).value));
 +//! 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, higher};
 +use rustc_ast::ast::{LitFloatType, LitKind};
 +use rustc_ast::LitIntType;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_hir as hir;
 +use rustc_hir::{
 +    ArrayLen, BindingAnnotation, Closure, ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +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
 +    /// Generates clippy code that detects the offending pattern
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// // ./tests/ui/my_lint.rs
 +    /// fn foo() {
 +    ///     // detect the following pattern
 +    ///     #[clippy::author]
 +    ///     if x == 42 {
 +    ///         // but ignore everything from here on
 +    ///         #![clippy::author = "ignore"]
 +    ///     }
 +    ///     ()
 +    /// }
 +    /// ```
 +    ///
 +    /// Running `TESTNAME=ui/my_lint cargo uitest` will produce
 +    /// a `./tests/ui/new_lint.stdout` file with the generated code:
 +    ///
 +    /// ```rust,ignore
 +    /// // ./tests/ui/new_lint.stdout
 +    /// if_chain! {
 +    ///     if let ExprKind::If(ref cond, ref then, None) = item.kind,
 +    ///     if let ExprKind::Binary(BinOp::Eq, ref left, ref right) = cond.kind,
 +    ///     if let ExprKind::Path(ref path) = left.kind,
 +    ///     if let ExprKind::Lit(ref lit) = right.kind,
 +    ///     if let LitKind::Int(42, _) = lit.node,
 +    ///     then {
 +    ///         // report your lint here
 +    ///     }
 +    /// }
 +    /// ```
 +    pub LINT_AUTHOR,
 +    internal_warn,
 +    "helper for writing lints"
 +}
 +
 +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>` variables 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! {{");
 +}
 +
 +fn done() {
 +    println!("    then {{");
 +    println!("        // report your lint here");
 +    println!("    }}");
 +    println!("}}");
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for Author {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +        check_item(cx, item.hir_id());
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
 +        check_item(cx, item.hir_id());
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
 +        check_item(cx, item.hir_id());
 +    }
 +
 +    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<'_>) {
 +        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<'_>) {
 +        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.expect_owner()) {
 +        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();
 +        f(&PrintVisitor::new(cx));
 +        done();
 +    }
 +}
 +
 +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})"),
 +        }
 +    }
 +}
 +
 +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 {
 +            cx,
 +            ids: std::cell::Cell::default(),
 +        }
 +    }
 +
 +    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 slice<T>(&self, slice: &Binding<&[T]>, f: impl Fn(&Binding<&T>)) {
 +        if slice.value.is_empty() {
 +            out!("if {slice}.is_empty();");
 +        } else {
 +            out!("if {slice}.len() == {};", slice.value.len());
 +            for (i, value) in slice.value.iter().enumerate() {
 +                let name = format!("{slice}[{i}]");
 +                f(&Binding { name, value });
 +            }
 +        }
 +    }
 +
 +    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());
 +    }
 +
 +    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 => kind!("Err"),
 +            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(let_expr)) => {
 +                bind!(self, let_expr);
 +                out!("if let Some(Guard::IfLet({let_expr}) = {arm}.guard;");
 +                self.pat(field!(let_expr.pat));
 +                self.expr(field!(let_expr.init));
 +            },
 +        }
 +        self.expr(field!(arm.body));
 +    }
 +
 +    #[allow(clippy::too_many_lines)]
 +    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(let_expr) => {
 +                bind!(self, let_expr);
 +                kind!("Let({let_expr})");
 +                self.pat(field!(let_expr.pat));
 +                // Does what ExprKind::Cast does, only adds a clause for the type
 +                // if it's a path
 +                if let Some(TyKind::Path(ref qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) {
 +                    bind!(self, qpath);
 +                    out!("if let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind;");
 +                    self.qpath(qpath);
 +                }
 +                self.expr(field!(let_expr.init));
 +            },
 +            ExprKind::Box(inner) => {
 +                bind!(self, inner);
 +                kind!("Box({inner})");
 +                self.expr(inner);
 +            },
 +            ExprKind::Array(elements) => {
 +                bind!(self, elements);
 +                kind!("Array({elements})");
 +                self.slice(elements, |e| self.expr(e));
 +            },
 +            ExprKind::Call(func, args) => {
 +                bind!(self, func, args);
 +                kind!("Call({func}, {args})");
 +                self.expr(func);
 +                self.slice(args, |e| self.expr(e));
 +            },
 +            ExprKind::MethodCall(method_name, receiver, args, _) => {
 +                bind!(self, method_name, receiver, args);
 +                kind!("MethodCall({method_name}, {receiver}, {args}, _)");
 +                self.ident(field!(method_name.ident));
 +                self.expr(receiver);
 +                self.slice(args, |e| self.expr(e));
 +            },
 +            ExprKind::Tup(elements) => {
 +                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) => {
 +                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);
 +                }
 +                self.expr(expr);
 +            },
 +            ExprKind::Type(expr, _ty) => {
 +                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(&Closure {
 +                capture_clause,
 +                fn_decl,
 +                body: 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_clause:?}, {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, _) => {
 +                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) => {
 +                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) => {
 +                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(_) => {
 +                kind!("InlineAsm(_)");
 +                out!("// unimplemented: `ExprKind::InlineAsm` 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);
 +                match length.value {
 +                    ArrayLen::Infer(..) => out!("if let ArrayLen::Infer(..) = length;"),
 +                    ArrayLen::Body(anon_const) => {
 +                        bind!(self, anon_const);
 +                        out!("if let ArrayLen::Body({anon_const}) = {length};");
 +                        self.body(field!(anon_const.body));
 +                    },
 +                }
 +            },
 +            ExprKind::Err => kind!("Err"),
 +            ExprKind::DropTemps(expr) => {
 +                bind!(self, expr);
 +                kind!("DropTemps({expr})");
 +                self.expr(expr);
 +            },
 +        }
 +    }
 +
 +    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);
 +        });
 +    }
 +
 +    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(ann, _, name, sub) => {
 +                bind!(self, name);
 +                opt_bind!(self, sub);
 +                let ann = match ann {
 +                    BindingAnnotation::NONE => "NONE",
 +                    BindingAnnotation::REF => "REF",
 +                    BindingAnnotation::MUT => "MUT",
 +                    BindingAnnotation::REF_MUT => "REF_MUT",
 +                };
 +                kind!("Binding(BindingAnnotation::{ann}, _, {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) => {
 +                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) => {
 +                bind!(self, fields);
 +                kind!("Tuple({fields}, {skip_pos:?})");
 +                self.slice(fields, |field| self.pat(field));
 +            },
 +            PatKind::Box(pat) => {
 +                bind!(self, pat);
 +                kind!("Box({pat})");
 +                self.pat(pat);
 +            },
 +            PatKind::Ref(pat, muta) => {
 +                bind!(self, pat);
 +                kind!("Ref({pat}, Mutability::{muta:?})");
 +                self.pat(pat);
 +            },
 +            PatKind::Lit(lit_expr) => {
 +                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 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)*)));
 +        }
 +
 +        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) => {
 +                bind!(self, e);
 +                kind!("Expr({e})");
 +                self.expr(e);
 +            },
 +            StmtKind::Semi(e) => {
 +                bind!(self, e);
 +                kind!("Semi({e})");
 +                self.expr(e);
 +            },
 +        }
 +    }
 +}
 +
 +fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool {
 +    let attrs = cx.tcx.hir().attrs(hir_id);
 +    get_attr(cx.sess(), attrs, "author").count() > 0
 +}
 +
 +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();
 +                }
 +            },
 +            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
 +}
index 84e65d5fa0b719044d3d43705141e5656aa9f63e,0000000000000000000000000000000000000000..a8500beb25747aa9d95516cd24bbeaffac094e79
mode 100644,000000..100644
--- /dev/null
@@@ -1,557 -1,0 +1,557 @@@
-     (arithmetic_allowed: rustc_data_structures::fx::FxHashSet<String> = <_>::default()),
 +//! Read configurations files.
 +
 +#![allow(clippy::module_name_repetitions)]
 +
 +use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor};
 +use serde::Deserialize;
 +use std::error::Error;
 +use std::path::{Path, PathBuf};
 +use std::str::FromStr;
 +use std::{cmp, env, fmt, fs, io, iter};
 +
 +#[rustfmt::skip]
 +const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[
 +    "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
 +    "DirectX",
 +    "ECMAScript",
 +    "GPLv2", "GPLv3",
 +    "GitHub", "GitLab",
 +    "IPv4", "IPv6",
 +    "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript",
 +    "NaN", "NaNs",
 +    "OAuth", "GraphQL",
 +    "OCaml",
 +    "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS",
 +    "WebGL",
 +    "TensorFlow",
 +    "TrueType",
 +    "iOS", "macOS", "FreeBSD",
 +    "TeX", "LaTeX", "BibTeX", "BibLaTeX",
 +    "MinGW",
 +    "CamelCase",
 +];
 +const DEFAULT_DISALLOWED_NAMES: &[&str] = &["foo", "baz", "quux"];
 +
 +/// Holds information used by `MISSING_ENFORCED_IMPORT_RENAMES` lint.
 +#[derive(Clone, Debug, Deserialize)]
 +pub struct Rename {
 +    pub path: String,
 +    pub rename: String,
 +}
 +
 +/// A single disallowed method, used by the `DISALLOWED_METHODS` lint.
 +#[derive(Clone, Debug, Deserialize)]
 +#[serde(untagged)]
 +pub enum DisallowedMethod {
 +    Simple(String),
 +    WithReason { path: String, reason: Option<String> },
 +}
 +
 +impl DisallowedMethod {
 +    pub fn path(&self) -> &str {
 +        let (Self::Simple(path) | Self::WithReason { path, .. }) = self;
 +
 +        path
 +    }
 +}
 +
 +/// A single disallowed type, used by the `DISALLOWED_TYPES` lint.
 +#[derive(Clone, Debug, Deserialize)]
 +#[serde(untagged)]
 +pub enum DisallowedType {
 +    Simple(String),
 +    WithReason { path: String, reason: Option<String> },
 +}
 +
 +/// Conf with parse errors
 +#[derive(Default)]
 +pub struct TryConf {
 +    pub conf: Conf,
 +    pub errors: Vec<Box<dyn Error>>,
 +    pub warnings: Vec<Box<dyn Error>>,
 +}
 +
 +impl TryConf {
 +    fn from_error(error: impl Error + 'static) -> Self {
 +        Self {
 +            conf: Conf::default(),
 +            errors: vec![Box::new(error)],
 +            warnings: vec![],
 +        }
 +    }
 +}
 +
 +#[derive(Debug)]
 +struct ConfError(String);
 +
 +impl fmt::Display for ConfError {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        <String as fmt::Display>::fmt(&self.0, f)
 +    }
 +}
 +
 +impl Error for ConfError {}
 +
 +fn conf_error(s: impl Into<String>) -> Box<dyn Error> {
 +    Box::new(ConfError(s.into()))
 +}
 +
 +macro_rules! define_Conf {
 +    ($(
 +        $(#[doc = $doc:literal])+
 +        $(#[conf_deprecated($dep:literal, $new_conf:ident)])?
 +        ($name:ident: $ty:ty = $default:expr),
 +    )*) => {
 +        /// Clippy lint configuration
 +        pub struct Conf {
 +            $($(#[doc = $doc])+ pub $name: $ty,)*
 +        }
 +
 +        mod defaults {
 +            $(pub fn $name() -> $ty { $default })*
 +        }
 +
 +        impl Default for Conf {
 +            fn default() -> Self {
 +                Self { $($name: defaults::$name(),)* }
 +            }
 +        }
 +
 +        impl<'de> Deserialize<'de> for TryConf {
 +            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
 +                deserializer.deserialize_map(ConfVisitor)
 +            }
 +        }
 +
 +        #[derive(Deserialize)]
 +        #[serde(field_identifier, rename_all = "kebab-case")]
 +        #[allow(non_camel_case_types)]
 +        enum Field { $($name,)* third_party, }
 +
 +        struct ConfVisitor;
 +
 +        impl<'de> Visitor<'de> for ConfVisitor {
 +            type Value = TryConf;
 +
 +            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
 +                formatter.write_str("Conf")
 +            }
 +
 +            fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error> where V: MapAccess<'de> {
 +                let mut errors = Vec::new();
 +                let mut warnings = Vec::new();
 +                $(let mut $name = None;)*
 +                // could get `Field` here directly, but get `str` first for diagnostics
 +                while let Some(name) = map.next_key::<&str>()? {
 +                    match Field::deserialize(name.into_deserializer())? {
 +                        $(Field::$name => {
 +                            $(warnings.push(conf_error(format!("deprecated field `{}`. {}", name, $dep)));)?
 +                            match map.next_value() {
 +                                Err(e) => errors.push(conf_error(e.to_string())),
 +                                Ok(value) => match $name {
 +                                    Some(_) => errors.push(conf_error(format!("duplicate field `{}`", name))),
 +                                    None => {
 +                                        $name = Some(value);
 +                                        // $new_conf is the same as one of the defined `$name`s, so
 +                                        // this variable is defined in line 2 of this function.
 +                                        $(match $new_conf {
 +                                            Some(_) => errors.push(conf_error(concat!(
 +                                                "duplicate field `", stringify!($new_conf),
 +                                                "` (provided as `", stringify!($name), "`)"
 +                                            ))),
 +                                            None => $new_conf = $name.clone(),
 +                                        })?
 +                                    },
 +                                }
 +                            }
 +                        })*
 +                        // white-listed; ignore
 +                        Field::third_party => drop(map.next_value::<IgnoredAny>())
 +                    }
 +                }
 +                let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* };
 +                Ok(TryConf { conf, errors, warnings })
 +            }
 +        }
 +
 +        #[cfg(feature = "internal")]
 +        pub mod metadata {
 +            use crate::utils::internal_lints::metadata_collector::ClippyConfiguration;
 +
 +            macro_rules! wrap_option {
 +                () => (None);
 +                ($x:literal) => (Some($x));
 +            }
 +
 +            pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
 +                vec![
 +                    $(
 +                        {
 +                            let deprecation_reason = wrap_option!($($dep)?);
 +
 +                            ClippyConfiguration::new(
 +                                stringify!($name),
 +                                stringify!($ty),
 +                                format!("{:?}", super::defaults::$name()),
 +                                concat!($($doc, '\n',)*),
 +                                deprecation_reason,
 +                            )
 +                        },
 +                    )+
 +                ]
 +            }
 +        }
 +    };
 +}
 +
 +define_Conf! {
 +    /// Lint: Arithmetic.
 +    ///
 +    /// Suppress checking of the passed type names.
++    (arithmetic_side_effects_allowed: rustc_data_structures::fx::FxHashSet<String> = <_>::default()),
 +    /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
 +    ///
 +    /// Suppress lints whenever the suggested change would cause breakage for other crates.
 +    (avoid_breaking_exported_api: bool = true),
 +    /// Lint: MANUAL_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, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED.
 +    ///
 +    /// The minimum rust version that the project supports
 +    (msrv: Option<String> = None),
 +    /// DEPRECATED LINT: BLACKLISTED_NAME.
 +    ///
 +    /// Use the Disallowed Names lint instead
 +    #[conf_deprecated("Please use `disallowed-names` instead", disallowed_names)]
 +    (blacklisted_names: Vec<String> = Vec::new()),
 +    /// Lint: COGNITIVE_COMPLEXITY.
 +    ///
 +    /// The maximum cognitive complexity a function can have
 +    (cognitive_complexity_threshold: u64 = 25),
 +    /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY.
 +    ///
 +    /// Use the Cognitive Complexity lint instead.
 +    #[conf_deprecated("Please use `cognitive-complexity-threshold` instead", cognitive_complexity_threshold)]
 +    (cyclomatic_complexity_threshold: u64 = 25),
 +    /// Lint: DISALLOWED_NAMES.
 +    ///
 +    /// The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value
 +    /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the
 +    /// default configuration of Clippy. By default any configuration will replace the default value.
 +    (disallowed_names: Vec<String> = super::DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect()),
 +    /// Lint: DOC_MARKDOWN.
 +    ///
 +    /// The list of words this lint should not consider as identifiers needing ticks. The value
 +    /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the
 +    /// default configuration of Clippy. By default any configuraction will replace the default value. For example:
 +    /// * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`.
 +    /// * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list.
 +    ///
 +    /// Default list:
 +    (doc_valid_idents: Vec<String> = super::DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect()),
 +    /// Lint: TOO_MANY_ARGUMENTS.
 +    ///
 +    /// The maximum number of argument a function or method can have
 +    (too_many_arguments_threshold: u64 = 7),
 +    /// Lint: TYPE_COMPLEXITY.
 +    ///
 +    /// The maximum complexity a type can have
 +    (type_complexity_threshold: u64 = 250),
 +    /// Lint: MANY_SINGLE_CHAR_NAMES.
 +    ///
 +    /// The maximum number of single char bindings a scope may have
 +    (single_char_binding_names_threshold: u64 = 4),
 +    /// Lint: BOXED_LOCAL, USELESS_VEC.
 +    ///
 +    /// The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
 +    (too_large_for_stack: u64 = 200),
 +    /// Lint: ENUM_VARIANT_NAMES.
 +    ///
 +    /// The minimum number of enum variants for the lints about variant names to trigger
 +    (enum_variant_name_threshold: u64 = 3),
 +    /// Lint: LARGE_ENUM_VARIANT.
 +    ///
 +    /// The maximum size of an enum's variant to avoid box suggestion
 +    (enum_variant_size_threshold: u64 = 200),
 +    /// Lint: VERBOSE_BIT_MASK.
 +    ///
 +    /// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
 +    (verbose_bit_mask_threshold: u64 = 1),
 +    /// Lint: DECIMAL_LITERAL_REPRESENTATION.
 +    ///
 +    /// The lower bound for linting decimal literals
 +    (literal_representation_threshold: u64 = 16384),
 +    /// Lint: TRIVIALLY_COPY_PASS_BY_REF.
 +    ///
 +    /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
 +    (trivial_copy_size_limit: Option<u64> = None),
 +    /// Lint: LARGE_TYPE_PASS_BY_MOVE.
 +    ///
 +    /// The minimum size (in bytes) to consider a type for passing by reference instead of by value.
 +    (pass_by_value_size_limit: u64 = 256),
 +    /// Lint: TOO_MANY_LINES.
 +    ///
 +    /// The maximum number of lines a function or method can have
 +    (too_many_lines_threshold: u64 = 100),
 +    /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS.
 +    ///
 +    /// The maximum allowed size for arrays on the stack
 +    (array_size_threshold: u64 = 512_000),
 +    /// Lint: VEC_BOX.
 +    ///
 +    /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed
 +    (vec_box_size_threshold: u64 = 4096),
 +    /// Lint: TYPE_REPETITION_IN_BOUNDS.
 +    ///
 +    /// The maximum number of bounds a trait can have to be linted
 +    (max_trait_bounds: u64 = 3),
 +    /// Lint: STRUCT_EXCESSIVE_BOOLS.
 +    ///
 +    /// The maximum number of bool fields a struct can have
 +    (max_struct_bools: u64 = 3),
 +    /// Lint: FN_PARAMS_EXCESSIVE_BOOLS.
 +    ///
 +    /// The maximum number of bool parameters a function can have
 +    (max_fn_params_bools: u64 = 3),
 +    /// Lint: WILDCARD_IMPORTS.
 +    ///
 +    /// Whether to allow certain wildcard imports (prelude, super in tests).
 +    (warn_on_all_wildcard_imports: bool = false),
 +    /// Lint: DISALLOWED_METHODS.
 +    ///
 +    /// The list of disallowed methods, written as fully qualified paths.
 +    (disallowed_methods: Vec<crate::utils::conf::DisallowedMethod> = Vec::new()),
 +    /// Lint: DISALLOWED_TYPES.
 +    ///
 +    /// The list of disallowed types, written as fully qualified paths.
 +    (disallowed_types: Vec<crate::utils::conf::DisallowedType> = Vec::new()),
 +    /// Lint: UNREADABLE_LITERAL.
 +    ///
 +    /// Should the fraction of a decimal be linted to include separators.
 +    (unreadable_literal_lint_fractions: bool = true),
 +    /// Lint: UPPER_CASE_ACRONYMS.
 +    ///
 +    /// Enables verbose mode. Triggers if there is more than one uppercase char next to each other
 +    (upper_case_acronyms_aggressive: bool = false),
 +    /// Lint: _CARGO_COMMON_METADATA.
 +    ///
 +    /// For internal testing only, ignores the current `publish` settings in the Cargo manifest.
 +    (cargo_ignore_publish: bool = false),
 +    /// Lint: NONSTANDARD_MACRO_BRACES.
 +    ///
 +    /// Enforce the named macros always use the braces specified.
 +    ///
 +    /// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro
 +    /// is could be used with a full path two `MacroMatcher`s have to be added one with the full path
 +    /// `crate_name::macro_name` and one with just the macro name.
 +    (standard_macro_braces: Vec<crate::nonstandard_macro_braces::MacroMatcher> = Vec::new()),
 +    /// Lint: MISSING_ENFORCED_IMPORT_RENAMES.
 +    ///
 +    /// The list of imports to always rename, a fully qualified path followed by the rename.
 +    (enforced_import_renames: Vec<crate::utils::conf::Rename> = Vec::new()),
 +    /// Lint: DISALLOWED_SCRIPT_IDENTS.
 +    ///
 +    /// The list of unicode scripts allowed to be used in the scope.
 +    (allowed_scripts: Vec<String> = vec!["Latin".to_string()]),
 +    /// Lint: NON_SEND_FIELDS_IN_SEND_TY.
 +    ///
 +    /// 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),
 +    /// Lint: AWAIT_HOLDING_INVALID_TYPE
 +    (await_holding_invalid_types: Vec<crate::utils::conf::DisallowedType> = Vec::new()),
 +    /// Lint: LARGE_INCLUDE_FILE.
 +    ///
 +    /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes
 +    (max_include_file_size: u64 = 1_000_000),
 +    /// Lint: EXPECT_USED.
 +    ///
 +    /// Whether `expect` should be allowed in test functions
 +    (allow_expect_in_tests: bool = false),
 +    /// Lint: UNWRAP_USED.
 +    ///
 +    /// Whether `unwrap` should be allowed in test functions
 +    (allow_unwrap_in_tests: bool = false),
 +    /// Lint: DBG_MACRO.
 +    ///
 +    /// Whether `dbg!` should be allowed in test functions
 +    (allow_dbg_in_tests: bool = false),
 +    /// Lint: RESULT_LARGE_ERR
 +    ///
 +    /// The maximum size of the `Err`-variant in a `Result` returned from a function
 +    (large_error_threshold: u64 = 128),
 +}
 +
 +/// Search for the configuration file.
 +pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
 +    /// Possible filename to search for.
 +    const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];
 +
 +    // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR.
 +    // If neither of those exist, use ".".
 +    let mut current = env::var_os("CLIPPY_CONF_DIR")
 +        .or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
 +        .map_or_else(|| PathBuf::from("."), PathBuf::from);
 +
 +    let mut found_config: Option<PathBuf> = None;
 +
 +    loop {
 +        for config_file_name in &CONFIG_FILE_NAMES {
 +            if let Ok(config_file) = current.join(config_file_name).canonicalize() {
 +                match fs::metadata(&config_file) {
 +                    Err(e) if e.kind() == io::ErrorKind::NotFound => {},
 +                    Err(e) => return Err(e),
 +                    Ok(md) if md.is_dir() => {},
 +                    Ok(_) => {
 +                        // warn if we happen to find two config files #8323
 +                        if let Some(ref found_config_) = found_config {
 +                            eprintln!(
 +                                "Using config file `{}`\nWarning: `{}` will be ignored.",
 +                                found_config_.display(),
 +                                config_file.display(),
 +                            );
 +                        } else {
 +                            found_config = Some(config_file);
 +                        }
 +                    },
 +                }
 +            }
 +        }
 +
 +        if found_config.is_some() {
 +            return Ok(found_config);
 +        }
 +
 +        // If the current directory has no parent, we're done searching.
 +        if !current.pop() {
 +            return Ok(None);
 +        }
 +    }
 +}
 +
 +/// Read the `toml` configuration file.
 +///
 +/// In case of error, the function tries to continue as much as possible.
 +pub fn read(path: &Path) -> TryConf {
 +    let content = match fs::read_to_string(path) {
 +        Err(e) => return TryConf::from_error(e),
 +        Ok(content) => content,
 +    };
 +    match toml::from_str::<TryConf>(&content) {
 +        Ok(mut conf) => {
 +            extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS);
 +            extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES);
 +
 +            conf
 +        },
 +        Err(e) => TryConf::from_error(e),
 +    }
 +}
 +
 +fn extend_vec_if_indicator_present(vec: &mut Vec<String>, default: &[&str]) {
 +    if vec.contains(&"..".to_string()) {
 +        vec.extend(default.iter().map(ToString::to_string));
 +    }
 +}
 +
 +const SEPARATOR_WIDTH: usize = 4;
 +
 +// Check whether the error is "unknown field" and, if so, list the available fields sorted and at
 +// least one per line, more if `CLIPPY_TERMINAL_WIDTH` is set and allows it.
 +pub fn format_error(error: Box<dyn Error>) -> String {
 +    let s = error.to_string();
 +
 +    if_chain! {
 +        if error.downcast::<toml::de::Error>().is_ok();
 +        if let Some((prefix, mut fields, suffix)) = parse_unknown_field_message(&s);
 +        then {
 +            use fmt::Write;
 +
 +            fields.sort_unstable();
 +
 +            let (rows, column_widths) = calculate_dimensions(&fields);
 +
 +            let mut msg = String::from(prefix);
 +            for row in 0..rows {
 +                write!(msg, "\n").unwrap();
 +                for (column, column_width) in column_widths.iter().copied().enumerate() {
 +                    let index = column * rows + row;
 +                    let field = fields.get(index).copied().unwrap_or_default();
 +                    write!(
 +                        msg,
 +                        "{:separator_width$}{:field_width$}",
 +                        " ",
 +                        field,
 +                        separator_width = SEPARATOR_WIDTH,
 +                        field_width = column_width
 +                    )
 +                    .unwrap();
 +                }
 +            }
 +            write!(msg, "\n{}", suffix).unwrap();
 +            msg
 +        } else {
 +            s
 +        }
 +    }
 +}
 +
 +// `parse_unknown_field_message` will become unnecessary if
 +// https://github.com/alexcrichton/toml-rs/pull/364 is merged.
 +fn parse_unknown_field_message(s: &str) -> Option<(&str, Vec<&str>, &str)> {
 +    // An "unknown field" message has the following form:
 +    //   unknown field `UNKNOWN`, expected one of `FIELD0`, `FIELD1`, ..., `FIELDN` at line X column Y
 +    //                                           ^^      ^^^^                     ^^
 +    if_chain! {
 +        if s.starts_with("unknown field");
 +        let slices = s.split("`, `").collect::<Vec<_>>();
 +        let n = slices.len();
 +        if n >= 2;
 +        if let Some((prefix, first_field)) = slices[0].rsplit_once(" `");
 +        if let Some((last_field, suffix)) = slices[n - 1].split_once("` ");
 +        then {
 +            let fields = iter::once(first_field)
 +                .chain(slices[1..n - 1].iter().copied())
 +                .chain(iter::once(last_field))
 +                .collect::<Vec<_>>();
 +            Some((prefix, fields, suffix))
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +fn calculate_dimensions(fields: &[&str]) -> (usize, Vec<usize>) {
 +    let columns = env::var("CLIPPY_TERMINAL_WIDTH")
 +        .ok()
 +        .and_then(|s| <usize as FromStr>::from_str(&s).ok())
 +        .map_or(1, |terminal_width| {
 +            let max_field_width = fields.iter().map(|field| field.len()).max().unwrap();
 +            cmp::max(1, terminal_width / (SEPARATOR_WIDTH + max_field_width))
 +        });
 +
 +    let rows = (fields.len() + (columns - 1)) / columns;
 +
 +    let column_widths = (0..columns)
 +        .map(|column| {
 +            if column < columns - 1 {
 +                (0..rows)
 +                    .map(|row| {
 +                        let index = column * rows + row;
 +                        let field = fields.get(index).copied().unwrap_or_default();
 +                        field.len()
 +                    })
 +                    .max()
 +                    .unwrap()
 +            } else {
 +                // Avoid adding extra space to the last column.
 +                0
 +            }
 +        })
 +        .collect::<Vec<_>>();
 +
 +    (rows, column_widths)
 +}
index ae1c11ef83c31cab317d5702cdacd8082c4100d2,0000000000000000000000000000000000000000..17d9a041857678adfe81e0d950defc3e6a417431
mode 100644,000000..100644
--- /dev/null
@@@ -1,1438 -1,0 +1,1437 @@@
-                 collector.visit_expr(&cx.tcx.hir().body(body_id).value);
 +use crate::utils::internal_lints::metadata_collector::is_deprecated_lint;
 +use clippy_utils::consts::{constant_simple, Constant};
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::macros::root_macro_call_first_node;
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::match_type;
 +use clippy_utils::{
 +    def_path_res, higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path,
 +    method_calls, paths, peel_blocks_with_stmt, 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};
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::hir_id::CRATE_HIR_ID;
 +use rustc_hir::intravisit::Visitor;
 +use rustc_hir::{
 +    BinOpKind, Block, Closure, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, Ty,
 +    TyKind, UnOp,
 +};
 +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::mir::interpret::ConstValue;
 +use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, FloatTy};
 +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;
 +use rustc_span::{sym, BytePos, Span};
 +use rustc_typeck::hir_ty_to_ty;
 +
 +use std::borrow::{Borrow, Cow};
 +
 +#[cfg(feature = "internal")]
 +pub mod metadata_collector;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for various things we like to keep tidy in clippy.
 +    ///
 +    /// ### Why is this bad?
 +    /// We like to pretend we're an example of tidy code.
 +    ///
 +    /// ### Example
 +    /// Wrong ordering of the util::paths constants.
 +    pub CLIPPY_LINTS_INTERNAL,
 +    internal,
 +    "various things that will negatively affect your clippy experience"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Ensures every lint is associated to a `LintPass`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The compiler only knows lints via a `LintPass`. Without
 +    /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not
 +    /// know the name of the lint.
 +    ///
 +    /// ### Known problems
 +    /// Only checks for lints associated using the
 +    /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// declare_lint! { pub LINT_1, ... }
 +    /// declare_lint! { pub LINT_2, ... }
 +    /// declare_lint! { pub FORGOTTEN_LINT, ... }
 +    /// // ...
 +    /// declare_lint_pass!(Pass => [LINT_1, LINT_2]);
 +    /// // missing FORGOTTEN_LINT
 +    /// ```
 +    pub LINT_WITHOUT_LINT_PASS,
 +    internal,
 +    "declaring a lint without associating it in a LintPass"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `cx.span_lint*` and suggests to use the `utils::*`
 +    /// variant of the function.
 +    ///
 +    /// ### Why is this bad?
 +    /// The `utils::*` variants also add a link to the Clippy documentation to the
 +    /// warning/error messages.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// cx.span_lint(LINT_NAME, "message");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// utils::span_lint(cx, LINT_NAME, "message");
 +    /// ```
 +    pub COMPILER_LINT_FUNCTIONS,
 +    internal,
 +    "usage of the lint functions of the compiler instead of the utils::* variant"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `cx.outer().expn_data()` and suggests to use
 +    /// the `cx.outer_expn_data()`
 +    ///
 +    /// ### Why is this bad?
 +    /// `cx.outer_expn_data()` is faster and more concise.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// expr.span.ctxt().outer().expn_data()
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// expr.span.ctxt().outer_expn_data()
 +    /// ```
 +    pub OUTER_EXPN_EXPN_DATA,
 +    internal,
 +    "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Not an actual lint. This lint is only meant for testing our customized internal compiler
 +    /// error message by calling `panic`.
 +    ///
 +    /// ### Why is this bad?
 +    /// ICE in large quantities can damage your teeth
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// 🍦🍦🍦🍦🍦
 +    /// ```
 +    pub PRODUCE_ICE,
 +    internal,
 +    "this message should not appear anywhere as we ICE before and don't emit the lint"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for cases of an auto-generated lint without an updated description,
 +    /// i.e. `default lint description`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Indicates that the lint is not finished.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// declare_lint! { pub COOL_LINT, nursery, "default lint description" }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" }
 +    /// ```
 +    pub DEFAULT_LINT,
 +    internal,
 +    "found 'default lint description' in a lint declaration"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Lints `span_lint_and_then` function calls, where the
 +    /// closure argument has only one statement and that statement is a method
 +    /// call to `span_suggestion`, `span_help`, `span_note` (using the same
 +    /// span), `help` or `note`.
 +    ///
 +    /// These usages of `span_lint_and_then` should be replaced with one of the
 +    /// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or
 +    /// `span_lint_and_note`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using the wrapper `span_lint_and_*` functions, is more
 +    /// convenient, readable and less error prone.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.span_suggestion(
 +    ///         expr.span,
 +    ///         help_msg,
 +    ///         sugg.to_string(),
 +    ///         Applicability::MachineApplicable,
 +    ///     );
 +    /// });
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.span_help(expr.span, help_msg);
 +    /// });
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.help(help_msg);
 +    /// });
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.span_note(expr.span, note_msg);
 +    /// });
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.note(note_msg);
 +    /// });
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// span_lint_and_sugg(
 +    ///     cx,
 +    ///     TEST_LINT,
 +    ///     expr.span,
 +    ///     lint_msg,
 +    ///     help_msg,
 +    ///     sugg.to_string(),
 +    ///     Applicability::MachineApplicable,
 +    /// );
 +    /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg);
 +    /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg);
 +    /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg);
 +    /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg);
 +    /// ```
 +    pub COLLAPSIBLE_SPAN_LINT_CALLS,
 +    internal,
 +    "found collapsible `span_lint_and_then` calls"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `utils::match_type()` on a type diagnostic item
 +    /// and suggests to use `utils::is_type_diagnostic_item()` instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// `utils::is_type_diagnostic_item()` does not require hardcoded paths.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// utils::match_type(cx, ty, &paths::VEC)
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// utils::is_type_diagnostic_item(cx, ty, sym::Vec)
 +    /// ```
 +    pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
 +    internal,
 +    "using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks the paths module for invalid paths.
 +    ///
 +    /// ### Why is this bad?
 +    /// It indicates a bug in the code.
 +    ///
 +    /// ### Example
 +    /// None.
 +    pub INVALID_PATHS,
 +    internal,
 +    "invalid path"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for interning symbols that have already been pre-interned and defined as constants.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's faster and easier to use the symbol constant.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// let _ = sym!(f32);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// let _ = sym::f32;
 +    /// ```
 +    pub INTERNING_DEFINED_SYMBOL,
 +    internal,
 +    "interning a symbol that is pre-interned and defined as a constant"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnecessary conversion from Symbol to a string.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's faster use symbols directly instead of strings.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// symbol.as_str() == "clippy";
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// symbol == sym::clippy;
 +    /// ```
 +    pub UNNECESSARY_SYMBOL_STR,
 +    internal,
 +    "unnecessary conversion between Symbol and string"
 +}
 +
 +declare_clippy_lint! {
 +    /// Finds unidiomatic usage of `if_chain!`
 +    pub IF_CHAIN_STYLE,
 +    internal,
 +    "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_clippy_lint! {
 +    /// ### What it does
 +    /// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV.
 +    ///
 +    pub MISSING_MSRV_ATTR_IMPL,
 +    internal,
 +    "checking if all necessary steps were taken when adding a MSRV to a lint"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for cases of an auto-generated deprecated lint without an updated reason,
 +    /// i.e. `"default deprecation note"`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Indicates that the documentation is incomplete.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// declare_deprecated_lint! {
 +    ///     /// ### What it does
 +    ///     /// Nothing. This lint has been deprecated.
 +    ///     ///
 +    ///     /// ### Deprecation reason
 +    ///     /// TODO
 +    ///     #[clippy::version = "1.63.0"]
 +    ///     pub COOL_LINT,
 +    ///     "default deprecation note"
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// declare_deprecated_lint! {
 +    ///     /// ### What it does
 +    ///     /// Nothing. This lint has been deprecated.
 +    ///     ///
 +    ///     /// ### Deprecation reason
 +    ///     /// This lint has been replaced by `cooler_lint`
 +    ///     #[clippy::version = "1.63.0"]
 +    ///     pub COOL_LINT,
 +    ///     "this lint has been replaced by `cooler_lint`"
 +    /// }
 +    /// ```
 +    pub DEFAULT_DEPRECATION_REASON,
 +    internal,
 +    "found 'default deprecation note' in a deprecated lint declaration"
 +}
 +
 +declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
 +
 +impl EarlyLintPass for ClippyLintsInternal {
 +    fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
 +        if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") {
 +            if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind {
 +                if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") {
 +                    if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind {
 +                        let mut last_name: Option<&str> = None;
 +                        for item in items {
 +                            let name = item.ident.as_str();
 +                            if let Some(last_name) = last_name {
 +                                if *last_name > *name {
 +                                    span_lint(
 +                                        cx,
 +                                        CLIPPY_LINTS_INTERNAL,
 +                                        item.span,
 +                                        "this constant should be before the previous constant due to lexical \
 +                                         ordering",
 +                                    );
 +                                }
 +                            }
 +                            last_name = Some(name);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Debug, Default)]
 +pub struct LintWithoutLintPass {
 +    declared_lints: FxHashMap<Symbol, Span>,
 +    registered_lints: FxHashSet<Symbol>,
 +}
 +
 +impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE, DEFAULT_DEPRECATION_REASON]);
 +
 +impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id())
 +            || is_lint_allowed(cx, DEFAULT_DEPRECATION_REASON, item.hir_id())
 +        {
 +            return;
 +        }
 +
 +        if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind {
 +            let is_lint_ref_ty = is_lint_ref_type(cx, ty);
 +            if is_deprecated_lint(cx, ty) || is_lint_ref_ty {
 +                check_invalid_clippy_version_attribute(cx, item);
 +
 +                let expr = &cx.tcx.hir().body(body_id).value;
 +                let fields;
 +                if is_lint_ref_ty {
 +                    if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind
 +                        && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind {
 +                            fields = struct_fields;
 +                    } else {
 +                        return;
 +                    }
 +                } else if let ExprKind::Struct(_, struct_fields, _) = expr.kind {
 +                    fields = struct_fields;
 +                } else {
 +                    return;
 +                }
 +
 +                let field = fields
 +                    .iter()
 +                    .find(|f| f.ident.as_str() == "desc")
 +                    .expect("lints must have a description field");
 +
 +                if let ExprKind::Lit(Spanned {
 +                    node: LitKind::Str(ref sym, _),
 +                    ..
 +                }) = field.expr.kind
 +                {
 +                    let sym_str = sym.as_str();
 +                    if is_lint_ref_ty {
 +                        if sym_str == "default lint description" {
 +                            span_lint(
 +                                cx,
 +                                DEFAULT_LINT,
 +                                item.span,
 +                                &format!("the lint `{}` has the default lint description", item.ident.name),
 +                            );
 +                        }
 +
 +                        self.declared_lints.insert(item.ident.name, item.span);
 +                    } else if sym_str == "default deprecation note" {
 +                        span_lint(
 +                            cx,
 +                            DEFAULT_DEPRECATION_REASON,
 +                            item.span,
 +                            &format!("the lint `{}` has the default deprecation reason", item.ident.name),
 +                        );
 +                    }
 +                }
 +            }
 +        } else if let Some(macro_call) = root_macro_call_first_node(cx, item) {
 +            if !matches!(
 +                cx.tcx.item_name(macro_call.def_id).as_str(),
 +                "impl_lint_pass" | "declare_lint_pass"
 +            ) {
 +                return;
 +            }
 +            if let hir::ItemKind::Impl(hir::Impl {
 +                of_trait: None,
 +                items: impl_item_refs,
 +                ..
 +            }) = item.kind
 +            {
 +                let mut collector = LintCollector {
 +                    output: &mut self.registered_lints,
 +                    cx,
 +                };
 +                let body_id = cx.tcx.hir().body_owned_by(
 +                    cx.tcx.hir().local_def_id(
 +                        impl_item_refs
 +                            .iter()
 +                            .find(|iiref| iiref.ident.as_str() == "get_lints")
 +                            .expect("LintPass needs to implement get_lints")
 +                            .id
 +                            .hir_id(),
 +                    ),
 +                );
-             if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
++                collector.visit_expr(cx.tcx.hir().body(body_id).value);
 +            }
 +        }
 +    }
 +
 +    fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
 +        if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) {
 +            return;
 +        }
 +
 +        for (lint_name, &lint_span) in &self.declared_lints {
 +            // When using the `declare_tool_lint!` macro, the original `lint_span`'s
 +            // file points to "<rustc macros>".
 +            // `compiletest-rs` thinks that's an error in a different file and
 +            // just ignores it. This causes the test in compile-fail/lint_pass
 +            // not able to capture the error.
 +            // Therefore, we need to climb the macro expansion tree and find the
 +            // actual span that invoked `declare_tool_lint!`:
 +            let lint_span = lint_span.ctxt().outer_expn_data().call_site;
 +
 +            if !self.registered_lints.contains(lint_name) {
 +                span_lint(
 +                    cx,
 +                    LINT_WITHOUT_LINT_PASS,
 +                    lint_span,
 +                    &format!("the lint `{}` is not added to any `LintPass`", lint_name),
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &Ty<'_>) -> bool {
 +    if let TyKind::Rptr(
 +        _,
 +        MutTy {
 +            ty: inner,
 +            mutbl: Mutability::Not,
 +        },
 +    ) = ty.kind
 +    {
 +        if let TyKind::Path(ref path) = inner.kind {
 +            if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) {
 +                return match_def_path(cx, def_id, &paths::LINT);
 +            }
 +        }
 +    }
 +
 +    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 semantic 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.item.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>,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) {
 +        if path.segments.len() == 1 {
 +            self.output.insert(path.segments[0].ident.name);
 +        }
 +    }
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +}
 +
 +#[derive(Clone, Default)]
 +pub struct CompilerLintFunctions {
 +    map: FxHashMap<&'static str, &'static str>,
 +}
 +
 +impl CompilerLintFunctions {
 +    #[must_use]
 +    pub fn new() -> Self {
 +        let mut map = FxHashMap::default();
 +        map.insert("span_lint", "utils::span_lint");
 +        map.insert("struct_span_lint", "utils::span_lint");
 +        map.insert("lint", "utils::span_lint");
 +        map.insert("span_lint_note", "utils::span_lint_and_note");
 +        map.insert("span_lint_help", "utils::span_lint_and_help");
 +        Self { map }
 +    }
 +}
 +
 +impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) {
 +            return;
 +        }
 +
 +        if_chain! {
-             let args = arg_lists[1];
-             if args.len() == 1;
-             let self_arg = &args.0;
++            if let ExprKind::MethodCall(path, self_arg, _, _) = &expr.kind;
 +            let fn_name = path.ident;
 +            if let Some(sugg) = self.map.get(fn_name.as_str());
 +            let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
 +            if match_type(cx, ty, &paths::EARLY_CONTEXT)
 +                || match_type(cx, ty, &paths::LATE_CONTEXT);
 +            then {
 +                span_lint_and_help(
 +                    cx,
 +                    COMPILER_LINT_FUNCTIONS,
 +                    path.ident.span,
 +                    "usage of a compiler lint function",
 +                    None,
 +                    &format!("please use the Clippy variant of this function: `{}`", sugg),
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]);
 +
 +impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) {
 +            return;
 +        }
 +
 +        let (method_names, arg_lists, spans) = method_calls(expr, 2);
 +        let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect();
 +        if_chain! {
 +            if let ["expn_data", "outer_expn"] = method_names.as_slice();
-             let only_expr = peel_blocks_with_stmt(&body.value);
-             if let ExprKind::MethodCall(ps, span_call_args, _) = &only_expr.kind;
-             if let ExprKind::Path(..) = span_call_args[0].kind;
++            let (self_arg, args)= arg_lists[1];
++            if args.is_empty();
 +            let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
 +            if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT);
 +            then {
 +                span_lint_and_sugg(
 +                    cx,
 +                    OUTER_EXPN_EXPN_DATA,
 +                    spans[1].with_hi(expr.span.hi()),
 +                    "usage of `outer_expn().expn_data()`",
 +                    "try",
 +                    "outer_expn_data()".to_string(),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +declare_lint_pass!(ProduceIce => [PRODUCE_ICE]);
 +
 +impl EarlyLintPass for ProduceIce {
 +    fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
 +        assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?");
 +    }
 +}
 +
 +fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool {
 +    match fn_kind {
 +        FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy",
 +        FnKind::Closure(..) => false,
 +    }
 +}
 +
 +declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let ExprKind::Call(func, and_then_args) = expr.kind;
 +            if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]);
 +            if and_then_args.len() == 5;
 +            if let ExprKind::Closure(&Closure { body, .. }) = &and_then_args[4].kind;
 +            let body = cx.tcx.hir().body(body);
-                     "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
++            let only_expr = peel_blocks_with_stmt(body.value);
++            if let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind;
++            if let ExprKind::Path(..) = recv.kind;
 +            then {
 +                let and_then_snippets = get_and_then_snippets(cx, and_then_args);
 +                let mut sle = SpanlessEq::new(cx).deny_side_effects();
 +                match ps.ident.as_str() {
-                     "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
-                         let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
++                    "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => {
 +                        suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args));
 +                    },
-                     "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
-                         let note_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
++                    "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => {
++                        let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
 +                        suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true);
 +                    },
-                         let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
++                    "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => {
++                        let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
 +                        suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true);
 +                    },
 +                    "help" => {
-                         let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
++                        let help_snippet = snippet(cx, span_call_args[0].span, r#""...""#);
 +                        suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false);
 +                    }
 +                    "note" => {
-     let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
-     let sugg_snippet = snippet(cx, span_call_args[3].span, "..");
-     let applicability_snippet = snippet(cx, span_call_args[4].span, "Applicability::MachineApplicable");
++                        let note_snippet = snippet(cx, span_call_args[0].span, r#""...""#);
 +                        suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false);
 +                    }
 +                    _  => (),
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +struct AndThenSnippets<'a> {
 +    cx: Cow<'a, str>,
 +    lint: Cow<'a, str>,
 +    span: Cow<'a, str>,
 +    msg: Cow<'a, str>,
 +}
 +
 +fn get_and_then_snippets<'a, 'hir>(cx: &LateContext<'_>, and_then_snippets: &'hir [Expr<'hir>]) -> AndThenSnippets<'a> {
 +    let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx");
 +    let lint_snippet = snippet(cx, and_then_snippets[1].span, "..");
 +    let span_snippet = snippet(cx, and_then_snippets[2].span, "span");
 +    let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#);
 +
 +    AndThenSnippets {
 +        cx: cx_snippet,
 +        lint: lint_snippet,
 +        span: span_snippet,
 +        msg: msg_snippet,
 +    }
 +}
 +
 +struct SpanSuggestionSnippets<'a> {
 +    help: Cow<'a, str>,
 +    sugg: Cow<'a, str>,
 +    applicability: Cow<'a, str>,
 +}
 +
 +fn span_suggestion_snippets<'a, 'hir>(
 +    cx: &LateContext<'_>,
 +    span_call_args: &'hir [Expr<'hir>],
 +) -> SpanSuggestionSnippets<'a> {
-                         return path_to_matched_type(cx, &body.value);
++    let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
++    let sugg_snippet = snippet(cx, span_call_args[2].span, "..");
++    let applicability_snippet = snippet(cx, span_call_args[3].span, "Applicability::MachineApplicable");
 +
 +    SpanSuggestionSnippets {
 +        help: help_snippet,
 +        sugg: sugg_snippet,
 +        applicability: applicability_snippet,
 +    }
 +}
 +
 +fn suggest_suggestion(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    and_then_snippets: &AndThenSnippets<'_>,
 +    span_suggestion_snippets: &SpanSuggestionSnippets<'_>,
 +) {
 +    span_lint_and_sugg(
 +        cx,
 +        COLLAPSIBLE_SPAN_LINT_CALLS,
 +        expr.span,
 +        "this call is collapsible",
 +        "collapse into",
 +        format!(
 +            "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})",
 +            and_then_snippets.cx,
 +            and_then_snippets.lint,
 +            and_then_snippets.span,
 +            and_then_snippets.msg,
 +            span_suggestion_snippets.help,
 +            span_suggestion_snippets.sugg,
 +            span_suggestion_snippets.applicability
 +        ),
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
 +fn suggest_help(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    and_then_snippets: &AndThenSnippets<'_>,
 +    help: &str,
 +    with_span: bool,
 +) {
 +    let option_span = if with_span {
 +        format!("Some({})", and_then_snippets.span)
 +    } else {
 +        "None".to_string()
 +    };
 +
 +    span_lint_and_sugg(
 +        cx,
 +        COLLAPSIBLE_SPAN_LINT_CALLS,
 +        expr.span,
 +        "this call is collapsible",
 +        "collapse into",
 +        format!(
 +            "span_lint_and_help({}, {}, {}, {}, {}, {})",
 +            and_then_snippets.cx,
 +            and_then_snippets.lint,
 +            and_then_snippets.span,
 +            and_then_snippets.msg,
 +            &option_span,
 +            help
 +        ),
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
 +fn suggest_note(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    and_then_snippets: &AndThenSnippets<'_>,
 +    note: &str,
 +    with_span: bool,
 +) {
 +    let note_span = if with_span {
 +        format!("Some({})", and_then_snippets.span)
 +    } else {
 +        "None".to_string()
 +    };
 +
 +    span_lint_and_sugg(
 +        cx,
 +        COLLAPSIBLE_SPAN_LINT_CALLS,
 +        expr.span,
 +        "this call is collapsible",
 +        "collapse into",
 +        format!(
 +            "span_lint_and_note({}, {}, {}, {}, {}, {})",
 +            and_then_snippets.cx,
 +            and_then_snippets.lint,
 +            and_then_snippets.span,
 +            and_then_snippets.msg,
 +            note_span,
 +            note
 +        ),
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
 +declare_lint_pass!(MatchTypeOnDiagItem => [MATCH_TYPE_ON_DIAGNOSTIC_ITEM]);
 +
 +impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if is_lint_allowed(cx, MATCH_TYPE_ON_DIAGNOSTIC_ITEM, expr.hir_id) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            // Check if this is a call to utils::match_type()
 +            if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind;
 +            if is_expr_path_def_path(cx, fn_path, &["clippy_utils", "ty", "match_type"]);
 +            // Extract the path to the matched type
 +            if let Some(segments) = path_to_matched_type(cx, ty_path);
 +            let segments: Vec<&str> = segments.iter().map(Symbol::as_str).collect();
 +            if let Some(ty_did) = def_path_res(cx, &segments[..]).opt_def_id();
 +            // Check if the matched type is a diagnostic item
 +            if let Some(item_name) = cx.tcx.get_diagnostic_name(ty_did);
 +            then {
 +                // TODO: check paths constants from external crates.
 +                let cx_snippet = snippet(cx, context.span, "_");
 +                let ty_snippet = snippet(cx, ty.span, "_");
 +
 +                span_lint_and_sugg(
 +                    cx,
 +                    MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
 +                    expr.span,
 +                    "usage of `clippy_utils::ty::match_type()` on a type diagnostic item",
 +                    "try",
 +                    format!("clippy_utils::ty::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name),
 +                    Applicability::MaybeIncorrect,
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Vec<Symbol>> {
 +    use rustc_hir::ItemKind;
 +
 +    match &expr.kind {
 +        ExprKind::AddrOf(.., expr) => return path_to_matched_type(cx, expr),
 +        ExprKind::Path(qpath) => match cx.qpath_res(qpath, expr.hir_id) {
 +            Res::Local(hir_id) => {
 +                let parent_id = cx.tcx.hir().get_parent_node(hir_id);
 +                if let Some(Node::Local(local)) = cx.tcx.hir().find(parent_id) {
 +                    if let Some(init) = local.init {
 +                        return path_to_matched_type(cx, init);
 +                    }
 +                }
 +            },
 +            Res::Def(DefKind::Const | DefKind::Static(..), def_id) => {
 +                if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) {
 +                    if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind {
 +                        let body = cx.tcx.hir().body(body_id);
-             if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, &body.value);
++                        return path_to_matched_type(cx, body.value);
 +                    }
 +                }
 +            },
 +            _ => {},
 +        },
 +        ExprKind::Array(exprs) => {
 +            let segments: Vec<Symbol> = exprs
 +                .iter()
 +                .filter_map(|expr| {
 +                    if let ExprKind::Lit(lit) = &expr.kind {
 +                        if let LitKind::Str(sym, _) = lit.node {
 +                            return Some(sym);
 +                        }
 +                    }
 +
 +                    None
 +                })
 +                .collect();
 +
 +            if segments.len() == exprs.len() {
 +                return Some(segments);
 +            }
 +        },
 +        _ => {},
 +    }
 +
 +    None
 +}
 +
 +// This is not a complete resolver for paths. It works on all the paths currently used in the paths
 +// module.  That's all it does and all it needs to do.
 +pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
 +    if def_path_res(cx, path) != Res::Err {
 +        return true;
 +    }
 +
 +    // Some implementations can't be found by `path_to_res`, particularly inherent
 +    // implementations of native types. Check lang items.
 +    let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect();
 +    let lang_items = cx.tcx.lang_items();
 +    // This list isn't complete, but good enough for our current list of paths.
 +    let incoherent_impls = [
 +        SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32),
 +        SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64),
 +        SimplifiedTypeGen::SliceSimplifiedType,
 +        SimplifiedTypeGen::StrSimplifiedType,
 +    ]
 +    .iter()
 +    .flat_map(|&ty| cx.tcx.incoherent_impls(ty));
 +    for item_def_id in lang_items.items().iter().flatten().chain(incoherent_impls) {
 +        let lang_item_path = cx.get_def_path(*item_def_id);
 +        if path_syms.starts_with(&lang_item_path) {
 +            if let [item] = &path_syms[lang_item_path.len()..] {
 +                if matches!(
 +                    cx.tcx.def_kind(*item_def_id),
 +                    DefKind::Mod | DefKind::Enum | DefKind::Trait
 +                ) {
 +                    for child in cx.tcx.module_children(*item_def_id) {
 +                        if child.ident.name == *item {
 +                            return true;
 +                        }
 +                    }
 +                } else {
 +                    for child in cx.tcx.associated_item_def_ids(*item_def_id) {
 +                        if cx.tcx.item_name(*child) == *item {
 +                            return true;
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    false
 +}
 +
 +declare_lint_pass!(InvalidPaths => [INVALID_PATHS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        let local_def_id = &cx.tcx.parent_module(item.hir_id());
 +        let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
 +        if_chain! {
 +            if mod_name.as_str() == "paths";
 +            if let hir::ItemKind::Const(ty, body_id) = item.kind;
 +            let ty = hir_ty_to_ty(cx.tcx, ty);
 +            if let ty::Array(el_ty, _) = &ty.kind();
 +            if let ty::Ref(_, el_ty, _) = &el_ty.kind();
 +            if el_ty.is_str();
 +            let body = cx.tcx.hir().body(body_id);
 +            let typeck_results = cx.tcx.typeck_body(body_id);
-             if let ExprKind::MethodCall(_, [item], _) = call.kind;
++            if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value);
 +            let path: Vec<&str> = path.iter().map(|x| {
 +                    if let Constant::Str(s) = x {
 +                        s.as_str()
 +                    } else {
 +                        // We checked the type of the constant above
 +                        unreachable!()
 +                    }
 +                }).collect();
 +            if !check_path(cx, &path[..]);
 +            then {
 +                span_lint(cx, INVALID_PATHS, item.span, "invalid path");
 +            }
 +        }
 +    }
 +}
 +
 +#[derive(Default)]
 +pub struct InterningDefinedSymbol {
 +    // Maps the symbol value to the constant DefId.
 +    symbol_map: FxHashMap<u32, DefId>,
 +}
 +
 +impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]);
 +
 +impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
 +    fn check_crate(&mut self, cx: &LateContext<'_>) {
 +        if !self.symbol_map.is_empty() {
 +            return;
 +        }
 +
 +        for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] {
 +            if let Some(def_id) = def_path_res(cx, module).opt_def_id() {
 +                for item in cx.tcx.module_children(def_id).iter() {
 +                    if_chain! {
 +                        if let Res::Def(DefKind::Const, item_def_id) = item.res;
 +                        let ty = cx.tcx.type_of(item_def_id);
 +                        if match_type(cx, ty, &paths::SYMBOL);
 +                        if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id);
 +                        if let Ok(value) = value.to_u32();
 +                        then {
 +                            self.symbol_map.insert(value, item_def_id);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            if let ExprKind::Call(func, [arg]) = &expr.kind;
 +            if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind();
 +            if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN);
 +            if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg);
 +            let value = Symbol::intern(&arg).as_u32();
 +            if let Some(&def_id) = self.symbol_map.get(&value);
 +            then {
 +                span_lint_and_sugg(
 +                    cx,
 +                    INTERNING_DEFINED_SYMBOL,
 +                    is_expn_of(expr.span, "sym").unwrap_or(expr.span),
 +                    "interning a defined symbol",
 +                    "try",
 +                    cx.tcx.def_path_str(def_id),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +        if let ExprKind::Binary(op, left, right) = expr.kind {
 +            if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) {
 +                let data = [
 +                    (left, self.symbol_str_expr(left, cx)),
 +                    (right, self.symbol_str_expr(right, cx)),
 +                ];
 +                match data {
 +                    // both operands are a symbol string
 +                    [(_, Some(left)), (_, Some(right))] => {
 +                        span_lint_and_sugg(
 +                            cx,
 +                            UNNECESSARY_SYMBOL_STR,
 +                            expr.span,
 +                            "unnecessary `Symbol` to string conversion",
 +                            "try",
 +                            format!(
 +                                "{} {} {}",
 +                                left.as_symbol_snippet(cx),
 +                                op.node.as_str(),
 +                                right.as_symbol_snippet(cx),
 +                            ),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    },
 +                    // one of the operands is a symbol string
 +                    [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => {
 +                        // creating an owned string for comparison
 +                        if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) {
 +                            span_lint_and_sugg(
 +                                cx,
 +                                UNNECESSARY_SYMBOL_STR,
 +                                expr.span,
 +                                "unnecessary string allocation",
 +                                "try",
 +                                format!("{}.as_str()", symbol.as_symbol_snippet(cx)),
 +                                Applicability::MachineApplicable,
 +                            );
 +                        }
 +                    },
 +                    // nothing found
 +                    [(_, None), (_, None)] => {},
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +impl InterningDefinedSymbol {
 +    fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option<SymbolStrExpr<'tcx>> {
 +        static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD];
 +        static SYMBOL_STR_PATHS: &[&[&str]] = &[
 +            &paths::SYMBOL_AS_STR,
 +            &paths::SYMBOL_TO_IDENT_STRING,
 +            &paths::TO_STRING_METHOD,
 +        ];
 +        let call = if_chain! {
 +            if let ExprKind::AddrOf(_, _, e) = expr.kind;
 +            if let ExprKind::Unary(UnOp::Deref, e) = e.kind;
 +            then { e } else { expr }
 +        };
 +        if_chain! {
 +            // is a method call
++            if let ExprKind::MethodCall(_, item, [], _) = call.kind;
 +            if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id);
 +            let ty = cx.typeck_results().expr_ty(item);
 +            // ...on either an Ident or a Symbol
 +            if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) {
 +                Some(false)
 +            } else if match_type(cx, ty, &paths::IDENT) {
 +                Some(true)
 +            } else {
 +                None
 +            };
 +            // ...which converts it to a string
 +            let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS };
 +            if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path));
 +            then {
 +                let is_to_owned = path.last().unwrap().ends_with("string");
 +                return Some(SymbolStrExpr::Expr {
 +                    item,
 +                    is_ident,
 +                    is_to_owned,
 +                });
 +            }
 +        }
 +        // is a string constant
 +        if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) {
 +            let value = Symbol::intern(&s).as_u32();
 +            // ...which matches a symbol constant
 +            if let Some(&def_id) = self.symbol_map.get(&value) {
 +                return Some(SymbolStrExpr::Const(def_id));
 +            }
 +        }
 +        None
 +    }
 +}
 +
 +enum SymbolStrExpr<'tcx> {
 +    /// a string constant with a corresponding symbol constant
 +    Const(DefId),
 +    /// a "symbol to string" expression like `symbol.as_str()`
 +    Expr {
 +        /// part that evaluates to `Symbol` or `Ident`
 +        item: &'tcx Expr<'tcx>,
 +        is_ident: bool,
 +        /// whether an owned `String` is created like `to_ident_string()`
 +        is_to_owned: bool,
 +    },
 +}
 +
 +impl<'tcx> SymbolStrExpr<'tcx> {
 +    /// Returns a snippet that evaluates to a `Symbol` and is const if possible
 +    fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> {
 +        match *self {
 +            Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(),
 +            Self::Expr { item, is_ident, .. } => {
 +                let mut snip = snippet(cx, item.span.source_callsite(), "..");
 +                if is_ident {
 +                    // get `Ident.name`
 +                    snip.to_mut().push_str(".name");
 +                }
 +                snip
 +            },
 +        }
 +    }
 +}
 +
 +declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for IfChainStyle {
 +    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
 +        let (local, after, if_chain_span) = if_chain! {
 +            if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts;
 +            if let Some(if_chain_span) = is_expn_of(block.span, "if_chain");
 +            then { (local, after, if_chain_span) } else { return }
 +        };
 +        if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) {
 +            span_lint(
 +                cx,
 +                IF_CHAIN_STYLE,
 +                if_chain_local_span(cx, local, if_chain_span),
 +                "`let` expression should be above the `if_chain!`",
 +            );
 +        } else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) {
 +            span_lint(
 +                cx,
 +                IF_CHAIN_STYLE,
 +                if_chain_local_span(cx, local, if_chain_span),
 +                "`let` expression should be inside `then { .. }`",
 +            );
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        let (cond, then, els) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) {
 +            (cond, then, r#else.is_some())
 +        } else {
 +            return;
 +        };
 +        let then_block = match then.kind {
 +            ExprKind::Block(block, _) => block,
 +            _ => return,
 +        };
 +        let if_chain_span = is_expn_of(expr.span, "if_chain");
 +        if !els {
 +            check_nested_if_chains(cx, expr, then_block, if_chain_span);
 +        }
 +        let if_chain_span = match if_chain_span {
 +            None => return,
 +            Some(span) => span,
 +        };
 +        // check for `if a && b;`
 +        if_chain! {
 +            if let ExprKind::Binary(op, _, _) = cond.kind;
 +            if op.node == BinOpKind::And;
 +            if cx.sess().source_map().is_multiline(cond.span);
 +            then {
 +                span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`");
 +            }
 +        }
 +        if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span)
 +            && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span)
 +        {
 +            span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`");
 +        }
 +    }
 +}
 +
 +fn check_nested_if_chains(
 +    cx: &LateContext<'_>,
 +    if_expr: &Expr<'_>,
 +    then_block: &Block<'_>,
 +    if_chain_span: Option<Span>,
 +) {
 +    #[rustfmt::skip]
 +    let (head, tail) = match *then_block {
 +        Block { stmts, expr: Some(tail), .. } => (stmts, tail),
 +        Block {
 +            stmts: &[
 +                ref head @ ..,
 +                Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. }
 +            ],
 +            ..
 +        } => (head, tail),
 +        _ => return,
 +    };
 +    if_chain! {
 +        if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail);
 +        let sm = cx.sess().source_map();
 +        if head
 +            .iter()
 +            .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span));
 +        if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr);
 +        then {} else { return }
 +    }
 +    let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) {
 +        (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"),
 +        (Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"),
 +        (Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"),
 +        _ => return,
 +    };
 +    span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| {
 +        let (span, msg) = match head {
 +            [] => return,
 +            [stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"),
 +            [a, .., b] => (
 +                a.span.to(b.span),
 +                "these `let` statements can also be in the `if_chain!`",
 +            ),
 +        };
 +        diag.span_help(span, msg);
 +    });
 +}
 +
 +fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool {
 +    cx.tcx
 +        .hir()
 +        .parent_iter(hir_id)
 +        .find(|(_, node)| {
 +            #[rustfmt::skip]
 +            !matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_))
 +        })
 +        .map_or(false, |(id, _)| {
 +            is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span)
 +        })
 +}
 +
 +/// Checks a trailing slice of statements and expression of a `Block` to see if they are part
 +/// of the `then {..}` portion of an `if_chain!`
 +fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool {
 +    let span = if let [stmt, ..] = stmts {
 +        stmt.span
 +    } else if let Some(expr) = expr {
 +        expr.span
 +    } else {
 +        // empty `then {}`
 +        return true;
 +    };
 +    is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span)
 +}
 +
 +/// Creates a `Span` for `let x = ..;` in an `if_chain!` call.
 +fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span {
 +    let mut span = local.pat.span;
 +    if let Some(init) = local.init {
 +        span = span.to(init.span);
 +    }
 +    span.adjust(if_chain_span.ctxt().outer_expn());
 +    let sm = cx.sess().source_map();
 +    let span = sm.span_extend_to_prev_str(span, "let", false, true).unwrap_or(span);
 +    let span = sm.span_extend_to_next_char(span, ';', false);
 +    Span::new(
 +        span.lo() - BytePos(3),
 +        span.hi() + BytePos(1),
 +        span.ctxt(),
 +        span.parent(),
 +    )
 +}
 +
 +declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]);
 +
 +impl LateLintPass<'_> for MsrvAttrImpl {
 +    fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
 +        if_chain! {
 +            if let hir::ItemKind::Impl(hir::Impl {
 +                of_trait: Some(lint_pass_trait_ref),
 +                self_ty,
 +                items,
 +                ..
 +            }) = &item.kind;
 +            if let Some(lint_pass_trait_def_id) = lint_pass_trait_ref.trait_def_id();
 +            let is_late_pass = match_def_path(cx, lint_pass_trait_def_id, &paths::LATE_LINT_PASS);
 +            if is_late_pass || match_def_path(cx, lint_pass_trait_def_id, &paths::EARLY_LINT_PASS);
 +            let self_ty = hir_ty_to_ty(cx.tcx, self_ty);
 +            if let ty::Adt(self_ty_def, _) = self_ty.kind();
 +            if self_ty_def.is_struct();
 +            if self_ty_def.all_fields().any(|f| {
 +                cx.tcx
 +                    .type_of(f.did)
 +                    .walk()
 +                    .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_)))
 +                    .any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION))
 +            });
 +            if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs));
 +            then {
 +                let context = if is_late_pass { "LateContext" } else { "EarlyContext" };
 +                let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" };
 +                let span = cx.sess().source_map().span_through_char(item.span, '{');
 +                span_lint_and_sugg(
 +                    cx,
 +                    MISSING_MSRV_ATTR_IMPL,
 +                    span,
 +                    &format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"),
 +                    &format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"),
 +                    format!("{}\n    extract_msrv_attr!({context});", snippet(cx, span, "..")),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +}
index b1148bccc2a283d025cb530f18197437bfbe9079,0000000000000000000000000000000000000000..342f627e38275ce925c224595542abadfef9b2e3
mode 100644,000000..100644
--- /dev/null
@@@ -1,1169 -1,0 +1,1169 @@@
-             ExprKind::MethodCall(path, arg, _arg_span) => {
-                 let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(&arg[0]));
 +//! This lint is used to collect metadata about clippy lints. This metadata is exported as a json
 +//! file and then used to generate the [clippy lint list](https://rust-lang.github.io/rust-clippy/master/index.html)
 +//!
 +//! This module and therefore the entire lint is guarded by a feature flag called `internal`
 +//!
 +//! The module transforms all lint names to ascii lowercase to ensure that we don't have mismatches
 +//! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such
 +//! a simple mistake)
 +
 +use crate::renamed_lints::RENAMED_LINTS;
 +use crate::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type};
 +
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::ty::{match_type, walk_ptrs_ty_depth};
 +use clippy_utils::{last_path_segment, match_def_path, match_function_call, match_path, paths};
 +use if_chain::if_chain;
 +use rustc_ast as ast;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_hir::{
 +    self as hir, def::DefKind, intravisit, intravisit::Visitor, Closure, ExprKind, Item, ItemKind, Mutability, QPath,
 +};
 +use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId};
 +use rustc_middle::hir::nested_filter;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::Ident;
 +use rustc_span::{sym, Loc, Span, Symbol};
 +use serde::{ser::SerializeStruct, Serialize, Serializer};
 +use std::collections::BinaryHeap;
 +use std::fmt;
 +use std::fmt::Write as _;
 +use std::fs::{self, OpenOptions};
 +use std::io::prelude::*;
 +use std::path::Path;
 +use std::path::PathBuf;
 +use std::process::Command;
 +
 +/// This is the output file of the lint collector.
 +const OUTPUT_FILE: &str = "../util/gh-pages/lints.json";
 +/// These lints are excluded from the export.
 +const BLACK_LISTED_LINTS: &[&str] = &["lint_author", "dump_hir", "internal_metadata_collector"];
 +/// These groups will be ignored by the lint group matcher. This is useful for collections like
 +/// `clippy::all`
 +const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"];
 +/// Lints within this group will be excluded from the collection. These groups
 +/// have to be defined without the `clippy::` prefix.
 +const EXCLUDED_LINT_GROUPS: [&str; 1] = ["internal"];
 +/// Collected deprecated lint will be assigned to this group in the JSON output
 +const DEPRECATED_LINT_GROUP_STR: &str = "deprecated";
 +/// This is the lint level for deprecated lints that will be displayed in the lint list
 +const DEPRECATED_LINT_LEVEL: &str = "none";
 +/// This array holds Clippy's lint groups with their corresponding default lint level. The
 +/// lint level for deprecated lints is set in `DEPRECATED_LINT_LEVEL`.
 +const DEFAULT_LINT_LEVELS: &[(&str, &str)] = &[
 +    ("correctness", "deny"),
 +    ("suspicious", "warn"),
 +    ("restriction", "allow"),
 +    ("style", "warn"),
 +    ("pedantic", "allow"),
 +    ("complexity", "warn"),
 +    ("perf", "warn"),
 +    ("cargo", "allow"),
 +    ("nursery", "allow"),
 +];
 +/// This prefix is in front of the lint groups in the lint store. The prefix will be trimmed
 +/// to only keep the actual lint group in the output.
 +const CLIPPY_LINT_GROUP_PREFIX: &str = "clippy::";
 +
 +/// This template will be used to format the configuration section in the lint documentation.
 +/// The `configurations` parameter will be replaced with one or multiple formatted
 +/// `ClippyConfiguration` instances. See `CONFIGURATION_VALUE_TEMPLATE` for further customizations
 +macro_rules! CONFIGURATION_SECTION_TEMPLATE {
 +    () => {
 +        r#"
 +### Configuration
 +This lint has the following configuration variables:
 +
 +{configurations}
 +"#
 +    };
 +}
 +/// This template will be used to format an individual `ClippyConfiguration` instance in the
 +/// lint documentation.
 +///
 +/// The format function will provide strings for the following parameters: `name`, `ty`, `doc` and
 +/// `default`
 +macro_rules! CONFIGURATION_VALUE_TEMPLATE {
 +    () => {
 +        "* `{name}`: `{ty}`: {doc} (defaults to `{default}`)\n"
 +    };
 +}
 +
 +macro_rules! RENAMES_SECTION_TEMPLATE {
 +    () => {
 +        r#"
 +### Past names
 +
 +{names}
 +"#
 +    };
 +}
 +macro_rules! RENAME_VALUE_TEMPLATE {
 +    () => {
 +        "* `{name}`\n"
 +    };
 +}
 +
 +const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [
 +    &["clippy_utils", "diagnostics", "span_lint"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_help"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_note"],
 +    &["clippy_utils", "diagnostics", "span_lint_hir"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_sugg"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_then"],
 +    &["clippy_utils", "diagnostics", "span_lint_hir_and_then"],
 +];
 +const SUGGESTION_DIAGNOSTIC_BUILDER_METHODS: [(&str, bool); 9] = [
 +    ("span_suggestion", false),
 +    ("span_suggestion_short", false),
 +    ("span_suggestion_verbose", false),
 +    ("span_suggestion_hidden", false),
 +    ("tool_only_span_suggestion", false),
 +    ("multipart_suggestion", true),
 +    ("multipart_suggestions", true),
 +    ("tool_only_multipart_suggestion", true),
 +    ("span_suggestions", true),
 +];
 +const SUGGESTION_FUNCTIONS: [&[&str]; 2] = [
 +    &["clippy_utils", "diagnostics", "multispan_sugg"],
 +    &["clippy_utils", "diagnostics", "multispan_sugg_with_applicability"],
 +];
 +const DEPRECATED_LINT_TYPE: [&str; 3] = ["clippy_lints", "deprecated_lints", "ClippyDeprecatedLint"];
 +
 +/// The index of the applicability name of `paths::APPLICABILITY_VALUES`
 +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 VERSION_DEFAULT_STR: &str = "Unknown";
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Collects metadata about clippy lints for the website.
 +    ///
 +    /// This lint will be used to report problems of syntax parsing. You should hopefully never
 +    /// see this but never say never I guess ^^
 +    ///
 +    /// ### Why is this bad?
 +    /// This is not a bad thing but definitely a hacky way to do it. See
 +    /// issue [#4310](https://github.com/rust-lang/rust-clippy/issues/4310) for a discussion
 +    /// about the implementation.
 +    ///
 +    /// ### Known problems
 +    /// Hopefully none. It would be pretty uncool to have a problem here :)
 +    ///
 +    /// ### Example output
 +    /// ```json,ignore
 +    /// {
 +    ///     "id": "internal_metadata_collector",
 +    ///     "id_span": {
 +    ///         "path": "clippy_lints/src/utils/internal_lints/metadata_collector.rs",
 +    ///         "line": 1
 +    ///     },
 +    ///     "group": "clippy::internal",
 +    ///     "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"
 +}
 +
 +impl_lint_pass!(MetadataCollector => [INTERNAL_METADATA_COLLECTOR]);
 +
 +#[allow(clippy::module_name_repetitions)]
 +#[derive(Debug, Clone)]
 +pub struct MetadataCollector {
 +    /// All collected lints
 +    ///
 +    /// We use a Heap here to have the lints added in alphabetic order in the export
 +    lints: BinaryHeap<LintMetadata>,
 +    applicability_info: FxHashMap<String, ApplicabilityInfo>,
 +    config: Vec<ClippyConfiguration>,
 +    clippy_project_root: PathBuf,
 +}
 +
 +impl MetadataCollector {
 +    pub fn new() -> Self {
 +        Self {
 +            lints: BinaryHeap::<LintMetadata>::default(),
 +            applicability_info: FxHashMap::<String, ApplicabilityInfo>::default(),
 +            config: collect_configs(),
 +            clippy_project_root: std::env::current_dir()
 +                .expect("failed to get current dir")
 +                .ancestors()
 +                .nth(1)
 +                .expect("failed to get project root")
 +                .to_path_buf(),
 +        }
 +    }
 +
 +    fn get_lint_configs(&self, lint_name: &str) -> Option<String> {
 +        self.config
 +            .iter()
 +            .filter(|config| config.lints.iter().any(|lint| lint == lint_name))
 +            .map(ToString::to_string)
 +            .reduce(|acc, x| acc + &x)
 +            .map(|configurations| format!(CONFIGURATION_SECTION_TEMPLATE!(), configurations = configurations))
 +    }
 +}
 +
 +impl Drop for MetadataCollector {
 +    /// You might ask: How hacky is this?
 +    /// My answer:     YES
 +    fn drop(&mut self) {
 +        // The metadata collector gets dropped twice, this makes sure that we only write
 +        // when the list is full
 +        if self.lints.is_empty() {
 +            return;
 +        }
 +
 +        let mut applicability_info = std::mem::take(&mut self.applicability_info);
 +
 +        // Mapping the final data
 +        let mut lints = std::mem::take(&mut self.lints).into_sorted_vec();
 +        for x in &mut lints {
 +            x.applicability = Some(applicability_info.remove(&x.id).unwrap_or_default());
 +            replace_produces(&x.id, &mut x.docs, &self.clippy_project_root);
 +        }
 +
 +        collect_renames(&mut lints);
 +
 +        // Outputting
 +        if Path::new(OUTPUT_FILE).exists() {
 +            fs::remove_file(OUTPUT_FILE).unwrap();
 +        }
 +        let mut file = OpenOptions::new().write(true).create(true).open(OUTPUT_FILE).unwrap();
 +        writeln!(file, "{}", serde_json::to_string_pretty(&lints).unwrap()).unwrap();
 +    }
 +}
 +
 +#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
 +struct LintMetadata {
 +    id: String,
 +    id_span: SerializableSpan,
 +    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,
 +        version: String,
 +        docs: String,
 +    ) -> Self {
 +        Self {
 +            id,
 +            id_span,
 +            group,
 +            level: level.to_string(),
 +            version,
 +            docs,
 +            applicability: None,
 +        }
 +    }
 +}
 +
 +fn replace_produces(lint_name: &str, docs: &mut String, clippy_project_root: &Path) {
 +    let mut doc_lines = docs.lines().map(ToString::to_string).collect::<Vec<_>>();
 +    let mut lines = doc_lines.iter_mut();
 +
 +    'outer: loop {
 +        // Find the start of the example
 +
 +        // ```rust
 +        loop {
 +            match lines.next() {
 +                Some(line) if line.trim_start().starts_with("```rust") => {
 +                    if line.contains("ignore") || line.contains("no_run") {
 +                        // A {{produces}} marker may have been put on a ignored code block by mistake,
 +                        // just seek to the end of the code block and continue checking.
 +                        if lines.any(|line| line.trim_start().starts_with("```")) {
 +                            continue;
 +                        }
 +
 +                        panic!("lint `{}` has an unterminated code block", lint_name)
 +                    }
 +
 +                    break;
 +                },
 +                Some(line) if line.trim_start() == "{{produces}}" => {
 +                    panic!(
 +                        "lint `{}` has marker {{{{produces}}}} with an ignored or missing code block",
 +                        lint_name
 +                    )
 +                },
 +                Some(line) => {
 +                    let line = line.trim();
 +                    // These are the two most common markers of the corrections section
 +                    if line.eq_ignore_ascii_case("Use instead:") || line.eq_ignore_ascii_case("Could be written as:") {
 +                        break 'outer;
 +                    }
 +                },
 +                None => break 'outer,
 +            }
 +        }
 +
 +        // Collect the example
 +        let mut example = Vec::new();
 +        loop {
 +            match lines.next() {
 +                Some(line) if line.trim_start() == "```" => break,
 +                Some(line) => example.push(line),
 +                None => panic!("lint `{}` has an unterminated code block", lint_name),
 +            }
 +        }
 +
 +        // Find the {{produces}} and attempt to generate the output
 +        loop {
 +            match lines.next() {
 +                Some(line) if line.is_empty() => {},
 +                Some(line) if line.trim() == "{{produces}}" => {
 +                    let output = get_lint_output(lint_name, &example, clippy_project_root);
 +                    line.replace_range(
 +                        ..,
 +                        &format!(
 +                            "<details>\
 +                            <summary>Produces</summary>\n\
 +                            \n\
 +                            ```text\n\
 +                            {}\n\
 +                            ```\n\
 +                        </details>",
 +                            output
 +                        ),
 +                    );
 +
 +                    break;
 +                },
 +                // No {{produces}}, we can move on to the next example
 +                Some(_) => break,
 +                None => break 'outer,
 +            }
 +        }
 +    }
 +
 +    *docs = cleanup_docs(&doc_lines);
 +}
 +
 +fn get_lint_output(lint_name: &str, example: &[&mut String], clippy_project_root: &Path) -> String {
 +    let dir = tempfile::tempdir().unwrap_or_else(|e| panic!("failed to create temp dir: {e}"));
 +    let file = dir.path().join("lint_example.rs");
 +
 +    let mut source = String::new();
 +    let unhidden = example
 +        .iter()
 +        .map(|line| line.trim_start().strip_prefix("# ").unwrap_or(line));
 +
 +    // Get any attributes
 +    let mut lines = unhidden.peekable();
 +    while let Some(line) = lines.peek() {
 +        if line.starts_with("#!") {
 +            source.push_str(line);
 +            source.push('\n');
 +            lines.next();
 +        } else {
 +            break;
 +        }
 +    }
 +
 +    let needs_main = !example.iter().any(|line| line.contains("fn main"));
 +    if needs_main {
 +        source.push_str("fn main() {\n");
 +    }
 +
 +    for line in lines {
 +        source.push_str(line);
 +        source.push('\n');
 +    }
 +
 +    if needs_main {
 +        source.push_str("}\n");
 +    }
 +
 +    if let Err(e) = fs::write(&file, &source) {
 +        panic!("failed to write to `{}`: {e}", file.as_path().to_string_lossy());
 +    }
 +
 +    let prefixed_name = format!("{}{lint_name}", CLIPPY_LINT_GROUP_PREFIX);
 +
 +    let mut cmd = Command::new("cargo");
 +
 +    cmd.current_dir(clippy_project_root)
 +        .env("CARGO_INCREMENTAL", "0")
 +        .env("CLIPPY_ARGS", "")
 +        .env("CLIPPY_DISABLE_DOCS_LINKS", "1")
 +        // We need to disable this to enable all lints
 +        .env("ENABLE_METADATA_COLLECTION", "0")
 +        .args(["run", "--bin", "clippy-driver"])
 +        .args(["--target-dir", "./clippy_lints/target"])
 +        .args(["--", "--error-format=json"])
 +        .args(["--edition", "2021"])
 +        .arg("-Cdebuginfo=0")
 +        .args(["-A", "clippy::all"])
 +        .args(["-W", &prefixed_name])
 +        .args(["-L", "./target/debug"])
 +        .args(["-Z", "no-codegen"]);
 +
 +    let output = cmd
 +        .arg(file.as_path())
 +        .output()
 +        .unwrap_or_else(|e| panic!("failed to run `{:?}`: {e}", cmd));
 +
 +    let tmp_file_path = file.to_string_lossy();
 +    let stderr = std::str::from_utf8(&output.stderr).unwrap();
 +    let msgs = stderr
 +        .lines()
 +        .filter(|line| line.starts_with('{'))
 +        .map(|line| serde_json::from_str(line).unwrap())
 +        .collect::<Vec<serde_json::Value>>();
 +
 +    let mut rendered = String::new();
 +    let iter = msgs
 +        .iter()
 +        .filter(|msg| matches!(&msg["code"]["code"], serde_json::Value::String(s) if s == &prefixed_name));
 +
 +    for message in iter {
 +        let rendered_part = message["rendered"].as_str().expect("rendered field should exist");
 +        rendered.push_str(rendered_part);
 +    }
 +
 +    if rendered.is_empty() {
 +        let rendered: Vec<&str> = msgs.iter().filter_map(|msg| msg["rendered"].as_str()).collect();
 +        let non_json: Vec<&str> = stderr.lines().filter(|line| !line.starts_with('{')).collect();
 +        panic!(
 +            "did not find lint `{}` in output of example, got:\n{}\n{}",
 +            lint_name,
 +            non_json.join("\n"),
 +            rendered.join("\n")
 +        );
 +    }
 +
 +    // The reader doesn't need to see `/tmp/.tmpfiy2Qd/lint_example.rs` :)
 +    rendered.trim_end().replace(&*tmp_file_path, "lint_example.rs")
 +}
 +
 +#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
 +struct SerializableSpan {
 +    path: String,
 +    line: usize,
 +}
 +
 +impl fmt::Display for SerializableSpan {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        write!(f, "{}:{}", self.path.rsplit('/').next().unwrap_or_default(), self.line)
 +    }
 +}
 +
 +impl SerializableSpan {
 +    fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Self {
 +        Self::from_span(cx, item.ident.span)
 +    }
 +
 +    fn from_span(cx: &LateContext<'_>, span: Span) -> Self {
 +        let loc: Loc = cx.sess().source_map().lookup_char_pos(span.lo());
 +
 +        Self {
 +            path: format!("{}", loc.file.name.prefer_remapped()),
 +            line: loc.line,
 +        }
 +    }
 +}
 +
 +#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
 +struct ApplicabilityInfo {
 +    /// Indicates if any of the lint emissions uses multiple spans. This is related to
 +    /// [rustfix#141](https://github.com/rust-lang/rustfix/issues/141) as such suggestions can
 +    /// currently not be applied automatically.
 +    is_multi_part_suggestion: bool,
 +    applicability: Option<usize>,
 +}
 +
 +impl Serialize for ApplicabilityInfo {
 +    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
 +    where
 +        S: Serializer,
 +    {
 +        let mut s = serializer.serialize_struct("ApplicabilityInfo", 2)?;
 +        s.serialize_field("is_multi_part_suggestion", &self.is_multi_part_suggestion)?;
 +        if let Some(index) = self.applicability {
 +            s.serialize_field(
 +                "applicability",
 +                &paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX],
 +            )?;
 +        } else {
 +            s.serialize_field("applicability", APPLICABILITY_UNRESOLVED_STR)?;
 +        }
 +        s.end()
 +    }
 +}
 +
 +// ==================================================================
 +// Configuration
 +// ==================================================================
 +#[derive(Debug, Clone, Default)]
 +pub struct ClippyConfiguration {
 +    name: String,
 +    config_type: &'static str,
 +    default: String,
 +    lints: Vec<String>,
 +    doc: String,
 +    #[allow(dead_code)]
 +    deprecation_reason: Option<&'static str>,
 +}
 +
 +impl ClippyConfiguration {
 +    pub fn new(
 +        name: &'static str,
 +        config_type: &'static str,
 +        default: String,
 +        doc_comment: &'static str,
 +        deprecation_reason: Option<&'static str>,
 +    ) -> Self {
 +        let (lints, doc) = parse_config_field_doc(doc_comment)
 +            .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string()));
 +
 +        Self {
 +            name: to_kebab(name),
 +            lints,
 +            doc,
 +            config_type,
 +            default,
 +            deprecation_reason,
 +        }
 +    }
 +}
 +
 +fn collect_configs() -> Vec<ClippyConfiguration> {
 +    crate::utils::conf::metadata::get_configuration_metadata()
 +}
 +
 +/// This parses the field documentation of the config struct.
 +///
 +/// ```rust, ignore
 +/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin")
 +/// ```
 +///
 +/// Would yield:
 +/// ```rust, ignore
 +/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin")
 +/// ```
 +fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> {
 +    const DOC_START: &str = " Lint: ";
 +    if_chain! {
 +        if doc_comment.starts_with(DOC_START);
 +        if let Some(split_pos) = doc_comment.find('.');
 +        then {
 +            let mut doc_comment = doc_comment.to_string();
 +            let mut documentation = doc_comment.split_off(split_pos);
 +
 +            // Extract lints
 +            doc_comment.make_ascii_lowercase();
 +            let lints: Vec<String> = doc_comment.split_off(DOC_START.len()).split(", ").map(str::to_string).collect();
 +
 +            // Format documentation correctly
 +            // split off leading `.` from lint name list and indent for correct formatting
 +            documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n    ");
 +
 +            Some((lints, documentation))
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +/// Transforms a given `snake_case_string` to a tasty `kebab-case-string`
 +fn to_kebab(config_name: &str) -> String {
 +    config_name.replace('_', "-")
 +}
 +
 +impl fmt::Display for ClippyConfiguration {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
 +        write!(
 +            f,
 +            CONFIGURATION_VALUE_TEMPLATE!(),
 +            name = self.name,
 +            ty = self.config_type,
 +            doc = self.doc,
 +            default = self.default
 +        )
 +    }
 +}
 +
 +// ==================================================================
 +// Lint pass
 +// ==================================================================
 +impl<'hir> LateLintPass<'hir> for MetadataCollector {
 +    /// Collecting lint declarations like:
 +    /// ```rust, ignore
 +    /// declare_clippy_lint! {
 +    ///     /// ### What it does
 +    ///     /// Something IDK.
 +    ///     pub SOME_LINT,
 +    ///     internal,
 +    ///     "Who am I?"
 +    /// }
 +    /// ```
 +    fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) {
 +        if let ItemKind::Static(ty, Mutability::Not, _) = item.kind {
 +            // Normal lint
 +            if_chain! {
 +                // item validation
 +                if is_lint_ref_type(cx, ty);
 +                // disallow check
 +                let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
 +                if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
 +                // metadata extraction
 +                if let Some((group, level)) = get_lint_group_and_level_or_lint(cx, &lint_name, item);
 +                if let Some(mut raw_docs) = extract_attr_docs_or_lint(cx, item);
 +                then {
 +                    if let Some(configuration_section) = self.get_lint_configs(&lint_name) {
 +                        raw_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,
 +                        raw_docs,
 +                    ));
 +                }
 +            }
 +
 +            if_chain! {
 +                if is_deprecated_lint(cx, ty);
 +                // disallow check
 +                let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
 +                if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
 +                // Metadata the little we can get from a deprecated lint
 +                if let Some(raw_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,
 +                        raw_docs,
 +                    ));
 +                }
 +            }
 +        }
 +    }
 +
 +    /// Collecting constant applicability from the actual lint emissions
 +    ///
 +    /// Example:
 +    /// ```rust, ignore
 +    /// span_lint_and_sugg(
 +    ///     cx,
 +    ///     SOME_LINT,
 +    ///     item.span,
 +    ///     "Le lint message",
 +    ///     "Here comes help:",
 +    ///     "#![allow(clippy::all)]",
 +    ///     Applicability::MachineApplicable, // <-- Extracts this constant value
 +    /// );
 +    /// ```
 +    fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) {
 +        if let Some(args) = match_lint_emission(cx, expr) {
 +            let emission_info = extract_emission_info(cx, args);
 +            if emission_info.is_empty() {
 +                // See:
 +                // - src/misc.rs:734:9
 +                // - src/methods/mod.rs:3545:13
 +                // - src/methods/mod.rs:3496:13
 +                // We are basically unable to resolve the lint name itself.
 +                return;
 +            }
 +
 +            for (lint_name, applicability, is_multi_part) in emission_info {
 +                let app_info = self.applicability_info.entry(lint_name).or_default();
 +                app_info.applicability = applicability;
 +                app_info.is_multi_part_suggestion = is_multi_part;
 +            }
 +        }
 +    }
 +}
 +
 +// ==================================================================
 +// Lint definition extraction
 +// ==================================================================
 +fn sym_to_string(sym: Symbol) -> String {
 +    sym.as_str().to_string()
 +}
 +
 +fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
 +    extract_attr_docs(cx, item).or_else(|| {
 +        lint_collection_error_item(cx, item, "could not collect the lint documentation");
 +        None
 +    })
 +}
 +
 +/// This function collects all documentation that has been added to an item using
 +/// `#[doc = r""]` attributes. Several attributes are aggravated using line breaks
 +///
 +/// ```ignore
 +/// #[doc = r"Hello world!"]
 +/// #[doc = r"=^.^="]
 +/// struct SomeItem {}
 +/// ```
 +///
 +/// Would result in `Hello world!\n=^.^=\n`
 +fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
 +    let attrs = cx.tcx.hir().attrs(item.hir_id());
 +    let mut lines = attrs.iter().filter_map(ast::Attribute::doc_str);
 +
 +    if let Some(line) = lines.next() {
 +        let raw_docs = lines.fold(String::from(line.as_str()) + "\n", |s, line| s + line.as_str() + "\n");
 +        return Some(raw_docs);
 +    }
 +
 +    None
 +}
 +
 +/// This function may modify the doc comment to ensure that the string can be displayed using a
 +/// markdown viewer in Clippy's lint list. The following modifications could be applied:
 +/// * Removal of leading space after a new line. (Important to display tables)
 +/// * Ensures that code blocks only contain language information
 +fn cleanup_docs(docs_collection: &Vec<String>) -> String {
 +    let mut in_code_block = false;
 +    let mut is_code_block_rust = false;
 +
 +    let mut docs = String::new();
 +    for line in docs_collection {
 +        // Rustdoc hides code lines starting with `# ` and this removes them from Clippy's lint list :)
 +        if is_code_block_rust && line.trim_start().starts_with("# ") {
 +            continue;
 +        }
 +
 +        // The line should be represented in the lint list, even if it's just an empty line
 +        docs.push('\n');
 +        if let Some(info) = line.trim_start().strip_prefix("```") {
 +            in_code_block = !in_code_block;
 +            is_code_block_rust = false;
 +            if in_code_block {
 +                let lang = info
 +                    .trim()
 +                    .split(',')
 +                    // remove rustdoc directives
 +                    .find(|&s| !matches!(s, "" | "ignore" | "no_run" | "should_panic"))
 +                    // if no language is present, fill in "rust"
 +                    .unwrap_or("rust");
 +                docs.push_str("```");
 +                docs.push_str(lang);
 +
 +                is_code_block_rust = lang == "rust";
 +                continue;
 +            }
 +        }
 +        // This removes the leading space that the macro translation introduces
 +        if let Some(stripped_doc) = line.strip_prefix(' ') {
 +            docs.push_str(stripped_doc);
 +        } else if !line.is_empty() {
 +            docs.push_str(line);
 +        }
 +    }
 +
 +    docs
 +}
 +
 +fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String {
 +    extract_clippy_version_value(cx, item).map_or_else(
 +        || VERSION_DEFAULT_STR.to_string(),
 +        |version| version.as_str().to_string(),
 +    )
 +}
 +
 +fn get_lint_group_and_level_or_lint(
 +    cx: &LateContext<'_>,
 +    lint_name: &str,
 +    item: &Item<'_>,
 +) -> Option<(String, &'static str)> {
 +    let result = cx.lint_store.check_lint_name(
 +        lint_name,
 +        Some(sym::clippy),
 +        &std::iter::once(Ident::with_dummy_span(sym::clippy)).collect(),
 +    );
 +    if let CheckLintNameResult::Tool(Ok(lint_lst)) = result {
 +        if let Some(group) = get_lint_group(cx, lint_lst[0]) {
 +            if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) {
 +                return None;
 +            }
 +
 +            if let Some(level) = get_lint_level_from_group(&group) {
 +                Some((group, level))
 +            } else {
 +                lint_collection_error_item(
 +                    cx,
 +                    item,
 +                    &format!("Unable to determine lint level for found group `{}`", group),
 +                );
 +                None
 +            }
 +        } else {
 +            lint_collection_error_item(cx, item, "Unable to determine lint group");
 +            None
 +        }
 +    } else {
 +        lint_collection_error_item(cx, item, "Unable to find lint in lint_store");
 +        None
 +    }
 +}
 +
 +fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option<String> {
 +    for (group_name, lints, _) in cx.lint_store.get_lint_groups() {
 +        if IGNORED_LINT_GROUPS.contains(&group_name) {
 +            continue;
 +        }
 +
 +        if lints.iter().any(|group_lint| *group_lint == lint_id) {
 +            let group = group_name.strip_prefix(CLIPPY_LINT_GROUP_PREFIX).unwrap_or(group_name);
 +            return Some((*group).to_string());
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn get_lint_level_from_group(lint_group: &str) -> Option<&'static str> {
 +    DEFAULT_LINT_LEVELS
 +        .iter()
 +        .find_map(|(group_name, group_level)| (*group_name == lint_group).then_some(*group_level))
 +}
 +
 +pub(super) fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
 +    if let hir::TyKind::Path(ref path) = ty.kind {
 +        if let hir::def::Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, ty.hir_id) {
 +            return match_def_path(cx, def_id, &DEPRECATED_LINT_TYPE);
 +        }
 +    }
 +
 +    false
 +}
 +
 +fn collect_renames(lints: &mut Vec<LintMetadata>) {
 +    for lint in lints {
 +        let mut collected = String::new();
 +        let mut names = vec![lint.id.clone()];
 +
 +        loop {
 +            if let Some(lint_name) = names.pop() {
 +                for (k, v) in RENAMED_LINTS {
 +                    if_chain! {
 +                        if let Some(name) = v.strip_prefix(CLIPPY_LINT_GROUP_PREFIX);
 +                        if name == lint_name;
 +                        if let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX);
 +                        then {
 +                            write!(collected, RENAME_VALUE_TEMPLATE!(), name = past_name).unwrap();
 +                            names.push(past_name.to_string());
 +                        }
 +                    }
 +                }
 +
 +                continue;
 +            }
 +
 +            break;
 +        }
 +
 +        if !collected.is_empty() {
 +            write!(&mut lint.docs, RENAMES_SECTION_TEMPLATE!(), names = collected).unwrap();
 +        }
 +    }
 +}
 +
 +// ==================================================================
 +// Lint emission
 +// ==================================================================
 +fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &str) {
 +    span_lint(
 +        cx,
 +        INTERNAL_METADATA_COLLECTOR,
 +        item.ident.span,
 +        &format!("metadata collection error for `{}`: {}", item.ident.name, message),
 +    );
 +}
 +
 +// ==================================================================
 +// Applicability
 +// ==================================================================
 +/// This function checks if a given expression is equal to a simple lint emission function call.
 +/// It will return the function arguments if the emission matched any function.
 +fn match_lint_emission<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) -> Option<&'hir [hir::Expr<'hir>]> {
 +    LINT_EMISSION_FUNCTIONS
 +        .iter()
 +        .find_map(|emission_fn| match_function_call(cx, expr, emission_fn))
 +}
 +
 +fn take_higher_applicability(a: Option<usize>, b: Option<usize>) -> Option<usize> {
 +    a.map_or(b, |a| a.max(b.unwrap_or_default()).into())
 +}
 +
 +fn extract_emission_info<'hir>(
 +    cx: &LateContext<'hir>,
 +    args: &'hir [hir::Expr<'hir>],
 +) -> Vec<(String, Option<usize>, bool)> {
 +    let mut lints = Vec::new();
 +    let mut applicability = None;
 +    let mut multi_part = false;
 +
 +    for arg in args {
 +        let (arg_ty, _) = walk_ptrs_ty_depth(cx.typeck_results().expr_ty(arg));
 +
 +        if match_type(cx, arg_ty, &paths::LINT) {
 +            // If we found the lint arg, extract the lint name
 +            let mut resolved_lints = resolve_lints(cx, arg);
 +            lints.append(&mut resolved_lints);
 +        } else if match_type(cx, arg_ty, &paths::APPLICABILITY) {
 +            applicability = resolve_applicability(cx, arg);
 +        } else if arg_ty.is_closure() {
 +            multi_part |= check_is_multi_part(cx, arg);
 +            applicability = applicability.or_else(|| resolve_applicability(cx, arg));
 +        }
 +    }
 +
 +    lints
 +        .into_iter()
 +        .map(|lint_name| (lint_name, applicability, multi_part))
 +        .collect()
 +}
 +
 +/// Resolves the possible lints that this expression could reference
 +fn resolve_lints<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec<String> {
 +    let mut resolver = LintResolver::new(cx);
 +    resolver.visit_expr(expr);
 +    resolver.lints
 +}
 +
 +/// This function tries to resolve the linked applicability to the given expression.
 +fn resolve_applicability<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<usize> {
 +    let mut resolver = ApplicabilityResolver::new(cx);
 +    resolver.visit_expr(expr);
 +    resolver.complete()
 +}
 +
 +fn check_is_multi_part<'hir>(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool {
 +    if let ExprKind::Closure(&Closure { body, .. }) = closure_expr.kind {
 +        let mut scanner = IsMultiSpanScanner::new(cx);
 +        intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body));
 +        return scanner.is_multi_part();
 +    } else if let Some(local) = get_parent_local(cx, closure_expr) {
 +        if let Some(local_init) = local.init {
 +            return check_is_multi_part(cx, local_init);
 +        }
 +    }
 +
 +    false
 +}
 +
 +struct LintResolver<'a, 'hir> {
 +    cx: &'a LateContext<'hir>,
 +    lints: Vec<String>,
 +}
 +
 +impl<'a, 'hir> LintResolver<'a, 'hir> {
 +    fn new(cx: &'a LateContext<'hir>) -> Self {
 +        Self {
 +            cx,
 +            lints: Vec::<String>::default(),
 +        }
 +    }
 +}
 +
 +impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
 +        if_chain! {
 +            if let ExprKind::Path(qpath) = &expr.kind;
 +            if let QPath::Resolved(_, path) = qpath;
 +
 +            let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr));
 +            if match_type(self.cx, expr_ty, &paths::LINT);
 +            then {
 +                if let hir::def::Res::Def(DefKind::Static(..), _) = path.res {
 +                    let lint_name = last_path_segment(qpath).ident.name;
 +                    self.lints.push(sym_to_string(lint_name).to_ascii_lowercase());
 +                } else if let Some(local) = get_parent_local(self.cx, expr) {
 +                    if let Some(local_init) = local.init {
 +                        intravisit::walk_expr(self, local_init);
 +                    }
 +                }
 +            }
 +        }
 +
 +        intravisit::walk_expr(self, expr);
 +    }
 +}
 +
 +/// This visitor finds the highest applicability value in the visited expressions
 +struct ApplicabilityResolver<'a, 'hir> {
 +    cx: &'a LateContext<'hir>,
 +    /// This is the index of highest `Applicability` for `paths::APPLICABILITY_VALUES`
 +    applicability_index: Option<usize>,
 +}
 +
 +impl<'a, 'hir> ApplicabilityResolver<'a, 'hir> {
 +    fn new(cx: &'a LateContext<'hir>) -> Self {
 +        Self {
 +            cx,
 +            applicability_index: None,
 +        }
 +    }
 +
 +    fn add_new_index(&mut self, new_index: usize) {
 +        self.applicability_index = take_higher_applicability(self.applicability_index, Some(new_index));
 +    }
 +
 +    fn complete(self) -> Option<usize> {
 +        self.applicability_index
 +    }
 +}
 +
 +impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +
 +    fn visit_path(&mut self, path: &'hir hir::Path<'hir>, _id: hir::HirId) {
 +        for (index, enum_value) in paths::APPLICABILITY_VALUES.iter().enumerate() {
 +            if match_path(path, enum_value) {
 +                self.add_new_index(index);
 +                return;
 +            }
 +        }
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
 +        let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr));
 +
 +        if_chain! {
 +            if match_type(self.cx, expr_ty, &paths::APPLICABILITY);
 +            if let Some(local) = get_parent_local(self.cx, expr);
 +            if let Some(local_init) = local.init;
 +            then {
 +                intravisit::walk_expr(self, local_init);
 +            }
 +        };
 +
 +        intravisit::walk_expr(self, expr);
 +    }
 +}
 +
 +/// This returns the parent local node if the expression is a reference one
 +fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Local<'hir>> {
 +    if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind {
 +        if let hir::def::Res::Local(local_hir) = path.res {
 +            return get_parent_local_hir_id(cx, local_hir);
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> {
 +    let map = cx.tcx.hir();
 +
 +    match map.find(map.get_parent_node(hir_id)) {
 +        Some(hir::Node::Local(local)) => Some(local),
 +        Some(hir::Node::Pat(pattern)) => get_parent_local_hir_id(cx, pattern.hir_id),
 +        _ => None,
 +    }
 +}
 +
 +/// This visitor finds the highest applicability value in the visited expressions
 +struct IsMultiSpanScanner<'a, 'hir> {
 +    cx: &'a LateContext<'hir>,
 +    suggestion_count: usize,
 +}
 +
 +impl<'a, 'hir> IsMultiSpanScanner<'a, 'hir> {
 +    fn new(cx: &'a LateContext<'hir>) -> Self {
 +        Self {
 +            cx,
 +            suggestion_count: 0,
 +        }
 +    }
 +
 +    /// Add a new single expression suggestion to the counter
 +    fn add_single_span_suggestion(&mut self) {
 +        self.suggestion_count += 1;
 +    }
 +
 +    /// Signals that a suggestion with possible multiple spans was found
 +    fn add_multi_part_suggestion(&mut self) {
 +        self.suggestion_count += 2;
 +    }
 +
 +    /// Checks if the suggestions include multiple spans
 +    fn is_multi_part(&self) -> bool {
 +        self.suggestion_count > 1
 +    }
 +}
 +
 +impl<'a, 'hir> intravisit::Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
 +        // Early return if the lint is already multi span
 +        if self.is_multi_part() {
 +            return;
 +        }
 +
 +        match &expr.kind {
 +            ExprKind::Call(fn_expr, _args) => {
 +                let found_function = SUGGESTION_FUNCTIONS
 +                    .iter()
 +                    .any(|func_path| match_function_call(self.cx, fn_expr, func_path).is_some());
 +                if found_function {
 +                    // These functions are all multi part suggestions
 +                    self.add_single_span_suggestion();
 +                }
 +            },
++            ExprKind::MethodCall(path, recv, _, _arg_span) => {
++                let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(recv));
 +                if match_type(self.cx, self_ty, &paths::DIAGNOSTIC_BUILDER) {
 +                    let called_method = path.ident.name.as_str().to_string();
 +                    for (method_name, is_multi_part) in &SUGGESTION_DIAGNOSTIC_BUILDER_METHODS {
 +                        if *method_name == called_method {
 +                            if *is_multi_part {
 +                                self.add_multi_part_suggestion();
 +                            } else {
 +                                self.add_single_span_suggestion();
 +                            }
 +                            break;
 +                        }
 +                    }
 +                }
 +            },
 +            _ => {},
 +        }
 +
 +        intravisit::walk_expr(self, expr);
 +    }
 +}
index 8425837fd73366277219447d83e196b11c71ffa3,0000000000000000000000000000000000000000..bd5be0c9d7eda70d5db3c9e973c7471c034d47ee
mode 100644,000000..100644
--- /dev/null
@@@ -1,225 -1,0 +1,225 @@@
-                     while let Some(parent) = get_parent_expr(cx, parent) {
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::higher::{get_vec_init_kind, VecInitKind};
 +use clippy_utils::source::snippet;
 +use clippy_utils::visitors::for_each_local_use_after_expr;
 +use clippy_utils::{get_parent_expr, path_to_local_id};
 +use core::ops::ControlFlow;
 +use rustc_errors::Applicability;
 +use rustc_hir::def::Res;
 +use rustc_hir::{
 +    BindingAnnotation, Block, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, Stmt, StmtKind, UnOp,
 +};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{Span, Symbol};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `push` immediately after creating a new `Vec`.
 +    ///
 +    /// If the `Vec` is created using `with_capacity` this will only lint if the capacity is a
 +    /// constant and the number of pushes is greater than or equal to the initial capacity.
 +    ///
 +    /// If the `Vec` is extended after the initial sequence of pushes and it was default initialized
 +    /// then this will only lint after there were at least four pushes. This number may change in
 +    /// the future.
 +    ///
 +    /// ### Why is this bad?
 +    /// The `vec![]` macro is both more performant and easier to read than
 +    /// multiple `push` calls.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut v = Vec::new();
 +    /// v.push(0);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let v = vec![0];
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub VEC_INIT_THEN_PUSH,
 +    perf,
 +    "`push` immediately after `Vec` creation"
 +}
 +
 +impl_lint_pass!(VecInitThenPush => [VEC_INIT_THEN_PUSH]);
 +
 +#[derive(Default)]
 +pub struct VecInitThenPush {
 +    searcher: Option<VecPushSearcher>,
 +}
 +
 +struct VecPushSearcher {
 +    local_id: HirId,
 +    init: VecInitKind,
 +    lhs_is_let: bool,
 +    let_ty_span: Option<Span>,
 +    name: Symbol,
 +    err_span: Span,
 +    found: u128,
 +    last_push_expr: HirId,
 +}
 +impl VecPushSearcher {
 +    fn display_err(&self, cx: &LateContext<'_>) {
 +        let required_pushes_before_extension = match self.init {
 +            _ if self.found == 0 => return,
 +            VecInitKind::WithConstCapacity(x) if x > self.found => return,
 +            VecInitKind::WithConstCapacity(x) => x,
 +            VecInitKind::WithExprCapacity(_) => return,
 +            _ => 3,
 +        };
 +
 +        let mut needs_mut = false;
 +        let res = for_each_local_use_after_expr(cx, self.local_id, self.last_push_expr, |e| {
 +            let Some(parent) = get_parent_expr(cx, e) else {
 +                return ControlFlow::Continue(())
 +            };
 +            let adjusted_ty = cx.typeck_results().expr_ty_adjusted(e);
 +            let adjusted_mut = adjusted_ty.ref_mutability().unwrap_or(Mutability::Not);
 +            needs_mut |= adjusted_mut == Mutability::Mut;
 +            match parent.kind {
 +                ExprKind::AddrOf(_, Mutability::Mut, _) => {
 +                    needs_mut = true;
 +                    return ControlFlow::Break(true);
 +                },
 +                ExprKind::Unary(UnOp::Deref, _) | ExprKind::Index(..) if !needs_mut => {
 +                    let mut last_place = parent;
++                    while let Some(parent) = get_parent_expr(cx, last_place) {
 +                        if matches!(parent.kind, ExprKind::Unary(UnOp::Deref, _) | ExprKind::Field(..))
 +                            || matches!(parent.kind, ExprKind::Index(e, _) if e.hir_id == last_place.hir_id)
 +                        {
 +                            last_place = parent;
 +                        } else {
 +                            break;
 +                        }
 +                    }
 +                    needs_mut |= cx.typeck_results().expr_ty_adjusted(last_place).ref_mutability()
 +                        == Some(Mutability::Mut)
 +                        || get_parent_expr(cx, last_place)
 +                            .map_or(false, |e| matches!(e.kind, ExprKind::AddrOf(_, Mutability::Mut, _)));
 +                },
 +                ExprKind::MethodCall(_, recv, ..)
 +                    if recv.hir_id == e.hir_id
 +                        && adjusted_mut == Mutability::Mut
 +                        && !adjusted_ty.peel_refs().is_slice() =>
 +                {
 +                    // No need to set `needs_mut` to true. The receiver will be either explicitly borrowed, or it will
 +                    // be implicitly borrowed via an adjustment. Both of these cases are already handled by this point.
 +                    return ControlFlow::Break(true);
 +                },
 +                ExprKind::Assign(lhs, ..) if e.hir_id == lhs.hir_id => {
 +                    needs_mut = true;
 +                    return ControlFlow::Break(false);
 +                },
 +                _ => (),
 +            }
 +            ControlFlow::Continue(())
 +        });
 +
 +        // Avoid allocating small `Vec`s when they'll be extended right after.
 +        if res == ControlFlow::Break(true) && self.found <= required_pushes_before_extension {
 +            return;
 +        }
 +
 +        let mut s = if self.lhs_is_let {
 +            String::from("let ")
 +        } else {
 +            String::new()
 +        };
 +        if needs_mut {
 +            s.push_str("mut ");
 +        }
 +        s.push_str(self.name.as_str());
 +        if let Some(span) = self.let_ty_span {
 +            s.push_str(": ");
 +            s.push_str(&snippet(cx, span, "_"));
 +        }
 +        s.push_str(" = vec![..];");
 +
 +        span_lint_and_sugg(
 +            cx,
 +            VEC_INIT_THEN_PUSH,
 +            self.err_span,
 +            "calls to `push` immediately after creation",
 +            "consider using the `vec![]` macro",
 +            s,
 +            Applicability::HasPlaceholders,
 +        );
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for VecInitThenPush {
 +    fn check_block(&mut self, _: &LateContext<'tcx>, _: &'tcx Block<'tcx>) {
 +        self.searcher = None;
 +    }
 +
 +    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
 +        if let Some(init_expr) = local.init
 +            && let PatKind::Binding(BindingAnnotation::MUT, id, name, None) = local.pat.kind
 +            && !in_external_macro(cx.sess(), local.span)
 +            && let Some(init) = get_vec_init_kind(cx, init_expr)
 +            && !matches!(init, VecInitKind::WithExprCapacity(_))
 +        {
 +            self.searcher = Some(VecPushSearcher {
 +                local_id: id,
 +                init,
 +                lhs_is_let: true,
 +                name: name.name,
 +                let_ty_span: local.ty.map(|ty| ty.span),
 +                err_span: local.span,
 +                found: 0,
 +                last_push_expr: init_expr.hir_id,
 +            });
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if self.searcher.is_none()
 +            && let ExprKind::Assign(left, right, _) = expr.kind
 +            && let ExprKind::Path(QPath::Resolved(None, path)) = left.kind
 +            && let [name] = &path.segments
 +            && let Res::Local(id) = path.res
 +            && !in_external_macro(cx.sess(), expr.span)
 +            && let Some(init) = get_vec_init_kind(cx, right)
 +            && !matches!(init, VecInitKind::WithExprCapacity(_))
 +        {
 +            self.searcher = Some(VecPushSearcher {
 +                local_id: id,
 +                init,
 +                lhs_is_let: false,
 +                let_ty_span: None,
 +                name: name.ident.name,
 +                err_span: expr.span,
 +                found: 0,
 +                last_push_expr: expr.hir_id,
 +            });
 +        }
 +    }
 +
 +    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
 +        if let Some(searcher) = self.searcher.take() {
 +            if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind
 +                && let ExprKind::MethodCall(name, self_arg, [_], _) = expr.kind
 +                && path_to_local_id(self_arg, searcher.local_id)
 +                && name.ident.as_str() == "push"
 +            {
 +                self.searcher = Some(VecPushSearcher {
 +                    found: searcher.found + 1,
 +                    err_span: searcher.err_span.to(stmt.span),
 +                    last_push_expr: expr.hir_id,
 +                    .. searcher
 +                });
 +            } else {
 +                searcher.display_err(cx);
 +            }
 +        }
 +    }
 +
 +    fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Block<'tcx>) {
 +        if let Some(searcher) = self.searcher.take() {
 +            searcher.display_err(cx);
 +        }
 +    }
 +}
index 347165d9704a100717d0e61bffe3cee3ab571269,0000000000000000000000000000000000000000..640a09a7a912347396816d4fec4c0b41c5f09c55
mode 100644,000000..100644
--- /dev/null
@@@ -1,826 -1,0 +1,830 @@@
-         let c = c.unwrap();
 +use std::borrow::Cow;
 +use std::iter;
 +use std::ops::{Deref, Range};
 +
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
 +use rustc_ast::ast::{Expr, ExprKind, Impl, Item, ItemKind, MacCall, Path, StrLit, StrStyle};
 +use rustc_ast::ptr::P;
 +use rustc_ast::token::{self, LitKind};
 +use rustc_ast::tokenstream::TokenStream;
 +use rustc_errors::{Applicability, DiagnosticBuilder};
 +use rustc_lexer::unescape::{self, EscapeError};
 +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 +use rustc_parse::parser;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::{kw, Symbol};
 +use rustc_span::{sym, BytePos, InnerSpan, Span, DUMMY_SP};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns when you use `println!("")` to
 +    /// print a newline.
 +    ///
 +    /// ### Why is this bad?
 +    /// You should use `println!()`, which is simpler.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// println!("");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// println!();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub PRINTLN_EMPTY_STRING,
 +    style,
 +    "using `println!(\"\")` with an empty string"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns when you use `print!()` with a format
 +    /// string that ends in a newline.
 +    ///
 +    /// ### Why is this bad?
 +    /// You should use `println!()` instead, which appends the
 +    /// newline.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let name = "World";
 +    /// print!("Hello {}!\n", name);
 +    /// ```
 +    /// use println!() instead
 +    /// ```rust
 +    /// # 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for printing on *stdout*. The purpose of this lint
 +    /// is to catch debugging remnants.
 +    ///
 +    /// ### Why is this bad?
 +    /// People often print on *stdout* while debugging an
 +    /// application and might forget to remove those prints afterward.
 +    ///
 +    /// ### Known problems
 +    /// * 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for printing on *stderr*. The purpose of this lint
 +    /// is to catch debugging remnants.
 +    ///
 +    /// ### Why is this bad?
 +    /// People often print on *stderr* while debugging an
 +    /// application and might forget to remove those prints afterward.
 +    ///
 +    /// ### Known problems
 +    /// * 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `Debug` formatting. The purpose of this
 +    /// lint is to catch debugging remnants.
 +    ///
 +    /// ### Why is this bad?
 +    /// The purpose of the `Debug` trait is to facilitate
 +    /// debugging Rust code. It should not be used in user-facing output.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let foo = "bar";
 +    /// println!("{:?}", foo);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub USE_DEBUG,
 +    restriction,
 +    "use of `Debug`-based formatting"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns about the use of literals as `print!`/`println!` args.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using literals as `println!` args is inefficient
 +    /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
 +    /// (i.e., just put the literal in the format string)
 +    ///
 +    /// ### Known problems
 +    /// Will also warn with macro calls as arguments that expand to literals
 +    /// -- e.g., `println!("{}", env!("FOO"))`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// println!("{}", "foo");
 +    /// ```
 +    /// use the literal without formatting:
 +    /// ```rust
 +    /// println!("foo");
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub PRINT_LITERAL,
 +    style,
 +    "printing a literal with a format string"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns when you use `writeln!(buf, "")` to
 +    /// print a newline.
 +    ///
 +    /// ### Why is this bad?
 +    /// You should use `writeln!(buf)`, which is simpler.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// writeln!(buf, "");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// writeln!(buf);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub WRITELN_EMPTY_STRING,
 +    style,
 +    "using `writeln!(buf, \"\")` with an empty string"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns when you use `write!()` with a format
 +    /// string that
 +    /// ends in a newline.
 +    ///
 +    /// ### Why is this bad?
 +    /// You should use `writeln!()` instead, which appends the
 +    /// newline.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// # let name = "World";
 +    /// write!(buf, "Hello {}!\n", name);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// # let name = "World";
 +    /// 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"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns about the use of literals as `write!`/`writeln!` args.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using literals as `writeln!` args is inefficient
 +    /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
 +    /// (i.e., just put the literal in the format string)
 +    ///
 +    /// ### Known problems
 +    /// Will also warn with macro calls as arguments that expand to literals
 +    /// -- e.g., `writeln!(buf, "{}", env!("FOO"))`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// writeln!(buf, "{}", "foo");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// writeln!(buf, "foo");
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub WRITE_LITERAL,
 +    style,
 +    "writing a literal with a format string"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns when a named parameter in a format string is used as a positional one.
 +    ///
 +    /// ### Why is this bad?
 +    /// It may be confused for an assignment and obfuscates which parameter is being used.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// println!("{}", x = 10);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// println!("{x}", x = 10);
 +    /// ```
 +    #[clippy::version = "1.63.0"]
 +    pub POSITIONAL_NAMED_FORMAT_PARAMETERS,
 +    suspicious,
 +    "named parameter in a format string is used positionally"
 +}
 +
 +#[derive(Default)]
 +pub struct Write {
 +    in_debug_impl: bool,
 +}
 +
 +impl_lint_pass!(Write => [
 +    PRINT_WITH_NEWLINE,
 +    PRINTLN_EMPTY_STRING,
 +    PRINT_STDOUT,
 +    PRINT_STDERR,
 +    USE_DEBUG,
 +    PRINT_LITERAL,
 +    WRITE_WITH_NEWLINE,
 +    WRITELN_EMPTY_STRING,
 +    WRITE_LITERAL,
 +    POSITIONAL_NAMED_FORMAT_PARAMETERS,
 +]);
 +
 +impl EarlyLintPass for Write {
 +    fn check_item(&mut self, _: &EarlyContext<'_>, item: &Item) {
 +        if let ItemKind::Impl(box Impl {
 +            of_trait: Some(trait_ref),
 +            ..
 +        }) = &item.kind
 +        {
 +            let trait_name = trait_ref
 +                .path
 +                .segments
 +                .iter()
 +                .last()
 +                .expect("path has at least one segment")
 +                .ident
 +                .name;
 +            if trait_name == sym::Debug {
 +                self.in_debug_impl = true;
 +            }
 +        }
 +    }
 +
 +    fn check_item_post(&mut self, _: &EarlyContext<'_>, _: &Item) {
 +        self.in_debug_impl = false;
 +    }
 +
 +    fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) {
 +        fn is_build_script(cx: &EarlyContext<'_>) -> bool {
 +            // Cargo sets the crate name for build scripts to `build_script_build`
 +            cx.sess()
 +                .opts
 +                .crate_name
 +                .as_ref()
 +                .map_or(false, |crate_name| crate_name == "build_script_build")
 +        }
 +
 +        if mac.path == sym!(print) {
 +            if !is_build_script(cx) {
 +                span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`");
 +            }
 +            self.lint_print_with_newline(cx, mac);
 +        } else if mac.path == sym!(println) {
 +            if !is_build_script(cx) {
 +                span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`");
 +            }
 +            self.lint_println_empty_string(cx, mac);
 +        } else if mac.path == sym!(eprint) {
 +            span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprint!`");
 +            self.lint_print_with_newline(cx, mac);
 +        } else if mac.path == sym!(eprintln) {
 +            span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`");
 +            self.lint_println_empty_string(cx, mac);
 +        } else if mac.path == sym!(write) {
 +            if let (Some(fmt_str), dest) = self.check_tts(cx, mac.args.inner_tokens(), true) {
 +                if check_newlines(&fmt_str) {
 +                    let (nl_span, only_nl) = newline_span(&fmt_str);
 +                    let nl_span = match (dest, only_nl) {
 +                        // Special case of `write!(buf, "\n")`: Mark everything from the end of
 +                        // `buf` for removal so no trailing comma [`writeln!(buf, )`] remains.
 +                        (Some(dest_expr), true) => nl_span.with_lo(dest_expr.span.hi()),
 +                        _ => nl_span,
 +                    };
 +                    span_lint_and_then(
 +                        cx,
 +                        WRITE_WITH_NEWLINE,
 +                        mac.span(),
 +                        "using `write!()` with a format string that ends in a single newline",
 +                        |err| {
 +                            err.multipart_suggestion(
 +                                "use `writeln!()` instead",
 +                                vec![(mac.path.span, String::from("writeln")), (nl_span, String::new())],
 +                                Applicability::MachineApplicable,
 +                            );
 +                        },
 +                    );
 +                }
 +            }
 +        } else if mac.path == sym!(writeln) {
 +            if let (Some(fmt_str), expr) = self.check_tts(cx, mac.args.inner_tokens(), true) {
 +                if fmt_str.symbol == kw::Empty {
 +                    let mut applicability = Applicability::MachineApplicable;
 +                    let suggestion = if let Some(e) = expr {
 +                        snippet_with_applicability(cx, e.span, "v", &mut applicability)
 +                    } else {
 +                        applicability = Applicability::HasPlaceholders;
 +                        Cow::Borrowed("v")
 +                    };
 +
 +                    span_lint_and_sugg(
 +                        cx,
 +                        WRITELN_EMPTY_STRING,
 +                        mac.span(),
 +                        format!("using `writeln!({}, \"\")`", suggestion).as_str(),
 +                        "replace it with",
 +                        format!("writeln!({})", suggestion),
 +                        applicability,
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/// Given a format string that ends in a newline and its span, calculates the span of the
 +/// newline, or the format string itself if the format string consists solely of a newline.
 +/// Return this and a boolean indicating whether it only consisted of a newline.
 +fn newline_span(fmtstr: &StrLit) -> (Span, bool) {
 +    let sp = fmtstr.span;
 +    let contents = fmtstr.symbol.as_str();
 +
 +    if contents == r"\n" {
 +        return (sp, true);
 +    }
 +
 +    let newline_sp_hi = sp.hi()
 +        - match fmtstr.style {
 +            StrStyle::Cooked => BytePos(1),
 +            StrStyle::Raw(hashes) => BytePos((1 + hashes).into()),
 +        };
 +
 +    let newline_sp_len = if contents.ends_with('\n') {
 +        BytePos(1)
 +    } else if contents.ends_with(r"\n") {
 +        BytePos(2)
 +    } else {
 +        panic!("expected format string to contain a newline");
 +    };
 +
 +    (sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi), false)
 +}
 +
 +/// Stores a list of replacement spans for each argument, but only if all the replacements used an
 +/// empty format string.
 +#[derive(Default)]
 +struct SimpleFormatArgs {
 +    unnamed: Vec<Vec<Span>>,
 +    complex_unnamed: Vec<Vec<Span>>,
 +    named: Vec<(Symbol, Vec<Span>)>,
 +}
 +impl SimpleFormatArgs {
 +    fn get_unnamed(&self) -> impl Iterator<Item = &[Span]> {
 +        self.unnamed.iter().map(|x| match x.as_slice() {
 +            // Ignore the dummy span added from out of order format arguments.
 +            [DUMMY_SP] => &[],
 +            x => x,
 +        })
 +    }
 +
 +    fn get_complex_unnamed(&self) -> impl Iterator<Item = &[Span]> {
 +        self.complex_unnamed.iter().map(Vec::as_slice)
 +    }
 +
 +    fn get_named(&self, n: &Path) -> &[Span] {
 +        self.named.iter().find(|x| *n == x.0).map_or(&[], |x| x.1.as_slice())
 +    }
 +
 +    fn push(&mut self, arg: rustc_parse_format::Argument<'_>, span: Span) {
 +        use rustc_parse_format::{
 +            AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec,
 +        };
 +
 +        const SIMPLE: FormatSpec<'_> = FormatSpec {
 +            fill: None,
 +            align: AlignUnknown,
 +            flags: 0,
 +            precision: CountImplied,
 +            precision_span: None,
 +            width: CountImplied,
 +            width_span: None,
 +            ty: "",
 +            ty_span: None,
 +        };
 +
 +        match arg.position {
 +            ArgumentIs(n) | ArgumentImplicitlyIs(n) => {
 +                if self.unnamed.len() <= n {
 +                    // Use a dummy span to mark all unseen arguments.
 +                    self.unnamed.resize_with(n, || vec![DUMMY_SP]);
 +                    if arg.format == SIMPLE {
 +                        self.unnamed.push(vec![span]);
 +                    } else {
 +                        self.unnamed.push(Vec::new());
 +                    }
 +                } else {
 +                    let args = &mut self.unnamed[n];
 +                    match (args.as_mut_slice(), arg.format == SIMPLE) {
 +                        // A non-empty format string has been seen already.
 +                        ([], _) => (),
 +                        // Replace the dummy span, if it exists.
 +                        ([dummy @ DUMMY_SP], true) => *dummy = span,
 +                        ([_, ..], true) => args.push(span),
 +                        ([_, ..], false) => *args = Vec::new(),
 +                    }
 +                }
 +            },
 +            ArgumentNamed(n) => {
 +                let n = Symbol::intern(n);
 +                if let Some(x) = self.named.iter_mut().find(|x| x.0 == n) {
 +                    match x.1.as_slice() {
 +                        // A non-empty format string has been seen already.
 +                        [] => (),
 +                        [_, ..] if arg.format == SIMPLE => x.1.push(span),
 +                        [_, ..] => x.1 = Vec::new(),
 +                    }
 +                } else if arg.format == SIMPLE {
 +                    self.named.push((n, vec![span]));
 +                } else {
 +                    self.named.push((n, Vec::new()));
 +                }
 +            },
 +        };
 +    }
 +
 +    fn push_to_complex(&mut self, span: Span, position: usize) {
 +        if self.complex_unnamed.len() <= position {
 +            self.complex_unnamed.resize_with(position, Vec::new);
 +            self.complex_unnamed.push(vec![span]);
 +        } else {
 +            let args: &mut Vec<Span> = &mut self.complex_unnamed[position];
 +            args.push(span);
 +        }
 +    }
 +
 +    fn push_complex(
 +        &mut self,
 +        cx: &EarlyContext<'_>,
 +        arg: rustc_parse_format::Argument<'_>,
 +        str_lit_span: Span,
 +        fmt_span: Span,
 +    ) {
 +        use rustc_parse_format::{ArgumentImplicitlyIs, ArgumentIs, CountIsParam, CountIsStar};
 +
 +        let snippet = snippet_opt(cx, fmt_span);
 +
 +        let end = snippet
 +            .as_ref()
 +            .and_then(|s| s.find(':'))
 +            .or_else(|| fmt_span.hi().0.checked_sub(fmt_span.lo().0 + 1).map(|u| u as usize));
 +
 +        if let (ArgumentIs(n) | ArgumentImplicitlyIs(n), Some(end)) = (arg.position, end) {
 +            let span = fmt_span.from_inner(InnerSpan::new(1, end));
 +            self.push_to_complex(span, n);
 +        };
 +
 +        if let (CountIsParam(n) | CountIsStar(n), Some(span)) = (arg.format.precision, arg.format.precision_span) {
 +            // We need to do this hack as precision spans should be converted from .* to .foo$
 +            let hack = if snippet.as_ref().and_then(|s| s.find('*')).is_some() {
 +                0
 +            } else {
 +                1
 +            };
 +
 +            let span = str_lit_span.from_inner(InnerSpan {
 +                start: span.start + 1,
 +                end: span.end - hack,
 +            });
 +            self.push_to_complex(span, n);
 +        };
 +
 +        if let (CountIsParam(n), Some(span)) = (arg.format.width, arg.format.width_span) {
 +            let span = str_lit_span.from_inner(InnerSpan {
 +                start: span.start,
 +                end: span.end - 1,
 +            });
 +            self.push_to_complex(span, n);
 +        };
 +    }
 +}
 +
 +impl Write {
 +    /// Parses a format string into a collection of spans for each argument. This only keeps track
 +    /// of empty format arguments. Will also lint usages of debug format strings outside of debug
 +    /// impls.
 +    fn parse_fmt_string(&self, cx: &EarlyContext<'_>, str_lit: &StrLit) -> Option<SimpleFormatArgs> {
 +        use rustc_parse_format::{ParseMode, Parser, Piece};
 +
 +        let str_sym = str_lit.symbol_unescaped.as_str();
 +        let style = match str_lit.style {
 +            StrStyle::Cooked => None,
 +            StrStyle::Raw(n) => Some(n as usize),
 +        };
 +
 +        let mut parser = Parser::new(str_sym, style, snippet_opt(cx, str_lit.span), false, ParseMode::Format);
 +        let mut args = SimpleFormatArgs::default();
 +
 +        while let Some(arg) = parser.next() {
 +            let arg = match arg {
 +                Piece::String(_) => continue,
 +                Piece::NextArgument(arg) => arg,
 +            };
 +            let span = parser
 +                .arg_places
 +                .last()
 +                .map_or(DUMMY_SP, |&x| str_lit.span.from_inner(InnerSpan::new(x.start, x.end)));
 +
 +            if !self.in_debug_impl && arg.format.ty == "?" {
 +                // FIXME: modify rustc's fmt string parser to give us the current span
 +                span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting");
 +            }
 +            args.push(arg, span);
 +            args.push_complex(cx, arg, str_lit.span, span);
 +        }
 +
 +        parser.errors.is_empty().then_some(args)
 +    }
 +
 +    /// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two
 +    /// `Option`s. The first `Option` of the tuple is the macro's format string. It includes
 +    /// the contents of the string, whether it's a raw string, and the span of the literal in the
 +    /// source. The second `Option` in the tuple is, in the `write[ln]!` case, the expression the
 +    /// `format_str` should be written to.
 +    ///
 +    /// Example:
 +    ///
 +    /// Calling this function on
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// # let something = "something";
 +    /// writeln!(buf, "string to write: {}", something);
 +    /// ```
 +    /// will return
 +    /// ```rust,ignore
 +    /// (Some("string to write: {}"), Some(buf))
 +    /// ```
 +    fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) -> (Option<StrLit>, Option<Expr>) {
 +        let mut parser = parser::Parser::new(&cx.sess().parse_sess, tts, false, None);
 +        let expr = if is_write {
 +            match parser
 +                .parse_expr()
 +                .map(rustc_ast::ptr::P::into_inner)
 +                .map_err(DiagnosticBuilder::cancel)
 +            {
 +                // write!(e, ...)
 +                Ok(p) if parser.eat(&token::Comma) => Some(p),
 +                // write!(e) or error
 +                e => return (None, e.ok()),
 +            }
 +        } else {
 +            None
 +        };
 +
 +        let fmtstr = match parser.parse_str_lit() {
 +            Ok(fmtstr) => fmtstr,
 +            Err(_) => return (None, expr),
 +        };
 +
 +        let args = match self.parse_fmt_string(cx, &fmtstr) {
 +            Some(args) => args,
 +            None => return (Some(fmtstr), expr),
 +        };
 +
 +        let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL };
 +        let mut unnamed_args = args.get_unnamed();
 +        let mut complex_unnamed_args = args.get_complex_unnamed();
 +        loop {
 +            if !parser.eat(&token::Comma) {
 +                return (Some(fmtstr), expr);
 +            }
 +
 +            let comma_span = parser.prev_token.span;
 +            let token_expr = if let Ok(expr) = parser.parse_expr().map_err(DiagnosticBuilder::cancel) {
 +                expr
 +            } else {
 +                return (Some(fmtstr), None);
 +            };
 +            let complex_unnamed_arg = complex_unnamed_args.next();
 +
 +            let (fmt_spans, lit) = match &token_expr.kind {
 +                ExprKind::Lit(lit) => (unnamed_args.next().unwrap_or(&[]), lit),
 +                ExprKind::Assign(lhs, rhs, _) => {
 +                    if let Some(span) = complex_unnamed_arg {
 +                        for x in span {
 +                            Self::report_positional_named_param(cx, *x, lhs, rhs);
 +                        }
 +                    }
 +                    match (&lhs.kind, &rhs.kind) {
 +                        (ExprKind::Path(_, p), ExprKind::Lit(lit)) => (args.get_named(p), lit),
 +                        _ => continue,
 +                    }
 +                },
 +                _ => {
 +                    unnamed_args.next();
 +                    continue;
 +                },
 +            };
 +
 +            let replacement: String = match lit.token_lit.kind {
 +                LitKind::StrRaw(_) | LitKind::ByteStrRaw(_) if matches!(fmtstr.style, StrStyle::Raw(_)) => {
 +                    lit.token_lit.symbol.as_str().replace('{', "{{").replace('}', "}}")
 +                },
 +                LitKind::Str | LitKind::ByteStr if matches!(fmtstr.style, StrStyle::Cooked) => {
 +                    lit.token_lit.symbol.as_str().replace('{', "{{").replace('}', "}}")
 +                },
 +                LitKind::StrRaw(_)
 +                | LitKind::Str
 +                | LitKind::ByteStrRaw(_)
 +                | LitKind::ByteStr
 +                | LitKind::Integer
 +                | LitKind::Float
 +                | LitKind::Err => continue,
 +                LitKind::Byte | LitKind::Char => match lit.token_lit.symbol.as_str() {
 +                    "\"" if matches!(fmtstr.style, StrStyle::Cooked) => "\\\"",
 +                    "\"" if matches!(fmtstr.style, StrStyle::Raw(0)) => continue,
 +                    "\\\\" if matches!(fmtstr.style, StrStyle::Raw(_)) => "\\",
 +                    "\\'" => "'",
 +                    "{" => "{{",
 +                    "}" => "}}",
 +                    x if matches!(fmtstr.style, StrStyle::Raw(_)) && x.starts_with('\\') => continue,
 +                    x => x,
 +                }
 +                .into(),
 +                LitKind::Bool => lit.token_lit.symbol.as_str().deref().into(),
 +            };
 +
 +            if !fmt_spans.is_empty() {
 +                span_lint_and_then(
 +                    cx,
 +                    lint,
 +                    token_expr.span,
 +                    "literal with an empty format string",
 +                    |diag| {
 +                        diag.multipart_suggestion(
 +                            "try this",
 +                            iter::once((comma_span.to(token_expr.span), String::new()))
 +                                .chain(fmt_spans.iter().copied().zip(iter::repeat(replacement)))
 +                                .collect(),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    },
 +                );
 +            }
 +        }
 +    }
 +
 +    fn report_positional_named_param(cx: &EarlyContext<'_>, span: Span, lhs: &P<Expr>, _rhs: &P<Expr>) {
 +        if let ExprKind::Path(_, _p) = &lhs.kind {
 +            let mut applicability = Applicability::MachineApplicable;
 +            let name = snippet_with_applicability(cx, lhs.span, "name", &mut applicability);
 +            // We need to do this hack as precision spans should be converted from .* to .foo$
 +            let hack = snippet(cx, span, "").contains('*');
 +
 +            span_lint_and_sugg(
 +                cx,
 +                POSITIONAL_NAMED_FORMAT_PARAMETERS,
 +                span,
 +                &format!("named parameter {} is used as a positional parameter", name),
 +                "replace it with",
 +                if hack {
 +                    format!("{}$", name)
 +                } else {
 +                    format!("{}", name)
 +                },
 +                applicability,
 +            );
 +        };
 +    }
 +
 +    fn lint_println_empty_string(&self, cx: &EarlyContext<'_>, mac: &MacCall) {
 +        if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
 +            if fmt_str.symbol == kw::Empty {
 +                let name = mac.path.segments[0].ident.name;
 +                span_lint_and_sugg(
 +                    cx,
 +                    PRINTLN_EMPTY_STRING,
 +                    mac.span(),
 +                    &format!("using `{}!(\"\")`", name),
 +                    "replace it with",
 +                    format!("{}!()", name),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +
 +    fn lint_print_with_newline(&self, cx: &EarlyContext<'_>, mac: &MacCall) {
 +        if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
 +            if check_newlines(&fmt_str) {
 +                let name = mac.path.segments[0].ident.name;
 +                let suggested = format!("{}ln", name);
 +                span_lint_and_then(
 +                    cx,
 +                    PRINT_WITH_NEWLINE,
 +                    mac.span(),
 +                    &format!("using `{}!()` with a format string that ends in a single newline", name),
 +                    |err| {
 +                        err.multipart_suggestion(
 +                            &format!("use `{}!` instead", suggested),
 +                            vec![(mac.path.span, suggested), (newline_span(&fmt_str).0, String::new())],
 +                            Applicability::MachineApplicable,
 +                        );
 +                    },
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +/// Checks if the format string contains a single newline that terminates it.
 +///
 +/// Literal and escaped newlines are both checked (only literal for raw strings).
 +fn check_newlines(fmtstr: &StrLit) -> bool {
 +    let mut has_internal_newline = false;
 +    let mut last_was_cr = false;
 +    let mut should_lint = false;
 +
 +    let contents = fmtstr.symbol.as_str();
 +
 +    let mut cb = |r: Range<usize>, c: Result<char, EscapeError>| {
++        let c = match c {
++            Ok(c) => c,
++            Err(e) if !e.is_fatal() => return,
++            Err(e) => panic!("{:?}", e),
++        };
 +
 +        if r.end == contents.len() && c == '\n' && !last_was_cr && !has_internal_newline {
 +            should_lint = true;
 +        } else {
 +            last_was_cr = c == '\r';
 +            if c == '\n' {
 +                has_internal_newline = true;
 +            }
 +        }
 +    };
 +
 +    match fmtstr.style {
 +        StrStyle::Cooked => unescape::unescape_literal(contents, unescape::Mode::Str, &mut cb),
 +        StrStyle::Raw(_) => unescape::unescape_literal(contents, unescape::Mode::RawStr, &mut cb),
 +    }
 +
 +    should_lint
 +}
index e8d2d579f097e5451a870788145360c3e7510c40,0000000000000000000000000000000000000000..7a8d4e8068ed6dc514031623c55a5d2e47333c07
mode 100644,000000..100644
--- /dev/null
@@@ -1,329 -1,0 +1,329 @@@
-         ExprKind::Closure(&Closure { body, .. }) => (Pat::Str(""), expr_search_pat(tcx, &tcx.hir().body(body).value).1),
 +//! This module handles checking if the span given is from a proc-macro or not.
 +//!
 +//! Proc-macros are capable of setting the span of every token they output to a few possible spans.
 +//! This includes spans we can detect easily as coming from a proc-macro (e.g. the call site
 +//! or the def site), and spans we can't easily detect as such (e.g. the span of any token
 +//! passed into the proc macro). This capability means proc-macros are capable of generating code
 +//! with a span that looks like it was written by the user, but which should not be linted by clippy
 +//! as it was generated by an external macro.
 +//!
 +//! That brings us to this module. The current approach is to determine a small bit of text which
 +//! must exist at both the start and the end of an item (e.g. an expression or a path) assuming the
 +//! code was written, and check if the span contains that text. Note this will only work correctly
 +//! if the span is not from a `macro_rules` based macro.
 +
 +use rustc_ast::ast::{IntTy, LitIntType, LitKind, StrStyle, UintTy};
 +use rustc_hir::{
 +    intravisit::FnKind, Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, HirId,
 +    Impl, ImplItem, ImplItemKind, IsAuto, Item, ItemKind, LoopSource, MatchSource, Node, QPath, TraitItem,
 +    TraitItemKind, UnOp, UnsafeSource, Unsafety, Variant, VariantData, YieldSource,
 +};
 +use rustc_lint::{LateContext, LintContext};
 +use rustc_middle::ty::TyCtxt;
 +use rustc_session::Session;
 +use rustc_span::{Span, Symbol};
 +use rustc_target::spec::abi::Abi;
 +
 +/// The search pattern to look for. Used by `span_matches_pat`
 +#[derive(Clone, Copy)]
 +pub enum Pat {
 +    /// A single string.
 +    Str(&'static str),
 +    /// Any of the given strings.
 +    MultiStr(&'static [&'static str]),
 +    /// The string representation of the symbol.
 +    Sym(Symbol),
 +    /// Any decimal or hexadecimal digit depending on the location.
 +    Num,
 +}
 +
 +/// Checks if the start and the end of the span's text matches the patterns. This will return false
 +/// if the span crosses multiple files or if source is not available.
 +fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) -> bool {
 +    let pos = sess.source_map().lookup_byte_offset(span.lo());
 +    let Some(ref src) = pos.sf.src else {
 +        return false;
 +    };
 +    let end = span.hi() - pos.sf.start_pos;
 +    src.get(pos.pos.0 as usize..end.0 as usize).map_or(false, |s| {
 +        // Spans can be wrapped in a mixture or parenthesis, whitespace, and trailing commas.
 +        let start_str = s.trim_start_matches(|c: char| c.is_whitespace() || c == '(');
 +        let end_str = s.trim_end_matches(|c: char| c.is_whitespace() || c == ')' || c == ',');
 +        (match start_pat {
 +            Pat::Str(text) => start_str.starts_with(text),
 +            Pat::MultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
 +            Pat::Sym(sym) => start_str.starts_with(sym.as_str()),
 +            Pat::Num => start_str.as_bytes().first().map_or(false, u8::is_ascii_digit),
 +        } && match end_pat {
 +            Pat::Str(text) => end_str.ends_with(text),
 +            Pat::MultiStr(texts) => texts.iter().any(|s| start_str.ends_with(s)),
 +            Pat::Sym(sym) => end_str.ends_with(sym.as_str()),
 +            Pat::Num => end_str.as_bytes().last().map_or(false, u8::is_ascii_hexdigit),
 +        })
 +    })
 +}
 +
 +/// Get the search patterns to use for the given literal
 +fn lit_search_pat(lit: &LitKind) -> (Pat, Pat) {
 +    match lit {
 +        LitKind::Str(_, StrStyle::Cooked) => (Pat::Str("\""), Pat::Str("\"")),
 +        LitKind::Str(_, StrStyle::Raw(0)) => (Pat::Str("r"), Pat::Str("\"")),
 +        LitKind::Str(_, StrStyle::Raw(_)) => (Pat::Str("r#"), Pat::Str("#")),
 +        LitKind::ByteStr(_) => (Pat::Str("b\""), Pat::Str("\"")),
 +        LitKind::Byte(_) => (Pat::Str("b'"), Pat::Str("'")),
 +        LitKind::Char(_) => (Pat::Str("'"), Pat::Str("'")),
 +        LitKind::Int(_, LitIntType::Signed(IntTy::Isize)) => (Pat::Num, Pat::Str("isize")),
 +        LitKind::Int(_, LitIntType::Unsigned(UintTy::Usize)) => (Pat::Num, Pat::Str("usize")),
 +        LitKind::Int(..) => (Pat::Num, Pat::Num),
 +        LitKind::Float(..) => (Pat::Num, Pat::Str("")),
 +        LitKind::Bool(true) => (Pat::Str("true"), Pat::Str("true")),
 +        LitKind::Bool(false) => (Pat::Str("false"), Pat::Str("false")),
 +        _ => (Pat::Str(""), Pat::Str("")),
 +    }
 +}
 +
 +/// Get the search patterns to use for the given path
 +fn qpath_search_pat(path: &QPath<'_>) -> (Pat, Pat) {
 +    match path {
 +        QPath::Resolved(ty, path) => {
 +            let start = if ty.is_some() {
 +                Pat::Str("<")
 +            } else {
 +                path.segments
 +                    .first()
 +                    .map_or(Pat::Str(""), |seg| Pat::Sym(seg.ident.name))
 +            };
 +            let end = path.segments.last().map_or(Pat::Str(""), |seg| {
 +                if seg.args.is_some() {
 +                    Pat::Str(">")
 +                } else {
 +                    Pat::Sym(seg.ident.name)
 +                }
 +            });
 +            (start, end)
 +        },
 +        QPath::TypeRelative(_, name) => (Pat::Str(""), Pat::Sym(name.ident.name)),
 +        QPath::LangItem(..) => (Pat::Str(""), Pat::Str("")),
 +    }
 +}
 +
 +/// Get the search patterns to use for the given expression
 +fn expr_search_pat(tcx: TyCtxt<'_>, e: &Expr<'_>) -> (Pat, Pat) {
 +    match e.kind {
 +        ExprKind::Box(e) => (Pat::Str("box"), expr_search_pat(tcx, e).1),
 +        ExprKind::ConstBlock(_) => (Pat::Str("const"), Pat::Str("}")),
 +        ExprKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")),
 +        ExprKind::Unary(UnOp::Deref, e) => (Pat::Str("*"), expr_search_pat(tcx, e).1),
 +        ExprKind::Unary(UnOp::Not, e) => (Pat::Str("!"), expr_search_pat(tcx, e).1),
 +        ExprKind::Unary(UnOp::Neg, e) => (Pat::Str("-"), expr_search_pat(tcx, e).1),
 +        ExprKind::Lit(ref lit) => lit_search_pat(&lit.node),
 +        ExprKind::Array(_) | ExprKind::Repeat(..) => (Pat::Str("["), Pat::Str("]")),
 +        ExprKind::Call(e, []) | ExprKind::MethodCall(_, e, [], _) => (expr_search_pat(tcx, e).0, Pat::Str("(")),
 +        ExprKind::Call(first, [.., last])
 +        | ExprKind::MethodCall(_, first, [.., last], _)
 +        | ExprKind::Binary(_, first, last)
 +        | ExprKind::Tup([first, .., last])
 +        | ExprKind::Assign(first, last, _)
 +        | ExprKind::AssignOp(_, first, last) => (expr_search_pat(tcx, first).0, expr_search_pat(tcx, last).1),
 +        ExprKind::Tup([e]) | ExprKind::DropTemps(e) => expr_search_pat(tcx, e),
 +        ExprKind::Cast(e, _) | ExprKind::Type(e, _) => (expr_search_pat(tcx, e).0, Pat::Str("")),
 +        ExprKind::Let(let_expr) => (Pat::Str("let"), expr_search_pat(tcx, let_expr.init).1),
 +        ExprKind::If(..) => (Pat::Str("if"), Pat::Str("}")),
 +        ExprKind::Loop(_, Some(_), _, _) | ExprKind::Block(_, Some(_)) => (Pat::Str("'"), Pat::Str("}")),
 +        ExprKind::Loop(_, None, LoopSource::Loop, _) => (Pat::Str("loop"), Pat::Str("}")),
 +        ExprKind::Loop(_, None, LoopSource::While, _) => (Pat::Str("while"), Pat::Str("}")),
 +        ExprKind::Loop(_, None, LoopSource::ForLoop, _) | ExprKind::Match(_, _, MatchSource::ForLoopDesugar) => {
 +            (Pat::Str("for"), Pat::Str("}"))
 +        },
 +        ExprKind::Match(_, _, MatchSource::Normal) => (Pat::Str("match"), Pat::Str("}")),
 +        ExprKind::Match(e, _, MatchSource::TryDesugar) => (expr_search_pat(tcx, e).0, Pat::Str("?")),
 +        ExprKind::Match(e, _, MatchSource::AwaitDesugar) | ExprKind::Yield(e, YieldSource::Await { .. }) => {
 +            (expr_search_pat(tcx, e).0, Pat::Str("await"))
 +        },
-         FnKind::Closure => return (Pat::Str(""), expr_search_pat(tcx, &body.value).1),
++        ExprKind::Closure(&Closure { body, .. }) => (Pat::Str(""), expr_search_pat(tcx, tcx.hir().body(body).value).1),
 +        ExprKind::Block(
 +            Block {
 +                rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
 +                ..
 +            },
 +            None,
 +        ) => (Pat::Str("unsafe"), Pat::Str("}")),
 +        ExprKind::Block(_, None) => (Pat::Str("{"), Pat::Str("}")),
 +        ExprKind::Field(e, name) => (expr_search_pat(tcx, e).0, Pat::Sym(name.name)),
 +        ExprKind::Index(e, _) => (expr_search_pat(tcx, e).0, Pat::Str("]")),
 +        ExprKind::Path(ref path) => qpath_search_pat(path),
 +        ExprKind::AddrOf(_, _, e) => (Pat::Str("&"), expr_search_pat(tcx, e).1),
 +        ExprKind::Break(Destination { label: None, .. }, None) => (Pat::Str("break"), Pat::Str("break")),
 +        ExprKind::Break(Destination { label: Some(name), .. }, None) => (Pat::Str("break"), Pat::Sym(name.ident.name)),
 +        ExprKind::Break(_, Some(e)) => (Pat::Str("break"), expr_search_pat(tcx, e).1),
 +        ExprKind::Continue(Destination { label: None, .. }) => (Pat::Str("continue"), Pat::Str("continue")),
 +        ExprKind::Continue(Destination { label: Some(name), .. }) => (Pat::Str("continue"), Pat::Sym(name.ident.name)),
 +        ExprKind::Ret(None) => (Pat::Str("return"), Pat::Str("return")),
 +        ExprKind::Ret(Some(e)) => (Pat::Str("return"), expr_search_pat(tcx, e).1),
 +        ExprKind::Struct(path, _, _) => (qpath_search_pat(path).0, Pat::Str("}")),
 +        ExprKind::Yield(e, YieldSource::Yield) => (Pat::Str("yield"), expr_search_pat(tcx, e).1),
 +        _ => (Pat::Str(""), Pat::Str("")),
 +    }
 +}
 +
 +fn fn_header_search_pat(header: FnHeader) -> Pat {
 +    if header.is_async() {
 +        Pat::Str("async")
 +    } else if header.is_const() {
 +        Pat::Str("const")
 +    } else if header.is_unsafe() {
 +        Pat::Str("unsafe")
 +    } else if header.abi != Abi::Rust {
 +        Pat::Str("extern")
 +    } else {
 +        Pat::MultiStr(&["fn", "extern"])
 +    }
 +}
 +
 +fn item_search_pat(item: &Item<'_>) -> (Pat, Pat) {
 +    let (start_pat, end_pat) = match &item.kind {
 +        ItemKind::ExternCrate(_) => (Pat::Str("extern"), Pat::Str(";")),
 +        ItemKind::Static(..) => (Pat::Str("static"), Pat::Str(";")),
 +        ItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")),
 +        ItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")),
 +        ItemKind::ForeignMod { .. } => (Pat::Str("extern"), Pat::Str("}")),
 +        ItemKind::TyAlias(..) | ItemKind::OpaqueTy(_) => (Pat::Str("type"), Pat::Str(";")),
 +        ItemKind::Enum(..) => (Pat::Str("enum"), Pat::Str("}")),
 +        ItemKind::Struct(VariantData::Struct(..), _) => (Pat::Str("struct"), Pat::Str("}")),
 +        ItemKind::Struct(..) => (Pat::Str("struct"), Pat::Str(";")),
 +        ItemKind::Union(..) => (Pat::Str("union"), Pat::Str("}")),
 +        ItemKind::Trait(_, Unsafety::Unsafe, ..)
 +        | ItemKind::Impl(Impl {
 +            unsafety: Unsafety::Unsafe,
 +            ..
 +        }) => (Pat::Str("unsafe"), Pat::Str("}")),
 +        ItemKind::Trait(IsAuto::Yes, ..) => (Pat::Str("auto"), Pat::Str("}")),
 +        ItemKind::Trait(..) => (Pat::Str("trait"), Pat::Str("}")),
 +        ItemKind::Impl(_) => (Pat::Str("impl"), Pat::Str("}")),
 +        _ => return (Pat::Str(""), Pat::Str("")),
 +    };
 +    if item.vis_span.is_empty() {
 +        (start_pat, end_pat)
 +    } else {
 +        (Pat::Str("pub"), end_pat)
 +    }
 +}
 +
 +fn trait_item_search_pat(item: &TraitItem<'_>) -> (Pat, Pat) {
 +    match &item.kind {
 +        TraitItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")),
 +        TraitItemKind::Type(..) => (Pat::Str("type"), Pat::Str(";")),
 +        TraitItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")),
 +    }
 +}
 +
 +fn impl_item_search_pat(item: &ImplItem<'_>) -> (Pat, Pat) {
 +    let (start_pat, end_pat) = match &item.kind {
 +        ImplItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")),
 +        ImplItemKind::TyAlias(..) => (Pat::Str("type"), Pat::Str(";")),
 +        ImplItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")),
 +    };
 +    if item.vis_span.is_empty() {
 +        (start_pat, end_pat)
 +    } else {
 +        (Pat::Str("pub"), end_pat)
 +    }
 +}
 +
 +fn field_def_search_pat(def: &FieldDef<'_>) -> (Pat, Pat) {
 +    if def.vis_span.is_empty() {
 +        if def.is_positional() {
 +            (Pat::Str(""), Pat::Str(""))
 +        } else {
 +            (Pat::Sym(def.ident.name), Pat::Str(""))
 +        }
 +    } else {
 +        (Pat::Str("pub"), Pat::Str(""))
 +    }
 +}
 +
 +fn variant_search_pat(v: &Variant<'_>) -> (Pat, Pat) {
 +    match v.data {
 +        VariantData::Struct(..) => (Pat::Sym(v.ident.name), Pat::Str("}")),
 +        VariantData::Tuple(..) => (Pat::Sym(v.ident.name), Pat::Str("")),
 +        VariantData::Unit(..) => (Pat::Sym(v.ident.name), Pat::Sym(v.ident.name)),
 +    }
 +}
 +
 +fn fn_kind_pat(tcx: TyCtxt<'_>, kind: &FnKind<'_>, body: &Body<'_>, hir_id: HirId) -> (Pat, Pat) {
 +    let (start_pat, end_pat) = match kind {
 +        FnKind::ItemFn(.., header) => (fn_header_search_pat(*header), Pat::Str("")),
 +        FnKind::Method(.., sig) => (fn_header_search_pat(sig.header), Pat::Str("")),
++        FnKind::Closure => return (Pat::Str(""), expr_search_pat(tcx, body.value).1),
 +    };
 +    let start_pat = match tcx.hir().get(hir_id) {
 +        Node::Item(Item { vis_span, .. }) | Node::ImplItem(ImplItem { vis_span, .. }) => {
 +            if vis_span.is_empty() {
 +                start_pat
 +            } else {
 +                Pat::Str("pub")
 +            }
 +        },
 +        Node::TraitItem(_) => start_pat,
 +        _ => Pat::Str(""),
 +    };
 +    (start_pat, end_pat)
 +}
 +
 +pub trait WithSearchPat {
 +    type Context: LintContext;
 +    fn search_pat(&self, cx: &Self::Context) -> (Pat, Pat);
 +    fn span(&self) -> Span;
 +}
 +macro_rules! impl_with_search_pat {
 +    ($cx:ident: $ty:ident with $fn:ident $(($tcx:ident))?) => {
 +        impl<'cx> WithSearchPat for $ty<'cx> {
 +            type Context = $cx<'cx>;
 +            #[allow(unused_variables)]
 +            fn search_pat(&self, cx: &Self::Context) -> (Pat, Pat) {
 +                $(let $tcx = cx.tcx;)?
 +                $fn($($tcx,)? self)
 +            }
 +            fn span(&self) -> Span {
 +                self.span
 +            }
 +        }
 +    };
 +}
 +impl_with_search_pat!(LateContext: Expr with expr_search_pat(tcx));
 +impl_with_search_pat!(LateContext: Item with item_search_pat);
 +impl_with_search_pat!(LateContext: TraitItem with trait_item_search_pat);
 +impl_with_search_pat!(LateContext: ImplItem with impl_item_search_pat);
 +impl_with_search_pat!(LateContext: FieldDef with field_def_search_pat);
 +impl_with_search_pat!(LateContext: Variant with variant_search_pat);
 +
 +impl<'cx> WithSearchPat for (&FnKind<'cx>, &Body<'cx>, HirId, Span) {
 +    type Context = LateContext<'cx>;
 +
 +    fn search_pat(&self, cx: &Self::Context) -> (Pat, Pat) {
 +        fn_kind_pat(cx.tcx, self.0, self.1, self.2)
 +    }
 +
 +    fn span(&self) -> Span {
 +        self.3
 +    }
 +}
 +
 +/// Checks if the item likely came from a proc-macro.
 +///
 +/// This should be called after `in_external_macro` and the initial pattern matching of the ast as
 +/// it is significantly slower than both of those.
 +pub fn is_from_proc_macro<T: WithSearchPat>(cx: &T::Context, item: &T) -> bool {
 +    let (start_pat, end_pat) = item.search_pat(cx);
 +    !span_matches_pat(cx.sess(), item.span(), start_pat, end_pat)
 +}
 +
 +/// Checks if the span actually refers to a match expression
 +pub fn is_span_match(cx: &impl LintContext, span: Span) -> bool {
 +    span_matches_pat(cx.sess(), span, Pat::Str("match"), Pat::Str("}"))
 +}
 +
 +/// Checks if the span actually refers to an if expression
 +pub fn is_span_if(cx: &impl LintContext, span: Span) -> bool {
 +    span_matches_pat(cx.sess(), span, Pat::Str("if"), Pat::Str("}"))
 +}
index 124a00d817847ae7a827a6c8694e0a9e1d888e83,0000000000000000000000000000000000000000..91c9c382c236bcbc87c535976c33944fba629bf8
mode 100644,000000..100644
--- /dev/null
@@@ -1,230 -1,0 +1,230 @@@
- fn fn_eagerness<'tcx>(cx: &LateContext<'tcx>, fn_id: DefId, name: Symbol, have_one_arg: bool) -> EagernessSuggestion {
 +//! Utilities for evaluating whether eagerly evaluated expressions can be made lazy and vice versa.
 +//!
 +//! Things to consider:
 +//!  - has the expression side-effects?
 +//!  - is the expression computationally expensive?
 +//!
 +//! See lints:
 +//!  - unnecessary-lazy-evaluations
 +//!  - or-fun-call
 +//!  - option-if-let-else
 +
 +use crate::ty::{all_predicates_of, is_copy};
 +use crate::visitors::is_const_evaluatable;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::intravisit::{walk_expr, Visitor};
 +use rustc_hir::{def_id::DefId, Block, Expr, ExprKind, QPath, UnOp};
 +use rustc_lint::LateContext;
 +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)
 +    }
 +}
 +impl ops::BitOrAssign for EagernessSuggestion {
 +    fn bitor_assign(&mut self, rhs: Self) {
 +        *self = *self | rhs;
 +    }
 +}
 +
 +/// Determine the eagerness of the given function call.
++fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: bool) -> EagernessSuggestion {
 +    use EagernessSuggestion::{Eager, Lazy, NoChange};
 +    let name = name.as_str();
 +
 +    let ty = match cx.tcx.impl_of_method(fn_id) {
 +        Some(id) => cx.tcx.type_of(id),
 +        None => return Lazy,
 +    };
 +
 +    if (name.starts_with("as_") || name == "len" || name == "is_empty") && have_one_arg {
 +        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
 +    }
 +}
 +
 +#[expect(clippy::too_many_lines)]
 +fn expr_eagerness<'tcx>(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> {
 +        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.is_empty());
 +                        },
 +                        QPath::TypeRelative(_, name) => {
 +                            self.eagerness |= fn_eagerness(self.cx, id, name.ident.name, !args.is_empty());
 +                        },
 +                        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, ..) => {
 +                    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, true));
 +                },
 +                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::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 v = V {
 +        cx,
 +        eagerness: EagernessSuggestion::Eager,
 +    };
 +    v.visit_expr(e);
 +    v.eagerness
 +}
 +
 +/// Whether the given expression should be changed to evaluate eagerly
 +pub fn switch_to_eager_eval<'tcx>(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
 +    expr_eagerness(cx, expr) == EagernessSuggestion::Eager
 +}
 +
 +/// Whether the given expression should be changed to evaluate lazily
 +pub fn switch_to_lazy_eval<'tcx>(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
 +    expr_eagerness(cx, expr) == EagernessSuggestion::Lazy
 +}
index f45cec9f0b430a0f7be230ddadb22aabcc8642e9,0000000000000000000000000000000000000000..7212d9cd744510fb8ba8763d6a4e9570b7bc4036
mode 100644,000000..100644
--- /dev/null
@@@ -1,1040 -1,0 +1,1040 @@@
-             &self.inner.cx.tcx.hir().body(left).value,
-             &self.inner.cx.tcx.hir().body(right).value,
 +use crate::consts::constant_simple;
 +use crate::macros::macro_backtrace;
 +use crate::source::snippet_opt;
 +use rustc_ast::ast::InlineAsmTemplatePiece;
 +use rustc_data_structures::fx::FxHasher;
 +use rustc_hir::def::Res;
 +use rustc_hir::HirIdMap;
 +use rustc_hir::{
 +    ArrayLen, BinOpKind, BindingAnnotation, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg,
 +    GenericArgs, Guard, HirId, InlineAsmOperand, Let, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path,
 +    PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding,
 +};
 +use rustc_lexer::{tokenize, TokenKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::TypeckResults;
 +use rustc_span::{sym, Symbol};
 +use std::hash::{Hash, Hasher};
 +
 +/// Callback that is called when two expressions are not equal in the sense of `SpanlessEq`, but
 +/// other conditions would make them equal.
 +type SpanlessEqCallback<'a> = dyn FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a;
 +
 +/// Type used to check whether two ast are the same. This is different from the
 +/// operator `==` on ast types as this operator would compare true equality with
 +/// ID and span.
 +///
 +/// Note that some expressions kinds are not considered but could be added.
 +pub struct SpanlessEq<'a, 'tcx> {
 +    /// Context used to evaluate constant expressions.
 +    cx: &'a LateContext<'tcx>,
 +    maybe_typeck_results: Option<(&'tcx TypeckResults<'tcx>, &'tcx TypeckResults<'tcx>)>,
 +    allow_side_effects: bool,
 +    expr_fallback: Option<Box<SpanlessEqCallback<'a>>>,
 +}
 +
 +impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
 +    pub fn new(cx: &'a LateContext<'tcx>) -> Self {
 +        Self {
 +            cx,
 +            maybe_typeck_results: cx.maybe_typeck_results().map(|x| (x, x)),
 +            allow_side_effects: true,
 +            expr_fallback: None,
 +        }
 +    }
 +
 +    /// Consider expressions containing potential side effects as not equal.
 +    #[must_use]
 +    pub fn deny_side_effects(self) -> Self {
 +        Self {
 +            allow_side_effects: false,
 +            ..self
 +        }
 +    }
 +
 +    #[must_use]
 +    pub fn expr_fallback(self, expr_fallback: impl FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a) -> Self {
 +        Self {
 +            expr_fallback: Some(Box::new(expr_fallback)),
 +            ..self
 +        }
 +    }
 +
 +    /// Use this method to wrap comparisons that may involve inter-expression context.
 +    /// See `self.locals`.
 +    pub fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> {
 +        HirEqInterExpr {
 +            inner: self,
 +            locals: HirIdMap::default(),
 +        }
 +    }
 +
 +    pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
 +        self.inter_expr().eq_block(left, right)
 +    }
 +
 +    pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
 +        self.inter_expr().eq_expr(left, right)
 +    }
 +
 +    pub fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool {
 +        self.inter_expr().eq_path(left, right)
 +    }
 +
 +    pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
 +        self.inter_expr().eq_path_segment(left, right)
 +    }
 +
 +    pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
 +        self.inter_expr().eq_path_segments(left, right)
 +    }
 +}
 +
 +pub struct HirEqInterExpr<'a, 'b, 'tcx> {
 +    inner: &'a mut SpanlessEq<'b, 'tcx>,
 +
 +    // When binding are declared, the binding ID in the left expression is mapped to the one on the
 +    // right. For example, when comparing `{ let x = 1; x + 2 }` and `{ let y = 1; y + 2 }`,
 +    // these blocks are considered equal since `x` is mapped to `y`.
 +    pub locals: HirIdMap<HirId>,
 +}
 +
 +impl HirEqInterExpr<'_, '_, '_> {
 +    pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
 +        match (&left.kind, &right.kind) {
 +            (&StmtKind::Local(l), &StmtKind::Local(r)) => {
 +                // This additional check ensures that the type of the locals are equivalent even if the init
 +                // expression or type have some inferred parts.
 +                if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results {
 +                    let l_ty = typeck_lhs.pat_ty(l.pat);
 +                    let r_ty = typeck_rhs.pat_ty(r.pat);
 +                    if l_ty != r_ty {
 +                        return false;
 +                    }
 +                }
 +
 +                // eq_pat adds the HirIds to the locals map. We therefor call it last to make sure that
 +                // these only get added if the init and type is equal.
 +                both(&l.init, &r.init, |l, r| self.eq_expr(l, r))
 +                    && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r))
 +                    && both(&l.els, &r.els, |l, r| self.eq_block(l, r))
 +                    && self.eq_pat(l.pat, r.pat)
 +            },
 +            (&StmtKind::Expr(l), &StmtKind::Expr(r)) | (&StmtKind::Semi(l), &StmtKind::Semi(r)) => self.eq_expr(l, r),
 +            _ => false,
 +        }
 +    }
 +
 +    /// Checks whether two blocks are the same.
 +    fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
 +        match (left.stmts, left.expr, right.stmts, right.expr) {
 +            ([], None, [], None) => {
 +                // For empty blocks, check to see if the tokens are equal. This will catch the case where a macro
 +                // expanded to nothing, or the cfg attribute was used.
 +                let (left, right) = match (
 +                    snippet_opt(self.inner.cx, left.span),
 +                    snippet_opt(self.inner.cx, right.span),
 +                ) {
 +                    (Some(left), Some(right)) => (left, right),
 +                    _ => return true,
 +                };
 +                let mut left_pos = 0;
 +                let left = tokenize(&left)
 +                    .map(|t| {
 +                        let end = left_pos + t.len as usize;
 +                        let s = &left[left_pos..end];
 +                        left_pos = end;
 +                        (t, s)
 +                    })
 +                    .filter(|(t, _)| {
 +                        !matches!(
 +                            t.kind,
 +                            TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
 +                        )
 +                    })
 +                    .map(|(_, s)| s);
 +                let mut right_pos = 0;
 +                let right = tokenize(&right)
 +                    .map(|t| {
 +                        let end = right_pos + t.len as usize;
 +                        let s = &right[right_pos..end];
 +                        right_pos = end;
 +                        (t, s)
 +                    })
 +                    .filter(|(t, _)| {
 +                        !matches!(
 +                            t.kind,
 +                            TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
 +                        )
 +                    })
 +                    .map(|(_, s)| s);
 +                left.eq(right)
 +            },
 +            _ => {
 +                over(left.stmts, right.stmts, |l, r| self.eq_stmt(l, r))
 +                    && both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r))
 +            },
 +        }
 +    }
 +
 +    fn should_ignore(&mut self, expr: &Expr<'_>) -> bool {
 +        macro_backtrace(expr.span).last().map_or(false, |macro_call| {
 +            matches!(
 +                &self.inner.cx.tcx.get_diagnostic_name(macro_call.def_id),
 +                Some(sym::todo_macro | sym::unimplemented_macro)
 +            )
 +        })
 +    }
 +
 +    pub fn eq_array_length(&mut self, left: ArrayLen, right: ArrayLen) -> bool {
 +        match (left, right) {
 +            (ArrayLen::Infer(..), ArrayLen::Infer(..)) => true,
 +            (ArrayLen::Body(l_ct), ArrayLen::Body(r_ct)) => self.eq_body(l_ct.body, r_ct.body),
 +            (_, _) => false,
 +        }
 +    }
 +
 +    pub fn eq_body(&mut self, left: BodyId, right: BodyId) -> bool {
 +        // swap out TypeckResults when hashing a body
 +        let old_maybe_typeck_results = self.inner.maybe_typeck_results.replace((
 +            self.inner.cx.tcx.typeck_body(left),
 +            self.inner.cx.tcx.typeck_body(right),
 +        ));
 +        let res = self.eq_expr(
-                 self.hash_expr(&self.cx.tcx.hir().body(body).value);
++            self.inner.cx.tcx.hir().body(left).value,
++            self.inner.cx.tcx.hir().body(right).value,
 +        );
 +        self.inner.maybe_typeck_results = old_maybe_typeck_results;
 +        res
 +    }
 +
 +    #[expect(clippy::similar_names)]
 +    pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
 +        if !self.inner.allow_side_effects && left.span.ctxt() != right.span.ctxt() {
 +            return false;
 +        }
 +
 +        if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results {
 +            if let (Some(l), Some(r)) = (
 +                constant_simple(self.inner.cx, typeck_lhs, left),
 +                constant_simple(self.inner.cx, typeck_rhs, right),
 +            ) {
 +                if l == r {
 +                    return true;
 +                }
 +            }
 +        }
 +
 +        let is_eq = match (
 +            reduce_exprkind(self.inner.cx, &left.kind),
 +            reduce_exprkind(self.inner.cx, &right.kind),
 +        ) {
 +            (&ExprKind::AddrOf(lb, l_mut, le), &ExprKind::AddrOf(rb, r_mut, re)) => {
 +                lb == rb && l_mut == r_mut && self.eq_expr(le, re)
 +            },
 +            (&ExprKind::Continue(li), &ExprKind::Continue(ri)) => {
 +                both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name)
 +            },
 +            (&ExprKind::Assign(ll, lr, _), &ExprKind::Assign(rl, rr, _)) => {
 +                self.inner.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
 +            },
 +            (&ExprKind::AssignOp(ref lo, ll, lr), &ExprKind::AssignOp(ref ro, rl, rr)) => {
 +                self.inner.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
 +            },
 +            (&ExprKind::Block(l, _), &ExprKind::Block(r, _)) => self.eq_block(l, r),
 +            (&ExprKind::Binary(l_op, ll, lr), &ExprKind::Binary(r_op, rl, rr)) => {
 +                l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
 +                    || swap_binop(l_op.node, ll, lr).map_or(false, |(l_op, ll, lr)| {
 +                        l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
 +                    })
 +            },
 +            (&ExprKind::Break(li, ref le), &ExprKind::Break(ri, ref re)) => {
 +                both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name)
 +                    && both(le, re, |l, r| self.eq_expr(l, r))
 +            },
 +            (&ExprKind::Box(l), &ExprKind::Box(r)) => self.eq_expr(l, r),
 +            (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => {
 +                self.inner.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args)
 +            },
 +            (&ExprKind::Cast(lx, lt), &ExprKind::Cast(rx, rt)) | (&ExprKind::Type(lx, lt), &ExprKind::Type(rx, rt)) => {
 +                self.eq_expr(lx, rx) && self.eq_ty(lt, rt)
 +            },
 +            (&ExprKind::Field(l_f_exp, ref l_f_ident), &ExprKind::Field(r_f_exp, ref r_f_ident)) => {
 +                l_f_ident.name == r_f_ident.name && self.eq_expr(l_f_exp, r_f_exp)
 +            },
 +            (&ExprKind::Index(la, li), &ExprKind::Index(ra, ri)) => self.eq_expr(la, ra) && self.eq_expr(li, ri),
 +            (&ExprKind::If(lc, lt, ref le), &ExprKind::If(rc, rt, ref re)) => {
 +                self.eq_expr(lc, rc) && self.eq_expr(lt, rt) && both(le, re, |l, r| self.eq_expr(l, r))
 +            },
 +            (&ExprKind::Let(l), &ExprKind::Let(r)) => {
 +                self.eq_pat(l.pat, r.pat) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) && self.eq_expr(l.init, r.init)
 +            },
 +            (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node,
 +            (&ExprKind::Loop(lb, ref ll, ref lls, _), &ExprKind::Loop(rb, ref rl, ref rls, _)) => {
 +                lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name)
 +            },
 +            (&ExprKind::Match(le, la, ref ls), &ExprKind::Match(re, ra, ref rs)) => {
 +                ls == rs
 +                    && self.eq_expr(le, re)
 +                    && over(la, ra, |l, r| {
 +                        self.eq_pat(l.pat, r.pat)
 +                            && both(&l.guard, &r.guard, |l, r| self.eq_guard(l, r))
 +                            && self.eq_expr(l.body, r.body)
 +                    })
 +            },
 +            (
 +                &ExprKind::MethodCall(l_path, l_receiver, l_args, _),
 +                &ExprKind::MethodCall(r_path, r_receiver, r_args, _),
 +            ) => {
 +                self.inner.allow_side_effects
 +                    && self.eq_path_segment(l_path, r_path)
 +                    && self.eq_expr(l_receiver, r_receiver)
 +                    && self.eq_exprs(l_args, r_args)
 +            },
 +            (&ExprKind::Repeat(le, ll), &ExprKind::Repeat(re, rl)) => {
 +                self.eq_expr(le, re) && self.eq_array_length(ll, rl)
 +            },
 +            (&ExprKind::Ret(ref l), &ExprKind::Ret(ref r)) => both(l, r, |l, r| self.eq_expr(l, r)),
 +            (&ExprKind::Path(ref l), &ExprKind::Path(ref r)) => self.eq_qpath(l, r),
 +            (&ExprKind::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => {
 +                self.eq_qpath(l_path, r_path)
 +                    && both(lo, ro, |l, r| self.eq_expr(l, r))
 +                    && over(lf, rf, |l, r| self.eq_expr_field(l, r))
 +            },
 +            (&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup),
 +            (&ExprKind::Unary(l_op, le), &ExprKind::Unary(r_op, re)) => l_op == r_op && self.eq_expr(le, re),
 +            (&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r),
 +            (&ExprKind::DropTemps(le), &ExprKind::DropTemps(re)) => self.eq_expr(le, re),
 +            _ => false,
 +        };
 +        (is_eq && (!self.should_ignore(left) || !self.should_ignore(right)))
 +            || self.inner.expr_fallback.as_mut().map_or(false, |f| f(left, right))
 +    }
 +
 +    fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool {
 +        over(left, right, |l, r| self.eq_expr(l, r))
 +    }
 +
 +    fn eq_expr_field(&mut self, left: &ExprField<'_>, right: &ExprField<'_>) -> bool {
 +        left.ident.name == right.ident.name && self.eq_expr(left.expr, right.expr)
 +    }
 +
 +    fn eq_guard(&mut self, left: &Guard<'_>, right: &Guard<'_>) -> bool {
 +        match (left, right) {
 +            (Guard::If(l), Guard::If(r)) => self.eq_expr(l, r),
 +            (Guard::IfLet(l), Guard::IfLet(r)) => {
 +                self.eq_pat(l.pat, r.pat) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) && self.eq_expr(l.init, r.init)
 +            },
 +            _ => false,
 +        }
 +    }
 +
 +    fn eq_generic_arg(&mut self, left: &GenericArg<'_>, right: &GenericArg<'_>) -> bool {
 +        match (left, right) {
 +            (GenericArg::Const(l), GenericArg::Const(r)) => self.eq_body(l.value.body, r.value.body),
 +            (GenericArg::Lifetime(l_lt), GenericArg::Lifetime(r_lt)) => Self::eq_lifetime(l_lt, r_lt),
 +            (GenericArg::Type(l_ty), GenericArg::Type(r_ty)) => self.eq_ty(l_ty, r_ty),
 +            (GenericArg::Infer(l_inf), GenericArg::Infer(r_inf)) => self.eq_ty(&l_inf.to_ty(), &r_inf.to_ty()),
 +            _ => false,
 +        }
 +    }
 +
 +    fn eq_lifetime(left: &Lifetime, right: &Lifetime) -> bool {
 +        left.name == right.name
 +    }
 +
 +    fn eq_pat_field(&mut self, left: &PatField<'_>, right: &PatField<'_>) -> bool {
 +        let (PatField { ident: li, pat: lp, .. }, PatField { ident: ri, pat: rp, .. }) = (&left, &right);
 +        li.name == ri.name && self.eq_pat(lp, rp)
 +    }
 +
 +    /// Checks whether two patterns are the same.
 +    fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool {
 +        match (&left.kind, &right.kind) {
 +            (&PatKind::Box(l), &PatKind::Box(r)) => self.eq_pat(l, r),
 +            (&PatKind::Struct(ref lp, la, ..), &PatKind::Struct(ref rp, ra, ..)) => {
 +                self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat_field(l, r))
 +            },
 +            (&PatKind::TupleStruct(ref lp, la, ls), &PatKind::TupleStruct(ref rp, ra, rs)) => {
 +                self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs
 +            },
 +            (&PatKind::Binding(lb, li, _, ref lp), &PatKind::Binding(rb, ri, _, ref rp)) => {
 +                let eq = lb == rb && both(lp, rp, |l, r| self.eq_pat(l, r));
 +                if eq {
 +                    self.locals.insert(li, ri);
 +                }
 +                eq
 +            },
 +            (&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r),
 +            (&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_expr(l, r),
 +            (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)),
 +            (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => {
 +                both(ls, rs, |a, b| self.eq_expr(a, b)) && both(le, re, |a, b| self.eq_expr(a, b)) && (li == ri)
 +            },
 +            (&PatKind::Ref(le, ref lm), &PatKind::Ref(re, ref rm)) => lm == rm && self.eq_pat(le, re),
 +            (&PatKind::Slice(ls, ref li, le), &PatKind::Slice(rs, ref ri, re)) => {
 +                over(ls, rs, |l, r| self.eq_pat(l, r))
 +                    && over(le, re, |l, r| self.eq_pat(l, r))
 +                    && both(li, ri, |l, r| self.eq_pat(l, r))
 +            },
 +            (&PatKind::Wild, &PatKind::Wild) => true,
 +            _ => false,
 +        }
 +    }
 +
 +    #[expect(clippy::similar_names)]
 +    fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool {
 +        match (left, right) {
 +            (&QPath::Resolved(ref lty, lpath), &QPath::Resolved(ref rty, rpath)) => {
 +                both(lty, rty, |l, r| self.eq_ty(l, r)) && self.eq_path(lpath, rpath)
 +            },
 +            (&QPath::TypeRelative(lty, lseg), &QPath::TypeRelative(rty, rseg)) => {
 +                self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg)
 +            },
 +            (&QPath::LangItem(llang_item, ..), &QPath::LangItem(rlang_item, ..)) => llang_item == rlang_item,
 +            _ => false,
 +        }
 +    }
 +
 +    pub fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool {
 +        match (left.res, right.res) {
 +            (Res::Local(l), Res::Local(r)) => l == r || self.locals.get(&l) == Some(&r),
 +            (Res::Local(_), _) | (_, Res::Local(_)) => false,
 +            _ => over(left.segments, right.segments, |l, r| self.eq_path_segment(l, r)),
 +        }
 +    }
 +
 +    fn eq_path_parameters(&mut self, left: &GenericArgs<'_>, right: &GenericArgs<'_>) -> bool {
 +        if !(left.parenthesized || right.parenthesized) {
 +            over(left.args, right.args, |l, r| self.eq_generic_arg(l, r)) // FIXME(flip1995): may not work
 +                && over(left.bindings, right.bindings, |l, r| self.eq_type_binding(l, r))
 +        } else if left.parenthesized && right.parenthesized {
 +            over(left.inputs(), right.inputs(), |l, r| self.eq_ty(l, r))
 +                && both(&Some(&left.bindings[0].ty()), &Some(&right.bindings[0].ty()), |l, r| {
 +                    self.eq_ty(l, r)
 +                })
 +        } else {
 +            false
 +        }
 +    }
 +
 +    pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
 +        left.len() == right.len() && left.iter().zip(right).all(|(l, r)| self.eq_path_segment(l, r))
 +    }
 +
 +    pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
 +        // The == of idents doesn't work with different contexts,
 +        // we have to be explicit about hygiene
 +        left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r))
 +    }
 +
 +    pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
 +        match (&left.kind, &right.kind) {
 +            (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec),
 +            (&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_array_length(ll, rl),
 +            (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => {
 +                l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty)
 +            },
 +            (&TyKind::Rptr(_, ref l_rmut), &TyKind::Rptr(_, ref r_rmut)) => {
 +                l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(l_rmut.ty, r_rmut.ty)
 +            },
 +            (&TyKind::Path(ref l), &TyKind::Path(ref r)) => self.eq_qpath(l, r),
 +            (&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)),
 +            (&TyKind::Infer, &TyKind::Infer) => true,
 +            _ => false,
 +        }
 +    }
 +
 +    fn eq_type_binding(&mut self, left: &TypeBinding<'_>, right: &TypeBinding<'_>) -> bool {
 +        left.ident.name == right.ident.name && self.eq_ty(left.ty(), right.ty())
 +    }
 +}
 +
 +/// Some simple reductions like `{ return }` => `return`
 +fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'hir ExprKind<'hir> {
 +    if let ExprKind::Block(block, _) = kind {
 +        match (block.stmts, block.expr) {
 +            // From an `if let` expression without an `else` block. The arm for the implicit wild pattern is an empty
 +            // block with an empty span.
 +            ([], None) if block.span.is_empty() => &ExprKind::Tup(&[]),
 +            // `{}` => `()`
 +            ([], None) => match snippet_opt(cx, block.span) {
 +                // Don't reduce if there are any tokens contained in the braces
 +                Some(snip)
 +                    if tokenize(&snip)
 +                        .map(|t| t.kind)
 +                        .filter(|t| {
 +                            !matches!(
 +                                t,
 +                                TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
 +                            )
 +                        })
 +                        .ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied()) =>
 +                {
 +                    kind
 +                },
 +                _ => &ExprKind::Tup(&[]),
 +            },
 +            ([], Some(expr)) => match expr.kind {
 +                // `{ return .. }` => `return ..`
 +                ExprKind::Ret(..) => &expr.kind,
 +                _ => kind,
 +            },
 +            ([stmt], None) => match stmt.kind {
 +                StmtKind::Expr(expr) | StmtKind::Semi(expr) => match expr.kind {
 +                    // `{ return ..; }` => `return ..`
 +                    ExprKind::Ret(..) => &expr.kind,
 +                    _ => kind,
 +                },
 +                _ => kind,
 +            },
 +            _ => kind,
 +        }
 +    } else {
 +        kind
 +    }
 +}
 +
 +fn swap_binop<'a>(
 +    binop: BinOpKind,
 +    lhs: &'a Expr<'a>,
 +    rhs: &'a Expr<'a>,
 +) -> Option<(BinOpKind, &'a Expr<'a>, &'a Expr<'a>)> {
 +    match binop {
 +        BinOpKind::Add | BinOpKind::Eq | BinOpKind::Ne | BinOpKind::BitAnd | BinOpKind::BitXor | BinOpKind::BitOr => {
 +            Some((binop, rhs, lhs))
 +        },
 +        BinOpKind::Lt => Some((BinOpKind::Gt, rhs, lhs)),
 +        BinOpKind::Le => Some((BinOpKind::Ge, rhs, lhs)),
 +        BinOpKind::Ge => Some((BinOpKind::Le, rhs, lhs)),
 +        BinOpKind::Gt => Some((BinOpKind::Lt, rhs, lhs)),
 +        BinOpKind::Mul // Not always commutative, e.g. with matrices. See issue #5698
 +        | BinOpKind::Shl
 +        | BinOpKind::Shr
 +        | BinOpKind::Rem
 +        | BinOpKind::Sub
 +        | BinOpKind::Div
 +        | BinOpKind::And
 +        | BinOpKind::Or => None,
 +    }
 +}
 +
 +/// Checks if the two `Option`s are both `None` or some equal values as per
 +/// `eq_fn`.
 +pub fn both<X>(l: &Option<X>, r: &Option<X>, mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
 +    l.as_ref()
 +        .map_or_else(|| r.is_none(), |x| r.as_ref().map_or(false, |y| eq_fn(x, y)))
 +}
 +
 +/// Checks if two slices are equal as per `eq_fn`.
 +pub fn over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
 +    left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y))
 +}
 +
 +/// Counts how many elements of the slices are equal as per `eq_fn`.
 +pub fn count_eq<X: Sized>(
 +    left: &mut dyn Iterator<Item = X>,
 +    right: &mut dyn Iterator<Item = X>,
 +    mut eq_fn: impl FnMut(&X, &X) -> bool,
 +) -> usize {
 +    left.zip(right).take_while(|(l, r)| eq_fn(l, r)).count()
 +}
 +
 +/// Checks if two expressions evaluate to the same value, and don't contain any side effects.
 +pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool {
 +    SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right)
 +}
 +
 +/// Type used to hash an ast element. This is different from the `Hash` trait
 +/// on ast types as this
 +/// trait would consider IDs and spans.
 +///
 +/// All expressions kind are hashed, but some might have a weaker hash.
 +pub struct SpanlessHash<'a, 'tcx> {
 +    /// Context used to evaluate constant expressions.
 +    cx: &'a LateContext<'tcx>,
 +    maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
 +    s: FxHasher,
 +}
 +
 +impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
 +    pub fn new(cx: &'a LateContext<'tcx>) -> Self {
 +        Self {
 +            cx,
 +            maybe_typeck_results: cx.maybe_typeck_results(),
 +            s: FxHasher::default(),
 +        }
 +    }
 +
 +    pub fn finish(self) -> u64 {
 +        self.s.finish()
 +    }
 +
 +    pub fn hash_block(&mut self, b: &Block<'_>) {
 +        for s in b.stmts {
 +            self.hash_stmt(s);
 +        }
 +
 +        if let Some(e) = b.expr {
 +            self.hash_expr(e);
 +        }
 +
 +        std::mem::discriminant(&b.rules).hash(&mut self.s);
 +    }
 +
 +    #[expect(clippy::too_many_lines)]
 +    pub fn hash_expr(&mut self, e: &Expr<'_>) {
 +        let simple_const = self
 +            .maybe_typeck_results
 +            .and_then(|typeck_results| constant_simple(self.cx, typeck_results, e));
 +
 +        // const hashing may result in the same hash as some unrelated node, so add a sort of
 +        // discriminant depending on which path we're choosing next
 +        simple_const.hash(&mut self.s);
 +        if simple_const.is_some() {
 +            return;
 +        }
 +
 +        std::mem::discriminant(&e.kind).hash(&mut self.s);
 +
 +        match e.kind {
 +            ExprKind::AddrOf(kind, m, e) => {
 +                std::mem::discriminant(&kind).hash(&mut self.s);
 +                m.hash(&mut self.s);
 +                self.hash_expr(e);
 +            },
 +            ExprKind::Continue(i) => {
 +                if let Some(i) = i.label {
 +                    self.hash_name(i.ident.name);
 +                }
 +            },
 +            ExprKind::Assign(l, r, _) => {
 +                self.hash_expr(l);
 +                self.hash_expr(r);
 +            },
 +            ExprKind::AssignOp(ref o, l, r) => {
 +                std::mem::discriminant(&o.node).hash(&mut self.s);
 +                self.hash_expr(l);
 +                self.hash_expr(r);
 +            },
 +            ExprKind::Block(b, _) => {
 +                self.hash_block(b);
 +            },
 +            ExprKind::Binary(op, l, r) => {
 +                std::mem::discriminant(&op.node).hash(&mut self.s);
 +                self.hash_expr(l);
 +                self.hash_expr(r);
 +            },
 +            ExprKind::Break(i, ref j) => {
 +                if let Some(i) = i.label {
 +                    self.hash_name(i.ident.name);
 +                }
 +                if let Some(j) = *j {
 +                    self.hash_expr(j);
 +                }
 +            },
 +            ExprKind::Box(e) | ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => {
 +                self.hash_expr(e);
 +            },
 +            ExprKind::Call(fun, args) => {
 +                self.hash_expr(fun);
 +                self.hash_exprs(args);
 +            },
 +            ExprKind::Cast(e, ty) | ExprKind::Type(e, ty) => {
 +                self.hash_expr(e);
 +                self.hash_ty(ty);
 +            },
 +            ExprKind::Closure(&Closure {
 +                capture_clause, body, ..
 +            }) => {
 +                std::mem::discriminant(&capture_clause).hash(&mut self.s);
 +                // closures inherit TypeckResults
-         self.hash_expr(&self.cx.tcx.hir().body(body_id).value);
++                self.hash_expr(self.cx.tcx.hir().body(body).value);
 +            },
 +            ExprKind::Field(e, ref f) => {
 +                self.hash_expr(e);
 +                self.hash_name(f.name);
 +            },
 +            ExprKind::Index(a, i) => {
 +                self.hash_expr(a);
 +                self.hash_expr(i);
 +            },
 +            ExprKind::InlineAsm(asm) => {
 +                for piece in asm.template {
 +                    match piece {
 +                        InlineAsmTemplatePiece::String(s) => s.hash(&mut self.s),
 +                        InlineAsmTemplatePiece::Placeholder {
 +                            operand_idx,
 +                            modifier,
 +                            span: _,
 +                        } => {
 +                            operand_idx.hash(&mut self.s);
 +                            modifier.hash(&mut self.s);
 +                        },
 +                    }
 +                }
 +                asm.options.hash(&mut self.s);
 +                for (op, _op_sp) in asm.operands {
 +                    match op {
 +                        InlineAsmOperand::In { reg, expr } => {
 +                            reg.hash(&mut self.s);
 +                            self.hash_expr(expr);
 +                        },
 +                        InlineAsmOperand::Out { reg, late, expr } => {
 +                            reg.hash(&mut self.s);
 +                            late.hash(&mut self.s);
 +                            if let Some(expr) = expr {
 +                                self.hash_expr(expr);
 +                            }
 +                        },
 +                        InlineAsmOperand::InOut { reg, late, expr } => {
 +                            reg.hash(&mut self.s);
 +                            late.hash(&mut self.s);
 +                            self.hash_expr(expr);
 +                        },
 +                        InlineAsmOperand::SplitInOut {
 +                            reg,
 +                            late,
 +                            in_expr,
 +                            out_expr,
 +                        } => {
 +                            reg.hash(&mut self.s);
 +                            late.hash(&mut self.s);
 +                            self.hash_expr(in_expr);
 +                            if let Some(out_expr) = out_expr {
 +                                self.hash_expr(out_expr);
 +                            }
 +                        },
 +                        InlineAsmOperand::Const { anon_const } | InlineAsmOperand::SymFn { anon_const } => {
 +                            self.hash_body(anon_const.body);
 +                        },
 +                        InlineAsmOperand::SymStatic { path, def_id: _ } => self.hash_qpath(path),
 +                    }
 +                }
 +            },
 +            ExprKind::Let(Let { pat, init, ty, .. }) => {
 +                self.hash_expr(init);
 +                if let Some(ty) = ty {
 +                    self.hash_ty(ty);
 +                }
 +                self.hash_pat(pat);
 +            },
 +            ExprKind::Err => {},
 +            ExprKind::Lit(ref l) => {
 +                l.node.hash(&mut self.s);
 +            },
 +            ExprKind::Loop(b, ref i, ..) => {
 +                self.hash_block(b);
 +                if let Some(i) = *i {
 +                    self.hash_name(i.ident.name);
 +                }
 +            },
 +            ExprKind::If(cond, then, ref else_opt) => {
 +                self.hash_expr(cond);
 +                self.hash_expr(then);
 +                if let Some(e) = *else_opt {
 +                    self.hash_expr(e);
 +                }
 +            },
 +            ExprKind::Match(e, arms, ref s) => {
 +                self.hash_expr(e);
 +
 +                for arm in arms {
 +                    self.hash_pat(arm.pat);
 +                    if let Some(ref e) = arm.guard {
 +                        self.hash_guard(e);
 +                    }
 +                    self.hash_expr(arm.body);
 +                }
 +
 +                s.hash(&mut self.s);
 +            },
 +            ExprKind::MethodCall(path, receiver, args, ref _fn_span) => {
 +                self.hash_name(path.ident.name);
 +                self.hash_expr(receiver);
 +                self.hash_exprs(args);
 +            },
 +            ExprKind::ConstBlock(ref l_id) => {
 +                self.hash_body(l_id.body);
 +            },
 +            ExprKind::Repeat(e, len) => {
 +                self.hash_expr(e);
 +                self.hash_array_length(len);
 +            },
 +            ExprKind::Ret(ref e) => {
 +                if let Some(e) = *e {
 +                    self.hash_expr(e);
 +                }
 +            },
 +            ExprKind::Path(ref qpath) => {
 +                self.hash_qpath(qpath);
 +            },
 +            ExprKind::Struct(path, fields, ref expr) => {
 +                self.hash_qpath(path);
 +
 +                for f in fields {
 +                    self.hash_name(f.ident.name);
 +                    self.hash_expr(f.expr);
 +                }
 +
 +                if let Some(e) = *expr {
 +                    self.hash_expr(e);
 +                }
 +            },
 +            ExprKind::Tup(tup) => {
 +                self.hash_exprs(tup);
 +            },
 +            ExprKind::Array(v) => {
 +                self.hash_exprs(v);
 +            },
 +            ExprKind::Unary(lop, le) => {
 +                std::mem::discriminant(&lop).hash(&mut self.s);
 +                self.hash_expr(le);
 +            },
 +        }
 +    }
 +
 +    pub fn hash_exprs(&mut self, e: &[Expr<'_>]) {
 +        for e in e {
 +            self.hash_expr(e);
 +        }
 +    }
 +
 +    pub fn hash_name(&mut self, n: Symbol) {
 +        n.hash(&mut self.s);
 +    }
 +
 +    pub fn hash_qpath(&mut self, p: &QPath<'_>) {
 +        match *p {
 +            QPath::Resolved(_, path) => {
 +                self.hash_path(path);
 +            },
 +            QPath::TypeRelative(_, path) => {
 +                self.hash_name(path.ident.name);
 +            },
 +            QPath::LangItem(lang_item, ..) => {
 +                std::mem::discriminant(&lang_item).hash(&mut self.s);
 +            },
 +        }
 +        // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s);
 +    }
 +
 +    pub fn hash_pat(&mut self, pat: &Pat<'_>) {
 +        std::mem::discriminant(&pat.kind).hash(&mut self.s);
 +        match pat.kind {
 +            PatKind::Binding(BindingAnnotation(by_ref, mutability), _, _, pat) => {
 +                std::mem::discriminant(&by_ref).hash(&mut self.s);
 +                std::mem::discriminant(&mutability).hash(&mut self.s);
 +                if let Some(pat) = pat {
 +                    self.hash_pat(pat);
 +                }
 +            },
 +            PatKind::Box(pat) => self.hash_pat(pat),
 +            PatKind::Lit(expr) => self.hash_expr(expr),
 +            PatKind::Or(pats) => {
 +                for pat in pats {
 +                    self.hash_pat(pat);
 +                }
 +            },
 +            PatKind::Path(ref qpath) => self.hash_qpath(qpath),
 +            PatKind::Range(s, e, i) => {
 +                if let Some(s) = s {
 +                    self.hash_expr(s);
 +                }
 +                if let Some(e) = e {
 +                    self.hash_expr(e);
 +                }
 +                std::mem::discriminant(&i).hash(&mut self.s);
 +            },
 +            PatKind::Ref(pat, mu) => {
 +                self.hash_pat(pat);
 +                std::mem::discriminant(&mu).hash(&mut self.s);
 +            },
 +            PatKind::Slice(l, m, r) => {
 +                for pat in l {
 +                    self.hash_pat(pat);
 +                }
 +                if let Some(pat) = m {
 +                    self.hash_pat(pat);
 +                }
 +                for pat in r {
 +                    self.hash_pat(pat);
 +                }
 +            },
 +            PatKind::Struct(ref qpath, fields, e) => {
 +                self.hash_qpath(qpath);
 +                for f in fields {
 +                    self.hash_name(f.ident.name);
 +                    self.hash_pat(f.pat);
 +                }
 +                e.hash(&mut self.s);
 +            },
 +            PatKind::Tuple(pats, e) => {
 +                for pat in pats {
 +                    self.hash_pat(pat);
 +                }
 +                e.hash(&mut self.s);
 +            },
 +            PatKind::TupleStruct(ref qpath, pats, e) => {
 +                self.hash_qpath(qpath);
 +                for pat in pats {
 +                    self.hash_pat(pat);
 +                }
 +                e.hash(&mut self.s);
 +            },
 +            PatKind::Wild => {},
 +        }
 +    }
 +
 +    pub fn hash_path(&mut self, path: &Path<'_>) {
 +        match path.res {
 +            // constant hash since equality is dependant on inter-expression context
 +            // e.g. The expressions `if let Some(x) = foo() {}` and `if let Some(y) = foo() {}` are considered equal
 +            // even though the binding names are different and they have different `HirId`s.
 +            Res::Local(_) => 1_usize.hash(&mut self.s),
 +            _ => {
 +                for seg in path.segments {
 +                    self.hash_name(seg.ident.name);
 +                    self.hash_generic_args(seg.args().args);
 +                }
 +            },
 +        }
 +    }
 +
 +    pub fn hash_stmt(&mut self, b: &Stmt<'_>) {
 +        std::mem::discriminant(&b.kind).hash(&mut self.s);
 +
 +        match &b.kind {
 +            StmtKind::Local(local) => {
 +                self.hash_pat(local.pat);
 +                if let Some(init) = local.init {
 +                    self.hash_expr(init);
 +                }
 +                if let Some(els) = local.els {
 +                    self.hash_block(els);
 +                }
 +            },
 +            StmtKind::Item(..) => {},
 +            StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
 +                self.hash_expr(expr);
 +            },
 +        }
 +    }
 +
 +    pub fn hash_guard(&mut self, g: &Guard<'_>) {
 +        match g {
 +            Guard::If(expr) | Guard::IfLet(Let { init: expr, .. }) => {
 +                self.hash_expr(expr);
 +            },
 +        }
 +    }
 +
 +    pub fn hash_lifetime(&mut self, lifetime: &Lifetime) {
 +        std::mem::discriminant(&lifetime.name).hash(&mut self.s);
 +        if let LifetimeName::Param(param_id, ref name) = lifetime.name {
 +            std::mem::discriminant(name).hash(&mut self.s);
 +            param_id.hash(&mut self.s);
 +            match name {
 +                ParamName::Plain(ref ident) => {
 +                    ident.name.hash(&mut self.s);
 +                },
 +                ParamName::Fresh | ParamName::Error => {},
 +            }
 +        }
 +    }
 +
 +    pub fn hash_ty(&mut self, ty: &Ty<'_>) {
 +        std::mem::discriminant(&ty.kind).hash(&mut self.s);
 +        self.hash_tykind(&ty.kind);
 +    }
 +
 +    pub fn hash_tykind(&mut self, ty: &TyKind<'_>) {
 +        match ty {
 +            TyKind::Slice(ty) => {
 +                self.hash_ty(ty);
 +            },
 +            &TyKind::Array(ty, len) => {
 +                self.hash_ty(ty);
 +                self.hash_array_length(len);
 +            },
 +            TyKind::Ptr(ref mut_ty) => {
 +                self.hash_ty(mut_ty.ty);
 +                mut_ty.mutbl.hash(&mut self.s);
 +            },
 +            TyKind::Rptr(lifetime, ref mut_ty) => {
 +                self.hash_lifetime(*lifetime);
 +                self.hash_ty(mut_ty.ty);
 +                mut_ty.mutbl.hash(&mut self.s);
 +            },
 +            TyKind::BareFn(bfn) => {
 +                bfn.unsafety.hash(&mut self.s);
 +                bfn.abi.hash(&mut self.s);
 +                for arg in bfn.decl.inputs {
 +                    self.hash_ty(arg);
 +                }
 +                std::mem::discriminant(&bfn.decl.output).hash(&mut self.s);
 +                match bfn.decl.output {
 +                    FnRetTy::DefaultReturn(_) => {},
 +                    FnRetTy::Return(ty) => {
 +                        self.hash_ty(ty);
 +                    },
 +                }
 +                bfn.decl.c_variadic.hash(&mut self.s);
 +            },
 +            TyKind::Tup(ty_list) => {
 +                for ty in *ty_list {
 +                    self.hash_ty(ty);
 +                }
 +            },
 +            TyKind::Path(ref qpath) => self.hash_qpath(qpath),
 +            TyKind::OpaqueDef(_, arg_list, in_trait) => {
 +                self.hash_generic_args(arg_list);
 +                in_trait.hash(&mut self.s);
 +            },
 +            TyKind::TraitObject(_, lifetime, _) => {
 +                self.hash_lifetime(*lifetime);
 +            },
 +            TyKind::Typeof(anon_const) => {
 +                self.hash_body(anon_const.body);
 +            },
 +            TyKind::Err | TyKind::Infer | TyKind::Never => {},
 +        }
 +    }
 +
 +    pub fn hash_array_length(&mut self, length: ArrayLen) {
 +        match length {
 +            ArrayLen::Infer(..) => {},
 +            ArrayLen::Body(anon_const) => self.hash_body(anon_const.body),
 +        }
 +    }
 +
 +    pub fn hash_body(&mut self, body_id: BodyId) {
 +        // swap out TypeckResults when hashing a body
 +        let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.cx.tcx.typeck_body(body_id));
-                 GenericArg::Type(ref ty) => self.hash_ty(ty),
++        self.hash_expr(self.cx.tcx.hir().body(body_id).value);
 +        self.maybe_typeck_results = old_maybe_typeck_results;
 +    }
 +
 +    fn hash_generic_args(&mut self, arg_list: &[GenericArg<'_>]) {
 +        for arg in arg_list {
 +            match *arg {
 +                GenericArg::Lifetime(l) => self.hash_lifetime(l),
++                GenericArg::Type(ty) => self.hash_ty(ty),
 +                GenericArg::Const(ref ca) => self.hash_body(ca.value.body),
 +                GenericArg::Infer(ref inf) => self.hash_ty(&inf.to_ty()),
 +            }
 +        }
 +    }
 +}
 +
 +pub fn hash_stmt(cx: &LateContext<'_>, s: &Stmt<'_>) -> u64 {
 +    let mut h = SpanlessHash::new(cx);
 +    h.hash_stmt(s);
 +    h.finish()
 +}
 +
 +pub fn hash_expr(cx: &LateContext<'_>, e: &Expr<'_>) -> u64 {
 +    let mut h = SpanlessHash::new(cx);
 +    h.hash_expr(e);
 +    h.finish()
 +}
index 3cf043f22df5dc60e92265bb06133cc341a3b9d9,0000000000000000000000000000000000000000..bdb858e1f9384b307172f2802f92d7ce4001b0ff
mode 100644,000000..100644
--- /dev/null
@@@ -1,2328 -1,0 +1,2328 @@@
- pub fn method_calls<'tcx>(
-     expr: &'tcx Expr<'tcx>,
-     max_depth: usize,
- ) -> (Vec<Symbol>, Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>, Vec<Span>) {
 +#![feature(array_chunks)]
 +#![feature(box_patterns)]
 +#![feature(control_flow_enum)]
 +#![feature(let_else)]
 +#![feature(let_chains)]
 +#![feature(lint_reasons)]
 +#![feature(once_cell)]
 +#![feature(rustc_private)]
 +#![recursion_limit = "512"]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
 +// warn on the same lints as `clippy_lints`
 +#![warn(trivial_casts, trivial_numeric_casts)]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +// warn on rustc internal lints
 +#![warn(rustc::internal)]
 +
 +// FIXME: switch to something more ergonomic here, once available.
 +// (Currently there is no way to opt into sysroot crates without `extern crate`.)
 +extern crate rustc_ast;
 +extern crate rustc_ast_pretty;
 +extern crate rustc_attr;
 +extern crate rustc_data_structures;
 +extern crate rustc_errors;
 +extern crate rustc_hir;
 +extern crate rustc_infer;
 +extern crate rustc_lexer;
 +extern crate rustc_lint;
 +extern crate rustc_middle;
 +extern crate rustc_parse_format;
 +extern crate rustc_session;
 +extern crate rustc_span;
 +extern crate rustc_target;
 +extern crate rustc_trait_selection;
 +extern crate rustc_typeck;
 +
 +#[macro_use]
 +pub mod sym_helper;
 +
 +pub mod ast_utils;
 +pub mod attrs;
 +mod check_proc_macro;
 +pub mod comparisons;
 +pub mod consts;
 +pub mod diagnostics;
 +pub mod eager_or_lazy;
 +pub mod higher;
 +mod hir_utils;
 +pub mod macros;
 +pub mod msrvs;
 +pub mod numeric_literal;
 +pub mod paths;
 +pub mod ptr;
 +pub mod qualify_min_const_fn;
 +pub mod source;
 +pub mod str_utils;
 +pub mod sugg;
 +pub mod ty;
 +pub mod usage;
 +pub mod visitors;
 +
 +pub use self::attrs::*;
 +pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
 +pub use self::hir_utils::{
 +    both, count_eq, eq_expr_value, hash_expr, hash_stmt, over, HirEqInterExpr, SpanlessEq, SpanlessHash,
 +};
 +
 +use std::collections::hash_map::Entry;
 +use std::hash::BuildHasherDefault;
 +use std::sync::OnceLock;
 +use std::sync::{Mutex, MutexGuard};
 +
 +use if_chain::if_chain;
 +use rustc_ast::ast::{self, LitKind};
 +use rustc_ast::Attribute;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_data_structures::unhash::UnhashMap;
 +use rustc_hir as hir;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_ID};
 +use rustc_hir::hir_id::{HirIdMap, HirIdSet};
 +use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
 +use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
 +use rustc_hir::{
 +    def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness, Destination, Expr,
 +    ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource,
 +    Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind,
 +    TraitRef, TyKind, UnOp,
 +};
 +use rustc_lexer::{tokenize, TokenKind};
 +use rustc_lint::{LateContext, Level, Lint, LintContext};
 +use rustc_middle::hir::place::PlaceBase;
 +use rustc_middle::ty as rustc_ty;
 +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
 +use rustc_middle::ty::binding::BindingMode;
 +use rustc_middle::ty::fast_reject::SimplifiedTypeGen::{
 +    ArraySimplifiedType, BoolSimplifiedType, CharSimplifiedType, FloatSimplifiedType, IntSimplifiedType,
 +    PtrSimplifiedType, SliceSimplifiedType, StrSimplifiedType, UintSimplifiedType,
 +};
 +use rustc_middle::ty::{
 +    layout::IntegerExt, BorrowKind, ClosureKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitable, UpvarCapture,
 +};
 +use rustc_middle::ty::{FloatTy, IntTy, UintTy};
 +use rustc_semver::RustcVersion;
 +use rustc_session::Session;
 +use rustc_span::hygiene::{ExpnKind, MacroKind};
 +use rustc_span::source_map::original_sp;
 +use rustc_span::source_map::SourceMap;
 +use rustc_span::sym;
 +use rustc_span::symbol::{kw, Symbol};
 +use rustc_span::{Span, DUMMY_SP};
 +use rustc_target::abi::Integer;
 +
 +use crate::consts::{constant, Constant};
 +use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param};
 +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) {
 +        return Some(version);
 +    } else if let Some(sess) = sess {
 +        if let Some(span) = span {
 +            sess.span_err(span, &format!("`{}` is not a valid Rust version", msrv));
 +        }
 +    }
 +    None
 +}
 +
 +pub fn meets_msrv(msrv: Option<RustcVersion>, lint_msrv: RustcVersion) -> bool {
 +    msrv.map_or(true, |msrv| msrv.meets(lint_msrv))
 +}
 +
 +#[macro_export]
 +macro_rules! extract_msrv_attr {
 +    ($context:ident) => {
 +        fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
 +            let sess = rustc_lint::LintContext::sess(cx);
 +            match $crate::get_unique_inner_attr(sess, attrs, "msrv") {
 +                Some(msrv_attr) => {
 +                    if let Some(msrv) = msrv_attr.value_str() {
 +                        self.msrv = $crate::parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span));
 +                    } else {
 +                        sess.span_err(msrv_attr.span, "bad clippy attribute");
 +                    }
 +                },
 +                _ => (),
 +            }
 +        }
 +    };
 +}
 +
 +/// If the given expression is a local binding, find the initializer expression.
 +/// If that initializer expression is another local binding, find its initializer again.
 +/// This process repeats as long as possible (but usually no more than once). Initializer
 +/// expressions with adjustments are ignored. If this is not desired, use [`find_binding_init`]
 +/// instead.
 +///
 +/// Examples:
 +/// ```
 +/// let abc = 1;
 +/// //        ^ output
 +/// let def = abc;
 +/// dbg!(def);
 +/// //   ^^^ input
 +///
 +/// // or...
 +/// let abc = 1;
 +/// let def = abc + 2;
 +/// //        ^^^^^^^ output
 +/// dbg!(def);
 +/// //   ^^^ input
 +/// ```
 +pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
 +    while let Some(init) = path_to_local(expr)
 +        .and_then(|id| find_binding_init(cx, id))
 +        .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
 +    {
 +        expr = init;
 +    }
 +    expr
 +}
 +
 +/// Finds the initializer expression for a local binding. Returns `None` if the binding is mutable.
 +/// By only considering immutable bindings, we guarantee that the returned expression represents the
 +/// value of the binding wherever it is referenced.
 +///
 +/// Example: For `let x = 1`, if the `HirId` of `x` is provided, the `Expr` `1` is returned.
 +/// Note: If you have an expression that references a binding `x`, use `path_to_local` to get the
 +/// canonical binding `HirId`.
 +pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
 +    let hir = cx.tcx.hir();
 +    if_chain! {
 +        if let Some(Node::Pat(pat)) = hir.find(hir_id);
 +        if matches!(pat.kind, PatKind::Binding(BindingAnnotation::NONE, ..));
 +        let parent = hir.get_parent_node(hir_id);
 +        if let Some(Node::Local(local)) = hir.find(parent);
 +        then {
 +            return local.init;
 +        }
 +    }
 +    None
 +}
 +
 +/// Returns `true` if the given `NodeId` is inside a constant context
 +///
 +/// # Example
 +///
 +/// ```rust,ignore
 +/// if in_constant(cx, expr.hir_id) {
 +///     // Do something
 +/// }
 +/// ```
 +pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool {
 +    let parent_id = cx.tcx.hir().get_parent_item(id);
 +    match cx.tcx.hir().get_by_def_id(parent_id) {
 +        Node::Item(&Item {
 +            kind: ItemKind::Const(..) | ItemKind::Static(..),
 +            ..
 +        })
 +        | Node::TraitItem(&TraitItem {
 +            kind: TraitItemKind::Const(..),
 +            ..
 +        })
 +        | Node::ImplItem(&ImplItem {
 +            kind: ImplItemKind::Const(..),
 +            ..
 +        })
 +        | Node::AnonConst(_) => true,
 +        Node::Item(&Item {
 +            kind: ItemKind::Fn(ref sig, ..),
 +            ..
 +        })
 +        | Node::ImplItem(&ImplItem {
 +            kind: ImplItemKind::Fn(ref sig, _),
 +            ..
 +        }) => sig.header.constness == Constness::Const,
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if a `QPath` resolves to a constructor of a `LangItem`.
 +/// For example, use this to check whether a function call or a pattern is `Some(..)`.
 +pub fn is_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem) -> bool {
 +    if let QPath::Resolved(_, path) = qpath {
 +        if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res {
 +            if let Ok(item_id) = cx.tcx.lang_items().require(lang_item) {
 +                return cx.tcx.parent(ctor_id) == item_id;
 +            }
 +        }
 +    }
 +    false
 +}
 +
 +pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
 +    matches!(
 +        expr.kind,
 +        ExprKind::Block(
 +            Block {
 +                stmts: [],
 +                expr: None,
 +                ..
 +            },
 +            _
 +        ) | ExprKind::Tup([])
 +    )
 +}
 +
 +/// Checks if given pattern is a wildcard (`_`)
 +pub fn is_wild(pat: &Pat<'_>) -> bool {
 +    matches!(pat.kind, PatKind::Wild)
 +}
 +
 +/// Checks if the method call given in `expr` belongs to the given trait.
 +/// This is a deprecated function, consider using [`is_trait_method`].
 +pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
 +    let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
 +    let trt_id = cx.tcx.trait_of_item(def_id);
 +    trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
 +}
 +
 +/// Checks if a method is defined in an impl of a diagnostic item
 +pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
 +    if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
 +        if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
 +            return cx.tcx.is_diagnostic_item(diag_item, adt.did());
 +        }
 +    }
 +    false
 +}
 +
 +/// Checks if a method is in a diagnostic item trait
 +pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
 +    if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
 +        return cx.tcx.is_diagnostic_item(diag_item, trait_did);
 +    }
 +    false
 +}
 +
 +/// Checks if the method call given in `expr` belongs to the given trait.
 +pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
 +    cx.typeck_results()
 +        .type_dependent_def_id(expr.hir_id)
 +        .map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
 +}
 +
 +/// Checks if the given expression is a path referring an item on the trait
 +/// that is marked with the given diagnostic item.
 +///
 +/// For checking method call expressions instead of path expressions, use
 +/// [`is_trait_method`].
 +///
 +/// For example, this can be used to find if an expression like `u64::default`
 +/// refers to an item of the trait `Default`, which is associated with the
 +/// `diag_item` of `sym::Default`.
 +pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
 +    if let hir::ExprKind::Path(ref qpath) = expr.kind {
 +        cx.qpath_res(qpath, expr.hir_id)
 +            .opt_def_id()
 +            .map_or(false, |def_id| is_diag_trait_item(cx, def_id, diag_item))
 +    } else {
 +        false
 +    }
 +}
 +
 +pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
 +    match *path {
 +        QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
 +        QPath::TypeRelative(_, seg) => seg,
 +        QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
 +    }
 +}
 +
 +pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
 +    last_path_segment(qpath)
 +        .args
 +        .map_or(&[][..], |a| a.args)
 +        .iter()
 +        .filter_map(|a| match a {
 +            hir::GenericArg::Type(ty) => Some(*ty),
 +            _ => None,
 +        })
 +}
 +
 +/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
 +/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
 +/// `QPath::Resolved.1.res.opt_def_id()`.
 +///
 +/// Matches a `QPath` against a slice of segment string literals.
 +///
 +/// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a
 +/// `rustc_hir::QPath`.
 +///
 +/// # Examples
 +/// ```rust,ignore
 +/// match_qpath(path, &["std", "rt", "begin_unwind"])
 +/// ```
 +pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
 +    match *path {
 +        QPath::Resolved(_, path) => match_path(path, segments),
 +        QPath::TypeRelative(ty, segment) => match ty.kind {
 +            TyKind::Path(ref inner_path) => {
 +                if let [prefix @ .., end] = segments {
 +                    if match_qpath(inner_path, prefix) {
 +                        return segment.ident.name.as_str() == *end;
 +                    }
 +                }
 +                false
 +            },
 +            _ => false,
 +        },
 +        QPath::LangItem(..) => false,
 +    }
 +}
 +
 +/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
 +///
 +/// Please use `is_path_diagnostic_item` if the target is a diagnostic item.
 +pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
 +    path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, segments))
 +}
 +
 +/// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
 +/// it matches the given diagnostic item.
 +pub fn is_path_diagnostic_item<'tcx>(
 +    cx: &LateContext<'_>,
 +    maybe_path: &impl MaybePath<'tcx>,
 +    diag_item: Symbol,
 +) -> bool {
 +    path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
 +}
 +
 +/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
 +/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
 +/// `QPath::Resolved.1.res.opt_def_id()`.
 +///
 +/// Matches a `Path` against a slice of segment string literals.
 +///
 +/// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a
 +/// `rustc_hir::Path`.
 +///
 +/// # Examples
 +///
 +/// ```rust,ignore
 +/// if match_path(&trait_ref.path, &paths::HASH) {
 +///     // This is the `std::hash::Hash` trait.
 +/// }
 +///
 +/// if match_path(ty_path, &["rustc", "lint", "Lint"]) {
 +///     // This is a `rustc_middle::lint::Lint`.
 +/// }
 +/// ```
 +pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
 +    path.segments
 +        .iter()
 +        .rev()
 +        .zip(segments.iter().rev())
 +        .all(|(a, b)| a.ident.name.as_str() == *b)
 +}
 +
 +/// If the expression is a path to a local, returns the canonical `HirId` of the local.
 +pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
 +    if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind {
 +        if let Res::Local(id) = path.res {
 +            return Some(id);
 +        }
 +    }
 +    None
 +}
 +
 +/// Returns true if the expression is a path to a local with the specified `HirId`.
 +/// Use this function to see if an expression matches a function argument or a match binding.
 +pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
 +    path_to_local(expr) == Some(id)
 +}
 +
 +pub trait MaybePath<'hir> {
 +    fn hir_id(&self) -> HirId;
 +    fn qpath_opt(&self) -> Option<&QPath<'hir>>;
 +}
 +
 +macro_rules! maybe_path {
 +    ($ty:ident, $kind:ident) => {
 +        impl<'hir> MaybePath<'hir> for hir::$ty<'hir> {
 +            fn hir_id(&self) -> HirId {
 +                self.hir_id
 +            }
 +            fn qpath_opt(&self) -> Option<&QPath<'hir>> {
 +                match &self.kind {
 +                    hir::$kind::Path(qpath) => Some(qpath),
 +                    _ => None,
 +                }
 +            }
 +        }
 +    };
 +}
 +maybe_path!(Expr, ExprKind);
 +maybe_path!(Pat, PatKind);
 +maybe_path!(Ty, TyKind);
 +
 +/// If `maybe_path` is a path node, resolves it, otherwise returns `Res::Err`
 +pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res {
 +    match maybe_path.qpath_opt() {
 +        None => Res::Err,
 +        Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()),
 +    }
 +}
 +
 +/// If `maybe_path` is a path node which resolves to an item, retrieves the item ID
 +pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option<DefId> {
 +    path_res(cx, maybe_path).opt_def_id()
 +}
 +
 +/// Resolves a def path like `std::vec::Vec`.
 +/// This function is expensive and should be used sparingly.
 +pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res {
 +    fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str) -> Option<Res> {
 +        match tcx.def_kind(def_id) {
 +            DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx
 +                .module_children(def_id)
 +                .iter()
 +                .find(|item| item.ident.name.as_str() == name)
 +                .map(|child| child.res.expect_non_local()),
 +            DefKind::Impl => tcx
 +                .associated_item_def_ids(def_id)
 +                .iter()
 +                .copied()
 +                .find(|assoc_def_id| tcx.item_name(*assoc_def_id).as_str() == name)
 +                .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)),
 +            _ => None,
 +        }
 +    }
 +    fn find_primitive<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx {
 +        let single = |ty| tcx.incoherent_impls(ty).iter().copied();
 +        let empty = || [].iter().copied();
 +        match name {
 +            "bool" => single(BoolSimplifiedType),
 +            "char" => single(CharSimplifiedType),
 +            "str" => single(StrSimplifiedType),
 +            "array" => single(ArraySimplifiedType),
 +            "slice" => single(SliceSimplifiedType),
 +            // FIXME: rustdoc documents these two using just `pointer`.
 +            //
 +            // Maybe this is something we should do here too.
 +            "const_ptr" => single(PtrSimplifiedType(Mutability::Not)),
 +            "mut_ptr" => single(PtrSimplifiedType(Mutability::Mut)),
 +            "isize" => single(IntSimplifiedType(IntTy::Isize)),
 +            "i8" => single(IntSimplifiedType(IntTy::I8)),
 +            "i16" => single(IntSimplifiedType(IntTy::I16)),
 +            "i32" => single(IntSimplifiedType(IntTy::I32)),
 +            "i64" => single(IntSimplifiedType(IntTy::I64)),
 +            "i128" => single(IntSimplifiedType(IntTy::I128)),
 +            "usize" => single(UintSimplifiedType(UintTy::Usize)),
 +            "u8" => single(UintSimplifiedType(UintTy::U8)),
 +            "u16" => single(UintSimplifiedType(UintTy::U16)),
 +            "u32" => single(UintSimplifiedType(UintTy::U32)),
 +            "u64" => single(UintSimplifiedType(UintTy::U64)),
 +            "u128" => single(UintSimplifiedType(UintTy::U128)),
 +            "f32" => single(FloatSimplifiedType(FloatTy::F32)),
 +            "f64" => single(FloatSimplifiedType(FloatTy::F64)),
 +            _ => empty(),
 +        }
 +    }
 +    fn find_crate(tcx: TyCtxt<'_>, name: &str) -> Option<DefId> {
 +        tcx.crates(())
 +            .iter()
 +            .copied()
 +            .find(|&num| tcx.crate_name(num).as_str() == name)
 +            .map(CrateNum::as_def_id)
 +    }
 +
 +    let (base, first, path) = match *path {
 +        [base, first, ref path @ ..] => (base, first, path),
 +        [primitive] => {
 +            return PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy);
 +        },
 +        _ => return Res::Err,
 +    };
 +    let tcx = cx.tcx;
 +    let starts = find_primitive(tcx, base)
 +        .chain(find_crate(tcx, base))
 +        .filter_map(|id| item_child_by_name(tcx, id, first));
 +
 +    for first in starts {
 +        let last = path
 +            .iter()
 +            .copied()
 +            // for each segment, find the child item
 +            .try_fold(first, |res, segment| {
 +                let def_id = res.def_id();
 +                if let Some(item) = item_child_by_name(tcx, def_id, segment) {
 +                    Some(item)
 +                } else if matches!(res, Res::Def(DefKind::Enum | DefKind::Struct, _)) {
 +                    // it is not a child item so check inherent impl items
 +                    tcx.inherent_impls(def_id)
 +                        .iter()
 +                        .find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment))
 +                } else {
 +                    None
 +                }
 +            });
 +
 +        if let Some(last) = last {
 +            return last;
 +        }
 +    }
 +
 +    Res::Err
 +}
 +
 +/// Convenience function to get the `DefId` of a trait by path.
 +/// It could be a trait or trait alias.
 +pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> {
 +    match def_path_res(cx, path) {
 +        Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
 +        _ => None,
 +    }
 +}
 +
 +/// Gets the `hir::TraitRef` of the trait the given method is implemented for.
 +///
 +/// Use this if you want to find the `TraitRef` of the `Add` trait in this example:
 +///
 +/// ```rust
 +/// struct Point(isize, isize);
 +///
 +/// impl std::ops::Add for Point {
 +///     type Output = Self;
 +///
 +///     fn add(self, other: Self) -> Self {
 +///         Point(0, 0)
 +///     }
 +/// }
 +/// ```
 +pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, def_id: LocalDefId) -> Option<&'tcx TraitRef<'tcx>> {
 +    // Get the implemented trait for the current function
 +    let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
 +    let parent_impl = cx.tcx.hir().get_parent_item(hir_id);
 +    if_chain! {
 +        if parent_impl != CRATE_DEF_ID;
 +        if let hir::Node::Item(item) = cx.tcx.hir().get_by_def_id(parent_impl);
 +        if let hir::ItemKind::Impl(impl_) = &item.kind;
 +        then {
 +            return impl_.of_trait.as_ref();
 +        }
 +    }
 +    None
 +}
 +
 +/// This method will return tuple of projection stack and root of the expression,
 +/// used in `can_mut_borrow_both`.
 +///
 +/// For example, if `e` represents the `v[0].a.b[x]`
 +/// this method will return a tuple, composed of a `Vec`
 +/// containing the `Expr`s for `v[0], v[0].a, v[0].a.b, v[0].a.b[x]`
 +/// and an `Expr` for root of them, `v`
 +fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
 +    let mut result = vec![];
 +    let root = loop {
 +        match e.kind {
 +            ExprKind::Index(ep, _) | ExprKind::Field(ep, _) => {
 +                result.push(e);
 +                e = ep;
 +            },
 +            _ => break e,
 +        };
 +    };
 +    result.reverse();
 +    (result, root)
 +}
 +
 +/// Gets the mutability of the custom deref adjustment, if any.
 +pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
 +    cx.typeck_results()
 +        .expr_adjustments(e)
 +        .iter()
 +        .find_map(|a| match a.kind {
 +            Adjust::Deref(Some(d)) => Some(Some(d.mutbl)),
 +            Adjust::Deref(None) => None,
 +            _ => Some(None),
 +        })
 +        .and_then(|x| x)
 +}
 +
 +/// Checks if two expressions can be mutably borrowed simultaneously
 +/// and they aren't dependent on borrowing same thing twice
 +pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
 +    let (s1, r1) = projection_stack(e1);
 +    let (s2, r2) = projection_stack(e2);
 +    if !eq_expr_value(cx, r1, r2) {
 +        return true;
 +    }
 +    if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
 +        return false;
 +    }
 +
 +    for (x1, x2) in s1.iter().zip(s2.iter()) {
 +        if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
 +            return false;
 +        }
 +
 +        match (&x1.kind, &x2.kind) {
 +            (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
 +                if i1 != i2 {
 +                    return true;
 +                }
 +            },
 +            (ExprKind::Index(_, i1), ExprKind::Index(_, i2)) => {
 +                if !eq_expr_value(cx, i1, i2) {
 +                    return false;
 +                }
 +            },
 +            _ => return false,
 +        }
 +    }
 +    false
 +}
 +
 +/// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent"
 +/// constructor from the std library
 +fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
 +    let std_types_symbols = &[
 +        sym::String,
 +        sym::Vec,
 +        sym::VecDeque,
 +        sym::LinkedList,
 +        sym::HashMap,
 +        sym::BTreeMap,
 +        sym::HashSet,
 +        sym::BTreeSet,
 +        sym::BinaryHeap,
 +    ];
 +
 +    if let QPath::TypeRelative(_, method) = path {
 +        if method.ident.name == sym::new {
 +            if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
 +                if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
 +                    return std_types_symbols
 +                        .iter()
 +                        .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did()));
 +                }
 +            }
 +        }
 +    }
 +    false
 +}
 +
 +/// Return true if the expr is equal to `Default::default` when evaluated.
 +pub fn is_default_equivalent_call(cx: &LateContext<'_>, repl_func: &Expr<'_>) -> bool {
 +    if_chain! {
 +        if let hir::ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
 +        if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
 +        if is_diag_trait_item(cx, repl_def_id, sym::Default)
 +            || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
 +        then { true } else { false }
 +    }
 +}
 +
 +/// Returns true if the expr is equal to `Default::default()` of it's type when evaluated.
 +/// It doesn't cover all cases, for example indirect function calls (some of std
 +/// functions are supported) but it is the best we have.
 +pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 +    match &e.kind {
 +        ExprKind::Lit(lit) => match lit.node {
 +            LitKind::Bool(false) | LitKind::Int(0, _) => true,
 +            LitKind::Str(s, _) => s.is_empty(),
 +            _ => false,
 +        },
 +        ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
 +        ExprKind::Repeat(x, ArrayLen::Body(len)) => if_chain! {
 +            if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind;
 +            if let LitKind::Int(v, _) = const_lit.node;
 +            if v <= 32 && is_default_equivalent(cx, x);
 +            then {
 +                true
 +            }
 +            else {
 +                false
 +            }
 +        },
 +        ExprKind::Call(repl_func, _) => is_default_equivalent_call(cx, repl_func),
 +        ExprKind::Path(qpath) => is_lang_ctor(cx, qpath, OptionNone),
 +        ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the top level expression can be moved into a closure as is.
 +/// Currently checks for:
 +/// * Break/Continue outside the given loop HIR ids.
 +/// * Yield/Return statements.
 +/// * Inline assembly.
 +/// * Usages of a field of a local where the type of the local can be partially moved.
 +///
 +/// For example, given the following function:
 +///
 +/// ```
 +/// fn f<'a>(iter: &mut impl Iterator<Item = (usize, &'a mut String)>) {
 +///     for item in iter {
 +///         let s = item.1;
 +///         if item.0 > 10 {
 +///             continue;
 +///         } else {
 +///             s.clear();
 +///         }
 +///     }
 +/// }
 +/// ```
 +///
 +/// When called on the expression `item.0` this will return false unless the local `item` is in the
 +/// `ignore_locals` set. The type `(usize, &mut String)` can have the second element moved, so it
 +/// isn't always safe to move into a closure when only a single field is needed.
 +///
 +/// When called on the `continue` expression this will return false unless the outer loop expression
 +/// is in the `loop_ids` set.
 +///
 +/// Note that this check is not recursive, so passing the `if` expression will always return true
 +/// even though sub-expressions might return false.
 +pub fn can_move_expr_to_closure_no_visit<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    loop_ids: &[HirId],
 +    ignore_locals: &HirIdSet,
 +) -> bool {
 +    match expr.kind {
 +        ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
 +        | ExprKind::Continue(Destination { target_id: Ok(id), .. })
 +            if loop_ids.contains(&id) =>
 +        {
 +            true
 +        },
 +        ExprKind::Break(..)
 +        | ExprKind::Continue(_)
 +        | ExprKind::Ret(_)
 +        | ExprKind::Yield(..)
 +        | ExprKind::InlineAsm(_) => false,
 +        // Accessing a field of a local value can only be done if the type isn't
 +        // partially moved.
 +        ExprKind::Field(
 +            &Expr {
 +                hir_id,
 +                kind:
 +                    ExprKind::Path(QPath::Resolved(
 +                        _,
 +                        Path {
 +                            res: Res::Local(local_id),
 +                            ..
 +                        },
 +                    )),
 +                ..
 +            },
 +            _,
 +        ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
 +            // TODO: check if the local has been partially moved. Assume it has for now.
 +            false
 +        },
 +        _ => true,
 +    }
 +}
 +
 +/// How a local is captured by a closure
 +#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 +pub enum CaptureKind {
 +    Value,
 +    Ref(Mutability),
 +}
 +impl CaptureKind {
 +    pub fn is_imm_ref(self) -> bool {
 +        self == Self::Ref(Mutability::Not)
 +    }
 +}
 +impl std::ops::BitOr for CaptureKind {
 +    type Output = Self;
 +    fn bitor(self, rhs: Self) -> Self::Output {
 +        match (self, rhs) {
 +            (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
 +            (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
 +            | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
 +            (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
 +        }
 +    }
 +}
 +impl std::ops::BitOrAssign for CaptureKind {
 +    fn bitor_assign(&mut self, rhs: Self) {
 +        *self = *self | rhs;
 +    }
 +}
 +
 +/// Given an expression referencing a local, determines how it would be captured in a closure.
 +/// Note as this will walk up to parent expressions until the capture can be determined it should
 +/// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or
 +/// function argument (other than a receiver).
 +pub fn capture_local_usage<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind {
 +    fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
 +        let mut capture = CaptureKind::Ref(Mutability::Not);
 +        pat.each_binding_or_first(&mut |_, id, span, _| match cx
 +            .typeck_results()
 +            .extract_binding_mode(cx.sess(), id, span)
 +            .unwrap()
 +        {
 +            BindingMode::BindByValue(_) if !is_copy(cx, cx.typeck_results().node_type(id)) => {
 +                capture = CaptureKind::Value;
 +            },
 +            BindingMode::BindByReference(Mutability::Mut) if capture != CaptureKind::Value => {
 +                capture = CaptureKind::Ref(Mutability::Mut);
 +            },
 +            _ => (),
 +        });
 +        capture
 +    }
 +
 +    debug_assert!(matches!(
 +        e.kind,
 +        ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
 +    ));
 +
 +    let mut child_id = e.hir_id;
 +    let mut capture = CaptureKind::Value;
 +    let mut capture_expr_ty = e;
 +
 +    for (parent_id, parent) in cx.tcx.hir().parent_iter(e.hir_id) {
 +        if let [
 +            Adjustment {
 +                kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
 +                target,
 +            },
 +            ref adjust @ ..,
 +        ] = *cx
 +            .typeck_results()
 +            .adjustments()
 +            .get(child_id)
 +            .map_or(&[][..], |x| &**x)
 +        {
 +            if let rustc_ty::RawPtr(TypeAndMut { mutbl: mutability, .. }) | rustc_ty::Ref(_, _, mutability) =
 +                *adjust.last().map_or(target, |a| a.target).kind()
 +            {
 +                return CaptureKind::Ref(mutability);
 +            }
 +        }
 +
 +        match parent {
 +            Node::Expr(e) => match e.kind {
 +                ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
 +                ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
 +                ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
 +                    return CaptureKind::Ref(Mutability::Mut);
 +                },
 +                ExprKind::Field(..) => {
 +                    if capture == CaptureKind::Value {
 +                        capture_expr_ty = e;
 +                    }
 +                },
 +                ExprKind::Let(let_expr) => {
 +                    let mutability = match pat_capture_kind(cx, let_expr.pat) {
 +                        CaptureKind::Value => Mutability::Not,
 +                        CaptureKind::Ref(m) => m,
 +                    };
 +                    return CaptureKind::Ref(mutability);
 +                },
 +                ExprKind::Match(_, arms, _) => {
 +                    let mut mutability = Mutability::Not;
 +                    for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
 +                        match capture {
 +                            CaptureKind::Value => break,
 +                            CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
 +                            CaptureKind::Ref(Mutability::Not) => (),
 +                        }
 +                    }
 +                    return CaptureKind::Ref(mutability);
 +                },
 +                _ => break,
 +            },
 +            Node::Local(l) => match pat_capture_kind(cx, l.pat) {
 +                CaptureKind::Value => break,
 +                capture @ CaptureKind::Ref(_) => return capture,
 +            },
 +            _ => break,
 +        }
 +
 +        child_id = parent_id;
 +    }
 +
 +    if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
 +        // Copy types are never automatically captured by value.
 +        CaptureKind::Ref(Mutability::Not)
 +    } else {
 +        capture
 +    }
 +}
 +
 +/// Checks if the expression can be moved into a closure as is. This will return a list of captures
 +/// if so, otherwise, `None`.
 +pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
 +    struct V<'cx, 'tcx> {
 +        cx: &'cx LateContext<'tcx>,
 +        // Stack of potential break targets contained in the expression.
 +        loops: Vec<HirId>,
 +        /// Local variables created in the expression. These don't need to be captured.
 +        locals: HirIdSet,
 +        /// Whether this expression can be turned into a closure.
 +        allow_closure: bool,
 +        /// Locals which need to be captured, and whether they need to be by value, reference, or
 +        /// mutable reference.
 +        captures: HirIdMap<CaptureKind>,
 +    }
 +    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if !self.allow_closure {
 +                return;
 +            }
 +
 +            match e.kind {
 +                ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
 +                    if !self.locals.contains(&l) {
 +                        let cap = capture_local_usage(self.cx, e);
 +                        self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
 +                    }
 +                },
 +                ExprKind::Closure { .. } => {
 +                    let closure_id = self.cx.tcx.hir().local_def_id(e.hir_id);
 +                    for capture in self.cx.typeck_results().closure_min_captures_flattened(closure_id) {
 +                        let local_id = match capture.place.base {
 +                            PlaceBase::Local(id) => id,
 +                            PlaceBase::Upvar(var) => var.var_path.hir_id,
 +                            _ => continue,
 +                        };
 +                        if !self.locals.contains(&local_id) {
 +                            let capture = match capture.info.capture_kind {
 +                                UpvarCapture::ByValue => CaptureKind::Value,
 +                                UpvarCapture::ByRef(kind) => match kind {
 +                                    BorrowKind::ImmBorrow => CaptureKind::Ref(Mutability::Not),
 +                                    BorrowKind::UniqueImmBorrow | BorrowKind::MutBorrow => {
 +                                        CaptureKind::Ref(Mutability::Mut)
 +                                    },
 +                                },
 +                            };
 +                            self.captures
 +                                .entry(local_id)
 +                                .and_modify(|e| *e |= capture)
 +                                .or_insert(capture);
 +                        }
 +                    }
 +                },
 +                ExprKind::Loop(b, ..) => {
 +                    self.loops.push(e.hir_id);
 +                    self.visit_block(b);
 +                    self.loops.pop();
 +                },
 +                _ => {
 +                    self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
 +                    walk_expr(self, e);
 +                },
 +            }
 +        }
 +
 +        fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
 +            p.each_binding_or_first(&mut |_, id, _, _| {
 +                self.locals.insert(id);
 +            });
 +        }
 +    }
 +
 +    let mut v = V {
 +        cx,
 +        allow_closure: true,
 +        loops: Vec::new(),
 +        locals: HirIdSet::default(),
 +        captures: HirIdMap::default(),
 +    };
 +    v.visit_expr(expr);
 +    v.allow_closure.then_some(v.captures)
 +}
 +
++/// Arguments of a method: the receiver and all the additional arguments.
++pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
++
 +/// Returns the method names and argument list of nested method call expressions that make up
 +/// `expr`. method/span lists are sorted with the most recent call first.
++pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
 +    let mut method_names = Vec::with_capacity(max_depth);
 +    let mut arg_lists = Vec::with_capacity(max_depth);
 +    let mut spans = Vec::with_capacity(max_depth);
 +
 +    let mut current = expr;
 +    for _ in 0..max_depth {
 +        if let ExprKind::MethodCall(path, receiver, args, _) = &current.kind {
 +            if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
 +                break;
 +            }
 +            method_names.push(path.ident.name);
 +            arg_lists.push((*receiver, &**args));
 +            spans.push(path.ident.span);
 +            current = receiver;
 +        } else {
 +            break;
 +        }
 +    }
 +
 +    (method_names, arg_lists, spans)
 +}
 +
 +/// Matches an `Expr` against a chain of methods, and return the matched `Expr`s.
 +///
 +/// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`,
 +/// `method_chain_args(expr, &["bar", "baz"])` will return a `Vec`
 +/// containing the `Expr`s for
 +/// `.bar()` and `.baz()`
 +pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
 +    let mut current = expr;
 +    let mut matched = Vec::with_capacity(methods.len());
 +    for method_name in methods.iter().rev() {
 +        // method chains are stored last -> first
 +        if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
 +            if path.ident.name.as_str() == *method_name {
 +                if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
 +                    return None;
 +                }
 +                matched.push((receiver, args)); // build up `matched` backwards
 +                current = receiver; // go to parent expression
 +            } else {
 +                return None;
 +            }
 +        } else {
 +            return None;
 +        }
 +    }
 +    // Reverse `matched` so that it is in the same order as `methods`.
 +    matched.reverse();
 +    Some(matched)
 +}
 +
 +/// Returns `true` if the provided `def_id` is an entrypoint to a program.
 +pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
 +    cx.tcx
 +        .entry_fn(())
 +        .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id)
 +}
 +
 +/// Returns `true` if the expression is in the program's `#[panic_handler]`.
 +pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 +    let parent = cx.tcx.hir().get_parent_item(e.hir_id);
 +    Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
 +}
 +
 +/// Gets the name of the item the expression is in, if available.
 +pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
 +    let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id);
 +    match cx.tcx.hir().find_by_def_id(parent_id) {
 +        Some(
 +            Node::Item(Item { ident, .. })
 +            | Node::TraitItem(TraitItem { ident, .. })
 +            | Node::ImplItem(ImplItem { ident, .. }),
 +        ) => Some(ident.name),
 +        _ => None,
 +    }
 +}
 +
 +pub struct ContainsName {
 +    pub name: Symbol,
 +    pub result: bool,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for ContainsName {
 +    fn visit_name(&mut self, _: Span, name: Symbol) {
 +        if self.name == name {
 +            self.result = true;
 +        }
 +    }
 +}
 +
 +/// Checks if an `Expr` contains a certain name.
 +pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool {
 +    let mut cn = ContainsName { name, result: false };
 +    cn.visit_expr(expr);
 +    cn.result
 +}
 +
 +/// Returns `true` if `expr` contains a return expression
 +pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
 +    let mut found = false;
 +    expr_visitor_no_bodies(|expr| {
 +        if !found {
 +            if let hir::ExprKind::Ret(..) = &expr.kind {
 +                found = true;
 +            }
 +        }
 +        !found
 +    })
 +    .visit_expr(expr);
 +    found
 +}
 +
 +/// Extends the span to the beginning of the spans line, incl. whitespaces.
 +///
 +/// ```rust
 +///        let x = ();
 +/// //             ^^
 +/// // will be converted to
 +///        let x = ();
 +/// // ^^^^^^^^^^^^^^
 +/// ```
 +fn line_span<T: LintContext>(cx: &T, span: Span) -> Span {
 +    let span = original_sp(span, DUMMY_SP);
 +    let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap();
 +    let line_no = source_map_and_line.line;
 +    let line_start = source_map_and_line.sf.lines(|lines| lines[line_no]);
 +    span.with_lo(line_start)
 +}
 +
 +/// Gets the parent node, if any.
 +pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option<Node<'_>> {
 +    tcx.hir().parent_iter(id).next().map(|(_, node)| node)
 +}
 +
 +/// Gets the parent expression, if any –- this is useful to constrain a lint.
 +pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
 +    get_parent_expr_for_hir(cx, e.hir_id)
 +}
 +
 +/// This retrieves the parent for the given `HirId` if it's an expression. This is useful for
 +/// constraint lints
 +pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::HirId) -> Option<&'tcx Expr<'tcx>> {
 +    match get_parent_node(cx.tcx, hir_id) {
 +        Some(Node::Expr(parent)) => Some(parent),
 +        _ => None,
 +    }
 +}
 +
 +pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
 +    let map = &cx.tcx.hir();
 +    let enclosing_node = map
 +        .get_enclosing_scope(hir_id)
 +        .and_then(|enclosing_id| map.find(enclosing_id));
 +    enclosing_node.and_then(|node| match node {
 +        Node::Block(block) => Some(block),
 +        Node::Item(&Item {
 +            kind: ItemKind::Fn(_, _, eid),
 +            ..
 +        })
 +        | Node::ImplItem(&ImplItem {
 +            kind: ImplItemKind::Fn(_, eid),
 +            ..
 +        }) => match cx.tcx.hir().body(eid).value.kind {
 +            ExprKind::Block(block, _) => Some(block),
 +            _ => None,
 +        },
 +        _ => None,
 +    })
 +}
 +
 +/// Gets the loop or closure enclosing the given expression, if any.
 +pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &Expr<'_>,
 +) -> Option<&'tcx Expr<'tcx>> {
 +    for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
 +        match node {
 +            Node::Expr(e) => match e.kind {
 +                ExprKind::Closure { .. } => {
 +                    if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
 +                        && subs.as_closure().kind() == ClosureKind::FnOnce
 +                    {
 +                        continue;
 +                    }
 +                    let is_once = walk_to_expr_usage(cx, e, |node, id| {
 +                        let Node::Expr(e) = node else {
 +                            return None;
 +                        };
 +                        match e.kind {
 +                            ExprKind::Call(f, _) if f.hir_id == id => Some(()),
 +                            ExprKind::Call(f, args) => {
 +                                let i = args.iter().position(|arg| arg.hir_id == id)?;
 +                                let sig = expr_sig(cx, f)?;
 +                                let predicates = sig
 +                                    .predicates_id()
 +                                    .map_or(cx.param_env, |id| cx.tcx.param_env(id))
 +                                    .caller_bounds();
 +                                sig.input(i).and_then(|ty| {
 +                                    ty_is_fn_once_param(cx.tcx, ty.skip_binder(), predicates).then_some(())
 +                                })
 +                            },
 +                            ExprKind::MethodCall(_, receiver, args, _) => {
 +                                let i = std::iter::once(receiver)
 +                                    .chain(args.iter())
 +                                    .position(|arg| arg.hir_id == id)?;
 +                                let id = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
 +                                let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i];
 +                                ty_is_fn_once_param(cx.tcx, ty, cx.tcx.param_env(id).caller_bounds()).then_some(())
 +                            },
 +                            _ => None,
 +                        }
 +                    })
 +                    .is_some();
 +                    if !is_once {
 +                        return Some(e);
 +                    }
 +                },
 +                ExprKind::Loop(..) => return Some(e),
 +                _ => (),
 +            },
 +            Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (),
 +            _ => break,
 +        }
 +    }
 +    None
 +}
 +
 +/// Gets the parent node if it's an impl block.
 +pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
 +    match tcx.hir().parent_iter(id).next() {
 +        Some((
 +            _,
 +            Node::Item(Item {
 +                kind: ItemKind::Impl(imp),
 +                ..
 +            }),
 +        )) => Some(imp),
 +        _ => None,
 +    }
 +}
 +
 +/// Removes blocks around an expression, only if the block contains just one expression
 +/// and no statements. Unsafe blocks are not removed.
 +///
 +/// Examples:
 +///  * `{}`               -> `{}`
 +///  * `{ x }`            -> `x`
 +///  * `{{ x }}`          -> `x`
 +///  * `{ x; }`           -> `{ x; }`
 +///  * `{ x; y }`         -> `{ x; y }`
 +///  * `{ unsafe { x } }` -> `unsafe { x }`
 +pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
 +    while let ExprKind::Block(
 +        Block {
 +            stmts: [],
 +            expr: Some(inner),
 +            rules: BlockCheckMode::DefaultBlock,
 +            ..
 +        },
 +        _,
 +    ) = expr.kind
 +    {
 +        expr = inner;
 +    }
 +    expr
 +}
 +
 +/// Removes blocks around an expression, only if the block contains just one expression
 +/// or just one expression statement with a semicolon. Unsafe blocks are not removed.
 +///
 +/// Examples:
 +///  * `{}`               -> `{}`
 +///  * `{ x }`            -> `x`
 +///  * `{ x; }`           -> `x`
 +///  * `{{ x; }}`         -> `x`
 +///  * `{ x; y }`         -> `{ x; y }`
 +///  * `{ unsafe { x } }` -> `unsafe { x }`
 +pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
 +    while let ExprKind::Block(
 +        Block {
 +            stmts: [],
 +            expr: Some(inner),
 +            rules: BlockCheckMode::DefaultBlock,
 +            ..
 +        }
 +        | Block {
 +            stmts:
 +                [
 +                    Stmt {
 +                        kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
 +                        ..
 +                    },
 +                ],
 +            expr: None,
 +            rules: BlockCheckMode::DefaultBlock,
 +            ..
 +        },
 +        _,
 +    ) = expr.kind
 +    {
 +        expr = inner;
 +    }
 +    expr
 +}
 +
 +/// Checks if the given expression is the else clause of either an `if` or `if let` expression.
 +pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
 +    let mut iter = tcx.hir().parent_iter(expr.hir_id);
 +    match iter.next() {
 +        Some((
 +            _,
 +            Node::Expr(Expr {
 +                kind: ExprKind::If(_, _, Some(else_expr)),
 +                ..
 +            }),
 +        )) => else_expr.hir_id == expr.hir_id,
 +        _ => false,
 +    }
 +}
 +
 +/// Checks whether the given expression is a constant integer of the given value.
 +/// unlike `is_integer_literal`, this version does const folding
 +pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
 +    if is_integer_literal(e, value) {
 +        return true;
 +    }
 +    let enclosing_body = cx.tcx.hir().enclosing_body_owner(e.hir_id);
 +    if let Some((Constant::Int(v), _)) = constant(cx, cx.tcx.typeck(enclosing_body), e) {
 +        return value == v;
 +    }
 +    false
 +}
 +
 +/// Checks whether the given expression is a constant literal of the given value.
 +pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
 +    // FIXME: use constant folding
 +    if let ExprKind::Lit(ref spanned) = expr.kind {
 +        if let LitKind::Int(v, _) = spanned.node {
 +            return v == value;
 +        }
 +    }
 +    false
 +}
 +
 +/// Returns `true` if the given `Expr` has been coerced before.
 +///
 +/// Examples of coercions can be found in the Nomicon at
 +/// <https://doc.rust-lang.org/nomicon/coercions.html>.
 +///
 +/// See `rustc_middle::ty::adjustment::Adjustment` and `rustc_typeck::check::coercion` for more
 +/// information on adjustments and coercions.
 +pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 +    cx.typeck_results().adjustments().get(e.hir_id).is_some()
 +}
 +
 +/// Returns the pre-expansion span if this comes from an expansion of the
 +/// macro `name`.
 +/// See also [`is_direct_expn_of`].
 +#[must_use]
 +pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
 +    loop {
 +        if span.from_expansion() {
 +            let data = span.ctxt().outer_expn_data();
 +            let new_span = data.call_site;
 +
 +            if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
 +                if mac_name.as_str() == name {
 +                    return Some(new_span);
 +                }
 +            }
 +
 +            span = new_span;
 +        } else {
 +            return None;
 +        }
 +    }
 +}
 +
 +/// Returns the pre-expansion span if the span directly comes from an expansion
 +/// of the macro `name`.
 +/// The difference with [`is_expn_of`] is that in
 +/// ```rust
 +/// # macro_rules! foo { ($name:tt!$args:tt) => { $name!$args } }
 +/// # macro_rules! bar { ($e:expr) => { $e } }
 +/// foo!(bar!(42));
 +/// ```
 +/// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only
 +/// from `bar!` by `is_direct_expn_of`.
 +#[must_use]
 +pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
 +    if span.from_expansion() {
 +        let data = span.ctxt().outer_expn_data();
 +        let new_span = data.call_site;
 +
 +        if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
 +            if mac_name.as_str() == name {
 +                return Some(new_span);
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +/// Convenience function to get the return type of a function.
 +pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> {
 +    let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
 +    let ret_ty = cx.tcx.fn_sig(fn_def_id).output();
 +    cx.tcx.erase_late_bound_regions(ret_ty)
 +}
 +
 +/// Convenience function to get the nth argument type of a function.
 +pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId, nth: usize) -> Ty<'tcx> {
 +    let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
 +    let arg = cx.tcx.fn_sig(fn_def_id).input(nth);
 +    cx.tcx.erase_late_bound_regions(arg)
 +}
 +
 +/// Checks if an expression is constructing a tuple-like enum variant or struct
 +pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    if let ExprKind::Call(fun, _) = expr.kind {
 +        if let ExprKind::Path(ref qp) = fun.kind {
 +            let res = cx.qpath_res(qp, fun.hir_id);
 +            return match res {
 +                def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
 +                def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
 +                _ => false,
 +            };
 +        }
 +    }
 +    false
 +}
 +
 +/// Returns `true` if a pattern is refutable.
 +// TODO: should be implemented using rustc/mir_build/thir machinery
 +pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
 +    fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
 +        matches!(
 +            cx.qpath_res(qpath, id),
 +            def::Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
 +        )
 +    }
 +
 +    fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
 +        i.into_iter().any(|pat| is_refutable(cx, pat))
 +    }
 +
 +    match pat.kind {
 +        PatKind::Wild => false,
 +        PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)),
 +        PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
 +        PatKind::Lit(..) | PatKind::Range(..) => true,
 +        PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
 +        PatKind::Or(pats) => {
 +            // TODO: should be the honest check, that pats is exhaustive set
 +            are_refutable(cx, pats)
 +        },
 +        PatKind::Tuple(pats, _) => are_refutable(cx, pats),
 +        PatKind::Struct(ref qpath, fields, _) => {
 +            is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
 +        },
 +        PatKind::TupleStruct(ref qpath, pats, _) => is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats),
 +        PatKind::Slice(head, middle, tail) => {
 +            match &cx.typeck_results().node_type(pat.hir_id).kind() {
 +                rustc_ty::Slice(..) => {
 +                    // [..] is the only irrefutable slice pattern.
 +                    !head.is_empty() || middle.is_none() || !tail.is_empty()
 +                },
 +                rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
 +                _ => {
 +                    // unreachable!()
 +                    true
 +                },
 +            }
 +        },
 +    }
 +}
 +
 +/// If the pattern is an `or` pattern, call the function once for each sub pattern. Otherwise, call
 +/// the function once on the given pattern.
 +pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
 +    if let PatKind::Or(pats) = pat.kind {
 +        pats.iter().for_each(f);
 +    } else {
 +        f(pat);
 +    }
 +}
 +
 +pub fn is_self(slf: &Param<'_>) -> bool {
 +    if let PatKind::Binding(.., name, _) = slf.pat.kind {
 +        name.name == kw::SelfLower
 +    } else {
 +        false
 +    }
 +}
 +
 +pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
 +    if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind {
 +        if let Res::SelfTy { .. } = path.res {
 +            return true;
 +        }
 +    }
 +    false
 +}
 +
 +pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
 +    (0..decl.inputs.len()).map(move |i| &body.params[i])
 +}
 +
 +/// Checks if a given expression is a match expression expanded from the `?`
 +/// operator or the `try` macro.
 +pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
 +    fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
 +        if_chain! {
 +            if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind;
 +            if ddpos.as_opt_usize().is_none();
 +            if is_lang_ctor(cx, path, ResultOk);
 +            if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind;
 +            if path_to_local_id(arm.body, hir_id);
 +            then {
 +                return true;
 +            }
 +        }
 +        false
 +    }
 +
 +    fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
 +        if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
 +            is_lang_ctor(cx, path, ResultErr)
 +        } else {
 +            false
 +        }
 +    }
 +
 +    if let ExprKind::Match(_, arms, ref source) = expr.kind {
 +        // desugared from a `?` operator
 +        if *source == MatchSource::TryDesugar {
 +            return Some(expr);
 +        }
 +
 +        if_chain! {
 +            if arms.len() == 2;
 +            if arms[0].guard.is_none();
 +            if arms[1].guard.is_none();
 +            if (is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0]));
 +            then {
 +                return Some(expr);
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +/// Returns `true` if the lint is allowed in the current context. This is useful for
 +/// skipping long running code when it's unnecessary
 +///
 +/// This function should check the lint level for the same node, that the lint will
 +/// be emitted at. If the information is buffered to be emitted at a later point, please
 +/// make sure to use `span_lint_hir` functions to emit the lint. This ensures that
 +/// expectations at the checked nodes will be fulfilled.
 +pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
 +    cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
 +}
 +
 +pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
 +    while let PatKind::Ref(subpat, _) = pat.kind {
 +        pat = subpat;
 +    }
 +    pat
 +}
 +
 +pub fn int_bits(tcx: TyCtxt<'_>, ity: rustc_ty::IntTy) -> u64 {
 +    Integer::from_int_ty(&tcx, ity).size().bits()
 +}
 +
 +#[expect(clippy::cast_possible_wrap)]
 +/// Turn a constant int byte representation into an i128
 +pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::IntTy) -> i128 {
 +    let amt = 128 - int_bits(tcx, ity);
 +    ((u as i128) << amt) >> amt
 +}
 +
 +#[expect(clippy::cast_sign_loss)]
 +/// clip unused bytes
 +pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: rustc_ty::IntTy) -> u128 {
 +    let amt = 128 - int_bits(tcx, ity);
 +    ((u as u128) << amt) >> amt
 +}
 +
 +/// clip unused bytes
 +pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::UintTy) -> u128 {
 +    let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
 +    let amt = 128 - bits;
 +    (u << amt) >> amt
 +}
 +
 +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 has_attr(map.attrs(enclosing_node), symbol) {
 +            return true;
 +        }
 +        prev_enclosing_node = Some(enclosing_node);
 +        enclosing_node = map.local_def_id_to_hir_id(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:
 +///
 +/// ```rust,ignore
 +/// if let Some(args) = match_function_call(cx, cmp_max_call, &paths::CMP_MAX);
 +/// ```
 +pub fn match_function_call<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    path: &[&str],
 +) -> Option<&'tcx [Expr<'tcx>]> {
 +    if_chain! {
 +        if let ExprKind::Call(fun, args) = expr.kind;
 +        if let ExprKind::Path(ref qpath) = fun.kind;
 +        if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
 +        if match_def_path(cx, fun_def_id, path);
 +        then {
 +            return Some(args);
 +        }
 +    };
 +    None
 +}
 +
 +/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if
 +/// any.
 +///
 +/// Please use `tcx.get_diagnostic_name` if the targets are all diagnostic items.
 +pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
 +    let search_path = cx.get_def_path(did);
 +    paths
 +        .iter()
 +        .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
 +}
 +
 +/// Checks if the given `DefId` matches the path.
 +pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {
 +    // We should probably move to Symbols in Clippy as well rather than interning every time.
 +    let path = cx.get_def_path(did);
 +    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)
 +}
 +
 +/// Returns the list of condition expressions and the list of blocks in a
 +/// sequence of `if/else`.
 +/// E.g., this returns `([a, b], [c, d, e])` for the expression
 +/// `if a { c } else if b { d } else { e }`.
 +pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
 +    let mut conds = Vec::new();
 +    let mut blocks: Vec<&Block<'_>> = Vec::new();
 +
 +    while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
 +        conds.push(cond);
 +        if let ExprKind::Block(block, _) = then.kind {
 +            blocks.push(block);
 +        } else {
 +            panic!("ExprKind::If node is not an ExprKind::Block");
 +        }
 +
 +        if let Some(else_expr) = r#else {
 +            expr = else_expr;
 +        } else {
 +            break;
 +        }
 +    }
 +
 +    // final `else {..}`
 +    if !blocks.is_empty() {
 +        if let ExprKind::Block(block, _) = expr.kind {
 +            blocks.push(block);
 +        }
 +    }
 +
 +    (conds, blocks)
 +}
 +
 +/// Checks if the given function kind is an async function.
 +pub fn is_async_fn(kind: FnKind<'_>) -> bool {
 +    matches!(kind, FnKind::ItemFn(_, _, header) if header.asyncness == IsAsync::Async)
 +}
 +
 +/// Peels away all the compiler generated code surrounding the body of an async function,
 +pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
 +    if let ExprKind::Call(
 +        _,
 +        &[
 +            Expr {
 +                kind: ExprKind::Closure(&Closure { body, .. }),
 +                ..
 +            },
 +        ],
 +    ) = body.value.kind
 +    {
 +        if let ExprKind::Block(
 +            Block {
 +                stmts: [],
 +                expr:
 +                    Some(Expr {
 +                        kind: ExprKind::DropTemps(expr),
 +                        ..
 +                    }),
 +                ..
 +            },
 +            _,
 +        ) = tcx.hir().body(body).value.kind
 +        {
 +            return Some(expr);
 +        }
 +    };
 +    None
 +}
 +
 +// check if expr is calling method or function with #[must_use] attribute
 +pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    let did = match expr.kind {
 +        ExprKind::Call(path, _) => if_chain! {
 +            if let ExprKind::Path(ref qpath) = path.kind;
 +            if let def::Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id);
 +            then {
 +                Some(did)
 +            } else {
 +                None
 +            }
 +        },
 +        ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
 +        _ => None,
 +    };
 +
 +    did.map_or(false, |did| cx.tcx.has_attr(did, sym::must_use))
 +}
 +
 +/// Checks if an expression represents the identity function
 +/// Only examines closures and `std::convert::identity`
 +pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    /// Checks if a function's body represents the identity function. Looks for bodies of the form:
 +    /// * `|x| x`
 +    /// * `|x| return x`
 +    /// * `|x| { return x }`
 +    /// * `|x| { return x; }`
 +    fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
 +        let id = if_chain! {
 +            if let [param] = func.params;
 +            if let PatKind::Binding(_, id, _, _) = param.pat.kind;
 +            then {
 +                id
 +            } else {
 +                return false;
 +            }
 +        };
 +
 +        let mut expr = func.value;
 +        loop {
 +            match expr.kind {
 +                #[rustfmt::skip]
 +                ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _, )
 +                | ExprKind::Ret(Some(e)) => expr = e,
 +                #[rustfmt::skip]
 +                ExprKind::Block(&Block { stmts: [stmt], expr: None, .. }, _) => {
 +                    if_chain! {
 +                        if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind;
 +                        if let ExprKind::Ret(Some(ret_val)) = e.kind;
 +                        then {
 +                            expr = ret_val;
 +                        } else {
 +                            return false;
 +                        }
 +                    }
 +                },
 +                _ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(),
 +            }
 +        }
 +    }
 +
 +    match expr.kind {
 +        ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir().body(body)),
 +        _ => path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, &paths::CONVERT_IDENTITY)),
 +    }
 +}
 +
 +/// Gets the node where an expression is either used, or it's type is unified with another branch.
 +/// Returns both the node and the `HirId` of the closest child node.
 +pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
 +    let mut child_id = expr.hir_id;
 +    let mut iter = tcx.hir().parent_iter(child_id);
 +    loop {
 +        match iter.next() {
 +            None => break None,
 +            Some((id, Node::Block(_))) => child_id = id,
 +            Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
 +            Some((_, Node::Expr(expr))) => match expr.kind {
 +                ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
 +                ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
 +                ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
 +                _ => break Some((Node::Expr(expr), child_id)),
 +            },
 +            Some((_, node)) => break Some((node, child_id)),
 +        }
 +    }
 +}
 +
 +/// Checks if the result of an expression is used, or it's type is unified with another branch.
 +pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
 +    !matches!(
 +        get_expr_use_or_unification_node(tcx, expr),
 +        None | Some((
 +            Node::Stmt(Stmt {
 +                kind: StmtKind::Expr(_)
 +                    | StmtKind::Semi(_)
 +                    | StmtKind::Local(Local {
 +                        pat: Pat {
 +                            kind: PatKind::Wild,
 +                            ..
 +                        },
 +                        ..
 +                    }),
 +                ..
 +            }),
 +            _
 +        ))
 +    )
 +}
 +
 +/// Checks if the expression is the final expression returned from a block.
 +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 normal) = attr.kind {
 +            normal.item.path == sym::no_std
 +        } else {
 +            false
 +        }
 +    })
 +}
 +
 +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 normal) = attr.kind {
 +            normal.item.path == sym::no_core
 +        } else {
 +            false
 +        }
 +    })
 +}
 +
 +/// Check if parent of a hir node is a trait implementation block.
 +/// For example, `f` in
 +/// ```rust
 +/// # struct S;
 +/// # trait Trait { fn f(); }
 +/// impl Trait for S {
 +///     fn f() {}
 +/// }
 +/// ```
 +pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
 +    if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
 +        matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
 +    } else {
 +        false
 +    }
 +}
 +
 +/// Check if it's even possible to satisfy the `where` clause for the item.
 +///
 +/// `trivial_bounds` feature allows functions with unsatisfiable bounds, for example:
 +///
 +/// ```ignore
 +/// fn foo() where i32: Iterator {
 +///     for _ in 2i32 {}
 +/// }
 +/// ```
 +pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
 +    use rustc_trait_selection::traits;
 +    let predicates = cx
 +        .tcx
 +        .predicates_of(did)
 +        .predicates
 +        .iter()
 +        .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
 +    traits::impossible_predicates(
 +        cx.tcx,
 +        traits::elaborate_predicates(cx.tcx, predicates)
 +            .map(|o| o.predicate)
 +            .collect::<Vec<_>>(),
 +    )
 +}
 +
 +/// Returns the `DefId` of the callee if the given expression is a function or method call.
 +pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
 +    match &expr.kind {
 +        ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
 +        ExprKind::Call(
 +            Expr {
 +                kind: ExprKind::Path(qpath),
 +                hir_id: path_hir_id,
 +                ..
 +            },
 +            ..,
 +        ) => {
 +            // Only return Fn-like DefIds, not the DefIds of statics/consts/etc that contain or
 +            // deref to fn pointers, dyn Fn, impl Fn - #8850
 +            if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
 +                cx.typeck_results().qpath_res(qpath, *path_hir_id)
 +            {
 +                Some(id)
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +/// Returns Option<String> where String is a textual representation of the type encapsulated in the
 +/// slice iff the given expression is a slice of primitives (as defined in the
 +/// `is_recursively_primitive_type` function) and None otherwise.
 +pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
 +    let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
 +    let expr_kind = expr_type.kind();
 +    let is_primitive = match expr_kind {
 +        rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
 +        rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
 +            if let rustc_ty::Slice(element_type) = inner_ty.kind() {
 +                is_recursively_primitive_type(*element_type)
 +            } else {
 +                unreachable!()
 +            }
 +        },
 +        _ => false,
 +    };
 +
 +    if is_primitive {
 +        // if we have wrappers like Array, Slice or Tuple, print these
 +        // and get the type enclosed in the slice ref
 +        match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
 +            rustc_ty::Slice(..) => return Some("slice".into()),
 +            rustc_ty::Array(..) => return Some("array".into()),
 +            rustc_ty::Tuple(..) => return Some("tuple".into()),
 +            _ => {
 +                // is_recursively_primitive_type() should have taken care
 +                // of the rest and we can rely on the type that is found
 +                let refs_peeled = expr_type.peel_refs();
 +                return Some(refs_peeled.walk().last().unwrap().to_string());
 +            },
 +        }
 +    }
 +    None
 +}
 +
 +/// returns list of all pairs (a, b) from `exprs` such that `eq(a, b)`
 +/// `hash` must be comformed with `eq`
 +pub fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
 +where
 +    Hash: Fn(&T) -> u64,
 +    Eq: Fn(&T, &T) -> bool,
 +{
 +    match exprs {
 +        [a, b] if eq(a, b) => return vec![(a, b)],
 +        _ if exprs.len() <= 2 => return vec![],
 +        _ => {},
 +    }
 +
 +    let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
 +
 +    let mut map: UnhashMap<u64, Vec<&_>> =
 +        UnhashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
 +
 +    for expr in exprs {
 +        match map.entry(hash(expr)) {
 +            Entry::Occupied(mut o) => {
 +                for o in o.get() {
 +                    if eq(o, expr) {
 +                        match_expr_list.push((o, expr));
 +                    }
 +                }
 +                o.get_mut().push(expr);
 +            },
 +            Entry::Vacant(v) => {
 +                v.insert(vec![expr]);
 +            },
 +        }
 +    }
 +
 +    match_expr_list
 +}
 +
 +/// Peels off all references on the pattern. Returns the underlying pattern and the number of
 +/// references removed.
 +pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
 +    fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
 +        if let PatKind::Ref(pat, _) = pat.kind {
 +            peel(pat, count + 1)
 +        } else {
 +            (pat, count)
 +        }
 +    }
 +    peel(pat, 0)
 +}
 +
 +/// Peels of expressions while the given closure returns `Some`.
 +pub fn peel_hir_expr_while<'tcx>(
 +    mut expr: &'tcx Expr<'tcx>,
 +    mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
 +) -> &'tcx Expr<'tcx> {
 +    while let Some(e) = f(expr) {
 +        expr = e;
 +    }
 +    expr
 +}
 +
 +/// Peels off up to the given number of references on the expression. Returns the underlying
 +/// expression and the number of references removed.
 +pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
 +    let mut remaining = count;
 +    let e = peel_hir_expr_while(expr, |e| match e.kind {
 +        ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
 +            remaining -= 1;
 +            Some(e)
 +        },
 +        _ => None,
 +    });
 +    (e, count - remaining)
 +}
 +
 +/// Peels off all references on the expression. Returns the underlying expression and the number of
 +/// references removed.
 +pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
 +    let mut count = 0;
 +    let e = peel_hir_expr_while(expr, |e| match e.kind {
 +        ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
 +            count += 1;
 +            Some(e)
 +        },
 +        _ => None,
 +    });
 +    (e, count)
 +}
 +
 +/// Peels off all references on the type. Returns the underlying type and the number of references
 +/// removed.
 +pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
 +    let mut count = 0;
 +    loop {
 +        match &ty.kind {
 +            TyKind::Rptr(_, ref_ty) => {
 +                ty = ref_ty.ty;
 +                count += 1;
 +            },
 +            _ => break (ty, count),
 +        }
 +    }
 +}
 +
 +/// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
 +/// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
 +pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
 +    loop {
 +        match expr.kind {
 +            ExprKind::AddrOf(_, _, e) => expr = e,
 +            ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
 +            _ => break,
 +        }
 +    }
 +    expr
 +}
 +
 +pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
 +    if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
 +        if let Res::Def(_, def_id) = path.res {
 +            return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
 +        }
 +    }
 +    false
 +}
 +
 +static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = OnceLock::new();
 +
 +fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
 +    let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
 +    let mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap();
 +    let value = map.entry(module);
 +    match value {
 +        Entry::Occupied(entry) => f(entry.get()),
 +        Entry::Vacant(entry) => {
 +            let mut names = Vec::new();
 +            for id in tcx.hir().module_items(module) {
 +                if matches!(tcx.def_kind(id.def_id), DefKind::Const)
 +                    && let item = tcx.hir().item(id)
 +                    && let ItemKind::Const(ty, _body) = item.kind {
 +                    if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
 +                        // We could also check for the type name `test::TestDescAndFn`
 +                        if let Res::Def(DefKind::Struct, _) = path.res {
 +                            let has_test_marker = tcx
 +                                .hir()
 +                                .attrs(item.hir_id())
 +                                .iter()
 +                                .any(|a| a.has_name(sym::rustc_test_marker));
 +                            if has_test_marker {
 +                                names.push(item.ident.name);
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +            names.sort_unstable();
 +            f(entry.insert(names))
 +        },
 +    }
 +}
 +
 +/// Checks if the function containing the given `HirId` is a `#[test]` function
 +///
 +/// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
 +pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
 +    with_test_item_names(tcx, tcx.parent_module(id), |names| {
 +        tcx.hir()
 +            .parent_iter(id)
 +            // Since you can nest functions we need to collect all until we leave
 +            // function scope
 +            .any(|(_id, node)| {
 +                if let Node::Item(item) = node {
 +                    if let ItemKind::Fn(_, _, _) = item.kind {
 +                        // Note that we have sorted the item names in the visitor,
 +                        // so the binary_search gets the same as `contains`, but faster.
 +                        return names.binary_search(&item.ident.name).is_ok();
 +                    }
 +                }
 +                false
 +            })
 +    })
 +}
 +
 +/// Checks if the item containing the given `HirId` has `#[cfg(test)]` attribute applied
 +///
 +/// Note: Add `// compile-flags: --test` to UI tests with a `#[cfg(test)]` function
 +pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
 +    fn is_cfg_test(attr: &Attribute) -> bool {
 +        if attr.has_name(sym::cfg)
 +            && let Some(items) = attr.meta_item_list()
 +            && let [item] = &*items
 +            && item.has_name(sym::test)
 +        {
 +            true
 +        } else {
 +            false
 +        }
 +    }
 +    tcx.hir()
 +        .parent_iter(id)
 +        .flat_map(|(parent_id, _)| tcx.hir().attrs(parent_id))
 +        .any(is_cfg_test)
 +}
 +
 +/// Checks whether item either has `test` attribute applied, or
 +/// is a module with `test` in its name.
 +///
 +/// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
 +pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
 +    is_in_test_function(tcx, item.hir_id())
 +        || matches!(item.kind, ItemKind::Mod(..))
 +            && item.ident.name.as_str().split('_').any(|a| a == "test" || a == "tests")
 +}
 +
 +/// Walks the HIR tree from the given expression, up to the node where the value produced by the
 +/// expression is consumed. Calls the function for every node encountered this way until it returns
 +/// `Some`.
 +///
 +/// This allows walking through `if`, `match`, `break`, block expressions to find where the value
 +/// produced by the expression is consumed.
 +pub fn walk_to_expr_usage<'tcx, T>(
 +    cx: &LateContext<'tcx>,
 +    e: &Expr<'tcx>,
 +    mut f: impl FnMut(Node<'tcx>, HirId) -> Option<T>,
 +) -> Option<T> {
 +    let map = cx.tcx.hir();
 +    let mut iter = map.parent_iter(e.hir_id);
 +    let mut child_id = e.hir_id;
 +
 +    while let Some((parent_id, parent)) = iter.next() {
 +        if let Some(x) = f(parent, child_id) {
 +            return Some(x);
 +        }
 +        let parent = match parent {
 +            Node::Expr(e) => e,
 +            Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
 +                child_id = parent_id;
 +                continue;
 +            },
 +            Node::Arm(a) if a.body.hir_id == child_id => {
 +                child_id = parent_id;
 +                continue;
 +            },
 +            _ => return None,
 +        };
 +        match parent.kind {
 +            ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
 +            ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
 +                child_id = id;
 +                iter = map.parent_iter(id);
 +            },
 +            ExprKind::Block(..) => child_id = parent_id,
 +            _ => return None,
 +        }
 +    }
 +    None
 +}
 +
 +/// Checks whether a given span has any comment token
 +/// This checks for all types of comment: line "//", block "/**", doc "///" "//!"
 +pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
 +    let Ok(snippet) = sm.span_to_snippet(span) else { return false };
 +    return tokenize(&snippet).any(|token| {
 +        matches!(
 +            token.kind,
 +            TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
 +        )
 +    });
 +}
 +
 +macro_rules! op_utils {
 +    ($($name:ident $assign:ident)*) => {
 +        /// Binary operation traits like `LangItem::Add`
 +        pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
 +
 +        /// Operator-Assign traits like `LangItem::AddAssign`
 +        pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
 +
 +        /// Converts `BinOpKind::Add` to `(LangItem::Add, LangItem::AddAssign)`, for example
 +        pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
 +            match kind {
 +                $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
 +                _ => None,
 +            }
 +        }
 +    };
 +}
 +
 +op_utils! {
 +    Add    AddAssign
 +    Sub    SubAssign
 +    Mul    MulAssign
 +    Div    DivAssign
 +    Rem    RemAssign
 +    BitXor BitXorAssign
 +    BitAnd BitAndAssign
 +    BitOr  BitOrAssign
 +    Shl    ShlAssign
 +    Shr    ShrAssign
 +}
index 43e53f3feebd31429644fca6943951a93720019c,0000000000000000000000000000000000000000..bd89ff977f87791dd6deacaf193b34e7781ffd62
mode 100644,000000..100644
--- /dev/null
@@@ -1,897 -1,0 +1,899 @@@
-         unescape_literal(inner, mode, &mut |_, ch| {
-             unescaped.push(ch.unwrap());
 +#![allow(clippy::similar_names)] // `expr` and `expn`
 +
 +use crate::is_path_diagnostic_item;
 +use crate::source::snippet_opt;
 +use crate::visitors::expr_visitor_no_bodies;
 +
 +use arrayvec::ArrayVec;
 +use itertools::{izip, Either, Itertools};
 +use rustc_ast::ast::LitKind;
 +use rustc_hir::intravisit::Visitor;
 +use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath};
 +use rustc_lexer::unescape::unescape_literal;
 +use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
 +use rustc_lint::LateContext;
 +use rustc_parse_format::{self as rpf, Alignment};
 +use rustc_span::def_id::DefId;
 +use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
 +use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Pos, Span, SpanData, Symbol};
 +use std::ops::ControlFlow;
 +
 +const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[
 +    sym::assert_eq_macro,
 +    sym::assert_macro,
 +    sym::assert_ne_macro,
 +    sym::debug_assert_eq_macro,
 +    sym::debug_assert_macro,
 +    sym::debug_assert_ne_macro,
 +    sym::eprint_macro,
 +    sym::eprintln_macro,
 +    sym::format_args_macro,
 +    sym::format_macro,
 +    sym::print_macro,
 +    sym::println_macro,
 +    sym::std_panic_macro,
 +    sym::write_macro,
 +    sym::writeln_macro,
 +];
 +
 +/// Returns true if a given Macro `DefId` is a format macro (e.g. `println!`)
 +pub fn is_format_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool {
 +    if let Some(name) = cx.tcx.get_diagnostic_name(macro_def_id) {
 +        FORMAT_MACRO_DIAG_ITEMS.contains(&name)
 +    } else {
 +        false
 +    }
 +}
 +
 +/// A macro call, like `vec![1, 2, 3]`.
 +///
 +/// Use `tcx.item_name(macro_call.def_id)` to get the macro name.
 +/// Even better is to check if it is a diagnostic item.
 +///
 +/// This structure is similar to `ExpnData` but it precludes desugaring expansions.
 +#[derive(Debug)]
 +pub struct MacroCall {
 +    /// Macro `DefId`
 +    pub def_id: DefId,
 +    /// Kind of macro
 +    pub kind: MacroKind,
 +    /// The expansion produced by the macro call
 +    pub expn: ExpnId,
 +    /// Span of the macro call site
 +    pub span: Span,
 +}
 +
 +impl MacroCall {
 +    pub fn is_local(&self) -> bool {
 +        span_is_local(self.span)
 +    }
 +}
 +
 +/// Returns an iterator of expansions that created the given span
 +pub fn expn_backtrace(mut span: Span) -> impl Iterator<Item = (ExpnId, ExpnData)> {
 +    std::iter::from_fn(move || {
 +        let ctxt = span.ctxt();
 +        if ctxt == SyntaxContext::root() {
 +            return None;
 +        }
 +        let expn = ctxt.outer_expn();
 +        let data = expn.expn_data();
 +        span = data.call_site;
 +        Some((expn, data))
 +    })
 +}
 +
 +/// Checks whether the span is from the root expansion or a locally defined macro
 +pub fn span_is_local(span: Span) -> bool {
 +    !span.from_expansion() || expn_is_local(span.ctxt().outer_expn())
 +}
 +
 +/// Checks whether the expansion is the root expansion or a locally defined macro
 +pub fn expn_is_local(expn: ExpnId) -> bool {
 +    if expn == ExpnId::root() {
 +        return true;
 +    }
 +    let data = expn.expn_data();
 +    let backtrace = expn_backtrace(data.call_site);
 +    std::iter::once((expn, data))
 +        .chain(backtrace)
 +        .find_map(|(_, data)| data.macro_def_id)
 +        .map_or(true, DefId::is_local)
 +}
 +
 +/// Returns an iterator of macro expansions that created the given span.
 +/// Note that desugaring expansions are skipped.
 +pub fn macro_backtrace(span: Span) -> impl Iterator<Item = MacroCall> {
 +    expn_backtrace(span).filter_map(|(expn, data)| match data {
 +        ExpnData {
 +            kind: ExpnKind::Macro(kind, _),
 +            macro_def_id: Some(def_id),
 +            call_site: span,
 +            ..
 +        } => Some(MacroCall {
 +            def_id,
 +            kind,
 +            expn,
 +            span,
 +        }),
 +        _ => None,
 +    })
 +}
 +
 +/// If the macro backtrace of `span` has a macro call at the root expansion
 +/// (i.e. not a nested macro call), returns `Some` with the `MacroCall`
 +pub fn root_macro_call(span: Span) -> Option<MacroCall> {
 +    macro_backtrace(span).last()
 +}
 +
 +/// Like [`root_macro_call`], but only returns `Some` if `node` is the "first node"
 +/// produced by the macro call, as in [`first_node_in_macro`].
 +pub fn root_macro_call_first_node(cx: &LateContext<'_>, node: &impl HirNode) -> Option<MacroCall> {
 +    if first_node_in_macro(cx, node) != Some(ExpnId::root()) {
 +        return None;
 +    }
 +    root_macro_call(node.span())
 +}
 +
 +/// Like [`macro_backtrace`], but only returns macro calls where `node` is the "first node" of the
 +/// macro call, as in [`first_node_in_macro`].
 +pub fn first_node_macro_backtrace(cx: &LateContext<'_>, node: &impl HirNode) -> impl Iterator<Item = MacroCall> {
 +    let span = node.span();
 +    first_node_in_macro(cx, node)
 +        .into_iter()
 +        .flat_map(move |expn| macro_backtrace(span).take_while(move |macro_call| macro_call.expn != expn))
 +}
 +
 +/// If `node` is the "first node" in a macro expansion, returns `Some` with the `ExpnId` of the
 +/// macro call site (i.e. the parent of the macro expansion). This generally means that `node`
 +/// is the outermost node of an entire macro expansion, but there are some caveats noted below.
 +/// This is useful for finding macro calls while visiting the HIR without processing the macro call
 +/// at every node within its expansion.
 +///
 +/// If you already have immediate access to the parent node, it is simpler to
 +/// just check the context of that span directly (e.g. `parent.span.from_expansion()`).
 +///
 +/// If a macro call is in statement position, it expands to one or more statements.
 +/// In that case, each statement *and* their immediate descendants will all yield `Some`
 +/// with the `ExpnId` of the containing block.
 +///
 +/// A node may be the "first node" of multiple macro calls in a macro backtrace.
 +/// The expansion of the outermost macro call site is returned in such cases.
 +pub fn first_node_in_macro(cx: &LateContext<'_>, node: &impl HirNode) -> Option<ExpnId> {
 +    // get the macro expansion or return `None` if not found
 +    // `macro_backtrace` importantly ignores desugaring expansions
 +    let expn = macro_backtrace(node.span()).next()?.expn;
 +
 +    // get the parent node, possibly skipping over a statement
 +    // if the parent is not found, it is sensible to return `Some(root)`
 +    let hir = cx.tcx.hir();
 +    let mut parent_iter = hir.parent_iter(node.hir_id());
 +    let (parent_id, _) = match parent_iter.next() {
 +        None => return Some(ExpnId::root()),
 +        Some((_, Node::Stmt(_))) => match parent_iter.next() {
 +            None => return Some(ExpnId::root()),
 +            Some(next) => next,
 +        },
 +        Some(next) => next,
 +    };
 +
 +    // get the macro expansion of the parent node
 +    let parent_span = hir.span(parent_id);
 +    let Some(parent_macro_call) = macro_backtrace(parent_span).next() else {
 +        // the parent node is not in a macro
 +        return Some(ExpnId::root());
 +    };
 +
 +    if parent_macro_call.expn.is_descendant_of(expn) {
 +        // `node` is input to a macro call
 +        return None;
 +    }
 +
 +    Some(parent_macro_call.expn)
 +}
 +
 +/* Specific Macro Utils */
 +
 +/// Is `def_id` of `std::panic`, `core::panic` or any inner implementation macros
 +pub fn is_panic(cx: &LateContext<'_>, def_id: DefId) -> bool {
 +    let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return false };
 +    matches!(
 +        name.as_str(),
 +        "core_panic_macro"
 +            | "std_panic_macro"
 +            | "core_panic_2015_macro"
 +            | "std_panic_2015_macro"
 +            | "core_panic_2021_macro"
 +    )
 +}
 +
 +pub enum PanicExpn<'a> {
 +    /// No arguments - `panic!()`
 +    Empty,
 +    /// A string literal or any `&str` - `panic!("message")` or `panic!(message)`
 +    Str(&'a Expr<'a>),
 +    /// A single argument that implements `Display` - `panic!("{}", object)`
 +    Display(&'a Expr<'a>),
 +    /// Anything else - `panic!("error {}: {}", a, b)`
 +    Format(FormatArgsExpn<'a>),
 +}
 +
 +impl<'a> PanicExpn<'a> {
 +    pub fn parse(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Self> {
 +        if !macro_backtrace(expr.span).any(|macro_call| is_panic(cx, macro_call.def_id)) {
 +            return None;
 +        }
 +        let ExprKind::Call(callee, [arg]) = &expr.kind else { return None };
 +        let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { return None };
 +        let result = match path.segments.last().unwrap().ident.as_str() {
 +            "panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty,
 +            "panic" | "panic_str" => Self::Str(arg),
 +            "panic_display" => {
 +                let ExprKind::AddrOf(_, _, e) = &arg.kind else { return None };
 +                Self::Display(e)
 +            },
 +            "panic_fmt" => Self::Format(FormatArgsExpn::parse(cx, arg)?),
 +            _ => return None,
 +        };
 +        Some(result)
 +    }
 +}
 +
 +/// Finds the arguments of an `assert!` or `debug_assert!` macro call within the macro expansion
 +pub fn find_assert_args<'a>(
 +    cx: &LateContext<'_>,
 +    expr: &'a Expr<'a>,
 +    expn: ExpnId,
 +) -> Option<(&'a Expr<'a>, PanicExpn<'a>)> {
 +    find_assert_args_inner(cx, expr, expn).map(|([e], p)| (e, p))
 +}
 +
 +/// Finds the arguments of an `assert_eq!` or `debug_assert_eq!` macro call within the macro
 +/// expansion
 +pub fn find_assert_eq_args<'a>(
 +    cx: &LateContext<'_>,
 +    expr: &'a Expr<'a>,
 +    expn: ExpnId,
 +) -> Option<(&'a Expr<'a>, &'a Expr<'a>, PanicExpn<'a>)> {
 +    find_assert_args_inner(cx, expr, expn).map(|([a, b], p)| (a, b, p))
 +}
 +
 +fn find_assert_args_inner<'a, const N: usize>(
 +    cx: &LateContext<'_>,
 +    expr: &'a Expr<'a>,
 +    expn: ExpnId,
 +) -> Option<([&'a Expr<'a>; N], PanicExpn<'a>)> {
 +    let macro_id = expn.expn_data().macro_def_id?;
 +    let (expr, expn) = match cx.tcx.item_name(macro_id).as_str().strip_prefix("debug_") {
 +        None => (expr, expn),
 +        Some(inner_name) => find_assert_within_debug_assert(cx, expr, expn, Symbol::intern(inner_name))?,
 +    };
 +    let mut args = ArrayVec::new();
 +    let mut panic_expn = None;
 +    expr_visitor_no_bodies(|e| {
 +        if args.is_full() {
 +            if panic_expn.is_none() && e.span.ctxt() != expr.span.ctxt() {
 +                panic_expn = PanicExpn::parse(cx, e);
 +            }
 +            panic_expn.is_none()
 +        } else if is_assert_arg(cx, e, expn) {
 +            args.push(e);
 +            false
 +        } else {
 +            true
 +        }
 +    })
 +    .visit_expr(expr);
 +    let args = args.into_inner().ok()?;
 +    // if no `panic!(..)` is found, use `PanicExpn::Empty`
 +    // to indicate that the default assertion message is used
 +    let panic_expn = panic_expn.unwrap_or(PanicExpn::Empty);
 +    Some((args, panic_expn))
 +}
 +
 +fn find_assert_within_debug_assert<'a>(
 +    cx: &LateContext<'_>,
 +    expr: &'a Expr<'a>,
 +    expn: ExpnId,
 +    assert_name: Symbol,
 +) -> Option<(&'a Expr<'a>, ExpnId)> {
 +    let mut found = None;
 +    expr_visitor_no_bodies(|e| {
 +        if found.is_some() || !e.span.from_expansion() {
 +            return false;
 +        }
 +        let e_expn = e.span.ctxt().outer_expn();
 +        if e_expn == expn {
 +            return true;
 +        }
 +        if e_expn.expn_data().macro_def_id.map(|id| cx.tcx.item_name(id)) == Some(assert_name) {
 +            found = Some((e, e_expn));
 +        }
 +        false
 +    })
 +    .visit_expr(expr);
 +    found
 +}
 +
 +fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) -> bool {
 +    if !expr.span.from_expansion() {
 +        return true;
 +    }
 +    let result = macro_backtrace(expr.span).try_for_each(|macro_call| {
 +        if macro_call.expn == assert_expn {
 +            ControlFlow::Break(false)
 +        } else {
 +            match cx.tcx.item_name(macro_call.def_id) {
 +                // `cfg!(debug_assertions)` in `debug_assert!`
 +                sym::cfg => ControlFlow::CONTINUE,
 +                // assert!(other_macro!(..))
 +                _ => ControlFlow::Break(true),
 +            }
 +        }
 +    });
 +    match result {
 +        ControlFlow::Break(is_assert_arg) => is_assert_arg,
 +        ControlFlow::Continue(()) => true,
 +    }
 +}
 +
 +/// The format string doesn't exist in the HIR, so we reassemble it from source code
 +#[derive(Debug)]
 +pub struct FormatString {
 +    /// Span of the whole format string literal, including `[r#]"`.
 +    pub span: Span,
 +    /// Snippet of the whole format string literal, including `[r#]"`.
 +    pub snippet: String,
 +    /// If the string is raw `r"..."`/`r#""#`, how many `#`s does it have on each side.
 +    pub style: Option<usize>,
 +    /// The unescaped value of the format string, e.g. `"val – {}"` for the literal
 +    /// `"val \u{2013} {}"`.
 +    pub unescaped: String,
 +    /// The format string split by format args like `{..}`.
 +    pub parts: Vec<Symbol>,
 +}
 +
 +impl FormatString {
 +    fn new(cx: &LateContext<'_>, pieces: &Expr<'_>) -> Option<Self> {
 +        // format_args!(r"a {} b \", 1);
 +        //
 +        // expands to
 +        //
 +        // ::core::fmt::Arguments::new_v1(&["a ", " b \\"],
 +        //      &[::core::fmt::ArgumentV1::new_display(&1)]);
 +        //
 +        // where `pieces` is the expression `&["a ", " b \\"]`. It has the span of `r"a {} b \"`
 +        let span = pieces.span;
 +        let snippet = snippet_opt(cx, span)?;
 +
 +        let (inner, style) = match tokenize(&snippet).next()?.kind {
 +            TokenKind::Literal { kind, .. } => {
 +                let style = match kind {
 +                    LiteralKind::Str { .. } => None,
 +                    LiteralKind::RawStr { n_hashes: Some(n), .. } => Some(n.into()),
 +                    _ => return None,
 +                };
 +
 +                let start = style.map_or(1, |n| 2 + n);
 +                let end = snippet.len() - style.map_or(1, |n| 1 + n);
 +
 +                (&snippet[start..end], style)
 +            },
 +            _ => return None,
 +        };
 +
 +        let mode = if style.is_some() {
 +            unescape::Mode::RawStr
 +        } else {
 +            unescape::Mode::Str
 +        };
 +
 +        let mut unescaped = String::with_capacity(inner.len());
++        unescape_literal(inner, mode, &mut |_, ch| match ch {
++            Ok(ch) => unescaped.push(ch),
++            Err(e) if !e.is_fatal() => (),
++            Err(e) => panic!("{:?}", e),
 +        });
 +
 +        let mut parts = Vec::new();
 +        expr_visitor_no_bodies(|expr| {
 +            if let ExprKind::Lit(lit) = &expr.kind {
 +                if let LitKind::Str(symbol, _) = lit.node {
 +                    parts.push(symbol);
 +                }
 +            }
 +
 +            true
 +        })
 +        .visit_expr(pieces);
 +
 +        Some(Self {
 +            span,
 +            snippet,
 +            style,
 +            unescaped,
 +            parts,
 +        })
 +    }
 +}
 +
 +struct FormatArgsValues<'tcx> {
 +    /// See `FormatArgsExpn::value_args`
 +    value_args: Vec<&'tcx Expr<'tcx>>,
 +    /// Maps an `rt::v1::Argument::position` or an `rt::v1::Count::Param` to its index in
 +    /// `value_args`
 +    pos_to_value_index: Vec<usize>,
 +    /// Used to check if a value is declared inline & to resolve `InnerSpan`s.
 +    format_string_span: SpanData,
 +}
 +
 +impl<'tcx> FormatArgsValues<'tcx> {
 +    fn new(args: &'tcx Expr<'tcx>, format_string_span: SpanData) -> Self {
 +        let mut pos_to_value_index = Vec::new();
 +        let mut value_args = Vec::new();
 +        expr_visitor_no_bodies(|expr| {
 +            if expr.span.ctxt() == args.span.ctxt() {
 +                // ArgumentV1::new_<format_trait>(<val>)
 +                // ArgumentV1::from_usize(<val>)
 +                if let ExprKind::Call(callee, [val]) = expr.kind
 +                    && let ExprKind::Path(QPath::TypeRelative(ty, _)) = callee.kind
 +                    && let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind
 +                    && path.segments.last().unwrap().ident.name == sym::ArgumentV1
 +                {
 +                    let val_idx = if val.span.ctxt() == expr.span.ctxt()
 +                        && let ExprKind::Field(_, field) = val.kind
 +                        && let Ok(idx) = field.name.as_str().parse()
 +                    {
 +                        // tuple index
 +                        idx
 +                    } else {
 +                        // assume the value expression is passed directly
 +                        pos_to_value_index.len()
 +                    };
 +
 +                    pos_to_value_index.push(val_idx);
 +                }
 +
 +                true
 +            } else {
 +                // assume that any expr with a differing span is a value
 +                value_args.push(expr);
 +
 +                false
 +            }
 +        })
 +        .visit_expr(args);
 +
 +        Self {
 +            value_args,
 +            pos_to_value_index,
 +            format_string_span,
 +        }
 +    }
 +}
 +
 +/// The positions of a format argument's value, precision and width
 +///
 +/// A position is an index into the second argument of `Arguments::new_v1[_formatted]`
 +#[derive(Debug, Default, Copy, Clone)]
 +struct ParamPosition {
 +    /// The position stored in `rt::v1::Argument::position`.
 +    value: usize,
 +    /// The position stored in `rt::v1::FormatSpec::width` if it is a `Count::Param`.
 +    width: Option<usize>,
 +    /// The position stored in `rt::v1::FormatSpec::precision` if it is a `Count::Param`.
 +    precision: Option<usize>,
 +}
 +
 +/// Parses the `fmt` arg of `Arguments::new_v1_formatted(pieces, args, fmt, _)`
 +fn parse_rt_fmt<'tcx>(fmt_arg: &'tcx Expr<'tcx>) -> Option<impl Iterator<Item = ParamPosition> + 'tcx> {
 +    fn parse_count(expr: &Expr<'_>) -> Option<usize> {
 +        // ::core::fmt::rt::v1::Count::Param(1usize),
 +        if let ExprKind::Call(ctor, [val]) = expr.kind
 +            && let ExprKind::Path(QPath::Resolved(_, path)) = ctor.kind
 +            && path.segments.last()?.ident.name == sym::Param
 +            && let ExprKind::Lit(lit) = &val.kind
 +            && let LitKind::Int(pos, _) = lit.node
 +        {
 +            Some(pos as usize)
 +        } else {
 +            None
 +        }
 +    }
 +
 +    if let ExprKind::AddrOf(.., array) = fmt_arg.kind
 +        && let ExprKind::Array(specs) = array.kind
 +    {
 +        Some(specs.iter().map(|spec| {
 +            let mut position = ParamPosition::default();
 +
 +            // ::core::fmt::rt::v1::Argument {
 +            //     position: 0usize,
 +            //     format: ::core::fmt::rt::v1::FormatSpec {
 +            //         ..
 +            //         precision: ::core::fmt::rt::v1::Count::Implied,
 +            //         width: ::core::fmt::rt::v1::Count::Implied,
 +            //     },
 +            // }
 +
 +            // TODO: this can be made much nicer next sync with `Visitor::visit_expr_field`
 +            if let ExprKind::Struct(_, fields, _) = spec.kind {
 +                for field in fields {
 +                    match (field.ident.name, &field.expr.kind) {
 +                        (sym::position, ExprKind::Lit(lit)) => {
 +                            if let LitKind::Int(pos, _) = lit.node {
 +                                position.value = pos as usize;
 +                            }
 +                        },
 +                        (sym::format, &ExprKind::Struct(_, spec_fields, _)) => {
 +                            for spec_field in spec_fields {
 +                                match spec_field.ident.name {
 +                                    sym::precision => {
 +                                        position.precision = parse_count(spec_field.expr);
 +                                    },
 +                                    sym::width => {
 +                                        position.width = parse_count(spec_field.expr);
 +                                    },
 +                                    _ => {},
 +                                }
 +                            }
 +                        },
 +                        _ => {},
 +                    }
 +                }
 +            }
 +
 +            position
 +        }))
 +    } else {
 +        None
 +    }
 +}
 +
 +/// `Span::from_inner`, but for `rustc_parse_format`'s `InnerSpan`
 +fn span_from_inner(base: SpanData, inner: rpf::InnerSpan) -> Span {
 +    Span::new(
 +        base.lo + BytePos::from_usize(inner.start),
 +        base.lo + BytePos::from_usize(inner.end),
 +        base.ctxt,
 +        base.parent,
 +    )
 +}
 +
 +#[derive(Debug, Copy, Clone, PartialEq, Eq)]
 +pub enum FormatParamKind {
 +    /// An implicit parameter , such as `{}` or `{:?}`.
 +    Implicit,
 +    /// A parameter with an explicit number, or an asterisk precision. e.g. `{1}`, `{0:?}`,
 +    /// `{:.0$}` or `{:.*}`.
 +    Numbered,
 +    /// A named parameter with a named `value_arg`, such as the `x` in `format!("{x}", x = 1)`.
 +    Named(Symbol),
 +    /// An implicit named parameter, such as the `y` in `format!("{y}")`.
 +    NamedInline(Symbol),
 +}
 +
 +/// A `FormatParam` is any place in a `FormatArgument` that refers to a supplied value, e.g.
 +///
 +/// ```
 +/// let precision = 2;
 +/// format!("{:.precision$}", 0.1234);
 +/// ```
 +///
 +/// has two `FormatParam`s, a [`FormatParamKind::Implicit`] `.kind` with a `.value` of `0.1234`
 +/// and a [`FormatParamKind::NamedInline("precision")`] `.kind` with a `.value` of `2`
 +#[derive(Debug, Copy, Clone)]
 +pub struct FormatParam<'tcx> {
 +    /// The expression this parameter refers to.
 +    pub value: &'tcx Expr<'tcx>,
 +    /// How this parameter refers to its `value`.
 +    pub kind: FormatParamKind,
 +    /// Span of the parameter, may be zero width. Includes the whitespace of implicit parameters.
 +    ///
 +    /// ```text
 +    /// format!("{}, {  }, {0}, {name}", ...);
 +    ///          ^    ~~    ~    ~~~~
 +    /// ```
 +    pub span: Span,
 +}
 +
 +impl<'tcx> FormatParam<'tcx> {
 +    fn new(
 +        mut kind: FormatParamKind,
 +        position: usize,
 +        inner: rpf::InnerSpan,
 +        values: &FormatArgsValues<'tcx>,
 +    ) -> Option<Self> {
 +        let value_index = *values.pos_to_value_index.get(position)?;
 +        let value = *values.value_args.get(value_index)?;
 +        let span = span_from_inner(values.format_string_span, inner);
 +
 +        // if a param is declared inline, e.g. `format!("{x}")`, the generated expr's span points
 +        // into the format string
 +        if let FormatParamKind::Named(name) = kind && values.format_string_span.contains(value.span.data()) {
 +            kind = FormatParamKind::NamedInline(name);
 +        }
 +
 +        Some(Self { value, kind, span })
 +    }
 +}
 +
 +/// Used by [width](https://doc.rust-lang.org/std/fmt/#width) and
 +/// [precision](https://doc.rust-lang.org/std/fmt/#precision) specifiers.
 +#[derive(Debug, Copy, Clone)]
 +pub enum Count<'tcx> {
 +    /// Specified with a literal number, stores the value.
 +    Is(usize, Span),
 +    /// Specified using `$` and `*` syntaxes. The `*` format is still considered to be
 +    /// `FormatParamKind::Numbered`.
 +    Param(FormatParam<'tcx>),
 +    /// Not specified.
 +    Implied,
 +}
 +
 +impl<'tcx> Count<'tcx> {
 +    fn new(
 +        count: rpf::Count<'_>,
 +        position: Option<usize>,
 +        inner: Option<rpf::InnerSpan>,
 +        values: &FormatArgsValues<'tcx>,
 +    ) -> Option<Self> {
 +        Some(match count {
 +            rpf::Count::CountIs(val) => Self::Is(val, span_from_inner(values.format_string_span, inner?)),
 +            rpf::Count::CountIsName(name, span) => Self::Param(FormatParam::new(
 +                FormatParamKind::Named(Symbol::intern(name)),
 +                position?,
 +                span,
 +                values,
 +            )?),
 +            rpf::Count::CountIsParam(_) | rpf::Count::CountIsStar(_) => {
 +                Self::Param(FormatParam::new(FormatParamKind::Numbered, position?, inner?, values)?)
 +            },
 +            rpf::Count::CountImplied => Self::Implied,
 +        })
 +    }
 +
 +    pub fn is_implied(self) -> bool {
 +        matches!(self, Count::Implied)
 +    }
 +
 +    pub fn param(self) -> Option<FormatParam<'tcx>> {
 +        match self {
 +            Count::Param(param) => Some(param),
 +            _ => None,
 +        }
 +    }
 +}
 +
 +/// Specification for the formatting of an argument in the format string. See
 +/// <https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters> for the precise meanings.
 +#[derive(Debug)]
 +pub struct FormatSpec<'tcx> {
 +    /// Optionally specified character to fill alignment with.
 +    pub fill: Option<char>,
 +    /// Optionally specified alignment.
 +    pub align: Alignment,
 +    /// Packed version of various flags provided, see [`rustc_parse_format::Flag`].
 +    pub flags: u32,
 +    /// Represents either the maximum width or the integer precision.
 +    pub precision: Count<'tcx>,
 +    /// The minimum width, will be padded according to `width`/`align`
 +    pub width: Count<'tcx>,
 +    /// The formatting trait used by the argument, e.g. `sym::Display` for `{}`, `sym::Debug` for
 +    /// `{:?}`.
 +    pub r#trait: Symbol,
 +    pub trait_span: Option<Span>,
 +}
 +
 +impl<'tcx> FormatSpec<'tcx> {
 +    fn new(spec: rpf::FormatSpec<'_>, positions: ParamPosition, values: &FormatArgsValues<'tcx>) -> Option<Self> {
 +        Some(Self {
 +            fill: spec.fill,
 +            align: spec.align,
 +            flags: spec.flags,
 +            precision: Count::new(spec.precision, positions.precision, spec.precision_span, values)?,
 +            width: Count::new(spec.width, positions.width, spec.width_span, values)?,
 +            r#trait: match spec.ty {
 +                "" => sym::Display,
 +                "?" => sym::Debug,
 +                "o" => sym!(Octal),
 +                "x" => sym!(LowerHex),
 +                "X" => sym!(UpperHex),
 +                "p" => sym::Pointer,
 +                "b" => sym!(Binary),
 +                "e" => sym!(LowerExp),
 +                "E" => sym!(UpperExp),
 +                _ => return None,
 +            },
 +            trait_span: spec
 +                .ty_span
 +                .map(|span| span_from_inner(values.format_string_span, span)),
 +        })
 +    }
 +
 +    /// Returns true if this format spec would change the contents of a string when formatted
 +    pub fn has_string_formatting(&self) -> bool {
 +        self.r#trait != sym::Display || !self.width.is_implied() || !self.precision.is_implied()
 +    }
 +}
 +
 +/// A format argument, such as `{}`, `{foo:?}`.
 +#[derive(Debug)]
 +pub struct FormatArg<'tcx> {
 +    /// The parameter the argument refers to.
 +    pub param: FormatParam<'tcx>,
 +    /// How to format `param`.
 +    pub format: FormatSpec<'tcx>,
 +    /// span of the whole argument, `{..}`.
 +    pub span: Span,
 +}
 +
 +/// A parsed `format_args!` expansion.
 +#[derive(Debug)]
 +pub struct FormatArgsExpn<'tcx> {
 +    /// The format string literal.
 +    pub format_string: FormatString,
 +    // The format arguments, such as `{:?}`.
 +    pub args: Vec<FormatArg<'tcx>>,
 +    /// Has an added newline due to `println!()`/`writeln!()`/etc. The last format string part will
 +    /// include this added newline.
 +    pub newline: bool,
 +    /// Values passed after the format string and implicit captures. `[1, z + 2, x]` for
 +    /// `format!("{x} {} {y}", 1, z + 2)`.
 +    value_args: Vec<&'tcx Expr<'tcx>>,
 +}
 +
 +impl<'tcx> FormatArgsExpn<'tcx> {
 +    pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<Self> {
 +        let macro_name = macro_backtrace(expr.span)
 +            .map(|macro_call| cx.tcx.item_name(macro_call.def_id))
 +            .find(|&name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl))?;
 +        let newline = macro_name == sym::format_args_nl;
 +
 +        // ::core::fmt::Arguments::new_v1(pieces, args)
 +        // ::core::fmt::Arguments::new_v1_formatted(pieces, args, fmt, _unsafe_arg)
 +        if let ExprKind::Call(callee, [pieces, args, rest @ ..]) = expr.kind
 +            && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind
 +            && is_path_diagnostic_item(cx, ty, sym::Arguments)
 +            && matches!(seg.ident.as_str(), "new_v1" | "new_v1_formatted")
 +        {
 +            let format_string = FormatString::new(cx, pieces)?;
 +
 +            let mut parser = rpf::Parser::new(
 +                &format_string.unescaped,
 +                format_string.style,
 +                Some(format_string.snippet.clone()),
 +                // `format_string.unescaped` does not contain the appended newline
 +                false,
 +                rpf::ParseMode::Format,
 +            );
 +
 +            let parsed_args = parser
 +                .by_ref()
 +                .filter_map(|piece| match piece {
 +                    rpf::Piece::NextArgument(a) => Some(a),
 +                    rpf::Piece::String(_) => None,
 +                })
 +                .collect_vec();
 +            if !parser.errors.is_empty() {
 +                return None;
 +            }
 +
 +            let positions = if let Some(fmt_arg) = rest.first() {
 +                // If the argument contains format specs, `new_v1_formatted(_, _, fmt, _)`, parse
 +                // them.
 +
 +                Either::Left(parse_rt_fmt(fmt_arg)?)
 +            } else {
 +                // If no format specs are given, the positions are in the given order and there are
 +                // no `precision`/`width`s to consider.
 +
 +                Either::Right((0..).map(|n| ParamPosition {
 +                    value: n,
 +                    width: None,
 +                    precision: None,
 +                }))
 +            };
 +
 +            let values = FormatArgsValues::new(args, format_string.span.data());
 +
 +            let args = izip!(positions, parsed_args, parser.arg_places)
 +                .map(|(position, parsed_arg, arg_span)| {
 +                    Some(FormatArg {
 +                        param: FormatParam::new(
 +                            match parsed_arg.position {
 +                                rpf::Position::ArgumentImplicitlyIs(_) => FormatParamKind::Implicit,
 +                                rpf::Position::ArgumentIs(_) => FormatParamKind::Numbered,
 +                                // NamedInline is handled by `FormatParam::new()`
 +                                rpf::Position::ArgumentNamed(name) => FormatParamKind::Named(Symbol::intern(name)),
 +                            },
 +                            position.value,
 +                            parsed_arg.position_span,
 +                            &values,
 +                        )?,
 +                        format: FormatSpec::new(parsed_arg.format, position, &values)?,
 +                        span: span_from_inner(values.format_string_span, arg_span),
 +                    })
 +                })
 +                .collect::<Option<Vec<_>>>()?;
 +
 +            Some(Self {
 +                format_string,
 +                args,
 +                value_args: values.value_args,
 +                newline,
 +            })
 +        } else {
 +            None
 +        }
 +    }
 +
 +    pub fn find_nested(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expn_id: ExpnId) -> Option<Self> {
 +        let mut format_args = None;
 +        expr_visitor_no_bodies(|e| {
 +            if format_args.is_some() {
 +                return false;
 +            }
 +            let e_ctxt = e.span.ctxt();
 +            if e_ctxt == expr.span.ctxt() {
 +                return true;
 +            }
 +            if e_ctxt.outer_expn().is_descendant_of(expn_id) {
 +                format_args = FormatArgsExpn::parse(cx, e);
 +            }
 +            false
 +        })
 +        .visit_expr(expr);
 +        format_args
 +    }
 +
 +    /// Source callsite span of all inputs
 +    pub fn inputs_span(&self) -> Span {
 +        match *self.value_args {
 +            [] => self.format_string.span,
 +            [.., last] => self
 +                .format_string
 +                .span
 +                .to(hygiene::walk_chain(last.span, self.format_string.span.ctxt())),
 +        }
 +    }
 +
 +    /// Iterator of all format params, both values and those referenced by `width`/`precision`s.
 +    pub fn params(&'tcx self) -> impl Iterator<Item = FormatParam<'tcx>> {
 +        self.args
 +            .iter()
 +            .flat_map(|arg| [Some(arg.param), arg.format.precision.param(), arg.format.width.param()])
 +            .flatten()
 +    }
 +}
 +
 +/// A node with a `HirId` and a `Span`
 +pub trait HirNode {
 +    fn hir_id(&self) -> HirId;
 +    fn span(&self) -> Span;
 +}
 +
 +macro_rules! impl_hir_node {
 +    ($($t:ident),*) => {
 +        $(impl HirNode for hir::$t<'_> {
 +            fn hir_id(&self) -> HirId {
 +                self.hir_id
 +            }
 +            fn span(&self) -> Span {
 +                self.span
 +            }
 +        })*
 +    };
 +}
 +
 +impl_hir_node!(Expr, Pat);
 +
 +impl HirNode for hir::Item<'_> {
 +    fn hir_id(&self) -> HirId {
 +        self.hir_id()
 +    }
 +
 +    fn span(&self) -> Span {
 +        self.span
 +    }
 +}
index b22a9c817460db7761586f8899b839affd5be31d,0000000000000000000000000000000000000000..d5f64e5118f563b97cc2c497b1d1fa2dd7429d78
mode 100644,000000..100644
--- /dev/null
@@@ -1,378 -1,0 +1,376 @@@
-     Body, CastKind, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
-     TerminatorKind, NonDivergingIntrinsic
 +// This code used to be a part of `rustc` but moved to Clippy as a result of
 +// https://github.com/rust-lang/rust/issues/76618. Because of that, it contains unused code and some
 +// of terminologies might not be relevant in the context of Clippy. Note that its behavior might
 +// differ from the time of `rustc` even if the name stays the same.
 +
 +use rustc_hir as hir;
 +use rustc_hir::def_id::DefId;
 +use rustc_middle::mir::{
-         StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => {
-             check_operand(tcx, op, span, body)
-         },
++    Body, CastKind, NonDivergingIntrinsic, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind,
++    Terminator, TerminatorKind,
 +};
 +use rustc_middle::ty::subst::GenericArgKind;
 +use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
 +use rustc_semver::RustcVersion;
 +use rustc_span::symbol::sym;
 +use rustc_span::Span;
 +use std::borrow::Cow;
 +
 +type McfResult = Result<(), (Span, Cow<'static, str>)>;
 +
 +pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<RustcVersion>) -> McfResult {
 +    let def_id = body.source.def_id();
 +    let mut current = def_id;
 +    loop {
 +        let predicates = tcx.predicates_of(current);
 +        for (predicate, _) in predicates.predicates {
 +            match predicate.kind().skip_binder() {
 +                ty::PredicateKind::RegionOutlives(_)
 +                | ty::PredicateKind::TypeOutlives(_)
 +                | ty::PredicateKind::WellFormed(_)
 +                | ty::PredicateKind::Projection(_)
 +                | ty::PredicateKind::ConstEvaluatable(..)
 +                | ty::PredicateKind::ConstEquate(..)
 +                | ty::PredicateKind::Trait(..)
 +                | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
 +                ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate),
 +                ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate),
 +                ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate),
 +                ty::PredicateKind::Coerce(_) => panic!("coerce predicate on function: {:#?}", predicate),
 +            }
 +        }
 +        match predicates.parent {
 +            Some(parent) => current = parent,
 +            None => break,
 +        }
 +    }
 +
 +    for local in &body.local_decls {
 +        check_ty(tcx, local.ty, local.source_info.span)?;
 +    }
 +    // impl trait is gone in MIR, so check the return type manually
 +    check_ty(
 +        tcx,
 +        tcx.fn_sig(def_id).output().skip_binder(),
 +        body.local_decls.iter().next().unwrap().source_info.span,
 +    )?;
 +
 +    for bb in body.basic_blocks.iter() {
 +        check_terminator(tcx, body, bb.terminator(), msrv)?;
 +        for stmt in &bb.statements {
 +            check_statement(tcx, body, def_id, stmt)?;
 +        }
 +    }
 +    Ok(())
 +}
 +
 +fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
 +    for arg in ty.walk() {
 +        let ty = match arg.unpack() {
 +            GenericArgKind::Type(ty) => ty,
 +
 +            // No constraints on lifetimes or constants, except potentially
 +            // constants' types, but `walk` will get to them as well.
 +            GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
 +        };
 +
 +        match ty.kind() {
 +            ty::Ref(_, _, hir::Mutability::Mut) => {
 +                return Err((span, "mutable references in const fn are unstable".into()));
 +            },
 +            ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
 +            ty::FnPtr(..) => {
 +                return Err((span, "function pointers in const fn are unstable".into()));
 +            },
 +            ty::Dynamic(preds, _) => {
 +                for pred in preds.iter() {
 +                    match pred.skip_binder() {
 +                        ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => {
 +                            return Err((
 +                                span,
 +                                "trait bounds other than `Sized` \
 +                                 on const fn parameters are unstable"
 +                                    .into(),
 +                            ));
 +                        },
 +                        ty::ExistentialPredicate::Trait(trait_ref) => {
 +                            if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
 +                                return Err((
 +                                    span,
 +                                    "trait bounds other than `Sized` \
 +                                     on const fn parameters are unstable"
 +                                        .into(),
 +                                ));
 +                            }
 +                        },
 +                    }
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +    Ok(())
 +}
 +
 +fn check_rvalue<'tcx>(
 +    tcx: TyCtxt<'tcx>,
 +    body: &Body<'tcx>,
 +    def_id: DefId,
 +    rvalue: &Rvalue<'tcx>,
 +    span: Span,
 +) -> McfResult {
 +    match rvalue {
 +        Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
 +        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
 +            check_place(tcx, *place, span, body)
 +        },
 +        Rvalue::CopyForDeref(place) => check_place(tcx, *place, span, body),
 +        Rvalue::Repeat(operand, _)
 +        | Rvalue::Use(operand)
 +        | Rvalue::Cast(
 +            CastKind::PointerFromExposedAddress
 +            | CastKind::Misc
 +            | CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer),
 +            operand,
 +            _,
 +        ) => check_operand(tcx, operand, span, body),
 +        Rvalue::Cast(
 +            CastKind::Pointer(
 +                PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer,
 +            ),
 +            _,
 +            _,
 +        ) => Err((span, "function pointer casts are not allowed in const fn".into())),
 +        Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), op, cast_ty) => {
 +            let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) {
 +                deref_ty.ty
 +            } else {
 +                // We cannot allow this for now.
 +                return Err((span, "unsizing casts are only allowed for references right now".into()));
 +            };
 +            let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
 +            if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
 +                check_operand(tcx, op, span, body)?;
 +                // Casting/coercing things to slices is fine.
 +                Ok(())
 +            } else {
 +                // We just can't allow trait objects until we have figured out trait method calls.
 +                Err((span, "unsizing casts are not allowed in const fn".into()))
 +            }
 +        },
 +        Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => {
 +            Err((span, "casting pointers to ints is unstable in const fn".into()))
 +        },
 +        // binops are fine on integers
 +        Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => {
 +            check_operand(tcx, lhs, span, body)?;
 +            check_operand(tcx, rhs, span, body)?;
 +            let ty = lhs.ty(body, tcx);
 +            if ty.is_integral() || ty.is_bool() || ty.is_char() {
 +                Ok(())
 +            } else {
 +                Err((
 +                    span,
 +                    "only int, `bool` and `char` operations are stable in const fn".into(),
 +                ))
 +            }
 +        },
 +        Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) | Rvalue::ShallowInitBox(_, _) => Ok(()),
 +        Rvalue::UnaryOp(_, operand) => {
 +            let ty = operand.ty(body, tcx);
 +            if ty.is_integral() || ty.is_bool() {
 +                check_operand(tcx, operand, span, body)
 +            } else {
 +                Err((span, "only int and `bool` operations are stable in const fn".into()))
 +            }
 +        },
 +        Rvalue::Aggregate(_, operands) => {
 +            for operand in operands {
 +                check_operand(tcx, operand, span, body)?;
 +            }
 +            Ok(())
 +        },
 +    }
 +}
 +
 +fn check_statement<'tcx>(
 +    tcx: TyCtxt<'tcx>,
 +    body: &Body<'tcx>,
 +    def_id: DefId,
 +    statement: &Statement<'tcx>,
 +) -> McfResult {
 +    let span = statement.source_info.span;
 +    match &statement.kind {
 +        StatementKind::Assign(box (place, rval)) => {
 +            check_place(tcx, *place, span, body)?;
 +            check_rvalue(tcx, body, def_id, rval, span)
 +        },
 +
 +        StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body),
 +        // just an assignment
 +        StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => {
 +            check_place(tcx, **place, span, body)
 +        },
 +
++        StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => check_operand(tcx, op, span, body),
 +
 +        StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(
 +            rustc_middle::mir::CopyNonOverlapping { dst, src, count },
 +        )) => {
 +            check_operand(tcx, dst, span, body)?;
 +            check_operand(tcx, src, span, body)?;
 +            check_operand(tcx, count, span, body)
 +        },
 +
 +        // These are all NOPs
 +        StatementKind::StorageLive(_)
 +        | StatementKind::StorageDead(_)
 +        | StatementKind::Retag { .. }
 +        | StatementKind::AscribeUserType(..)
 +        | StatementKind::Coverage(..)
 +        | StatementKind::Nop => Ok(()),
 +    }
 +}
 +
 +fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
 +    match operand {
 +        Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body),
 +        Operand::Constant(c) => match c.check_static_ptr(tcx) {
 +            Some(_) => Err((span, "cannot access `static` items in const fn".into())),
 +            None => Ok(()),
 +        },
 +    }
 +}
 +
 +fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
 +    let mut cursor = place.projection.as_ref();
 +    while let [ref proj_base @ .., elem] = *cursor {
 +        cursor = proj_base;
 +        match elem {
 +            ProjectionElem::Field(..) => {
 +                let base_ty = Place::ty_from(place.local, proj_base, body, tcx).ty;
 +                if let Some(def) = base_ty.ty_adt_def() {
 +                    // No union field accesses in `const fn`
 +                    if def.is_union() {
 +                        return Err((span, "accessing union fields is unstable".into()));
 +                    }
 +                }
 +            },
 +            ProjectionElem::ConstantIndex { .. }
 +            | ProjectionElem::Downcast(..)
 +            | ProjectionElem::Subslice { .. }
 +            | ProjectionElem::Deref
 +            | ProjectionElem::Index(_) => {},
 +        }
 +    }
 +
 +    Ok(())
 +}
 +
 +fn check_terminator<'a, 'tcx>(
 +    tcx: TyCtxt<'tcx>,
 +    body: &'a Body<'tcx>,
 +    terminator: &Terminator<'tcx>,
 +    msrv: Option<RustcVersion>,
 +) -> McfResult {
 +    let span = terminator.source_info.span;
 +    match &terminator.kind {
 +        TerminatorKind::FalseEdge { .. }
 +        | TerminatorKind::FalseUnwind { .. }
 +        | TerminatorKind::Goto { .. }
 +        | TerminatorKind::Return
 +        | TerminatorKind::Resume
 +        | TerminatorKind::Unreachable => Ok(()),
 +
 +        TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body),
 +        TerminatorKind::DropAndReplace { place, value, .. } => {
 +            check_place(tcx, *place, span, body)?;
 +            check_operand(tcx, value, span, body)
 +        },
 +
 +        TerminatorKind::SwitchInt {
 +            discr,
 +            switch_ty: _,
 +            targets: _,
 +        } => check_operand(tcx, discr, span, body),
 +
 +        TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())),
 +        TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
 +            Err((span, "const fn generators are unstable".into()))
 +        },
 +
 +        TerminatorKind::Call {
 +            func,
 +            args,
 +            from_hir_call: _,
 +            destination: _,
 +            target: _,
 +            cleanup: _,
 +            fn_span: _,
 +        } => {
 +            let fn_ty = func.ty(body, tcx);
 +            if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
 +                if !is_const_fn(tcx, fn_def_id, msrv) {
 +                    return Err((
 +                        span,
 +                        format!(
 +                            "can only call other `const fn` within a `const fn`, \
 +                             but `{:?}` is not stable as `const fn`",
 +                            func,
 +                        )
 +                        .into(),
 +                    ));
 +                }
 +
 +                // HACK: This is to "unstabilize" the `transmute` intrinsic
 +                // within const fns. `transmute` is allowed in all other const contexts.
 +                // This won't really scale to more intrinsics or functions. Let's allow const
 +                // transmutes in const fn before we add more hacks to this.
 +                if tcx.is_intrinsic(fn_def_id) && tcx.item_name(fn_def_id) == sym::transmute {
 +                    return Err((
 +                        span,
 +                        "can only call `transmute` from const items, not `const fn`".into(),
 +                    ));
 +                }
 +
 +                check_operand(tcx, func, span, body)?;
 +
 +                for arg in args {
 +                    check_operand(tcx, arg, span, body)?;
 +                }
 +                Ok(())
 +            } else {
 +                Err((span, "can only call other const fns within const fn".into()))
 +            }
 +        },
 +
 +        TerminatorKind::Assert {
 +            cond,
 +            expected: _,
 +            msg: _,
 +            target: _,
 +            cleanup: _,
 +        } => check_operand(tcx, cond, span, body),
 +
 +        TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
 +    }
 +}
 +
 +fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<RustcVersion>) -> bool {
 +    tcx.is_const_fn(def_id)
 +        && tcx.lookup_const_stability(def_id).map_or(true, |const_stab| {
 +            if let rustc_attr::StabilityLevel::Stable { since, .. } = const_stab.level {
 +                // Checking MSRV is manually necessary because `rustc` has no such concept. This entire
 +                // function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`.
 +                // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
 +                crate::meets_msrv(
 +                    msrv,
 +                    RustcVersion::parse(since.as_str())
 +                        .expect("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted"),
 +                )
 +            } else {
 +                // Unstable const fn with the feature enabled.
 +                msrv.is_none()
 +            }
 +        })
 +}
index 6a62002a4d12e3274f14773f79c9ac41b3524aec,0000000000000000000000000000000000000000..232d571902b6c20a8f84684e7ca1ca5e88206f47
mode 100644,000000..100644
--- /dev/null
@@@ -1,739 -1,0 +1,740 @@@
-     .visit_expr(&cx.tcx.hir().body(body).value);
 +use crate::ty::needs_ordered_drop;
 +use crate::{get_enclosing_block, path_to_local_id};
 +use core::ops::ControlFlow;
 +use rustc_hir as hir;
 +use rustc_hir::def::{CtorKind, DefKind, Res};
 +use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor};
 +use rustc_hir::{
 +    Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, Pat, QPath, Stmt, UnOp,
 +    UnsafeSource, Unsafety,
 +};
 +use rustc_lint::LateContext;
 +use rustc_middle::hir::map::Map;
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::ty::adjustment::Adjust;
 +use rustc_middle::ty::{self, Ty, TypeckResults};
 +use rustc_span::Span;
 +
 +mod internal {
 +    /// Trait for visitor functions to control whether or not to descend to child nodes. Implemented
 +    /// for only two types. `()` always descends. `Descend` allows controlled descent.
 +    pub trait Continue {
 +        fn descend(&self) -> bool;
 +    }
 +}
 +use internal::Continue;
 +
 +impl Continue for () {
 +    fn descend(&self) -> bool {
 +        true
 +    }
 +}
 +
 +/// Allows for controlled descent when using visitor functions. Use `()` instead when always
 +/// descending into child nodes.
 +#[derive(Clone, Copy)]
 +pub enum Descend {
 +    Yes,
 +    No,
 +}
 +impl From<bool> for Descend {
 +    fn from(from: bool) -> Self {
 +        if from { Self::Yes } else { Self::No }
 +    }
 +}
 +impl Continue for Descend {
 +    fn descend(&self) -> bool {
 +        matches!(self, Self::Yes)
 +    }
 +}
 +
 +/// Calls the given function once for each expression contained. This does not enter any bodies or
 +/// nested items.
 +pub fn for_each_expr<'tcx, B, C: Continue>(
 +    node: impl Visitable<'tcx>,
 +    f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>,
 +) -> Option<B> {
 +    struct V<B, F> {
 +        f: F,
 +        res: Option<B>,
 +    }
 +    impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<B, F> {
 +        fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
 +            if self.res.is_some() {
 +                return;
 +            }
 +            match (self.f)(e) {
 +                ControlFlow::Continue(c) if c.descend() => walk_expr(self, e),
 +                ControlFlow::Break(b) => self.res = Some(b),
 +                ControlFlow::Continue(_) => (),
 +            }
 +        }
 +
 +        // Avoid unnecessary `walk_*` calls.
 +        fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) {}
 +        fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) {}
 +        fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) {}
 +        // Avoid monomorphising all `visit_*` functions.
 +        fn visit_nested_item(&mut self, _: ItemId) {}
 +    }
 +    let mut v = V { f, res: None };
 +    node.visit(&mut v);
 +    v.res
 +}
 +
 +/// 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 NestedFilter = nested_filter::OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.hir
 +        }
 +
 +        fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
 +            if (self.f)(expr) {
 +                walk_expr(self, expr);
 +            }
 +        }
 +    }
 +    V { hir: cx.tcx.hir(), f }
 +}
 +
 +/// 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> {
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if (self.0)(e) {
 +                walk_expr(self, e);
 +            }
 +        }
 +    }
 +    V(f)
 +}
 +
 +/// 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
 +where
 +    F: FnMut(&'hir hir::Expr<'hir>) -> bool,
 +{
 +    struct RetFinder<F> {
 +        in_stmt: bool,
 +        failed: bool,
 +        cb: F,
 +    }
 +
 +    struct WithStmtGuarg<'a, F> {
 +        val: &'a mut RetFinder<F>,
 +        prev_in_stmt: bool,
 +    }
 +
 +    impl<F> RetFinder<F> {
 +        fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> {
 +            let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
 +            WithStmtGuarg {
 +                val: self,
 +                prev_in_stmt,
 +            }
 +        }
 +    }
 +
 +    impl<F> std::ops::Deref for WithStmtGuarg<'_, F> {
 +        type Target = RetFinder<F>;
 +
 +        fn deref(&self) -> &Self::Target {
 +            self.val
 +        }
 +    }
 +
 +    impl<F> std::ops::DerefMut for WithStmtGuarg<'_, F> {
 +        fn deref_mut(&mut self) -> &mut Self::Target {
 +            self.val
 +        }
 +    }
 +
 +    impl<F> Drop for WithStmtGuarg<'_, F> {
 +        fn drop(&mut self) {
 +            self.val.in_stmt = self.prev_in_stmt;
 +        }
 +    }
 +
 +    impl<'hir, F: FnMut(&'hir hir::Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder<F> {
 +        fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'_>) {
 +            intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt);
 +        }
 +
 +        fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) {
 +            if self.failed {
 +                return;
 +            }
 +            if self.in_stmt {
 +                match expr.kind {
 +                    hir::ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr),
 +                    _ => intravisit::walk_expr(self, expr),
 +                }
 +            } else {
 +                match expr.kind {
 +                    hir::ExprKind::If(cond, then, else_opt) => {
 +                        self.inside_stmt(true).visit_expr(cond);
 +                        self.visit_expr(then);
 +                        if let Some(el) = else_opt {
 +                            self.visit_expr(el);
 +                        }
 +                    },
 +                    hir::ExprKind::Match(cond, arms, _) => {
 +                        self.inside_stmt(true).visit_expr(cond);
 +                        for arm in arms {
 +                            self.visit_expr(arm.body);
 +                        }
 +                    },
 +                    hir::ExprKind::Block(..) => intravisit::walk_expr(self, expr),
 +                    hir::ExprKind::Ret(Some(expr)) => self.visit_expr(expr),
 +                    _ => self.failed |= !(self.cb)(expr),
 +                }
 +            }
 +        }
 +    }
 +
 +    !contains_try(expr) && {
 +        let mut ret_finder = RetFinder {
 +            in_stmt: false,
 +            failed: false,
 +            cb: callback,
 +        };
 +        ret_finder.visit_expr(expr);
 +        !ret_finder.failed
 +    }
 +}
 +
 +/// A type which can be visited.
 +pub trait Visitable<'tcx> {
 +    /// Calls the corresponding `visit_*` function on the visitor.
 +    fn visit<V: Visitor<'tcx>>(self, visitor: &mut V);
 +}
 +macro_rules! visitable_ref {
 +    ($t:ident, $f:ident) => {
 +        impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> {
 +            fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
 +                visitor.$f(self);
 +            }
 +        }
 +    };
 +}
 +visitable_ref!(Arm, visit_arm);
 +visitable_ref!(Block, visit_block);
 +visitable_ref!(Body, visit_body);
 +visitable_ref!(Expr, visit_expr);
 +visitable_ref!(Stmt, visit_stmt);
 +
 +// impl<'tcx, I: IntoIterator> Visitable<'tcx> for I
 +// where
 +//     I::Item: Visitable<'tcx>,
 +// {
 +//     fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
 +//         for x in self {
 +//             x.visit(visitor);
 +//         }
 +//     }
 +// }
 +
 +/// 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;
 +        }
 +
 +        if let ExprKind::Path(p) = &e.kind {
 +            if cx.qpath_res(p, e.hir_id) == res {
 +                found = true;
 +            }
 +        }
 +        !found
 +    })
++    .visit_expr(cx.tcx.hir().body(body).value);
 +    found
 +}
 +
 +/// Checks if the given local is used.
 +pub fn is_local_used<'tcx>(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 expression is a constant.
 +pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
 +    struct V<'a, 'tcx> {
 +        cx: &'a LateContext<'tcx>,
 +        is_const: bool,
 +    }
 +    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
 +        type NestedFilter = nested_filter::OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.cx.tcx.hir()
 +        }
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            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(_)
 +                    ) => {},
 +
 +                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, is_const: true };
 +    v.visit_expr(e);
 +    v.is_const
 +}
 +
 +/// Checks if the given expression performs an unsafe operation outside of an unsafe block.
 +pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
 +    struct V<'a, 'tcx> {
 +        cx: &'a LateContext<'tcx>,
 +        is_unsafe: bool,
 +    }
 +    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
 +        type NestedFilter = nested_filter::OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.cx.tcx.hir()
 +        }
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            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 { cx, is_unsafe: false };
 +    v.visit_expr(e);
 +    v.is_unsafe
 +}
 +
 +/// Checks if the given expression contains an unsafe block
 +pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
 +    struct V<'cx, 'tcx> {
 +        cx: &'cx LateContext<'tcx>,
 +        found_unsafe: bool,
 +    }
 +    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
 +        type NestedFilter = nested_filter::OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.cx.tcx.hir()
 +        }
 +
 +        fn visit_block(&mut self, b: &'tcx Block<'_>) {
 +            if self.found_unsafe {
 +                return;
 +            }
 +            if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
 +                self.found_unsafe = true;
 +                return;
 +            }
 +            walk_block(self, b);
 +        }
 +    }
 +    let mut v = V {
 +        cx,
 +        found_unsafe: false,
 +    };
 +    v.visit_expr(e);
 +    v.found_unsafe
 +}
 +
 +/// Runs the given function for each sub-expression producing the final value consumed by the parent
 +/// of the give expression.
 +///
 +/// e.g. for the following expression
 +/// ```rust,ignore
 +/// if foo {
 +///     f(0)
 +/// } else {
 +///     1 + 1
 +/// }
 +/// ```
 +/// this will pass both `f(0)` and `1+1` to the given function.
 +pub fn for_each_value_source<'tcx, B>(
 +    e: &'tcx Expr<'tcx>,
 +    f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
 +) -> ControlFlow<B> {
 +    match e.kind {
 +        ExprKind::Block(Block { expr: Some(e), .. }, _) => for_each_value_source(e, f),
 +        ExprKind::Match(_, arms, _) => {
 +            for arm in arms {
 +                for_each_value_source(arm.body, f)?;
 +            }
 +            ControlFlow::Continue(())
 +        },
 +        ExprKind::If(_, if_expr, Some(else_expr)) => {
 +            for_each_value_source(if_expr, f)?;
 +            for_each_value_source(else_expr, f)
 +        },
 +        ExprKind::DropTemps(e) => for_each_value_source(e, f),
 +        _ => f(e),
 +    }
 +}
 +
 +/// Runs the given function for each path expression referencing the given local which occur after
 +/// the given expression.
 +pub fn for_each_local_use_after_expr<'tcx, B>(
 +    cx: &LateContext<'tcx>,
 +    local_id: HirId,
 +    expr_id: HirId,
 +    f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
 +) -> ControlFlow<B> {
 +    struct V<'cx, 'tcx, F, B> {
 +        cx: &'cx LateContext<'tcx>,
 +        local_id: HirId,
 +        expr_id: HirId,
 +        found: bool,
 +        res: ControlFlow<B>,
 +        f: F,
 +    }
 +    impl<'cx, 'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'cx, 'tcx, F, B> {
 +        type NestedFilter = nested_filter::OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.cx.tcx.hir()
 +        }
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
 +            if !self.found {
 +                if e.hir_id == self.expr_id {
 +                    self.found = true;
 +                } else {
 +                    walk_expr(self, e);
 +                }
 +                return;
 +            }
 +            if self.res.is_break() {
 +                return;
 +            }
 +            if path_to_local_id(e, self.local_id) {
 +                self.res = (self.f)(e);
 +            } else {
 +                walk_expr(self, e);
 +            }
 +        }
 +    }
 +
 +    if let Some(b) = get_enclosing_block(cx, local_id) {
 +        let mut v = V {
 +            cx,
 +            local_id,
 +            expr_id,
 +            found: false,
 +            res: ControlFlow::Continue(()),
 +            f,
 +        };
 +        v.visit_block(b);
 +        v.res
 +    } else {
 +        ControlFlow::Continue(())
 +    }
 +}
 +
 +// Calls the given function for every unconsumed temporary created by the expression. Note the
 +// function is only guaranteed to be called for types which need to be dropped, but it may be called
 +// for other types.
++#[allow(clippy::too_many_lines)]
 +pub fn for_each_unconsumed_temporary<'tcx, B>(
 +    cx: &LateContext<'tcx>,
 +    e: &'tcx Expr<'tcx>,
 +    mut f: impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
 +) -> ControlFlow<B> {
 +    // Todo: Handle partially consumed values.
 +    fn helper<'tcx, B>(
 +        typeck: &'tcx TypeckResults<'tcx>,
 +        consume: bool,
 +        e: &'tcx Expr<'tcx>,
 +        f: &mut impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
 +    ) -> ControlFlow<B> {
 +        if !consume
 +            || matches!(
 +                typeck.expr_adjustments(e),
 +                [adjust, ..] if matches!(adjust.kind, Adjust::Borrow(_) | Adjust::Deref(_))
 +            )
 +        {
 +            match e.kind {
 +                ExprKind::Path(QPath::Resolved(None, p))
 +                    if matches!(p.res, Res::Def(DefKind::Ctor(_, CtorKind::Const), _)) =>
 +                {
 +                    f(typeck.expr_ty(e))?;
 +                },
 +                ExprKind::Path(_)
 +                | ExprKind::Unary(UnOp::Deref, _)
 +                | ExprKind::Index(..)
 +                | ExprKind::Field(..)
 +                | ExprKind::AddrOf(..) => (),
 +                _ => f(typeck.expr_ty(e))?,
 +            }
 +        }
 +        match e.kind {
 +            ExprKind::AddrOf(_, _, e)
 +            | ExprKind::Field(e, _)
 +            | ExprKind::Unary(UnOp::Deref, e)
 +            | ExprKind::Match(e, ..)
 +            | ExprKind::Let(&Let { init: e, .. }) => {
 +                helper(typeck, false, e, f)?;
 +            },
 +            ExprKind::Block(&Block { expr: Some(e), .. }, _)
 +            | ExprKind::Box(e)
 +            | ExprKind::Cast(e, _)
 +            | ExprKind::Unary(_, e) => {
 +                helper(typeck, true, e, f)?;
 +            },
 +            ExprKind::Call(callee, args) => {
 +                helper(typeck, true, callee, f)?;
 +                for arg in args {
 +                    helper(typeck, true, arg, f)?;
 +                }
 +            },
 +            ExprKind::MethodCall(_, receiver, args, _) => {
 +                helper(typeck, true, receiver, f)?;
 +                for arg in args {
 +                    helper(typeck, true, arg, f)?;
 +                }
 +            },
 +            ExprKind::Tup(args) | ExprKind::Array(args) => {
 +                for arg in args {
 +                    helper(typeck, true, arg, f)?;
 +                }
 +            },
 +            ExprKind::Index(borrowed, consumed)
 +            | ExprKind::Assign(borrowed, consumed, _)
 +            | ExprKind::AssignOp(_, borrowed, consumed) => {
 +                helper(typeck, false, borrowed, f)?;
 +                helper(typeck, true, consumed, f)?;
 +            },
 +            ExprKind::Binary(_, lhs, rhs) => {
 +                helper(typeck, true, lhs, f)?;
 +                helper(typeck, true, rhs, f)?;
 +            },
 +            ExprKind::Struct(_, fields, default) => {
 +                for field in fields {
 +                    helper(typeck, true, field.expr, f)?;
 +                }
 +                if let Some(default) = default {
 +                    helper(typeck, false, default, f)?;
 +                }
 +            },
 +            ExprKind::If(cond, then, else_expr) => {
 +                helper(typeck, true, cond, f)?;
 +                helper(typeck, true, then, f)?;
 +                if let Some(else_expr) = else_expr {
 +                    helper(typeck, true, else_expr, f)?;
 +                }
 +            },
 +            ExprKind::Type(e, _) => {
 +                helper(typeck, consume, e, f)?;
 +            },
 +
 +            // Either drops temporaries, jumps out of the current expression, or has no sub expression.
 +            ExprKind::DropTemps(_)
 +            | ExprKind::Ret(_)
 +            | ExprKind::Break(..)
 +            | ExprKind::Yield(..)
 +            | ExprKind::Block(..)
 +            | ExprKind::Loop(..)
 +            | ExprKind::Repeat(..)
 +            | ExprKind::Lit(_)
 +            | ExprKind::ConstBlock(_)
 +            | ExprKind::Closure { .. }
 +            | ExprKind::Path(_)
 +            | ExprKind::Continue(_)
 +            | ExprKind::InlineAsm(_)
 +            | ExprKind::Err => (),
 +        }
 +        ControlFlow::Continue(())
 +    }
 +    helper(cx.typeck_results(), true, e, &mut f)
 +}
 +
 +pub fn any_temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
 +    for_each_unconsumed_temporary(cx, e, |ty| {
 +        if needs_ordered_drop(cx, ty) {
 +            ControlFlow::Break(())
 +        } else {
 +            ControlFlow::Continue(())
 +        }
 +    })
 +    .is_break()
 +}
 +
 +/// Runs the given function for each path expression referencing the given local which occur after
 +/// the given expression.
 +pub fn for_each_local_assignment<'tcx, B>(
 +    cx: &LateContext<'tcx>,
 +    local_id: HirId,
 +    f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
 +) -> ControlFlow<B> {
 +    struct V<'cx, 'tcx, F, B> {
 +        cx: &'cx LateContext<'tcx>,
 +        local_id: HirId,
 +        res: ControlFlow<B>,
 +        f: F,
 +    }
 +    impl<'cx, 'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'cx, 'tcx, F, B> {
 +        type NestedFilter = nested_filter::OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.cx.tcx.hir()
 +        }
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
 +            if let ExprKind::Assign(lhs, rhs, _) = e.kind
 +                && self.res.is_continue()
 +                && path_to_local_id(lhs, self.local_id)
 +            {
 +                self.res = (self.f)(rhs);
 +                self.visit_expr(rhs);
 +            } else {
 +                walk_expr(self, e);
 +            }
 +        }
 +    }
 +
 +    if let Some(b) = get_enclosing_block(cx, local_id) {
 +        let mut v = V {
 +            cx,
 +            local_id,
 +            res: ControlFlow::Continue(()),
 +            f,
 +        };
 +        v.visit_block(b);
 +        v.res
 +    } else {
 +        ControlFlow::Continue(())
 +    }
 +}
index 4fbae8614ca3daa060945a2862d65432bba921d5,0000000000000000000000000000000000000000..ebbe9c9ae675f0039510d8e736cc2a79fbadf59c
mode 100644,000000..100644
--- /dev/null
@@@ -1,35 -1,0 +1,35 @@@
- cargo = {name = "cargo", versions = ['0.49.0']}
 +[crates]
 +# some of these are from cargotest
++cargo = {name = "cargo", versions = ['0.64.0']}
 +iron = {name = "iron", versions = ['0.6.1']}
 +ripgrep = {name = "ripgrep", versions = ['12.1.1']}
 +xsv = {name = "xsv", versions = ['0.13.0']}
 +# commented out because of 173K clippy::match_same_arms msgs in language_type.rs
 +#tokei = { name = "tokei", versions = ['12.0.4']}
 +rayon = {name = "rayon", versions = ['1.5.0']}
 +serde = {name = "serde", versions = ['1.0.118']}
 +# top 10 crates.io dls
 +bitflags = {name = "bitflags", versions = ['1.2.1']}
 +# crash = {name = "clippy_crash", path = "/tmp/clippy_crash"}
 +libc = {name = "libc", versions = ['0.2.81']}
 +log = {name = "log", versions = ['0.4.11']}
 +proc-macro2 = {name = "proc-macro2", versions = ['1.0.24']}
 +quote = {name = "quote", versions = ['1.0.7']}
 +rand = {name = "rand", versions = ['0.7.3']}
 +rand_core = {name = "rand_core", versions = ['0.6.0']}
 +regex = {name = "regex", versions = ['1.3.2']}
 +syn = {name = "syn", versions = ['1.0.54']}
 +unicode-xid = {name = "unicode-xid", versions = ['0.2.1']}
 +# some more of dtolnays crates
 +anyhow = {name = "anyhow", versions = ['1.0.38']}
 +async-trait = {name = "async-trait", versions = ['0.1.42']}
 +cxx = {name = "cxx", versions = ['1.0.32']}
 +ryu = {name = "ryu", versions = ['1.0.5']}
 +serde_yaml = {name = "serde_yaml", versions = ['0.8.17']}
 +thiserror = {name = "thiserror", versions = ['1.0.24']}
 +# some embark crates, there are other interesting crates but
 +# unfortunately adding them increases lintcheck runtime drastically
 +cfg-expr = {name = "cfg-expr", versions = ['0.7.1']}
 +puffin = {name = "puffin", git_url = "https://github.com/EmbarkStudios/puffin", git_hash = "02dd4a3"}
 +rpmalloc = {name = "rpmalloc", versions = ['0.2.0']}
 +tame-oidc = {name = "tame-oidc", versions = ['0.1.0']}
index 85b60fefd60fc4ab84a271c50ebc510bd7449221,0000000000000000000000000000000000000000..b6976366dafc9c66287a30ac986407e9865998eb
mode 100644,000000..100644
--- /dev/null
@@@ -1,3 -1,0 +1,3 @@@
- channel = "nightly-2022-08-27"
 +[toolchain]
++channel = "nightly-2022-09-08"
 +components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f3a5048e7fa824994f367ea83ee09805c27dec3b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,596 @@@
++// autogenerated. Please look at /clippy_dev/src/update_lints.rs
++
++macro_rules! include_lint {
++    ($file_name: expr) => {
++        include_str!($file_name)
++    };
++}
++
++macro_rules! docs {
++    ($($lint_name: expr,)*) => {
++        pub fn explain(lint: &str) {
++            println!("{}", match lint {
++                $(
++                    $lint_name => include_lint!(concat!("docs/", concat!($lint_name, ".txt"))),
++                )*
++                _ => "unknown lint",
++            })
++        }
++    }
++}
++
++docs! {
++    "absurd_extreme_comparisons",
++    "alloc_instead_of_core",
++    "allow_attributes_without_reason",
++    "almost_complete_letter_range",
++    "almost_swapped",
++    "approx_constant",
++    "arithmetic_side_effects",
++    "as_conversions",
++    "as_underscore",
++    "assertions_on_constants",
++    "assertions_on_result_states",
++    "assign_op_pattern",
++    "async_yields_async",
++    "await_holding_invalid_type",
++    "await_holding_lock",
++    "await_holding_refcell_ref",
++    "bad_bit_mask",
++    "bind_instead_of_map",
++    "blanket_clippy_restriction_lints",
++    "blocks_in_if_conditions",
++    "bool_assert_comparison",
++    "bool_comparison",
++    "bool_to_int_with_if",
++    "borrow_as_ptr",
++    "borrow_deref_ref",
++    "borrow_interior_mutable_const",
++    "borrowed_box",
++    "box_collection",
++    "boxed_local",
++    "branches_sharing_code",
++    "builtin_type_shadow",
++    "bytes_count_to_len",
++    "bytes_nth",
++    "cargo_common_metadata",
++    "case_sensitive_file_extension_comparisons",
++    "cast_abs_to_unsigned",
++    "cast_enum_constructor",
++    "cast_enum_truncation",
++    "cast_lossless",
++    "cast_possible_truncation",
++    "cast_possible_wrap",
++    "cast_precision_loss",
++    "cast_ptr_alignment",
++    "cast_ref_to_mut",
++    "cast_sign_loss",
++    "cast_slice_different_sizes",
++    "cast_slice_from_raw_parts",
++    "char_lit_as_u8",
++    "chars_last_cmp",
++    "chars_next_cmp",
++    "checked_conversions",
++    "clone_double_ref",
++    "clone_on_copy",
++    "clone_on_ref_ptr",
++    "cloned_instead_of_copied",
++    "cmp_nan",
++    "cmp_null",
++    "cmp_owned",
++    "cognitive_complexity",
++    "collapsible_else_if",
++    "collapsible_if",
++    "collapsible_match",
++    "collapsible_str_replace",
++    "comparison_chain",
++    "comparison_to_empty",
++    "copy_iterator",
++    "crate_in_macro_def",
++    "create_dir",
++    "crosspointer_transmute",
++    "dbg_macro",
++    "debug_assert_with_mut_call",
++    "decimal_literal_representation",
++    "declare_interior_mutable_const",
++    "default_instead_of_iter_empty",
++    "default_numeric_fallback",
++    "default_trait_access",
++    "default_union_representation",
++    "deprecated_cfg_attr",
++    "deprecated_semver",
++    "deref_addrof",
++    "deref_by_slicing",
++    "derivable_impls",
++    "derive_hash_xor_eq",
++    "derive_ord_xor_partial_ord",
++    "derive_partial_eq_without_eq",
++    "disallowed_methods",
++    "disallowed_names",
++    "disallowed_script_idents",
++    "disallowed_types",
++    "diverging_sub_expression",
++    "doc_link_with_quotes",
++    "doc_markdown",
++    "double_comparisons",
++    "double_must_use",
++    "double_neg",
++    "double_parens",
++    "drop_copy",
++    "drop_non_drop",
++    "drop_ref",
++    "duplicate_mod",
++    "duplicate_underscore_argument",
++    "duration_subsec",
++    "else_if_without_else",
++    "empty_drop",
++    "empty_enum",
++    "empty_line_after_outer_attr",
++    "empty_loop",
++    "empty_structs_with_brackets",
++    "enum_clike_unportable_variant",
++    "enum_glob_use",
++    "enum_variant_names",
++    "eq_op",
++    "equatable_if_let",
++    "erasing_op",
++    "err_expect",
++    "excessive_precision",
++    "exhaustive_enums",
++    "exhaustive_structs",
++    "exit",
++    "expect_fun_call",
++    "expect_used",
++    "expl_impl_clone_on_copy",
++    "explicit_auto_deref",
++    "explicit_counter_loop",
++    "explicit_deref_methods",
++    "explicit_into_iter_loop",
++    "explicit_iter_loop",
++    "explicit_write",
++    "extend_with_drain",
++    "extra_unused_lifetimes",
++    "fallible_impl_from",
++    "field_reassign_with_default",
++    "filetype_is_file",
++    "filter_map_identity",
++    "filter_map_next",
++    "filter_next",
++    "flat_map_identity",
++    "flat_map_option",
++    "float_arithmetic",
++    "float_cmp",
++    "float_cmp_const",
++    "float_equality_without_abs",
++    "fn_address_comparisons",
++    "fn_params_excessive_bools",
++    "fn_to_numeric_cast",
++    "fn_to_numeric_cast_any",
++    "fn_to_numeric_cast_with_truncation",
++    "for_kv_map",
++    "for_loops_over_fallibles",
++    "forget_copy",
++    "forget_non_drop",
++    "forget_ref",
++    "format_in_format_args",
++    "format_push_string",
++    "from_iter_instead_of_collect",
++    "from_over_into",
++    "from_str_radix_10",
++    "future_not_send",
++    "get_first",
++    "get_last_with_len",
++    "get_unwrap",
++    "identity_op",
++    "if_let_mutex",
++    "if_not_else",
++    "if_same_then_else",
++    "if_then_some_else_none",
++    "ifs_same_cond",
++    "implicit_clone",
++    "implicit_hasher",
++    "implicit_return",
++    "implicit_saturating_sub",
++    "imprecise_flops",
++    "inconsistent_digit_grouping",
++    "inconsistent_struct_constructor",
++    "index_refutable_slice",
++    "indexing_slicing",
++    "ineffective_bit_mask",
++    "inefficient_to_string",
++    "infallible_destructuring_match",
++    "infinite_iter",
++    "inherent_to_string",
++    "inherent_to_string_shadow_display",
++    "init_numbered_fields",
++    "inline_always",
++    "inline_asm_x86_att_syntax",
++    "inline_asm_x86_intel_syntax",
++    "inline_fn_without_body",
++    "inspect_for_each",
++    "int_plus_one",
++    "integer_arithmetic",
++    "integer_division",
++    "into_iter_on_ref",
++    "invalid_null_ptr_usage",
++    "invalid_regex",
++    "invalid_upcast_comparisons",
++    "invalid_utf8_in_unchecked",
++    "invisible_characters",
++    "is_digit_ascii_radix",
++    "items_after_statements",
++    "iter_cloned_collect",
++    "iter_count",
++    "iter_next_loop",
++    "iter_next_slice",
++    "iter_not_returning_iterator",
++    "iter_nth",
++    "iter_nth_zero",
++    "iter_on_empty_collections",
++    "iter_on_single_items",
++    "iter_overeager_cloned",
++    "iter_skip_next",
++    "iter_with_drain",
++    "iterator_step_by_zero",
++    "just_underscores_and_digits",
++    "large_const_arrays",
++    "large_digit_groups",
++    "large_enum_variant",
++    "large_include_file",
++    "large_stack_arrays",
++    "large_types_passed_by_value",
++    "len_without_is_empty",
++    "len_zero",
++    "let_and_return",
++    "let_underscore_drop",
++    "let_underscore_lock",
++    "let_underscore_must_use",
++    "let_unit_value",
++    "linkedlist",
++    "lossy_float_literal",
++    "macro_use_imports",
++    "main_recursion",
++    "manual_assert",
++    "manual_async_fn",
++    "manual_bits",
++    "manual_filter_map",
++    "manual_find",
++    "manual_find_map",
++    "manual_flatten",
++    "manual_instant_elapsed",
++    "manual_map",
++    "manual_memcpy",
++    "manual_non_exhaustive",
++    "manual_ok_or",
++    "manual_range_contains",
++    "manual_rem_euclid",
++    "manual_retain",
++    "manual_saturating_arithmetic",
++    "manual_split_once",
++    "manual_str_repeat",
++    "manual_string_new",
++    "manual_strip",
++    "manual_swap",
++    "manual_unwrap_or",
++    "many_single_char_names",
++    "map_clone",
++    "map_collect_result_unit",
++    "map_entry",
++    "map_err_ignore",
++    "map_flatten",
++    "map_identity",
++    "map_unwrap_or",
++    "match_as_ref",
++    "match_bool",
++    "match_like_matches_macro",
++    "match_on_vec_items",
++    "match_overlapping_arm",
++    "match_ref_pats",
++    "match_result_ok",
++    "match_same_arms",
++    "match_single_binding",
++    "match_str_case_mismatch",
++    "match_wild_err_arm",
++    "match_wildcard_for_single_variants",
++    "maybe_infinite_iter",
++    "mem_forget",
++    "mem_replace_option_with_none",
++    "mem_replace_with_default",
++    "mem_replace_with_uninit",
++    "min_max",
++    "mismatched_target_os",
++    "mismatching_type_param_order",
++    "misrefactored_assign_op",
++    "missing_const_for_fn",
++    "missing_docs_in_private_items",
++    "missing_enforced_import_renames",
++    "missing_errors_doc",
++    "missing_inline_in_public_items",
++    "missing_panics_doc",
++    "missing_safety_doc",
++    "missing_spin_loop",
++    "mistyped_literal_suffixes",
++    "mixed_case_hex_literals",
++    "mixed_read_write_in_expression",
++    "mod_module_files",
++    "module_inception",
++    "module_name_repetitions",
++    "modulo_arithmetic",
++    "modulo_one",
++    "multi_assignments",
++    "multiple_crate_versions",
++    "multiple_inherent_impl",
++    "must_use_candidate",
++    "must_use_unit",
++    "mut_from_ref",
++    "mut_mut",
++    "mut_mutex_lock",
++    "mut_range_bound",
++    "mutable_key_type",
++    "mutex_atomic",
++    "mutex_integer",
++    "naive_bytecount",
++    "needless_arbitrary_self_type",
++    "needless_bitwise_bool",
++    "needless_bool",
++    "needless_borrow",
++    "needless_borrowed_reference",
++    "needless_collect",
++    "needless_continue",
++    "needless_doctest_main",
++    "needless_for_each",
++    "needless_late_init",
++    "needless_lifetimes",
++    "needless_match",
++    "needless_option_as_deref",
++    "needless_option_take",
++    "needless_parens_on_range_literals",
++    "needless_pass_by_value",
++    "needless_question_mark",
++    "needless_range_loop",
++    "needless_return",
++    "needless_splitn",
++    "needless_update",
++    "neg_cmp_op_on_partial_ord",
++    "neg_multiply",
++    "negative_feature_names",
++    "never_loop",
++    "new_ret_no_self",
++    "new_without_default",
++    "no_effect",
++    "no_effect_replace",
++    "no_effect_underscore_binding",
++    "non_ascii_literal",
++    "non_octal_unix_permissions",
++    "non_send_fields_in_send_ty",
++    "nonminimal_bool",
++    "nonsensical_open_options",
++    "nonstandard_macro_braces",
++    "not_unsafe_ptr_arg_deref",
++    "obfuscated_if_else",
++    "octal_escapes",
++    "ok_expect",
++    "only_used_in_recursion",
++    "op_ref",
++    "option_as_ref_deref",
++    "option_env_unwrap",
++    "option_filter_map",
++    "option_if_let_else",
++    "option_map_or_none",
++    "option_map_unit_fn",
++    "option_option",
++    "or_fun_call",
++    "or_then_unwrap",
++    "out_of_bounds_indexing",
++    "overflow_check_conditional",
++    "overly_complex_bool_expr",
++    "panic",
++    "panic_in_result_fn",
++    "panicking_unwrap",
++    "partialeq_ne_impl",
++    "partialeq_to_none",
++    "path_buf_push_overwrite",
++    "pattern_type_mismatch",
++    "positional_named_format_parameters",
++    "possible_missing_comma",
++    "precedence",
++    "print_in_format_impl",
++    "print_literal",
++    "print_stderr",
++    "print_stdout",
++    "print_with_newline",
++    "println_empty_string",
++    "ptr_arg",
++    "ptr_as_ptr",
++    "ptr_eq",
++    "ptr_offset_with_cast",
++    "pub_use",
++    "question_mark",
++    "range_minus_one",
++    "range_plus_one",
++    "range_zip_with_len",
++    "rc_buffer",
++    "rc_clone_in_vec_init",
++    "rc_mutex",
++    "read_zero_byte_vec",
++    "recursive_format_impl",
++    "redundant_allocation",
++    "redundant_clone",
++    "redundant_closure",
++    "redundant_closure_call",
++    "redundant_closure_for_method_calls",
++    "redundant_else",
++    "redundant_feature_names",
++    "redundant_field_names",
++    "redundant_pattern",
++    "redundant_pattern_matching",
++    "redundant_pub_crate",
++    "redundant_slicing",
++    "redundant_static_lifetimes",
++    "ref_binding_to_reference",
++    "ref_option_ref",
++    "repeat_once",
++    "rest_pat_in_fully_bound_structs",
++    "result_large_err",
++    "result_map_or_into_option",
++    "result_map_unit_fn",
++    "result_unit_err",
++    "return_self_not_must_use",
++    "reversed_empty_ranges",
++    "same_functions_in_if_condition",
++    "same_item_push",
++    "same_name_method",
++    "search_is_some",
++    "self_assignment",
++    "self_named_constructors",
++    "self_named_module_files",
++    "semicolon_if_nothing_returned",
++    "separated_literal_suffix",
++    "serde_api_misuse",
++    "shadow_reuse",
++    "shadow_same",
++    "shadow_unrelated",
++    "short_circuit_statement",
++    "should_implement_trait",
++    "significant_drop_in_scrutinee",
++    "similar_names",
++    "single_char_add_str",
++    "single_char_lifetime_names",
++    "single_char_pattern",
++    "single_component_path_imports",
++    "single_element_loop",
++    "single_match",
++    "single_match_else",
++    "size_of_in_element_count",
++    "skip_while_next",
++    "slow_vector_initialization",
++    "stable_sort_primitive",
++    "std_instead_of_alloc",
++    "std_instead_of_core",
++    "str_to_string",
++    "string_add",
++    "string_add_assign",
++    "string_extend_chars",
++    "string_from_utf8_as_bytes",
++    "string_lit_as_bytes",
++    "string_slice",
++    "string_to_string",
++    "strlen_on_c_strings",
++    "struct_excessive_bools",
++    "suboptimal_flops",
++    "suspicious_arithmetic_impl",
++    "suspicious_assignment_formatting",
++    "suspicious_else_formatting",
++    "suspicious_map",
++    "suspicious_op_assign_impl",
++    "suspicious_operation_groupings",
++    "suspicious_splitn",
++    "suspicious_to_owned",
++    "suspicious_unary_op_formatting",
++    "swap_ptr_to_ref",
++    "tabs_in_doc_comments",
++    "temporary_assignment",
++    "to_digit_is_some",
++    "to_string_in_format_args",
++    "todo",
++    "too_many_arguments",
++    "too_many_lines",
++    "toplevel_ref_arg",
++    "trailing_empty_array",
++    "trait_duplication_in_bounds",
++    "transmute_bytes_to_str",
++    "transmute_float_to_int",
++    "transmute_int_to_bool",
++    "transmute_int_to_char",
++    "transmute_int_to_float",
++    "transmute_num_to_bytes",
++    "transmute_ptr_to_ptr",
++    "transmute_ptr_to_ref",
++    "transmute_undefined_repr",
++    "transmutes_expressible_as_ptr_casts",
++    "transmuting_null",
++    "trim_split_whitespace",
++    "trivial_regex",
++    "trivially_copy_pass_by_ref",
++    "try_err",
++    "type_complexity",
++    "type_repetition_in_bounds",
++    "undocumented_unsafe_blocks",
++    "undropped_manually_drops",
++    "unicode_not_nfc",
++    "unimplemented",
++    "uninit_assumed_init",
++    "uninit_vec",
++    "unit_arg",
++    "unit_cmp",
++    "unit_hash",
++    "unit_return_expecting_ord",
++    "unnecessary_cast",
++    "unnecessary_filter_map",
++    "unnecessary_find_map",
++    "unnecessary_fold",
++    "unnecessary_join",
++    "unnecessary_lazy_evaluations",
++    "unnecessary_mut_passed",
++    "unnecessary_operation",
++    "unnecessary_owned_empty_strings",
++    "unnecessary_self_imports",
++    "unnecessary_sort_by",
++    "unnecessary_to_owned",
++    "unnecessary_unwrap",
++    "unnecessary_wraps",
++    "unneeded_field_pattern",
++    "unneeded_wildcard_pattern",
++    "unnested_or_patterns",
++    "unreachable",
++    "unreadable_literal",
++    "unsafe_derive_deserialize",
++    "unsafe_removed_from_name",
++    "unseparated_literal_suffix",
++    "unsound_collection_transmute",
++    "unused_async",
++    "unused_io_amount",
++    "unused_peekable",
++    "unused_rounding",
++    "unused_self",
++    "unused_unit",
++    "unusual_byte_groupings",
++    "unwrap_in_result",
++    "unwrap_or_else_default",
++    "unwrap_used",
++    "upper_case_acronyms",
++    "use_debug",
++    "use_self",
++    "used_underscore_binding",
++    "useless_asref",
++    "useless_attribute",
++    "useless_conversion",
++    "useless_format",
++    "useless_let_if_seq",
++    "useless_transmute",
++    "useless_vec",
++    "vec_box",
++    "vec_init_then_push",
++    "vec_resize_to_zero",
++    "verbose_bit_mask",
++    "verbose_file_reads",
++    "vtable_address_comparisons",
++    "while_immutable_condition",
++    "while_let_loop",
++    "while_let_on_iterator",
++    "wildcard_dependencies",
++    "wildcard_enum_match_arm",
++    "wildcard_imports",
++    "wildcard_in_or_patterns",
++    "write_literal",
++    "write_with_newline",
++    "writeln_empty_string",
++    "wrong_self_convention",
++    "wrong_transmute",
++    "zero_divided_by_zero",
++    "zero_prefixed_literal",
++    "zero_ptr",
++    "zero_sized_map_values",
++    "zst_offset",
++
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..590bee28aa2376be4492aa4c9a53e918ec63805b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++### What it does
++Checks for comparisons where one side of the relation is
++either the minimum or maximum value for its type and warns if it involves a
++case that is always true or always false. Only integer and boolean types are
++checked.
++
++### Why is this bad?
++An expression like `min <= x` may misleadingly imply
++that it is possible for `x` to be less than the minimum. Expressions like
++`max < x` are probably mistakes.
++
++### Known problems
++For `usize` the size of the current compile target will
++be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such
++a comparison to detect target pointer width will trigger this lint. One can
++use `mem::sizeof` and compare its value or conditional compilation
++attributes
++like `#[cfg(target_pointer_width = "64")] ..` instead.
++
++### Example
++```
++let vec: Vec<isize> = Vec::new();
++if vec.len() <= 0 {}
++if 100 > i32::MAX {}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..488a36e9276c24d1bd1ded86983a6c423b39c9cb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++
++Finds items imported through `alloc` when available through `core`.
++
++### Why is this bad?
++
++Crates which have `no_std` compatibility and may optionally require alloc may wish to ensure types are
++imported from core to ensure disabling `alloc` does not cause the crate to fail to compile. This lint
++is also useful for crates migrating to become `no_std` compatible.
++
++### Example
++```
++use alloc::slice::from_ref;
++```
++Use instead:
++```
++use core::slice::from_ref;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fcc4f49b08b37be204fdc034de517c8ba7f1f747
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for attributes that allow lints without a reason.
++
++(This requires the `lint_reasons` feature)
++
++### Why is this bad?
++Allowing a lint should always have a reason. This reason should be documented to
++ensure that others understand the reasoning
++
++### Example
++```
++#![feature(lint_reasons)]
++
++#![allow(clippy::some_lint)]
++```
++
++Use instead:
++```
++#![feature(lint_reasons)]
++
++#![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")]
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..01cbaf9eae259e26155ea29a65fd71addcbd1825
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks for ranges which almost include the entire range of letters from 'a' to 'z', but
++don't because they're a half open range.
++
++### Why is this bad?
++This (`'a'..'z'`) is almost certainly a typo meant to include all letters.
++
++### Example
++```
++let _ = 'a'..'z';
++```
++Use instead:
++```
++let _ = 'a'..='z';
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cd10a8d5409b10afab8778ac88d91e4e5bbdbbfb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks for `foo = bar; bar = foo` sequences.
++
++### Why is this bad?
++This looks like a failed attempt to swap.
++
++### Example
++```
++a = b;
++b = a;
++```
++If swapping is intended, use `swap()` instead:
++```
++std::mem::swap(&mut a, &mut b);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..393fa4b5ef7ecd6734a6a78c64694646c45505be
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++Checks for floating point literals that approximate
++constants which are defined in
++[`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)
++or
++[`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),
++respectively, suggesting to use the predefined constant.
++
++### Why is this bad?
++Usually, the definition in the standard library is more
++precise than what people come up with. If you find that your definition is
++actually more precise, please [file a Rust
++issue](https://github.com/rust-lang/rust/issues).
++
++### Example
++```
++let x = 3.14;
++let y = 1_f64 / x;
++```
++Use instead:
++```
++let x = std::f32::consts::PI;
++let y = std::f64::consts::FRAC_1_PI;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6c7d51a4989e0b34f54d89c444399be3f502e8f2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++### What it does
++Checks any kind of arithmetic operation of any type.
++
++Operators like `+`, `-`, `*` or `<<` are usually capable of overflowing according to the [Rust
++Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),
++or can panic (`/`, `%`).
++
++Known safe built-in types like `Wrapping` or `Saturing`, floats, operations in constant
++environments, allowed types and non-constant operations that won't overflow are ignored.
++
++### Why is this bad?
++For integers, overflow will trigger a panic in debug builds or wrap the result in
++release mode; division by zero will cause a panic in either mode. As a result, it is
++desirable to explicitly call checked, wrapping or saturating arithmetic methods.
++
++#### Example
++```
++// `n` can be any number, including `i32::MAX`.
++fn foo(n: i32) -> i32 {
++  n + 1
++}
++```
++
++Third-party types can also overflow or present unwanted side-effects.
++
++#### Example
++```
++use rust_decimal::Decimal;
++let _n = Decimal::MAX + Decimal::MAX;
++```
++
++### Allowed types
++Custom allowed types can be specified through the "arithmetic-side-effects-allowed" filter.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4af479bd8111cd213e98fc7a7b14a172a9714b87
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++### What it does
++Checks for usage of `as` conversions.
++
++Note that this lint is specialized in linting *every single* use of `as`
++regardless of whether good alternatives exist or not.
++If you want more precise lints for `as`, please consider using these separate lints:
++`unnecessary_cast`, `cast_lossless/cast_possible_truncation/cast_possible_wrap/cast_precision_loss/cast_sign_loss`,
++`fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`.
++There is a good explanation the reason why this lint should work in this way and how it is useful
++[in this issue](https://github.com/rust-lang/rust-clippy/issues/5122).
++
++### Why is this bad?
++`as` conversions will perform many kinds of
++conversions, including silently lossy conversions and dangerous coercions.
++There are cases when it makes sense to use `as`, so the lint is
++Allow by default.
++
++### Example
++```
++let a: u32;
++...
++f(a as u16);
++```
++
++Use instead:
++```
++f(a.try_into()?);
++
++// or
++
++f(a.try_into().expect("Unexpected u16 overflow in f"));
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2d9b0c358936bc7c04d4c708da7a0bf72641cecf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Check for the usage of `as _` conversion using inferred type.
++
++### Why is this bad?
++The conversion might include lossy conversion and dangerous cast that might go
++undetected due to the type being inferred.
++
++The lint is allowed by default as using `_` is less wordy than always specifying the type.
++
++### Example
++```
++fn foo(n: usize) {}
++let n: u16 = 256;
++foo(n as _);
++```
++Use instead:
++```
++fn foo(n: usize) {}
++let n: u16 = 256;
++foo(n as usize);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..270c1e3b639d96fe496986e003eb62b3dae41f0c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++### What it does
++Checks for `assert!(true)` and `assert!(false)` calls.
++
++### Why is this bad?
++Will be optimized out by the compiler or should probably be replaced by a
++`panic!()` or `unreachable!()`
++
++### Example
++```
++assert!(false)
++assert!(true)
++const B: bool = false;
++assert!(B)
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0889084fd3ad61447b36aaf81a7fde499afd3577
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++### What it does
++Checks for `assert!(r.is_ok())` or `assert!(r.is_err())` calls.
++
++### Why is this bad?
++An assertion failure cannot output an useful message of the error.
++
++### Known problems
++The suggested replacement decreases the readability of code and log output.
++
++### Example
++```
++assert!(r.is_ok());
++assert!(r.is_err());
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f355c0cc18d37d69bf4563727d65ce09c28f5a4c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++### What it does
++Checks for `a = a op b` or `a = b commutative_op a`
++patterns.
++
++### Why is this bad?
++These can be written as the shorter `a op= b`.
++
++### Known problems
++While forbidden by the spec, `OpAssign` traits may have
++implementations that differ from the regular `Op` impl.
++
++### Example
++```
++let mut a = 5;
++let b = 0;
++// ...
++
++a = a + b;
++```
++
++Use instead:
++```
++let mut a = 5;
++let b = 0;
++// ...
++
++a += b;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a40de6d2e473642505f16e46054228bf658eb885
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++### What it does
++Checks for async blocks that yield values of types
++that can themselves be awaited.
++
++### Why is this bad?
++An await is likely missing.
++
++### Example
++```
++async fn foo() {}
++
++fn bar() {
++  let x = async {
++    foo()
++  };
++}
++```
++
++Use instead:
++```
++async fn foo() {}
++
++fn bar() {
++  let x = async {
++    foo().await
++  };
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e9c768772ff6b8ec4485802bb64d4fe9db265598
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++### What it does
++Allows users to configure types which should not be held across `await`
++suspension points.
++
++### Why is this bad?
++There are some types which are perfectly "safe" to be used concurrently
++from a memory access perspective but will cause bugs at runtime if they
++are held in such a way.
++
++### Example
++
++```
++await-holding-invalid-types = [
++  # You can specify a type name
++  "CustomLockType",
++  # You can (optionally) specify a reason
++  { path = "OtherCustomLockType", reason = "Relies on a thread local" }
++]
++```
++
++```
++struct CustomLockType;
++struct OtherCustomLockType;
++async fn foo() {
++  let _x = CustomLockType;
++  let _y = OtherCustomLockType;
++  baz().await; // Lint violation
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0f450a11160ccca10c011e7fef781770c421a372
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++### What it does
++Checks for calls to await while holding a non-async-aware MutexGuard.
++
++### Why is this bad?
++The Mutex types found in std::sync and parking_lot
++are not designed to operate in an async context across await points.
++
++There are two potential solutions. One is to use an async-aware Mutex
++type. Many asynchronous foundation crates provide such a Mutex type. The
++other solution is to ensure the mutex is unlocked before calling await,
++either by introducing a scope or an explicit call to Drop::drop.
++
++### Known problems
++Will report false positive for explicitly dropped guards
++([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)). A workaround for this is
++to wrap the `.lock()` call in a block instead of explicitly dropping the guard.
++
++### Example
++```
++async fn foo(x: &Mutex<u32>) {
++  let mut guard = x.lock().unwrap();
++  *guard += 1;
++  baz().await;
++}
++
++async fn bar(x: &Mutex<u32>) {
++  let mut guard = x.lock().unwrap();
++  *guard += 1;
++  drop(guard); // explicit drop
++  baz().await;
++}
++```
++
++Use instead:
++```
++async fn foo(x: &Mutex<u32>) {
++  {
++    let mut guard = x.lock().unwrap();
++    *guard += 1;
++  }
++  baz().await;
++}
++
++async fn bar(x: &Mutex<u32>) {
++  {
++    let mut guard = x.lock().unwrap();
++    *guard += 1;
++  } // guard dropped here at end of scope
++  baz().await;
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..226a261b9cc5208dcf26ea386ac9898f77685fdb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,47 @@@
++### What it does
++Checks for calls to await while holding a `RefCell` `Ref` or `RefMut`.
++
++### Why is this bad?
++`RefCell` refs only check for exclusive mutable access
++at runtime. Holding onto a `RefCell` ref across an `await` suspension point
++risks panics from a mutable ref shared while other refs are outstanding.
++
++### Known problems
++Will report false positive for explicitly dropped refs
++([#6353](https://github.com/rust-lang/rust-clippy/issues/6353)). A workaround for this is
++to wrap the `.borrow[_mut]()` call in a block instead of explicitly dropping the ref.
++
++### Example
++```
++async fn foo(x: &RefCell<u32>) {
++  let mut y = x.borrow_mut();
++  *y += 1;
++  baz().await;
++}
++
++async fn bar(x: &RefCell<u32>) {
++  let mut y = x.borrow_mut();
++  *y += 1;
++  drop(y); // explicit drop
++  baz().await;
++}
++```
++
++Use instead:
++```
++async fn foo(x: &RefCell<u32>) {
++  {
++     let mut y = x.borrow_mut();
++     *y += 1;
++  }
++  baz().await;
++}
++
++async fn bar(x: &RefCell<u32>) {
++  {
++    let mut y = x.borrow_mut();
++    *y += 1;
++  } // y dropped here at end of scope
++  baz().await;
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d40024ee56209f060cf84cb763ac9aa8911a1f7e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++### What it does
++Checks for incompatible bit masks in comparisons.
++
++The formula for detecting if an expression of the type `_ <bit_op> m
++<cmp_op> c` (where `<bit_op>` is one of {`&`, `|`} and `<cmp_op>` is one of
++{`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following
++table:
++
++|Comparison  |Bit Op|Example      |is always|Formula               |
++|------------|------|-------------|---------|----------------------|
++|`==` or `!=`| `&`  |`x & 2 == 3` |`false`  |`c & m != c`          |
++|`<`  or `>=`| `&`  |`x & 2 < 3`  |`true`   |`m < c`               |
++|`>`  or `<=`| `&`  |`x & 1 > 1`  |`false`  |`m <= c`              |
++|`==` or `!=`| `\|` |`x \| 1 == 0`|`false`  |`c \| m != c`         |
++|`<`  or `>=`| `\|` |`x \| 1 < 1` |`false`  |`m >= c`              |
++|`<=` or `>` | `\|` |`x \| 1 > 0` |`true`   |`m > c`               |
++
++### Why is this bad?
++If the bits that the comparison cares about are always
++set to zero or one by the bit mask, the comparison is constant `true` or
++`false` (depending on mask, compared value, and operators).
++
++So the code is actively misleading, and the only reason someone would write
++this intentionally is to win an underhanded Rust contest or create a
++test-case for this lint.
++
++### Example
++```
++if (x & 1 == 2) { }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..148575803d389de69b51e7e832d713d792c957df
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or
++`_.or_else(|x| Err(y))`.
++
++### Why is this bad?
++Readability, this can be written more concisely as
++`_.map(|x| y)` or `_.map_err(|x| y)`.
++
++### Example
++```
++let _ = opt().and_then(|s| Some(s.len()));
++let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) });
++let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) });
++```
++
++The correct use would be:
++
++```
++let _ = opt().map(|s| s.len());
++let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 });
++let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 });
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..28a4ebf7169bfa744c691d707c06f0cbce55bfaf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category.
++
++### Why is this bad?
++Restriction lints sometimes are in contrast with other lints or even go against idiomatic rust.
++These lints should only be enabled on a lint-by-lint basis and with careful consideration.
++
++### Example
++```
++#![deny(clippy::restriction)]
++```
++
++Use instead:
++```
++#![deny(clippy::as_conversions)]
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3afa14853fd216139e4ff8d891fcfab8b57e1eaf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checks for `if` conditions that use blocks containing an
++expression, statements or conditions that use closures with blocks.
++
++### Why is this bad?
++Style, using blocks in the condition makes it hard to read.
++
++### Examples
++```
++if { true } { /* ... */ }
++
++if { let x = somefunc(); x } { /* ... */ }
++```
++
++Use instead:
++```
++if true { /* ... */ }
++
++let res = { let x = somefunc(); x };
++if res { /* ... */ }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..df7ca00cc2ba497488c8e5b8ffcb2696e6eee7bb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++This lint warns about boolean comparisons in assert-like macros.
++
++### Why is this bad?
++It is shorter to use the equivalent.
++
++### Example
++```
++assert_eq!("a".is_empty(), false);
++assert_ne!("a".is_empty(), true);
++```
++
++Use instead:
++```
++assert!(!"a".is_empty());
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0996f60cec44ca3dc32c65c5810e743d146a3129
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for expressions of the form `x == true`,
++`x != true` and order comparisons such as `x < true` (or vice versa) and
++suggest using the variable directly.
++
++### Why is this bad?
++Unnecessary code.
++
++### Example
++```
++if x == true {}
++if y == false {}
++```
++use `x` directly:
++```
++if x {}
++if !y {}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..63535b454c9f12b961a6f1e0ed3de83335f80bb0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Instead of using an if statement to convert a bool to an int,
++this lint suggests using a `from()` function or an `as` coercion.
++
++### Why is this bad?
++Coercion or `from()` is idiomatic way to convert bool to a number.
++Both methods are guaranteed to return 1 for true, and 0 for false.
++
++See https://doc.rust-lang.org/std/primitive.bool.html#impl-From%3Cbool%3E
++
++### Example
++```
++if condition {
++    1_i64
++} else {
++    0
++};
++```
++Use instead:
++```
++i64::from(condition);
++```
++or
++```
++condition as i64;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0be865abd578095f3ec4c54651d234eb40d382ae
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Checks for the usage of `&expr as *const T` or
++`&mut expr as *mut T`, and suggest using `ptr::addr_of` or
++`ptr::addr_of_mut` instead.
++
++### Why is this bad?
++This would improve readability and avoid creating a reference
++that points to an uninitialized value or unaligned place.
++Read the `ptr::addr_of` docs for more information.
++
++### Example
++```
++let val = 1;
++let p = &val as *const i32;
++
++let mut val_mut = 1;
++let p_mut = &mut val_mut as *mut i32;
++```
++Use instead:
++```
++let val = 1;
++let p = std::ptr::addr_of!(val);
++
++let mut val_mut = 1;
++let p_mut = std::ptr::addr_of_mut!(val_mut);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..352480d3f26a723cab9c783a631cab10c5f91e65
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++### What it does
++Checks for `&*(&T)`.
++
++### Why is this bad?
++Dereferencing and then borrowing a reference value has no effect in most cases.
++
++### Known problems
++False negative on such code:
++```
++let x = &12;
++let addr_x = &x as *const _ as usize;
++let addr_y = &&*x as *const _ as usize; // assert ok now, and lint triggered.
++                                        // But if we fix it, assert will fail.
++assert_ne!(addr_x, addr_y);
++```
++
++### Example
++```
++let s = &String::new();
++
++let a: &String = &* s;
++```
++
++Use instead:
++```
++let a: &String = s;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e55b6a77e6667aa7679e1e35c5b033f24be3b1a1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++### What it does
++Checks if `const` items which is interior mutable (e.g.,
++contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly.
++
++### Why is this bad?
++Consts are copied everywhere they are referenced, i.e.,
++every time you refer to the const a fresh instance of the `Cell` or `Mutex`
++or `AtomicXxxx` will be created, which defeats the whole purpose of using
++these types in the first place.
++
++The `const` value should be stored inside a `static` item.
++
++### Known problems
++When an enum has variants with interior mutability, use of its non
++interior mutable variants can generate false positives. See issue
++[#3962](https://github.com/rust-lang/rust-clippy/issues/3962)
++
++Types that have underlying or potential interior mutability trigger the lint whether
++the interior mutable field is used or not. See issues
++[#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and
++[#3825](https://github.com/rust-lang/rust-clippy/issues/3825)
++
++### Example
++```
++use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
++const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);
++
++CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged
++assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct
++```
++
++Use instead:
++```
++use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
++const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);
++
++static STATIC_ATOM: AtomicUsize = CONST_ATOM;
++STATIC_ATOM.store(9, SeqCst);
++assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d7089be662a581bd9cf9718b2be607eb172d259f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++Checks for use of `&Box<T>` anywhere in the code.
++Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
++
++### Why is this bad?
++A `&Box<T>` parameter requires the function caller to box `T` first before passing it to a function.
++Using `&T` defines a concrete type for the parameter and generalizes the function, this would also
++auto-deref to `&T` at the function call site if passed a `&Box<T>`.
++
++### Example
++```
++fn foo(bar: &Box<T>) { ... }
++```
++
++Better:
++
++```
++fn foo(bar: &T) { ... }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..053f24c46281d397f8a1fbbe661a1cfdd1037a3e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Checks for use of `Box<T>` where T is a collection such as Vec anywhere in the code.
++Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
++
++### Why is this bad?
++Collections already keeps their contents in a separate area on
++the heap. So if you `Box` them, you just add another level of indirection
++without any benefit whatsoever.
++
++### Example
++```
++struct X {
++    values: Box<Vec<Foo>>,
++}
++```
++
++Better:
++
++```
++struct X {
++    values: Vec<Foo>,
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8b1febf1455fdc359e520db3c9f95f5c14bdb282
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for usage of `Box<T>` where an unboxed `T` would
++work fine.
++
++### Why is this bad?
++This is an unnecessary allocation, and bad for
++performance. It is only necessary to allocate if you wish to move the box
++into something.
++
++### Example
++```
++fn foo(x: Box<u32>) {}
++```
++
++Use instead:
++```
++fn foo(x: u32) {}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..79be6124798a254ec8db803b30ea7ba4e9676168
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++### What it does
++Checks if the `if` and `else` block contain shared code that can be
++moved out of the blocks.
++
++### Why is this bad?
++Duplicate code is less maintainable.
++
++### Known problems
++* The lint doesn't check if the moved expressions modify values that are being used in
++  the if condition. The suggestion can in that case modify the behavior of the program.
++  See [rust-clippy#7452](https://github.com/rust-lang/rust-clippy/issues/7452)
++
++### Example
++```
++let foo = if … {
++    println!("Hello World");
++    13
++} else {
++    println!("Hello World");
++    42
++};
++```
++
++Use instead:
++```
++println!("Hello World");
++let foo = if … {
++    13
++} else {
++    42
++};
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..15b1c9df7baab0738c2bb707218955b11d4db60f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Warns if a generic shadows a built-in type.
++
++### Why is this bad?
++This gives surprising type errors.
++
++### Example
++
++```
++impl<u32> Foo<u32> {
++    fn impl_func(&self) -> u32 {
++        42
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ca7bf9a38da8e25262eb0b4f761bcb622a3db13d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++It checks for `str::bytes().count()` and suggests replacing it with
++`str::len()`.
++
++### Why is this bad?
++`str::bytes().count()` is longer and may not be as performant as using
++`str::len()`.
++
++### Example
++```
++"hello".bytes().count();
++String::from("hello").bytes().count();
++```
++Use instead:
++```
++"hello".len();
++String::from("hello").len();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..260de343353d8d3c1807cf47e79d5dbfad8640cd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for the use of `.bytes().nth()`.
++
++### Why is this bad?
++`.as_bytes().get()` is more efficient and more
++readable.
++
++### Example
++```
++"Hello".bytes().nth(3);
++```
++
++Use instead:
++```
++"Hello".as_bytes().get(3);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1998647a92740b7ad0f99d6c2d42cc5424c4f9d3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++### What it does
++Checks to see if all common metadata is defined in
++`Cargo.toml`. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata
++
++### Why is this bad?
++It will be more difficult for users to discover the
++purpose of the crate, and key information related to it.
++
++### Example
++```
++[package]
++name = "clippy"
++version = "0.0.212"
++repository = "https://github.com/rust-lang/rust-clippy"
++readme = "README.md"
++license = "MIT OR Apache-2.0"
++keywords = ["clippy", "lint", "plugin"]
++categories = ["development-tools", "development-tools::cargo-plugins"]
++```
++
++Should include a description field like:
++
++```
++[package]
++name = "clippy"
++version = "0.0.212"
++description = "A bunch of helpful lints to avoid common pitfalls in Rust"
++repository = "https://github.com/rust-lang/rust-clippy"
++readme = "README.md"
++license = "MIT OR Apache-2.0"
++keywords = ["clippy", "lint", "plugin"]
++categories = ["development-tools", "development-tools::cargo-plugins"]
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8e6e18ed4e23a94da0e12c6a38c3064871fcc723
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checks for calls to `ends_with` with possible file extensions
++and suggests to use a case-insensitive approach instead.
++
++### Why is this bad?
++`ends_with` is case-sensitive and may not detect files with a valid extension.
++
++### Example
++```
++fn is_rust_file(filename: &str) -> bool {
++    filename.ends_with(".rs")
++}
++```
++Use instead:
++```
++fn is_rust_file(filename: &str) -> bool {
++    let filename = std::path::Path::new(filename);
++    filename.extension()
++        .map_or(false, |ext| ext.eq_ignore_ascii_case("rs"))
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c5d8ee034ce596d89a8ce62ae83d5962d7f892dc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for uses of the `abs()` method that cast the result to unsigned.
++
++### Why is this bad?
++The `unsigned_abs()` method avoids panic when called on the MIN value.
++
++### Example
++```
++let x: i32 = -42;
++let y: u32 = x.abs() as u32;
++```
++Use instead:
++```
++let x: i32 = -42;
++let y: u32 = x.unsigned_abs();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..675c03a42bc23679e2132be6d00b43a1735c188a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++### What it does
++Checks for casts from an enum tuple constructor to an integer.
++
++### Why is this bad?
++The cast is easily confused with casting a c-like enum value to an integer.
++
++### Example
++```
++enum E { X(i32) };
++let _ = E::X as usize;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..abe32a8296da228ff5447d8f715aa6425abcfbe9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++### What it does
++Checks for casts from an enum type to an integral type which will definitely truncate the
++value.
++
++### Why is this bad?
++The resulting integral value will not match the value of the variant it came from.
++
++### Example
++```
++enum E { X = 256 };
++let _ = E::X as u8;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c3a61dd470fc9b4feabd4d4db2612e8ca8c71f4f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Checks for casts between numerical types that may
++be replaced by safe conversion functions.
++
++### Why is this bad?
++Rust's `as` keyword will perform many kinds of
++conversions, including silently lossy conversions. Conversion functions such
++as `i32::from` will only perform lossless conversions. Using the conversion
++functions prevents conversions from turning into silent lossy conversions if
++the types of the input expressions ever change, and make it easier for
++people reading the code to know that the conversion is lossless.
++
++### Example
++```
++fn as_u64(x: u8) -> u64 {
++    x as u64
++}
++```
++
++Using `::from` would look like this:
++
++```
++fn as_u64(x: u8) -> u64 {
++    u64::from(x)
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0b164848cc7c23647a27ac5138e5f82346c876bb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for casts between numerical types that may
++truncate large values. This is expected behavior, so the cast is `Allow` by
++default.
++
++### Why is this bad?
++In some problem domains, it is good practice to avoid
++truncation. This lint can be activated to help assess where additional
++checks could be beneficial.
++
++### Example
++```
++fn as_u8(x: u64) -> u8 {
++    x as u8
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f883fc9cfb994793e5520453df94f183d2230319
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for casts from an unsigned type to a signed type of
++the same size. Performing such a cast is a 'no-op' for the compiler,
++i.e., nothing is changed at the bit level, and the binary representation of
++the value is reinterpreted. This can cause wrapping if the value is too big
++for the target signed type. However, the cast works as defined, so this lint
++is `Allow` by default.
++
++### Why is this bad?
++While such a cast is not bad in itself, the results can
++be surprising when this is not the intended behavior, as demonstrated by the
++example below.
++
++### Example
++```
++u32::MAX as i32; // will yield a value of `-1`
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f915d9f8a6d0d2bd191bfd7ea43e3b17fa36c22c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++Checks for casts from any numerical to a float type where
++the receiving type cannot store all values from the original type without
++rounding errors. This possible rounding is to be expected, so this lint is
++`Allow` by default.
++
++Basically, this warns on casting any integer with 32 or more bits to `f32`
++or any 64-bit integer to `f64`.
++
++### Why is this bad?
++It's not bad at all. But in some applications it can be
++helpful to know where precision loss can take place. This lint can help find
++those places in the code.
++
++### Example
++```
++let x = u64::MAX;
++x as f64;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6a6d4dcaa2ae5ecdce3d53b2cc2f9f51d59e2281
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checks for casts, using `as` or `pointer::cast`,
++from a less-strictly-aligned pointer to a more-strictly-aligned pointer
++
++### Why is this bad?
++Dereferencing the resulting pointer may be undefined
++behavior.
++
++### Known problems
++Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar
++on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like
++u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis.
++
++### Example
++```
++let _ = (&1u8 as *const u8) as *const u16;
++let _ = (&mut 1u8 as *mut u8) as *mut u16;
++
++(&1u8 as *const u8).cast::<u16>();
++(&mut 1u8 as *mut u8).cast::<u16>();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fb5b4dbb62d82386ab073958a1a97ded27bddeb9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++### What it does
++Checks for casts of `&T` to `&mut T` anywhere in the code.
++
++### Why is this bad?
++It’s basically guaranteed to be undefined behavior.
++`UnsafeCell` is the only way to obtain aliasable data that is considered
++mutable.
++
++### Example
++```
++fn x(r: &i32) {
++    unsafe {
++        *(r as *const _ as *mut _) += 1;
++    }
++}
++```
++
++Instead consider using interior mutability types.
++
++```
++use std::cell::UnsafeCell;
++
++fn x(r: &UnsafeCell<i32>) {
++    unsafe {
++        *r.get() += 1;
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d64fe1b07f46932d14dc2569ee8dab845a9b62ab
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks for casts from a signed to an unsigned numerical
++type. In this case, negative values wrap around to large positive values,
++which can be quite surprising in practice. However, as the cast works as
++defined, this lint is `Allow` by default.
++
++### Why is this bad?
++Possibly surprising results. You can activate this lint
++as a one-time check to see where numerical wrapping can arise.
++
++### Example
++```
++let y: i8 = -1;
++y as u128; // will return 18446744073709551615
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c01ef0ba92c03d0c66381a52e5506f7b9bfddeec
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,38 @@@
++### What it does
++Checks for `as` casts between raw pointers to slices with differently sized elements.
++
++### Why is this bad?
++The produced raw pointer to a slice does not update its length metadata. The produced
++pointer will point to a different number of bytes than the original pointer because the
++length metadata of a raw slice pointer is in elements rather than bytes.
++Producing a slice reference from the raw pointer will either create a slice with
++less data (which can be surprising) or create a slice with more data and cause Undefined Behavior.
++
++### Example
++// Missing data
++```
++let a = [1_i32, 2, 3, 4];
++let p = &a as *const [i32] as *const [u8];
++unsafe {
++    println!("{:?}", &*p);
++}
++```
++// Undefined Behavior (note: also potential alignment issues)
++```
++let a = [1_u8, 2, 3, 4];
++let p = &a as *const [u8] as *const [u32];
++unsafe {
++    println!("{:?}", &*p);
++}
++```
++Instead use `ptr::slice_from_raw_parts` to construct a slice from a data pointer and the correct length
++```
++let a = [1_i32, 2, 3, 4];
++let old_ptr = &a as *const [i32];
++// The data pointer is cast to a pointer to the target `u8` not `[u8]`
++// The length comes from the known length of 4 i32s times the 4 bytes per i32
++let new_ptr = core::ptr::slice_from_raw_parts(old_ptr as *const u8, 16);
++unsafe {
++    println!("{:?}", &*new_ptr);
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b58c739766aab1d482014b8b69f882151ea5da41
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++### What it does
++Checks for a raw slice being cast to a slice pointer
++
++### Why is this bad?
++This can result in multiple `&mut` references to the same location when only a pointer is
++required.
++`ptr::slice_from_raw_parts` is a safe alternative that doesn't require
++the same [safety requirements] to be upheld.
++
++### Example
++```
++let _: *const [u8] = std::slice::from_raw_parts(ptr, len) as *const _;
++let _: *mut [u8] = std::slice::from_raw_parts_mut(ptr, len) as *mut _;
++```
++Use instead:
++```
++let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, len);
++let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len);
++```
++[safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..00d60b9a451b9f4b82be87cded214d1a7b930b55
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checks for expressions where a character literal is cast
++to `u8` and suggests using a byte literal instead.
++
++### Why is this bad?
++In general, casting values to smaller types is
++error-prone and should be avoided where possible. In the particular case of
++converting a character literal to u8, it is easy to avoid by just using a
++byte literal instead. As an added bonus, `b'a'` is even slightly shorter
++than `'a' as u8`.
++
++### Example
++```
++'x' as u8
++```
++
++A better version, using the byte literal:
++
++```
++b'x'
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4c1d8838973a73d2c2699b68787cfe55d062901c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for usage of `_.chars().last()` or
++`_.chars().next_back()` on a `str` to check if it ends with a given char.
++
++### Why is this bad?
++Readability, this can be written more concisely as
++`_.ends_with(_)`.
++
++### Example
++```
++name.chars().last() == Some('_') || name.chars().next_back() == Some('-');
++```
++
++Use instead:
++```
++name.ends_with('_') || name.ends_with('-');
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..77cbce2de00f91f65298aacf0daddc3d3f6613d8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++Checks for usage of `.chars().next()` on a `str` to check
++if it starts with a given char.
++
++### Why is this bad?
++Readability, this can be written more concisely as
++`_.starts_with(_)`.
++
++### Example
++```
++let name = "foo";
++if name.chars().next() == Some('_') {};
++```
++
++Use instead:
++```
++let name = "foo";
++if name.starts_with('_') {};
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..536b01294ee170493d4c6f6acee6813dbe79a9a6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks for explicit bounds checking when casting.
++
++### Why is this bad?
++Reduces the readability of statements & is error prone.
++
++### Example
++```
++foo <= i32::MAX as u32;
++```
++
++Use instead:
++```
++i32::try_from(foo).is_ok();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2729635bd246f0710c1c2b8cbd9a4f4a35165023
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for usage of `.clone()` on an `&&T`.
++
++### Why is this bad?
++Cloning an `&&T` copies the inner `&T`, instead of
++cloning the underlying `T`.
++
++### Example
++```
++fn main() {
++    let x = vec![1];
++    let y = &&x;
++    let z = y.clone();
++    println!("{:p} {:p}", *y, z); // prints out the same pointer
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..99a0bdb4c4ac0f668c16d2e650b66af7289c4b07
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++### What it does
++Checks for usage of `.clone()` on a `Copy` type.
++
++### Why is this bad?
++The only reason `Copy` types implement `Clone` is for
++generics, not for using the `clone` method on a concrete type.
++
++### Example
++```
++42u64.clone();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2d83f8fefc12239c201a135679543a418f9ed02b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checks for usage of `.clone()` on a ref-counted pointer,
++(`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified
++function syntax instead (e.g., `Rc::clone(foo)`).
++
++### Why is this bad?
++Calling '.clone()' on an Rc, Arc, or Weak
++can obscure the fact that only the pointer is being cloned, not the underlying
++data.
++
++### Example
++```
++let x = Rc::new(1);
++
++x.clone();
++```
++
++Use instead:
++```
++Rc::clone(&x);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2f2014d5fd2990e7d559cf95e675d8d4fa1a9a17
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for usages of `cloned()` on an `Iterator` or `Option` where
++`copied()` could be used instead.
++
++### Why is this bad?
++`copied()` is better because it guarantees that the type being cloned
++implements `Copy`.
++
++### Example
++```
++[1, 2, 3].iter().cloned();
++```
++Use instead:
++```
++[1, 2, 3].iter().copied();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e2ad04d932359aa55e6cf5b1e0fd03e93aeeb8ec
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for comparisons to NaN.
++
++### Why is this bad?
++NaN does not compare meaningfully to anything – not
++even itself – so those comparisons are simply wrong.
++
++### Example
++```
++if x == f32::NAN { }
++```
++
++Use instead:
++```
++if x.is_nan() { }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..02fd15124f0389b6e7c0ba637fddb737d39f8df2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++This lint checks for equality comparisons with `ptr::null`
++
++### Why is this bad?
++It's easier and more readable to use the inherent
++`.is_null()`
++method instead
++
++### Example
++```
++use std::ptr;
++
++if x == ptr::null {
++    // ..
++}
++```
++
++Use instead:
++```
++if x.is_null() {
++    // ..
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f8d4956ff1d4b7b010df92dffb8ccc7a16dc1684
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for conversions to owned values just for the sake
++of a comparison.
++
++### Why is this bad?
++The comparison can operate on a reference, so creating
++an owned value effectively throws it away directly afterwards, which is
++needlessly consuming code and heap space.
++
++### Example
++```
++if x.to_owned() == y {}
++```
++
++Use instead:
++```
++if x == y {}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fdd75f6479cd00cf24d503b37b9f3d48c38249da
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++### What it does
++Checks for methods with high cognitive complexity.
++
++### Why is this bad?
++Methods of high cognitive complexity tend to be hard to
++both read and maintain. Also LLVM will tend to optimize small methods better.
++
++### Known problems
++Sometimes it's hard to find a way to reduce the
++complexity.
++
++### Example
++You'll see it when you get the warning.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4ddfca17731f7b95a2622c2255e8c75ff16ea89e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++### What it does
++Checks for collapsible `else { if ... }` expressions
++that can be collapsed to `else if ...`.
++
++### Why is this bad?
++Each `if`-statement adds one level of nesting, which
++makes code look more complex than it really is.
++
++### Example
++```
++
++if x {
++    …
++} else {
++    if y {
++        …
++    }
++}
++```
++
++Should be written:
++
++```
++if x {
++    …
++} else if y {
++    …
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e1264ee062e955437c7199981de2dd21a2eaa135
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Checks for nested `if` statements which can be collapsed
++by `&&`-combining their conditions.
++
++### Why is this bad?
++Each `if`-statement adds one level of nesting, which
++makes code look more complex than it really is.
++
++### Example
++```
++if x {
++    if y {
++        // …
++    }
++}
++```
++
++Use instead:
++```
++if x && y {
++    // …
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0d59594a03a27c2f1957912613c548ec7cde34dd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++### What it does
++Finds nested `match` or `if let` expressions where the patterns may be "collapsed" together
++without adding any branches.
++
++Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only
++cases where merging would most likely make the code more readable.
++
++### Why is this bad?
++It is unnecessarily verbose and complex.
++
++### Example
++```
++fn func(opt: Option<Result<u64, String>>) {
++    let n = match opt {
++        Some(n) => match n {
++            Ok(n) => n,
++            _ => return,
++        }
++        None => return,
++    };
++}
++```
++Use instead:
++```
++fn func(opt: Option<Result<u64, String>>) {
++    let n = match opt {
++        Some(Ok(n)) => n,
++        _ => return,
++    };
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c24c25a3028a5d5dce4806d16b028aa46125c7e2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++Checks for consecutive calls to `str::replace` (2 or more)
++that can be collapsed into a single call.
++
++### Why is this bad?
++Consecutive `str::replace` calls scan the string multiple times
++with repetitive code.
++
++### Example
++```
++let hello = "hesuo worpd"
++    .replace('s', "l")
++    .replace("u", "l")
++    .replace('p', "l");
++```
++Use instead:
++```
++let hello = "hesuo worpd".replace(&['s', 'u', 'p'], "l");
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..43b09f31ff4aa7a6596db9d2456d6c19f63185fa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++### What it does
++Checks comparison chains written with `if` that can be
++rewritten with `match` and `cmp`.
++
++### Why is this bad?
++`if` is not guaranteed to be exhaustive and conditionals can get
++repetitive
++
++### Known problems
++The match statement may be slower due to the compiler
++not inlining the call to cmp. See issue [#5354](https://github.com/rust-lang/rust-clippy/issues/5354)
++
++### Example
++```
++fn f(x: u8, y: u8) {
++    if x > y {
++        a()
++    } else if x < y {
++        b()
++    } else {
++        c()
++    }
++}
++```
++
++Use instead:
++```
++use std::cmp::Ordering;
++fn f(x: u8, y: u8) {
++     match x.cmp(&y) {
++         Ordering::Greater => a(),
++         Ordering::Less => b(),
++         Ordering::Equal => c()
++     }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..db6f74fe2706b5bbca627b24980f86865d219bc7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++### What it does
++Checks for comparing to an empty slice such as `""` or `[]`,
++and suggests using `.is_empty()` where applicable.
++
++### Why is this bad?
++Some structures can answer `.is_empty()` much faster
++than checking for equality. So it is good to get into the habit of using
++`.is_empty()`, and having it is cheap.
++Besides, it makes the intent clearer than a manual comparison in some contexts.
++
++### Example
++
++```
++if s == "" {
++    ..
++}
++
++if arr == [] {
++    ..
++}
++```
++Use instead:
++```
++if s.is_empty() {
++    ..
++}
++
++if arr.is_empty() {
++    ..
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5f9a2a015b86c7bd9bd2c3956bfeff4fe6702e31
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++### What it does
++Checks for types that implement `Copy` as well as
++`Iterator`.
++
++### Why is this bad?
++Implicit copies can be confusing when working with
++iterator combinators.
++
++### Example
++```
++#[derive(Copy, Clone)]
++struct Countdown(u8);
++
++impl Iterator for Countdown {
++    // ...
++}
++
++let a: Vec<_> = my_iterator.take(1).collect();
++let b: Vec<_> = my_iterator.collect();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..047e986dee71fae10dd59b6faf35eabbbff50db7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++### What it does
++Checks for use of `crate` as opposed to `$crate` in a macro definition.
++
++### Why is this bad?
++`crate` refers to the macro call's crate, whereas `$crate` refers to the macro definition's
++crate. Rarely is the former intended. See:
++https://doc.rust-lang.org/reference/macros-by-example.html#hygiene
++
++### Example
++```
++#[macro_export]
++macro_rules! print_message {
++    () => {
++        println!("{}", crate::MESSAGE);
++    };
++}
++pub const MESSAGE: &str = "Hello!";
++```
++Use instead:
++```
++#[macro_export]
++macro_rules! print_message {
++    () => {
++        println!("{}", $crate::MESSAGE);
++    };
++}
++pub const MESSAGE: &str = "Hello!";
++```
++
++Note that if the use of `crate` is intentional, an `allow` attribute can be applied to the
++macro definition, e.g.:
++```
++#[allow(clippy::crate_in_macro_def)]
++macro_rules! ok { ... crate::foo ... }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e4e7937684e6efebae130aed520866705fda8e77
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead.
++
++### Why is this bad?
++Sometimes `std::fs::create_dir` is mistakenly chosen over `std::fs::create_dir_all`.
++
++### Example
++```
++std::fs::create_dir("foo");
++```
++
++Use instead:
++```
++std::fs::create_dir_all("foo");
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..49dea154970e50ab216792d52a157dfd24e2f960
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++### What it does
++Checks for transmutes between a type `T` and `*T`.
++
++### Why is this bad?
++It's easy to mistakenly transmute between a type and a
++pointer to that type.
++
++### Example
++```
++core::intrinsics::transmute(t) // where the result type is the same as
++                               // `*t` or `&t`'s
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3e1a9a043f9f41617cefd86d3b2c044620856525
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for usage of dbg!() macro.
++
++### Why is this bad?
++`dbg!` macro is intended as a debugging tool. It
++should not be in version control.
++
++### Example
++```
++dbg!(true)
++```
++
++Use instead:
++```
++true
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2c44abe1f05c806108d329d07ccb021a0011a20b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for function/method calls with a mutable
++parameter in `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!` macros.
++
++### Why is this bad?
++In release builds `debug_assert!` macros are optimized out by the
++compiler.
++Therefore mutating something in a `debug_assert!` macro results in different behavior
++between a release and debug build.
++
++### Example
++```
++debug_assert_eq!(vec![3].pop(), Some(3));
++
++// or
++
++debug_assert!(takes_a_mut_parameter(&mut x));
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..daca9bbb3a848c7dd2b9314b952ee68760230ae5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++### What it does
++Warns if there is a better representation for a numeric literal.
++
++### Why is this bad?
++Especially for big powers of 2 a hexadecimal representation is more
++readable than a decimal representation.
++
++### Example
++```
++`255` => `0xFF`
++`65_535` => `0xFFFF`
++`4_042_322_160` => `0xF0F0_F0F0`
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2801b5ccff8028aaa4dc0c8899e28bf01915d3ca
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++### What it does
++Checks for declaration of `const` items which is interior
++mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.).
++
++### Why is this bad?
++Consts are copied everywhere they are referenced, i.e.,
++every time you refer to the const a fresh instance of the `Cell` or `Mutex`
++or `AtomicXxxx` will be created, which defeats the whole purpose of using
++these types in the first place.
++
++The `const` should better be replaced by a `static` item if a global
++variable is wanted, or replaced by a `const fn` if a constructor is wanted.
++
++### Known problems
++A "non-constant" const item is a legacy way to supply an
++initialized value to downstream `static` items (e.g., the
++`std::sync::ONCE_INIT` constant). In this case the use of `const` is legit,
++and this lint should be suppressed.
++
++Even though the lint avoids triggering on a constant whose type has enums that have variants
++with interior mutability, and its value uses non interior mutable variants (see
++[#3962](https://github.com/rust-lang/rust-clippy/issues/3962) and
++[#3825](https://github.com/rust-lang/rust-clippy/issues/3825) for examples);
++it complains about associated constants without default values only based on its types;
++which might not be preferable.
++There're other enums plus associated constants cases that the lint cannot handle.
++
++Types that have underlying or potential interior mutability trigger the lint whether
++the interior mutable field is used or not. See issues
++[#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and
++
++### Example
++```
++use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
++
++const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);
++CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged
++assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct
++```
++
++Use instead:
++```
++static STATIC_ATOM: AtomicUsize = AtomicUsize::new(15);
++STATIC_ATOM.store(9, SeqCst);
++assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b63ef3d18fc411d16f22e1f70f801b50d87a443f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++It checks for `std::iter::Empty::default()` and suggests replacing it with
++`std::iter::empty()`.
++### Why is this bad?
++`std::iter::empty()` is the more idiomatic way.
++### Example
++```
++let _ = std::iter::Empty::<usize>::default();
++let iter: std::iter::Empty<usize> = std::iter::Empty::default();
++```
++Use instead:
++```
++let _ = std::iter::empty::<usize>();
++let iter: std::iter::Empty<usize> = std::iter::empty();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..15076a0a68bf6717c884c45cd79bb90f076b1700
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++### What it does
++Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type
++inference.
++
++Default numeric fallback means that if numeric types have not yet been bound to concrete
++types at the end of type inference, then integer type is bound to `i32`, and similarly
++floating type is bound to `f64`.
++
++See [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback.
++
++### Why is this bad?
++For those who are very careful about types, default numeric fallback
++can be a pitfall that cause unexpected runtime behavior.
++
++### Known problems
++This lint can only be allowed at the function level or above.
++
++### Example
++```
++let i = 10;
++let f = 1.23;
++```
++
++Use instead:
++```
++let i = 10i32;
++let f = 1.23f64;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e69298969c8ed807f08c6023713ede2b5d5c4af2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for literal calls to `Default::default()`.
++
++### Why is this bad?
++It's easier for the reader if the name of the type is used, rather than the
++generic `Default`.
++
++### Example
++```
++let s: String = Default::default();
++```
++
++Use instead:
++```
++let s = String::default();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f79ff9760e57ebaf1dfea011d14fb91cdf3d837c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++### What it does
++Displays a warning when a union is declared with the default representation (without a `#[repr(C)]` attribute).
++
++### Why is this bad?
++Unions in Rust have unspecified layout by default, despite many people thinking that they
++lay out each field at the start of the union (like C does). That is, there are no guarantees
++about the offset of the fields for unions with multiple non-ZST fields without an explicitly
++specified layout. These cases may lead to undefined behavior in unsafe blocks.
++
++### Example
++```
++union Foo {
++    a: i32,
++    b: u32,
++}
++
++fn main() {
++    let _x: u32 = unsafe {
++        Foo { a: 0_i32 }.b // Undefined behavior: `b` is allowed to be padding
++    };
++}
++```
++Use instead:
++```
++#[repr(C)]
++union Foo {
++    a: i32,
++    b: u32,
++}
++
++fn main() {
++    let _x: u32 = unsafe {
++        Foo { a: 0_i32 }.b // Now defined behavior, this is just an i32 -> u32 transmute
++    };
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9f264887a057e3c4eb347abef608a7ab7dcc8c73
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it
++with `#[rustfmt::skip]`.
++
++### Why is this bad?
++Since tool_attributes ([rust-lang/rust#44690](https://github.com/rust-lang/rust/issues/44690))
++are stable now, they should be used instead of the old `cfg_attr(rustfmt)` attributes.
++
++### Known problems
++This lint doesn't detect crate level inner attributes, because they get
++processed before the PreExpansionPass lints get executed. See
++[#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765)
++
++### Example
++```
++#[cfg_attr(rustfmt, rustfmt_skip)]
++fn main() { }
++```
++
++Use instead:
++```
++#[rustfmt::skip]
++fn main() { }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c9574a99b2bec22abdd5b766a55f04b1dd41b494
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++### What it does
++Checks for `#[deprecated]` annotations with a `since`
++field that is not a valid semantic version.
++
++### Why is this bad?
++For checking the version of the deprecation, it must be
++a valid semver. Failing that, the contained information is useless.
++
++### Example
++```
++#[deprecated(since = "forever")]
++fn something_else() { /* ... */ }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fa711b924d48015fd81c79f45f0a4303f9ffb2fb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for usage of `*&` and `*&mut` in expressions.
++
++### Why is this bad?
++Immediately dereferencing a reference is no-op and
++makes the code less clear.
++
++### Known problems
++Multiple dereference/addrof pairs are not handled so
++the suggested fix for `x = **&&y` is `x = *&y`, which is still incorrect.
++
++### Example
++```
++let a = f(*&mut b);
++let c = *&d;
++```
++
++Use instead:
++```
++let a = f(b);
++let c = d;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4dad24ac00cad9ae2532af665ccb2516d939c1bb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for slicing expressions which are equivalent to dereferencing the
++value.
++
++### Why is this bad?
++Some people may prefer to dereference rather than slice.
++
++### Example
++```
++let vec = vec![1, 2, 3];
++let slice = &vec[..];
++```
++Use instead:
++```
++let vec = vec![1, 2, 3];
++let slice = &*vec;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5cee43956cc360eb5ca0af1d7767fc390d24625c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++### What it does
++Detects manual `std::default::Default` implementations that are identical to a derived implementation.
++
++### Why is this bad?
++It is less concise.
++
++### Example
++```
++struct Foo {
++    bar: bool
++}
++
++impl Default for Foo {
++    fn default() -> Self {
++        Self {
++            bar: false
++        }
++    }
++}
++```
++
++Use instead:
++```
++#[derive(Default)]
++struct Foo {
++    bar: bool
++}
++```
++
++### Known problems
++Derive macros [sometimes use incorrect bounds](https://github.com/rust-lang/rust/issues/26925)
++in generic types and the user defined `impl` may be more generalized or
++specialized than what derive will produce. This lint can't detect the manual `impl`
++has exactly equal bounds, and therefore this lint is disabled for types with
++generic parameters.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fbf623d5adbc10a0fa3890059c1522cca9f51940
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Checks for deriving `Hash` but implementing `PartialEq`
++explicitly or vice versa.
++
++### Why is this bad?
++The implementation of these traits must agree (for
++example for use with `HashMap`) so it’s probably a bad idea to use a
++default-generated `Hash` implementation with an explicitly defined
++`PartialEq`. In particular, the following must hold for any type:
++
++```
++k1 == k2 ⇒ hash(k1) == hash(k2)
++```
++
++### Example
++```
++#[derive(Hash)]
++struct Foo;
++
++impl PartialEq for Foo {
++    ...
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f2107a5f69eea93e87e2c7544940bf5020407629
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,44 @@@
++### What it does
++Checks for deriving `Ord` but implementing `PartialOrd`
++explicitly or vice versa.
++
++### Why is this bad?
++The implementation of these traits must agree (for
++example for use with `sort`) so it’s probably a bad idea to use a
++default-generated `Ord` implementation with an explicitly defined
++`PartialOrd`. In particular, the following must hold for any type
++implementing `Ord`:
++
++```
++k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap()
++```
++
++### Example
++```
++#[derive(Ord, PartialEq, Eq)]
++struct Foo;
++
++impl PartialOrd for Foo {
++    ...
++}
++```
++Use instead:
++```
++#[derive(PartialEq, Eq)]
++struct Foo;
++
++impl PartialOrd for Foo {
++    fn partial_cmp(&self, other: &Foo) -> Option<Ordering> {
++       Some(self.cmp(other))
++    }
++}
++
++impl Ord for Foo {
++    ...
++}
++```
++or, if you don't need a custom ordering:
++```
++#[derive(Ord, PartialOrd, PartialEq, Eq)]
++struct Foo;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..932fabad666c3431d3fdd39c3168cda2d418b900
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++### What it does
++Checks for types that derive `PartialEq` and could implement `Eq`.
++
++### Why is this bad?
++If a type `T` derives `PartialEq` and all of its members implement `Eq`,
++then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used
++in APIs that require `Eq` types. It also allows structs containing `T` to derive
++`Eq` themselves.
++
++### Example
++```
++#[derive(PartialEq)]
++struct Foo {
++    i_am_eq: i32,
++    i_am_eq_too: Vec<String>,
++}
++```
++Use instead:
++```
++#[derive(PartialEq, Eq)]
++struct Foo {
++    i_am_eq: i32,
++    i_am_eq_too: Vec<String>,
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d8ad5b6a6674cd2c4bb0a79f3b13ca391b1e06ce
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,41 @@@
++### What it does
++Denies the configured methods and functions in clippy.toml
++
++Note: Even though this lint is warn-by-default, it will only trigger if
++methods are defined in the clippy.toml file.
++
++### Why is this bad?
++Some methods are undesirable in certain contexts, and it's beneficial to
++lint for them as needed.
++
++### Example
++An example clippy.toml configuration:
++```
++disallowed-methods = [
++    # Can use a string as the path of the disallowed method.
++    "std::boxed::Box::new",
++    # Can also use an inline table with a `path` key.
++    { path = "std::time::Instant::now" },
++    # When using an inline table, can add a `reason` for why the method
++    # is disallowed.
++    { path = "std::vec::Vec::leak", reason = "no leaking memory" },
++]
++```
++
++```
++// Example code where clippy issues a warning
++let xs = vec![1, 2, 3, 4];
++xs.leak(); // Vec::leak is disallowed in the config.
++// The diagnostic contains the message "no leaking memory".
++
++let _now = Instant::now(); // Instant::now is disallowed in the config.
++
++let _box = Box::new(3); // Box::new is disallowed in the config.
++```
++
++Use instead:
++```
++// Example code which does not raise clippy warning
++let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config.
++xs.push(123); // Vec::push is _not_ disallowed in the config.
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f4aaee9c77b7baed6014435108a093d22b4cb55f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++### What it does
++Checks for usage of disallowed names for variables, such
++as `foo`.
++
++### Why is this bad?
++These names are usually placeholder names and should be
++avoided.
++
++### Example
++```
++let foo = 3.14;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2151b7a20dedc0bd92841cdc550c96f1c05b45ba
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++### What it does
++Checks for usage of unicode scripts other than those explicitly allowed
++by the lint config.
++
++This lint doesn't take into account non-text scripts such as `Unknown` and `Linear_A`.
++It also ignores the `Common` script type.
++While configuring, be sure to use official script name [aliases] from
++[the list of supported scripts][supported_scripts].
++
++See also: [`non_ascii_idents`].
++
++[aliases]: http://www.unicode.org/reports/tr24/tr24-31.html#Script_Value_Aliases
++[supported_scripts]: https://www.unicode.org/iso15924/iso15924-codes.html
++
++### Why is this bad?
++It may be not desired to have many different scripts for
++identifiers in the codebase.
++
++Note that if you only want to allow plain English, you might want to use
++built-in [`non_ascii_idents`] lint instead.
++
++[`non_ascii_idents`]: https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html#non-ascii-idents
++
++### Example
++```
++// Assuming that `clippy.toml` contains the following line:
++// allowed-locales = ["Latin", "Cyrillic"]
++let counter = 10; // OK, latin is allowed.
++let счётчик = 10; // OK, cyrillic is allowed.
++let zähler = 10; // OK, it's still latin.
++let カウンタ = 10; // Will spawn the lint.
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2bcbcddee5666d10d749568713f34c185e057afb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++### What it does
++Denies the configured types in clippy.toml.
++
++Note: Even though this lint is warn-by-default, it will only trigger if
++types are defined in the clippy.toml file.
++
++### Why is this bad?
++Some types are undesirable in certain contexts.
++
++### Example:
++An example clippy.toml configuration:
++```
++disallowed-types = [
++    # Can use a string as the path of the disallowed type.
++    "std::collections::BTreeMap",
++    # Can also use an inline table with a `path` key.
++    { path = "std::net::TcpListener" },
++    # When using an inline table, can add a `reason` for why the type
++    # is disallowed.
++    { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" },
++]
++```
++
++```
++use std::collections::BTreeMap;
++// or its use
++let x = std::collections::BTreeMap::new();
++```
++Use instead:
++```
++// A similar type that is allowed by the config
++use std::collections::HashMap;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..19436221802512d51c30904ba06904eada8a8de0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++Checks for diverging calls that are not match arms or
++statements.
++
++### Why is this bad?
++It is often confusing to read. In addition, the
++sub-expression evaluation order for Rust is not well documented.
++
++### Known problems
++Someone might want to use `some_bool || panic!()` as a
++shorthand.
++
++### Example
++```
++let a = b() || panic!() || c();
++// `c()` is dead, `panic!()` is only called if `b()` returns `false`
++let x = (a, b, c, panic!());
++// can simply be replaced by `panic!()`
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..107c8ac116d93bb836d29cacbff9d86f46b2fb96
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks)
++outside of code blocks
++### Why is this bad?
++It is likely a typo when defining an intra-doc link
++
++### Example
++```
++/// See also: ['foo']
++fn bar() {}
++```
++Use instead:
++```
++/// See also: [`foo`]
++fn bar() {}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..94f54c587e3021ab81cfe05aa1b5834cc3e2ad97
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++### What it does
++Checks for the presence of `_`, `::` or camel-case words
++outside ticks in documentation.
++
++### Why is this bad?
++*Rustdoc* supports markdown formatting, `_`, `::` and
++camel-case probably indicates some code which should be included between
++ticks. `_` can also be used for emphasis in markdown, this lint tries to
++consider that.
++
++### Known problems
++Lots of bad docs won’t be fixed, what the lint checks
++for is limited, and there are still false positives. HTML elements and their
++content are not linted.
++
++In addition, when writing documentation comments, including `[]` brackets
++inside a link text would trip the parser. Therefore, documenting link with
++`[`SmallVec<[T; INLINE_CAPACITY]>`]` and then [`SmallVec<[T; INLINE_CAPACITY]>`]: SmallVec
++would fail.
++
++### Examples
++```
++/// Do something with the foo_bar parameter. See also
++/// that::other::module::foo.
++// ^ `foo_bar` and `that::other::module::foo` should be ticked.
++fn doit(foo_bar: usize) {}
++```
++
++```
++// Link text with `[]` brackets should be written as following:
++/// Consume the array and return the inner
++/// [`SmallVec<[T; INLINE_CAPACITY]>`][SmallVec].
++/// [SmallVec]: SmallVec
++fn main() {}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7dc6818779f4b168e9e6d38e7b58fc75d074c53a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for double comparisons that could be simplified to a single expression.
++
++
++### Why is this bad?
++Readability.
++
++### Example
++```
++if x == y || x < y {}
++```
++
++Use instead:
++
++```
++if x <= y {}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0017d10d40d339344e8c1f4a7aec32fa2227a818
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for a `#[must_use]` attribute without
++further information on functions and methods that return a type already
++marked as `#[must_use]`.
++
++### Why is this bad?
++The attribute isn't needed. Not using the result
++will already be reported. Alternatively, one can add some text to the
++attribute to improve the lint message.
++
++### Examples
++```
++#[must_use]
++fn double_must_use() -> Result<(), ()> {
++    unimplemented!();
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a07f67496d7ccec92f2b90254a8bb68bd0632a1c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++### What it does
++Detects expressions of the form `--x`.
++
++### Why is this bad?
++It can mislead C/C++ programmers to think `x` was
++decremented.
++
++### Example
++```
++let mut x = 3;
++--x;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..260d7dd575e55049891e8cdd4aae82da7675e507
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++Checks for unnecessary double parentheses.
++
++### Why is this bad?
++This makes code harder to read and might indicate a
++mistake.
++
++### Example
++```
++fn simple_double_parens() -> i32 {
++    ((0))
++}
++
++foo((0));
++```
++
++Use instead:
++```
++fn simple_no_parens() -> i32 {
++    0
++}
++
++foo(0);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f917ca8ed21a69592c822db3bb9ca07dc863a006
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks for calls to `std::mem::drop` with a value
++that derives the Copy trait
++
++### Why is this bad?
++Calling `std::mem::drop` [does nothing for types that
++implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the
++value will be copied and moved into the function on invocation.
++
++### Example
++```
++let x: i32 = 42; // i32 implements Copy
++std::mem::drop(x) // A copy of x is passed to the function, leaving the
++                  // original unaffected
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ee1e3a6c216efc33146496d279267cddb3f1e148
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++### What it does
++Checks for calls to `std::mem::drop` with a value that does not implement `Drop`.
++
++### Why is this bad?
++Calling `std::mem::drop` is no different than dropping such a type. A different value may
++have been intended.
++
++### Example
++```
++struct Foo;
++let x = Foo;
++std::mem::drop(x);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c4f7adf0cfa33d9359bfa54fa878950b62da84da
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for calls to `std::mem::drop` with a reference
++instead of an owned value.
++
++### Why is this bad?
++Calling `drop` on a reference will only drop the
++reference itself, which is a no-op. It will not call the `drop` method (from
++the `Drop` trait implementation) on the underlying referenced value, which
++is likely what was intended.
++
++### Example
++```
++let mut lock_guard = mutex.lock();
++std::mem::drop(&lock_guard) // Should have been drop(lock_guard), mutex
++// still locked
++operation_that_requires_mutex_to_be_unlocked();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..709a9aba03ad29d1519993a1fd689e6c9855da9c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++### What it does
++Checks for files that are included as modules multiple times.
++
++### Why is this bad?
++Loading a file as a module more than once causes it to be compiled
++multiple times, taking longer and putting duplicate content into the
++module tree.
++
++### Example
++```
++// lib.rs
++mod a;
++mod b;
++```
++```
++// a.rs
++#[path = "./b.rs"]
++mod b;
++```
++
++Use instead:
++
++```
++// lib.rs
++mod a;
++mod b;
++```
++```
++// a.rs
++use crate::b;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a8fcd6a9fbe680cc7cd987ac16ff2d733879a15f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for function arguments having the similar names
++differing by an underscore.
++
++### Why is this bad?
++It affects code readability.
++
++### Example
++```
++fn foo(a: i32, _a: i32) {}
++```
++
++Use instead:
++```
++fn bar(a: i32, _b: i32) {}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e7e0ca88745ee1e616baf3b40734906fd72f05cb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++Checks for calculation of subsecond microseconds or milliseconds
++from other `Duration` methods.
++
++### Why is this bad?
++It's more concise to call `Duration::subsec_micros()` or
++`Duration::subsec_millis()` than to calculate them.
++
++### Example
++```
++let micros = duration.subsec_nanos() / 1_000;
++let millis = duration.subsec_nanos() / 1_000_000;
++```
++
++Use instead:
++```
++let micros = duration.subsec_micros();
++let millis = duration.subsec_millis();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..33f5d0f9185920519b53728ed28ad90ee8dff1d0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++### What it does
++Checks for usage of if expressions with an `else if` branch,
++but without a final `else` branch.
++
++### Why is this bad?
++Some coding guidelines require this (e.g., MISRA-C:2004 Rule 14.10).
++
++### Example
++```
++if x.is_positive() {
++    a();
++} else if x.is_negative() {
++    b();
++}
++```
++
++Use instead:
++
++```
++if x.is_positive() {
++    a();
++} else if x.is_negative() {
++    b();
++} else {
++    // We don't care about zero.
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d0c0c24a9c8802460603c23d738cf4dfda2be435
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++### What it does
++Checks for empty `Drop` implementations.
++
++### Why is this bad?
++Empty `Drop` implementations have no effect when dropping an instance of the type. They are
++most likely useless. However, an empty `Drop` implementation prevents a type from being
++destructured, which might be the intention behind adding the implementation as a marker.
++
++### Example
++```
++struct S;
++
++impl Drop for S {
++    fn drop(&mut self) {}
++}
++```
++Use instead:
++```
++struct S;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f7b41c41ee5a1c146475336cd786ef70038120ae
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++### What it does
++Checks for `enum`s with no variants.
++
++As of this writing, the `never_type` is still a
++nightly-only experimental API. Therefore, this lint is only triggered
++if the `never_type` is enabled.
++
++### Why is this bad?
++If you want to introduce a type which
++can't be instantiated, you should use `!` (the primitive type "never"),
++or a wrapper around it, because `!` has more extensive
++compiler support (type inference, etc...) and wrappers
++around it are the conventional way to define an uninhabited type.
++For further information visit [never type documentation](https://doc.rust-lang.org/std/primitive.never.html)
++
++
++### Example
++```
++enum Test {}
++```
++
++Use instead:
++```
++#![feature(never_type)]
++
++struct Test(!);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c85242bbee0ec503c806db781256ea270c6a166a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++### What it does
++Checks for empty lines after outer attributes
++
++### Why is this bad?
++Most likely the attribute was meant to be an inner attribute using a '!'.
++If it was meant to be an outer attribute, then the following item
++should not be separated by empty lines.
++
++### Known problems
++Can cause false positives.
++
++From the clippy side it's difficult to detect empty lines between an attributes and the
++following item because empty lines and comments are not part of the AST. The parsing
++currently works for basic cases but is not perfect.
++
++### Example
++```
++#[allow(dead_code)]
++
++fn not_quite_good_code() { }
++```
++
++Use instead:
++```
++// Good (as inner attribute)
++#![allow(dead_code)]
++
++fn this_is_fine() { }
++
++// or
++
++// Good (as outer attribute)
++#[allow(dead_code)]
++fn this_is_fine_too() { }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fea49a74d04e0cfebe15598e7848293327f02527
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++### What it does
++Checks for empty `loop` expressions.
++
++### Why is this bad?
++These busy loops burn CPU cycles without doing
++anything. It is _almost always_ a better idea to `panic!` than to have
++a busy loop.
++
++If panicking isn't possible, think of the environment and either:
++  - block on something
++  - sleep the thread for some microseconds
++  - yield or pause the thread
++
++For `std` targets, this can be done with
++[`std::thread::sleep`](https://doc.rust-lang.org/std/thread/fn.sleep.html)
++or [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html).
++
++For `no_std` targets, doing this is more complicated, especially because
++`#[panic_handler]`s can't panic. To stop/pause the thread, you will
++probably need to invoke some target-specific intrinsic. Examples include:
++  - [`x86_64::instructions::hlt`](https://docs.rs/x86_64/0.12.2/x86_64/instructions/fn.hlt.html)
++  - [`cortex_m::asm::wfi`](https://docs.rs/cortex-m/0.6.3/cortex_m/asm/fn.wfi.html)
++
++### Example
++```
++loop {}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ab5e35ae2ada8c475775f307275790b1bd7e16a2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++### What it does
++Finds structs without fields (a so-called "empty struct") that are declared with brackets.
++
++### Why is this bad?
++Empty brackets after a struct declaration can be omitted.
++
++### Example
++```
++struct Cookie {}
++```
++Use instead:
++```
++struct Cookie;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d30a973a5a1357eab121909c688ad05f160de47e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for C-like enumerations that are
++`repr(isize/usize)` and have values that don't fit into an `i32`.
++
++### Why is this bad?
++This will truncate the variant value on 32 bit
++architectures, but works fine on 64 bit.
++
++### Example
++```
++#[repr(usize)]
++enum NonPortable {
++    X = 0x1_0000_0000,
++    Y = 0,
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3776822c35b0c21a9d6330c1b7a374c14cf8d232
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++Checks for `use Enum::*`.
++
++### Why is this bad?
++It is usually better style to use the prefixed name of
++an enumeration variant, rather than importing variants.
++
++### Known problems
++Old-style enumerations that prefix the variants are
++still around.
++
++### Example
++```
++use std::cmp::Ordering::*;
++
++foo(Less);
++```
++
++Use instead:
++```
++use std::cmp::Ordering;
++
++foo(Ordering::Less)
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e726925edda82ca777546cf7e613ee78304c10a0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++### What it does
++Detects enumeration variants that are prefixed or suffixed
++by the same characters.
++
++### Why is this bad?
++Enumeration variant names should specify their variant,
++not repeat the enumeration name.
++
++### Limitations
++Characters with no casing will be considered when comparing prefixes/suffixes
++This applies to numbers and non-ascii characters without casing
++e.g. `Foo1` and `Foo2` is considered to have different prefixes
++(the prefixes are `Foo1` and `Foo2` respectively), as also `Bar螃`, `Bar蟹`
++
++### Example
++```
++enum Cake {
++    BlackForestCake,
++    HummingbirdCake,
++    BattenbergCake,
++}
++```
++Use instead:
++```
++enum Cake {
++    BlackForest,
++    Hummingbird,
++    Battenberg,
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2d75a0ec546e72043fb085d8cdfa4406311bc856
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for equal operands to comparison, logical and
++bitwise, difference and division binary operators (`==`, `>`, etc., `&&`,
++`||`, `&`, `|`, `^`, `-` and `/`).
++
++### Why is this bad?
++This is usually just a typo or a copy and paste error.
++
++### Known problems
++False negatives: We had some false positives regarding
++calls (notably [racer](https://github.com/phildawes/racer) had one instance
++of `x.pop() && x.pop()`), so we removed matching any function or method
++calls. We may introduce a list of known pure functions in the future.
++
++### Example
++```
++if x + 1 == x + 1 {}
++
++// or
++
++assert_eq!(a, a);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9997046954c20d3779fe82a2384579dfc6ca8be0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Checks for pattern matchings that can be expressed using equality.
++
++### Why is this bad?
++
++* It reads better and has less cognitive load because equality won't cause binding.
++* It is a [Yoda condition](https://en.wikipedia.org/wiki/Yoda_conditions). Yoda conditions are widely
++criticized for increasing the cognitive load of reading the code.
++* Equality is a simple bool expression and can be merged with `&&` and `||` and
++reuse if blocks
++
++### Example
++```
++if let Some(2) = x {
++    do_thing();
++}
++```
++Use instead:
++```
++if x == Some(2) {
++    do_thing();
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3d285a6d86e48f8e7e7ebbebd97b83ad5489c6f5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks for erasing operations, e.g., `x * 0`.
++
++### Why is this bad?
++The whole expression can be replaced by zero.
++This is most likely not the intended outcome and should probably be
++corrected
++
++### Example
++```
++let x = 1;
++0 / x;
++0 * x;
++x & 0;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1dc83c5ce0ee05ded2a906da263f6f2cdbedcfe2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for `.err().expect()` calls on the `Result` type.
++
++### Why is this bad?
++`.expect_err()` can be called directly to avoid the extra type conversion from `err()`.
++
++### Example
++```
++let x: Result<u32, &str> = Ok(10);
++x.err().expect("Testing err().expect()");
++```
++Use instead:
++```
++let x: Result<u32, &str> = Ok(10);
++x.expect_err("Testing expect_err");
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..517879c47152b20fb30798b191cb0d627e403ca3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for float literals with a precision greater
++than that supported by the underlying type.
++
++### Why is this bad?
++Rust will truncate the literal silently.
++
++### Example
++```
++let v: f32 = 0.123_456_789_9;
++println!("{}", v); //  0.123_456_789
++```
++
++Use instead:
++```
++let v: f64 = 0.123_456_789_9;
++println!("{}", v); //  0.123_456_789_9
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d1032a7a29aa04d68b3384edfc0dcc9dd4b310a2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Warns on any exported `enum`s that are not tagged `#[non_exhaustive]`
++
++### Why is this bad?
++Exhaustive enums are typically fine, but a project which does
++not wish to make a stability commitment around exported enums may wish to
++disable them by default.
++
++### Example
++```
++enum Foo {
++    Bar,
++    Baz
++}
++```
++Use instead:
++```
++#[non_exhaustive]
++enum Foo {
++    Bar,
++    Baz
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fd6e4f5caf1f257b04d4f0712b064fe44eb40ff6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Warns on any exported `structs`s that are not tagged `#[non_exhaustive]`
++
++### Why is this bad?
++Exhaustive structs are typically fine, but a project which does
++not wish to make a stability commitment around exported structs may wish to
++disable them by default.
++
++### Example
++```
++struct Foo {
++    bar: u8,
++    baz: String,
++}
++```
++Use instead:
++```
++#[non_exhaustive]
++struct Foo {
++    bar: u8,
++    baz: String,
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1e6154d43e0538550a3c6b40f984d79d4bb259ea
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++### What it does
++`exit()`  terminates the program and doesn't provide a
++stack trace.
++
++### Why is this bad?
++Ideally a program is terminated by finishing
++the main function.
++
++### Example
++```
++std::process::exit(0)
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d82d9aa9baff240c08aa50ceb41107973ffb44a9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`,
++etc., and suggests to use `unwrap_or_else` instead
++
++### Why is this bad?
++The function will always be called.
++
++### Known problems
++If the function has side-effects, not calling it will
++change the semantics of the program, but you shouldn't rely on that anyway.
++
++### Example
++```
++foo.expect(&format!("Err {}: {}", err_code, err_msg));
++
++// or
++
++foo.expect(format!("Err {}: {}", err_code, err_msg).as_str());
++```
++
++Use instead:
++```
++foo.unwrap_or_else(|| panic!("Err {}: {}", err_code, err_msg));
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4a6981e334fd368b1b2d521d395506c8d7fdb494
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Checks for `.expect()` or `.expect_err()` calls on `Result`s and `.expect()` call on `Option`s.
++
++### Why is this bad?
++Usually it is better to handle the `None` or `Err` case.
++Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why
++this lint is `Allow` by default.
++
++`result.expect()` will let the thread panic on `Err`
++values. Normally, you want to implement more sophisticated error handling,
++and propagate errors upwards with `?` operator.
++
++### Examples
++```
++option.expect("one");
++result.expect("one");
++```
++
++Use instead:
++```
++option?;
++
++// or
++
++result?;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..391d93b6713cca2005d7d14b2b97fd24c48e8098
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++### What it does
++Checks for explicit `Clone` implementations for `Copy`
++types.
++
++### Why is this bad?
++To avoid surprising behavior, these traits should
++agree and the behavior of `Copy` cannot be overridden. In almost all
++situations a `Copy` type should have a `Clone` implementation that does
++nothing more than copy the object, which is what `#[derive(Copy, Clone)]`
++gets you.
++
++### Example
++```
++#[derive(Copy)]
++struct Foo;
++
++impl Clone for Foo {
++    // ..
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..65b256317725288a2f95142e309ca0effd3db921
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for dereferencing expressions which would be covered by auto-deref.
++
++### Why is this bad?
++This unnecessarily complicates the code.
++
++### Example
++```
++let x = String::new();
++let y: &str = &*x;
++```
++Use instead:
++```
++let x = String::new();
++let y: &str = &x;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2661a43e10341f95851551e53b0b7695bc308cf4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checks `for` loops over slices with an explicit counter
++and suggests the use of `.enumerate()`.
++
++### Why is this bad?
++Using `.enumerate()` makes the intent more clear,
++declutters the code and may be faster in some instances.
++
++### Example
++```
++let mut i = 0;
++for item in &v {
++    bar(i, *item);
++    i += 1;
++}
++```
++
++Use instead:
++```
++for (i, item) in v.iter().enumerate() { bar(i, *item); }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e14e981c7073bee00239c22eb5420c6ca87669a3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++Checks for explicit `deref()` or `deref_mut()` method calls.
++
++### Why is this bad?
++Dereferencing by `&*x` or `&mut *x` is clearer and more concise,
++when not part of a method chain.
++
++### Example
++```
++use std::ops::Deref;
++let a: &mut String = &mut String::from("foo");
++let b: &str = a.deref();
++```
++
++Use instead:
++```
++let a: &mut String = &mut String::from("foo");
++let b = &*a;
++```
++
++This lint excludes:
++```
++let _ = d.unwrap().deref();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3931dfd69a318d74f9112bd2ea4b7baff6757b7a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++### What it does
++Checks for loops on `y.into_iter()` where `y` will do, and
++suggests the latter.
++
++### Why is this bad?
++Readability.
++
++### Example
++```
++// with `y` a `Vec` or slice:
++for x in y.into_iter() {
++    // ..
++}
++```
++can be rewritten to
++```
++for x in y {
++    // ..
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cabe72e91d04f6395df2a64f5180c9ec0ebe15e9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++### What it does
++Checks for loops on `x.iter()` where `&x` will do, and
++suggests the latter.
++
++### Why is this bad?
++Readability.
++
++### Known problems
++False negatives. We currently only warn on some known
++types.
++
++### Example
++```
++// with `y` a `Vec` or slice:
++for x in y.iter() {
++    // ..
++}
++```
++
++Use instead:
++```
++for x in &y {
++    // ..
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eafed5d39e5c63e48783ae846b87de9c1c901ffe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for usage of `write!()` / `writeln()!` which can be
++replaced with `(e)print!()` / `(e)println!()`
++
++### Why is this bad?
++Using `(e)println! is clearer and more concise
++
++### Example
++```
++writeln!(&mut std::io::stderr(), "foo: {:?}", bar).unwrap();
++writeln!(&mut std::io::stdout(), "foo: {:?}", bar).unwrap();
++```
++
++Use instead:
++```
++eprintln!("foo: {:?}", bar);
++println!("foo: {:?}", bar);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2f31dcf5f74013c933a071068115a2de1a20945e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checks for occurrences where one vector gets extended instead of append
++
++### Why is this bad?
++Using `append` instead of `extend` is more concise and faster
++
++### Example
++```
++let mut a = vec![1, 2, 3];
++let mut b = vec![4, 5, 6];
++
++a.extend(b.drain(..));
++```
++
++Use instead:
++```
++let mut a = vec![1, 2, 3];
++let mut b = vec![4, 5, 6];
++
++a.append(&mut b);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bc1814aa4752dae2cf7574f3fa00b98553edd2ad
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Checks for lifetimes in generics that are never used
++anywhere else.
++
++### Why is this bad?
++The additional lifetimes make the code look more
++complicated, while there is nothing out of the ordinary going on. Removing
++them leads to more readable code.
++
++### Example
++```
++// unnecessary lifetimes
++fn unused_lifetime<'a>(x: u8) {
++    // ..
++}
++```
++
++Use instead:
++```
++fn no_lifetime(x: u8) {
++    // ...
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..588a5bb103d46e8cccd30f84c1982069c9c1d5e7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++### What it does
++Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`
++
++### Why is this bad?
++`TryFrom` should be used if there's a possibility of failure.
++
++### Example
++```
++struct Foo(i32);
++
++impl From<String> for Foo {
++    fn from(s: String) -> Self {
++        Foo(s.parse().unwrap())
++    }
++}
++```
++
++Use instead:
++```
++struct Foo(i32);
++
++impl TryFrom<String> for Foo {
++    type Error = ();
++    fn try_from(s: String) -> Result<Self, Self::Error> {
++        if let Ok(parsed) = s.parse() {
++            Ok(Foo(parsed))
++        } else {
++            Err(())
++        }
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e58b7239fde9e039676d3208a95a8ebbcf991cce
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Checks for immediate reassignment of fields initialized
++with Default::default().
++
++### Why is this bad?
++It's more idiomatic to use the [functional update syntax](https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax).
++
++### Known problems
++Assignments to patterns that are of tuple type are not linted.
++
++### Example
++```
++let mut a: A = Default::default();
++a.i = 42;
++```
++
++Use instead:
++```
++let a = A {
++    i: 42,
++    .. Default::default()
++};
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ad14bd62c4de483b76e3d6d04f85a6120614996d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++### What it does
++Checks for `FileType::is_file()`.
++
++### Why is this bad?
++When people testing a file type with `FileType::is_file`
++they are testing whether a path is something they can get bytes from. But
++`is_file` doesn't cover special file types in unix-like systems, and doesn't cover
++symlink in windows. Using `!FileType::is_dir()` is a better way to that intention.
++
++### Example
++```
++let metadata = std::fs::metadata("foo.txt")?;
++let filetype = metadata.file_type();
++
++if filetype.is_file() {
++    // read file
++}
++```
++
++should be written as:
++
++```
++let metadata = std::fs::metadata("foo.txt")?;
++let filetype = metadata.file_type();
++
++if !filetype.is_dir() {
++    // read file
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..83b666f2e27820a7493ace384d6e5676dc81ed6d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++### What it does
++Checks for usage of `filter_map(|x| x)`.
++
++### Why is this bad?
++Readability, this can be written more concisely by using `flatten`.
++
++### Example
++```
++iter.filter_map(|x| x);
++```
++Use instead:
++```
++iter.flatten();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b38620b56a50fecc0abeb311a07f30a84c2a6bae
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for usage of `_.filter_map(_).next()`.
++
++### Why is this bad?
++Readability, this can be written more concisely as
++`_.find_map(_)`.
++
++### Example
++```
++ (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next();
++```
++Can be written as
++
++```
++ (0..3).find_map(|x| if x == 2 { Some(x) } else { None });
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..898a74166dc14649f0faa4fb561968049f14bc9a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for usage of `_.filter(_).next()`.
++
++### Why is this bad?
++Readability, this can be written more concisely as
++`_.find(_)`.
++
++### Example
++```
++vec.iter().filter(|x| **x == 0).next();
++```
++
++Use instead:
++```
++vec.iter().find(|x| **x == 0);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a5ee79b4982fb753d1d99c77b40491d9b26c0013
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++### What it does
++Checks for usage of `flat_map(|x| x)`.
++
++### Why is this bad?
++Readability, this can be written more concisely by using `flatten`.
++
++### Example
++```
++iter.flat_map(|x| x);
++```
++Can be written as
++```
++iter.flatten();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d50b9156d3654c19262e8460e2ddf1f3d22a5483
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for usages of `Iterator::flat_map()` where `filter_map()` could be
++used instead.
++
++### Why is this bad?
++When applicable, `filter_map()` is more clear since it shows that
++`Option` is used to produce 0 or 1 items.
++
++### Example
++```
++let nums: Vec<i32> = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect();
++```
++Use instead:
++```
++let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1f9bce5abd59fdd52e2210a73ea648687a55c1ce
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++### What it does
++Checks for float arithmetic.
++
++### Why is this bad?
++For some embedded systems or kernel development, it
++can be useful to rule out floating-point numbers.
++
++### Example
++```
++a + 1.0;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c19907c903e9df1d6592666ddc5f07bf58977841
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++### What it does
++Checks for (in-)equality comparisons on floating-point
++values (apart from zero), except in functions called `*eq*` (which probably
++implement equality for a type involving floats).
++
++### Why is this bad?
++Floating point calculations are usually imprecise, so
++asking if two values are *exactly* equal is asking for trouble. For a good
++guide on what to do, see [the floating point
++guide](http://www.floating-point-gui.de/errors/comparison).
++
++### Example
++```
++let x = 1.2331f64;
++let y = 1.2332f64;
++
++if y == 1.23f64 { }
++if y != x {} // where both are floats
++```
++
++Use instead:
++```
++let error_margin = f64::EPSILON; // Use an epsilon for comparison
++// Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
++// let error_margin = std::f64::EPSILON;
++if (y - 1.23f64).abs() < error_margin { }
++if (y - x).abs() > error_margin { }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9208feaacd81011198713479d3f80d72b33a2c41
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Checks for (in-)equality comparisons on floating-point
++value and constant, except in functions called `*eq*` (which probably
++implement equality for a type involving floats).
++
++### Why is this bad?
++Floating point calculations are usually imprecise, so
++asking if two values are *exactly* equal is asking for trouble. For a good
++guide on what to do, see [the floating point
++guide](http://www.floating-point-gui.de/errors/comparison).
++
++### Example
++```
++let x: f64 = 1.0;
++const ONE: f64 = 1.00;
++
++if x == ONE { } // where both are floats
++```
++
++Use instead:
++```
++let error_margin = f64::EPSILON; // Use an epsilon for comparison
++// Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
++// let error_margin = std::f64::EPSILON;
++if (x - ONE).abs() < error_margin { }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..556b574e15d303a4b8c6774b1f9e94ec6b3c89dd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Checks for statements of the form `(a - b) < f32::EPSILON` or
++`(a - b) < f64::EPSILON`. Notes the missing `.abs()`.
++
++### Why is this bad?
++The code without `.abs()` is more likely to have a bug.
++
++### Known problems
++If the user can ensure that b is larger than a, the `.abs()` is
++technically unnecessary. However, it will make the code more robust and doesn't have any
++large performance implications. If the abs call was deliberately left out for performance
++reasons, it is probably better to state this explicitly in the code, which then can be done
++with an allow.
++
++### Example
++```
++pub fn is_roughly_equal(a: f32, b: f32) -> bool {
++    (a - b) < f32::EPSILON
++}
++```
++Use instead:
++```
++pub fn is_roughly_equal(a: f32, b: f32) -> bool {
++    (a - b).abs() < f32::EPSILON
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7d2b7b681deb15ff2edf37bf9b60d9b3cd60a3b7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for comparisons with an address of a function item.
++
++### Why is this bad?
++Function item address is not guaranteed to be unique and could vary
++between different code generation units. Furthermore different function items could have
++the same address after being merged together.
++
++### Example
++```
++type F = fn();
++fn a() {}
++let f: F = a;
++if f == a {
++    // ...
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2eae0563368c1adb6e91dc7080d1c839e5c07141
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++### What it does
++Checks for excessive use of
++bools in function definitions.
++
++### Why is this bad?
++Calls to such functions
++are confusing and error prone, because it's
++hard to remember argument order and you have
++no type system support to back you up. Using
++two-variant enums instead of bools often makes
++API easier to use.
++
++### Example
++```
++fn f(is_round: bool, is_hot: bool) { ... }
++```
++
++Use instead:
++```
++enum Shape {
++    Round,
++    Spiky,
++}
++
++enum Temperature {
++    Hot,
++    IceCold,
++}
++
++fn f(shape: Shape, temperature: Temperature) { ... }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1f587f6d7176891c2999504b86bc80923eddf3ae
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checks for casts of function pointers to something other than usize
++
++### Why is this bad?
++Casting a function pointer to anything other than usize/isize is not portable across
++architectures, because you end up losing bits if the target type is too small or end up with a
++bunch of extra bits that waste space and add more instructions to the final binary than
++strictly necessary for the problem
++
++Casting to isize also doesn't make sense since there are no signed addresses.
++
++### Example
++```
++fn fun() -> i32 { 1 }
++let _ = fun as i64;
++```
++
++Use instead:
++```
++let _ = fun as usize;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ee3c33d237255999c8332a1722c1a2e2248527d8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++### What it does
++Checks for casts of a function pointer to any integer type.
++
++### Why is this bad?
++Casting a function pointer to an integer can have surprising results and can occur
++accidentally if parentheses are omitted from a function call. If you aren't doing anything
++low-level with function pointers then you can opt-out of casting functions to integers in
++order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function
++pointer casts in your code.
++
++### Example
++```
++// fn1 is cast as `usize`
++fn fn1() -> u16 {
++    1
++};
++let _ = fn1 as usize;
++```
++
++Use instead:
++```
++// maybe you intended to call the function?
++fn fn2() -> u16 {
++    1
++};
++let _ = fn2() as usize;
++
++// or
++
++// maybe you intended to cast it to a function type?
++fn fn3() -> u16 {
++    1
++}
++let _ = fn3 as fn() -> u16;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..69f12fa319f184c12807d8f7037ba971a0c441b0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Checks for casts of a function pointer to a numeric type not wide enough to
++store address.
++
++### Why is this bad?
++Such a cast discards some bits of the function's address. If this is intended, it would be more
++clearly expressed by casting to usize first, then casting the usize to the intended type (with
++a comment) to perform the truncation.
++
++### Example
++```
++fn fn1() -> i16 {
++    1
++};
++let _ = fn1 as i32;
++```
++
++Use instead:
++```
++// Cast to usize first, then comment with the reason for the truncation
++fn fn1() -> i16 {
++    1
++};
++let fn_ptr = fn1 as usize;
++let fn_ptr_truncated = fn_ptr as i32;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a9a2ffee9c743dc99302ab5af63bdb465d1d6f37
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for iterating a map (`HashMap` or `BTreeMap`) and
++ignoring either the keys or values.
++
++### Why is this bad?
++Readability. There are `keys` and `values` methods that
++can be used to express that don't need the values or keys.
++
++### Example
++```
++for (k, _) in &map {
++    ..
++}
++```
++
++could be replaced by
++
++```
++for k in map.keys() {
++    ..
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c5a7508e45d40e58cef17a27e3f92a855dd0b398
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++### What it does
++Checks for `for` loops over `Option` or `Result` values.
++
++### Why is this bad?
++Readability. This is more clearly expressed as an `if
++let`.
++
++### Example
++```
++for x in opt {
++    // ..
++}
++
++for x in &res {
++    // ..
++}
++
++for x in res.iter() {
++    // ..
++}
++```
++
++Use instead:
++```
++if let Some(x) = opt {
++    // ..
++}
++
++if let Ok(x) = res {
++    // ..
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1d100912e9a4858152077e45ecee4223f5ef154e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checks for calls to `std::mem::forget` with a value that
++derives the Copy trait
++
++### Why is this bad?
++Calling `std::mem::forget` [does nothing for types that
++implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the
++value will be copied and moved into the function on invocation.
++
++An alternative, but also valid, explanation is that Copy types do not
++implement
++the Drop trait, which means they have no destructors. Without a destructor,
++there
++is nothing for `std::mem::forget` to ignore.
++
++### Example
++```
++let x: i32 = 42; // i32 implements Copy
++std::mem::forget(x) // A copy of x is passed to the function, leaving the
++                    // original unaffected
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3307d654c17f2bd860f740606bf29b5aa91b4085
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++### What it does
++Checks for calls to `std::mem::forget` with a value that does not implement `Drop`.
++
++### Why is this bad?
++Calling `std::mem::forget` is no different than dropping such a type. A different value may
++have been intended.
++
++### Example
++```
++struct Foo;
++let x = Foo;
++std::mem::forget(x);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..874fb8786068d635fa10e65e5bce43bf27d6947b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks for calls to `std::mem::forget` with a reference
++instead of an owned value.
++
++### Why is this bad?
++Calling `forget` on a reference will only forget the
++reference itself, which is a no-op. It will not forget the underlying
++referenced
++value, which is likely what was intended.
++
++### Example
++```
++let x = Box::new(1);
++std::mem::forget(&x) // Should have been forget(x), x will still be dropped
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ac498472f017f04a35c2a7017e599dc40f3034d9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Detects `format!` within the arguments of another macro that does
++formatting such as `format!` itself, `write!` or `println!`. Suggests
++inlining the `format!` call.
++
++### Why is this bad?
++The recommended code is both shorter and avoids a temporary allocation.
++
++### Example
++```
++println!("error: {}", format!("something failed at {}", Location::caller()));
++```
++Use instead:
++```
++println!("error: something failed at {}", Location::caller());
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ca409ebc7ec2634e5befc04cdda49e710dd6c8dd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Detects cases where the result of a `format!` call is
++appended to an existing `String`.
++
++### Why is this bad?
++Introduces an extra, avoidable heap allocation.
++
++### Known problems
++`format!` returns a `String` but `write!` returns a `Result`.
++Thus you are forced to ignore the `Err` variant to achieve the same API.
++
++While using `write!` in the suggested way should never fail, this isn't necessarily clear to the programmer.
++
++### Example
++```
++let mut s = String::new();
++s += &format!("0x{:X}", 1024);
++s.push_str(&format!("0x{:X}", 1024));
++```
++Use instead:
++```
++use std::fmt::Write as _; // import without risk of name clashing
++
++let mut s = String::new();
++let _ = write!(s, "0x{:X}", 1024);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f3fd275972645c87168c6c141eb4eb95616e8fc9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++Checks for `from_iter()` function calls on types that implement the `FromIterator`
++trait.
++
++### Why is this bad?
++It is recommended style to use collect. See
++[FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)
++
++### Example
++```
++let five_fives = std::iter::repeat(5).take(5);
++
++let v = Vec::from_iter(five_fives);
++
++assert_eq!(v, vec![5, 5, 5, 5, 5]);
++```
++Use instead:
++```
++let five_fives = std::iter::repeat(5).take(5);
++
++let v: Vec<i32> = five_fives.collect();
++
++assert_eq!(v, vec![5, 5, 5, 5, 5]);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0770bcc42c2704e2a6e0335fd71dd4f5330dd798
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead.
++
++### Why is this bad?
++According the std docs implementing `From<..>` is preferred since it gives you `Into<..>` for free where the reverse isn't true.
++
++### Example
++```
++struct StringWrapper(String);
++
++impl Into<StringWrapper> for String {
++    fn into(self) -> StringWrapper {
++        StringWrapper(self)
++    }
++}
++```
++Use instead:
++```
++struct StringWrapper(String);
++
++impl From<String> for StringWrapper {
++    fn from(s: String) -> StringWrapper {
++        StringWrapper(s)
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f6f319d3eaa1811b7ef308a538757da595320b1f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++### What it does
++
++Checks for function invocations of the form `primitive::from_str_radix(s, 10)`
++
++### Why is this bad?
++
++This specific common use case can be rewritten as `s.parse::<primitive>()`
++(and in most cases, the turbofish can be removed), which reduces code length
++and complexity.
++
++### Known problems
++
++This lint may suggest using (&<expression>).parse() instead of <expression>.parse() directly
++in some cases, which is correct but adds unnecessary complexity to the code.
++
++### Example
++```
++let input: &str = get_input();
++let num = u16::from_str_radix(input, 10)?;
++```
++Use instead:
++```
++let input: &str = get_input();
++let num: u16 = input.parse()?;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0aa048d273551a3af129d5246c8a8a15a0f087c4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++### What it does
++This lint requires Future implementations returned from
++functions and methods to implement the `Send` marker trait. It is mostly
++used by library authors (public and internal) that target an audience where
++multithreaded executors are likely to be used for running these Futures.
++
++### Why is this bad?
++A Future implementation captures some state that it
++needs to eventually produce its final value. When targeting a multithreaded
++executor (which is the norm on non-embedded devices) this means that this
++state may need to be transported to other threads, in other words the
++whole Future needs to implement the `Send` marker trait. If it does not,
++then the resulting Future cannot be submitted to a thread pool in the
++end user’s code.
++
++Especially for generic functions it can be confusing to leave the
++discovery of this problem to the end user: the reported error location
++will be far from its cause and can in many cases not even be fixed without
++modifying the library where the offending Future implementation is
++produced.
++
++### Example
++```
++async fn not_send(bytes: std::rc::Rc<[u8]>) {}
++```
++Use instead:
++```
++async fn is_send(bytes: std::sync::Arc<[u8]>) {}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c905a737ddf3ff4ff2e27faf5f502b9e69a93c50
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++Checks for using `x.get(0)` instead of
++`x.first()`.
++
++### Why is this bad?
++Using `x.first()` is easier to read and has the same
++result.
++
++### Example
++```
++let x = vec![2, 3, 5];
++let first_element = x.get(0);
++```
++
++Use instead:
++```
++let x = vec![2, 3, 5];
++let first_element = x.first();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..31c7f269586ab148b329ab5cd6146139d686d2cf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Checks for using `x.get(x.len() - 1)` instead of
++`x.last()`.
++
++### Why is this bad?
++Using `x.last()` is easier to read and has the same
++result.
++
++Note that using `x[x.len() - 1]` is semantically different from
++`x.last()`.  Indexing into the array will panic on out-of-bounds
++accesses, while `x.get()` and `x.last()` will return `None`.
++
++There is another lint (get_unwrap) that covers the case of using
++`x.get(index).unwrap()` instead of `x[index]`.
++
++### Example
++```
++let x = vec![2, 3, 5];
++let last_element = x.get(x.len() - 1);
++```
++
++Use instead:
++```
++let x = vec![2, 3, 5];
++let last_element = x.last();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8defc2224416957864a017e09222554cdf5d1fcc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++### What it does
++Checks for use of `.get().unwrap()` (or
++`.get_mut().unwrap`) on a standard library type which implements `Index`
++
++### Why is this bad?
++Using the Index trait (`[]`) is more clear and more
++concise.
++
++### Known problems
++Not a replacement for error handling: Using either
++`.unwrap()` or the Index trait (`[]`) carries the risk of causing a `panic`
++if the value being accessed is `None`. If the use of `.get().unwrap()` is a
++temporary placeholder for dealing with the `Option` type, then this does
++not mitigate the need for error handling. If there is a chance that `.get()`
++will be `None` in your program, then it is advisable that the `None` case
++is handled in a future refactor instead of using `.unwrap()` or the Index
++trait.
++
++### Example
++```
++let mut some_vec = vec![0, 1, 2, 3];
++let last = some_vec.get(3).unwrap();
++*some_vec.get_mut(0).unwrap() = 1;
++```
++The correct use would be:
++```
++let mut some_vec = vec![0, 1, 2, 3];
++let last = some_vec[3];
++some_vec[0] = 1;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a8e40bb43e9d51b2d528bde622560da8a60175c4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++### What it does
++Checks for identity operations, e.g., `x + 0`.
++
++### Why is this bad?
++This code can be removed without changing the
++meaning. So it just obscures what's going on. Delete it mercilessly.
++
++### Example
++```
++x / 1 + 0 * 1 - 0 | 0;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4d873ade9ace346cfe7303717e3a4e1fe0d4810d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++### What it does
++Checks for `Mutex::lock` calls in `if let` expression
++with lock calls in any of the else blocks.
++
++### Why is this bad?
++The Mutex lock remains held for the whole
++`if let ... else` block and deadlocks.
++
++### Example
++```
++if let Ok(thing) = mutex.lock() {
++    do_thing();
++} else {
++    mutex.lock();
++}
++```
++Should be written
++```
++let locked = mutex.lock();
++if let Ok(thing) = locked {
++    do_thing(thing);
++} else {
++    use_locked(locked);
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0e5ac4ce6bb8047f8174bf2a1107f16646b347ef
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++### What it does
++Checks for usage of `!` or `!=` in an if condition with an
++else branch.
++
++### Why is this bad?
++Negations reduce the readability of statements.
++
++### Example
++```
++if !v.is_empty() {
++    a()
++} else {
++    b()
++}
++```
++
++Could be written:
++
++```
++if v.is_empty() {
++    b()
++} else {
++    a()
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..75127016bb8c18d0e8992b000e7436725a8e1ffa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks for `if/else` with the same body as the *then* part
++and the *else* part.
++
++### Why is this bad?
++This is probably a copy & paste error.
++
++### Example
++```
++let foo = if … {
++    42
++} else {
++    42
++};
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..13744f920e36237bc28e7fbf5daba8da93b8563e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Checks for if-else that could be written using either `bool::then` or `bool::then_some`.
++
++### Why is this bad?
++Looks a little redundant. Using `bool::then` is more concise and incurs no loss of clarity.
++For simple calculations and known values, use `bool::then_some`, which is eagerly evaluated
++in comparison to `bool::then`.
++
++### Example
++```
++let a = if v.is_empty() {
++    println!("true!");
++    Some(42)
++} else {
++    None
++};
++```
++
++Could be written:
++
++```
++let a = v.is_empty().then(|| {
++    println!("true!");
++    42
++});
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..024ba5df93a63a7a1651bb6075f95ab275493c54
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++### What it does
++Checks for consecutive `if`s with the same condition.
++
++### Why is this bad?
++This is probably a copy & paste error.
++
++### Example
++```
++if a == b {
++    …
++} else if a == b {
++    …
++}
++```
++
++Note that this lint ignores all conditions with a function call as it could
++have side effects:
++
++```
++if foo() {
++    …
++} else if foo() { // not linted
++    …
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f5aa112c52c36b98c8e1f92022001cddb04b8047
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer.
++
++### Why is this bad?
++These methods do the same thing as `_.clone()` but may be confusing as
++to why we are calling `to_vec` on something that is already a `Vec` or calling `to_owned` on something that is already owned.
++
++### Example
++```
++let a = vec![1, 2, 3];
++let b = a.to_vec();
++let c = a.to_owned();
++```
++Use instead:
++```
++let a = vec![1, 2, 3];
++let b = a.clone();
++let c = a.clone();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0c1f76620f51d5ff79516b1b274b6ae62a6e7ab5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Checks for public `impl` or `fn` missing generalization
++over different hashers and implicitly defaulting to the default hashing
++algorithm (`SipHash`).
++
++### Why is this bad?
++`HashMap` or `HashSet` with custom hashers cannot be
++used with them.
++
++### Known problems
++Suggestions for replacing constructors can contain
++false-positives. Also applying suggestions can require modification of other
++pieces of code, possibly including external crates.
++
++### Example
++```
++impl<K: Hash + Eq, V> Serialize for HashMap<K, V> { }
++
++pub fn foo(map: &mut HashMap<i32, i32>) { }
++```
++could be rewritten as
++```
++impl<K: Hash + Eq, V, S: BuildHasher> Serialize for HashMap<K, V, S> { }
++
++pub fn foo<S: BuildHasher>(map: &mut HashMap<i32, i32, S>) { }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ee65a636b38c5aa4d2640074d7887bc82ee7b4f5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for missing return statements at the end of a block.
++
++### Why is this bad?
++Actually omitting the return keyword is idiomatic Rust code. Programmers
++coming from other languages might prefer the expressiveness of `return`. It's possible to miss
++the last returning statement because the only difference is a missing `;`. Especially in bigger
++code with multiple return paths having a `return` keyword makes it easier to find the
++corresponding statements.
++
++### Example
++```
++fn foo(x: usize) -> usize {
++    x
++}
++```
++add return
++```
++fn foo(x: usize) -> usize {
++    return x;
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..03b47905a2118dc9e267293da15fed814ddabc42
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checks for implicit saturating subtraction.
++
++### Why is this bad?
++Simplicity and readability. Instead we can easily use an builtin function.
++
++### Example
++```
++let mut i: u32 = end - start;
++
++if i != 0 {
++    i -= 1;
++}
++```
++
++Use instead:
++```
++let mut i: u32 = end - start;
++
++i = i.saturating_sub(1);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e84d81cea98e0b70abb9cfb7e93e7c95d526bf28
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Looks for floating-point expressions that
++can be expressed using built-in methods to improve accuracy
++at the cost of performance.
++
++### Why is this bad?
++Negatively impacts accuracy.
++
++### Example
++```
++let a = 3f32;
++let _ = a.powf(1.0 / 3.0);
++let _ = (1.0 + a).ln();
++let _ = a.exp() - 1.0;
++```
++
++Use instead:
++```
++let a = 3f32;
++let _ = a.cbrt();
++let _ = a.ln_1p();
++let _ = a.exp_m1();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..aa0b072de1c4080303ccfe7f86ae3e07e3cde095
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Warns if an integral or floating-point constant is
++grouped inconsistently with underscores.
++
++### Why is this bad?
++Readers may incorrectly interpret inconsistently
++grouped digits.
++
++### Example
++```
++618_64_9189_73_511
++```
++
++Use instead:
++```
++61_864_918_973_511
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eb682109a54ee74cec3f50cf35b13600f8c534a2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++### What it does
++Checks for struct constructors where all fields are shorthand and
++the order of the field init shorthand in the constructor is inconsistent
++with the order in the struct definition.
++
++### Why is this bad?
++Since the order of fields in a constructor doesn't affect the
++resulted instance as the below example indicates,
++
++```
++#[derive(Debug, PartialEq, Eq)]
++struct Foo {
++    x: i32,
++    y: i32,
++}
++let x = 1;
++let y = 2;
++
++// This assertion never fails:
++assert_eq!(Foo { x, y }, Foo { y, x });
++```
++
++inconsistent order can be confusing and decreases readability and consistency.
++
++### Example
++```
++struct Foo {
++    x: i32,
++    y: i32,
++}
++let x = 1;
++let y = 2;
++
++Foo { y, x };
++```
++
++Use instead:
++```
++Foo { x, y };
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8a7d52761af816657c32bee2884c333b81b153e1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++### 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
++```
++let slice: Option<&[u32]> = Some(&[1, 2, 3]);
++
++if let Some(slice) = slice {
++    println!("{}", slice[0]);
++}
++```
++Use instead:
++```
++let slice: Option<&[u32]> = Some(&[1, 2, 3]);
++
++if let Some(&[first, ..]) = slice {
++    println!("{}", first);
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..76ca6ed318b38b88ecbe7320e2c9c5d7eff761c3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++### What it does
++Checks for usage of indexing or slicing. Arrays are special cases, this lint
++does report on arrays if we can tell that slicing operations are in bounds and does not
++lint on constant `usize` indexing on arrays because that is handled by rustc's `const_err` lint.
++
++### Why is this bad?
++Indexing and slicing can panic at runtime and there are
++safe alternatives.
++
++### Example
++```
++// Vector
++let x = vec![0; 5];
++
++x[2];
++&x[2..100];
++
++// Array
++let y = [0, 1, 2, 3];
++
++&y[10..100];
++&y[10..];
++```
++
++Use instead:
++```
++
++x.get(2);
++x.get(2..100);
++
++y.get(10);
++y.get(10..100);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f6e7ef556215b891e29665cb7f080ed4cdab315c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++### What it does
++Checks for bit masks in comparisons which can be removed
++without changing the outcome. The basic structure can be seen in the
++following table:
++
++|Comparison| Bit Op   |Example     |equals |
++|----------|----------|------------|-------|
++|`>` / `<=`|`\|` / `^`|`x \| 2 > 3`|`x > 3`|
++|`<` / `>=`|`\|` / `^`|`x ^ 1 < 4` |`x < 4`|
++
++### Why is this bad?
++Not equally evil as [`bad_bit_mask`](#bad_bit_mask),
++but still a bit misleading, because the bit mask is ineffective.
++
++### Known problems
++False negatives: This lint will only match instances
++where we have figured out the math (which is for a power-of-two compared
++value). This means things like `x | 1 >= 7` (which would be better written
++as `x >= 6`) will not be reported (but bit masks like this are fairly
++uncommon).
++
++### Example
++```
++if (x | 1 > 3) {  }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f7061d1ce7b082c10f14302b673f5b5d783c1669
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for usage of `.to_string()` on an `&&T` where
++`T` implements `ToString` directly (like `&&str` or `&&String`).
++
++### Why is this bad?
++This bypasses the specialized implementation of
++`ToString` and instead goes through the more expensive string formatting
++facilities.
++
++### Example
++```
++// Generic implementation for `T: Display` is used (slow)
++["foo", "bar"].iter().map(|s| s.to_string());
++
++// OK, the specialized impl is used
++["foo", "bar"].iter().map(|&s| s.to_string());
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4b5d3c4ba6c40c1b0657fa0195b977bacdb09453
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++### What it does
++Checks for matches being used to destructure a single-variant enum
++or tuple struct where a `let` will suffice.
++
++### Why is this bad?
++Just readability – `let` doesn't nest, whereas a `match` does.
++
++### Example
++```
++enum Wrapper {
++    Data(i32),
++}
++
++let wrapper = Wrapper::Data(42);
++
++let data = match wrapper {
++    Wrapper::Data(i) => i,
++};
++```
++
++The correct use would be:
++```
++enum Wrapper {
++    Data(i32),
++}
++
++let wrapper = Wrapper::Data(42);
++let Wrapper::Data(data) = wrapper;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8a22fabc5492c4f87689a75dbe149dafba04951a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++### What it does
++Checks for iteration that is guaranteed to be infinite.
++
++### Why is this bad?
++While there may be places where this is acceptable
++(e.g., in event streams), in most cases this is simply an error.
++
++### Example
++```
++use std::iter;
++
++iter::repeat(1_u8).collect::<Vec<_>>();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b18e600e9e67b056254eb92a07fec2dd9bd34faa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++### What it does
++Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`.
++
++### Why is this bad?
++This method is also implicitly defined if a type implements the `Display` trait. As the functionality of `Display` is much more versatile, it should be preferred.
++
++### Example
++```
++pub struct A;
++
++impl A {
++    pub fn to_string(&self) -> String {
++        "I am A".to_string()
++    }
++}
++```
++
++Use instead:
++```
++use std::fmt;
++
++pub struct A;
++
++impl fmt::Display for A {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        write!(f, "I am A")
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a4bd0b622c4f5833f59494a7999cd2d44d64a301
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++### What it does
++Checks for the definition of inherent methods with a signature of `to_string(&self) -> String` and if the type implementing this method also implements the `Display` trait.
++
++### Why is this bad?
++This method is also implicitly defined if a type implements the `Display` trait. The less versatile inherent method will then shadow the implementation introduced by `Display`.
++
++### Example
++```
++use std::fmt;
++
++pub struct A;
++
++impl A {
++    pub fn to_string(&self) -> String {
++        "I am A".to_string()
++    }
++}
++
++impl fmt::Display for A {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        write!(f, "I am A, too")
++    }
++}
++```
++
++Use instead:
++```
++use std::fmt;
++
++pub struct A;
++
++impl fmt::Display for A {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        write!(f, "I am A")
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ba40af6a5fa555df8eb9843ed1bc9202711ad1e0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++Checks for tuple structs initialized with field syntax.
++It will however not lint if a base initializer is present.
++The lint will also ignore code in macros.
++
++### Why is this bad?
++This may be confusing to the uninitiated and adds no
++benefit as opposed to tuple initializers
++
++### Example
++```
++struct TupleStruct(u8, u16);
++
++let _ = TupleStruct {
++    0: 1,
++    1: 23,
++};
++
++// should be written as
++let base = TupleStruct(1, 23);
++
++// This is OK however
++let _ = TupleStruct { 0: 42, ..base };
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7721da4c4cc71a26c26b6db49ef11516610c416c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Checks for items annotated with `#[inline(always)]`,
++unless the annotated function is empty or simply panics.
++
++### Why is this bad?
++While there are valid uses of this annotation (and once
++you know when to use it, by all means `allow` this lint), it's a common
++newbie-mistake to pepper one's code with it.
++
++As a rule of thumb, before slapping `#[inline(always)]` on a function,
++measure if that additional function call really affects your runtime profile
++sufficiently to make up for the increase in compile time.
++
++### Known problems
++False positives, big time. This lint is meant to be
++deactivated by everyone doing serious performance work. This means having
++done the measurement.
++
++### Example
++```
++#[inline(always)]
++fn not_quite_hot_code(..) { ... }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8eb49d122d89962e0e09afa804554b4eb94e7552
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for usage of AT&T x86 assembly syntax.
++
++### Why is this bad?
++The lint has been enabled to indicate a preference
++for Intel x86 assembly syntax.
++
++### Example
++
++```
++asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax));
++```
++Use instead:
++```
++asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5aa22c8ed235780c05515903fa8119d0e5e2465d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for usage of Intel x86 assembly syntax.
++
++### Why is this bad?
++The lint has been enabled to indicate a preference
++for AT&T x86 assembly syntax.
++
++### Example
++
++```
++asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr);
++```
++Use instead:
++```
++asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax));
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..127c161aaa25063e2f3f7fc89a602a2cb743c810
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++### What it does
++Checks for `#[inline]` on trait methods without bodies
++
++### Why is this bad?
++Only implementations of trait methods may be inlined.
++The inline attribute is ignored for trait methods without bodies.
++
++### Example
++```
++trait Animal {
++    #[inline]
++    fn name(&self) -> &'static str;
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..01a46d6c451f4536989dd60becd9d42536cbfa96
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Checks for usage of `inspect().for_each()`.
++
++### Why is this bad?
++It is the same as performing the computation
++inside `inspect` at the beginning of the closure in `for_each`.
++
++### Example
++```
++[1,2,3,4,5].iter()
++.inspect(|&x| println!("inspect the number: {}", x))
++.for_each(|&x| {
++    assert!(x >= 0);
++});
++```
++Can be written as
++```
++[1,2,3,4,5].iter()
++.for_each(|&x| {
++    println!("inspect the number: {}", x);
++    assert!(x >= 0);
++});
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1b68f3eeb64b44f709db8fdb51c607ebf4552ca1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block
++
++### Why is this bad?
++Readability -- better to use `> y` instead of `>= y + 1`.
++
++### Example
++```
++if x >= y + 1 {}
++```
++
++Use instead:
++```
++if x > y {}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ea57a2ef97bf5e0ca5c61aae77d878c22d41ed28
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for integer arithmetic operations which could overflow or panic.
++
++Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable
++of overflowing according to the [Rust
++Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),
++or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is
++attempted.
++
++### Why is this bad?
++Integer overflow will trigger a panic in debug builds or will wrap in
++release mode. Division by zero will cause a panic in either mode. In some applications one
++wants explicitly checked, wrapping or saturating arithmetic.
++
++### Example
++```
++a + 1;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f6d3349810ed86f7e0b7b07fa2a0d61e6b450250
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++Checks for division of integers
++
++### Why is this bad?
++When outside of some very specific algorithms,
++integer division is very often a mistake because it discards the
++remainder.
++
++### Example
++```
++let x = 3 / 2;
++println!("{}", x);
++```
++
++Use instead:
++```
++let x = 3f32 / 2f32;
++println!("{}", x);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..acb6bd474ebf31bbc3d1954d9f293d261d43b7ad
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for `into_iter` calls on references which should be replaced by `iter`
++or `iter_mut`.
++
++### Why is this bad?
++Readability. Calling `into_iter` on a reference will not move out its
++content into the resulting iterator, which is confusing. It is better just call `iter` or
++`iter_mut` directly.
++
++### Example
++```
++(&vec).into_iter();
++```
++
++Use instead:
++```
++(&vec).iter();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6fb3fa3f83d6672fa5cc8f84b418b2a0a484b42f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++This lint checks for invalid usages of `ptr::null`.
++
++### Why is this bad?
++This causes undefined behavior.
++
++### Example
++```
++// Undefined behavior
++unsafe { std::slice::from_raw_parts(ptr::null(), 0); }
++```
++
++Use instead:
++```
++unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6c9969b6e1a335a8672de9d225ccf60d497d3308
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++### What it does
++Checks [regex](https://crates.io/crates/regex) creation
++(with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`) for correct
++regex syntax.
++
++### Why is this bad?
++This will lead to a runtime panic.
++
++### Example
++```
++Regex::new("(")
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..77cb033080372924a3e379db7da01212cc265c6e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for comparisons where the relation is always either
++true or false, but where one side has been upcast so that the comparison is
++necessary. Only integer types are checked.
++
++### Why is this bad?
++An expression like `let x : u8 = ...; (x as u32) > 300`
++will mistakenly imply that it is possible for `x` to be outside the range of
++`u8`.
++
++### Known problems
++https://github.com/rust-lang/rust-clippy/issues/886
++
++### Example
++```
++let x: u8 = 1;
++(x as u32) > 300;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..afb5acbe9c51cf079e6498acd2bba609712a239b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++### What it does
++Checks for `std::str::from_utf8_unchecked` with an invalid UTF-8 literal
++
++### Why is this bad?
++Creating such a `str` would result in undefined behavior
++
++### Example
++```
++unsafe {
++    std::str::from_utf8_unchecked(b"cl\x82ippy");
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3dda380911f91589d5b496967bb64b21badf592d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++### What it does
++Checks for invisible Unicode characters in the code.
++
++### Why is this bad?
++Having an invisible character in the code makes for all
++sorts of April fools, but otherwise is very much frowned upon.
++
++### Example
++You don't see it, but there may be a zero-width space or soft hyphen
++some­where in this text.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9f11cf43054feddbe543d066c02597c7f392d233
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++### What it does
++Finds usages of [`char::is_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) that
++can be replaced with [`is_ascii_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_digit) or
++[`is_ascii_hexdigit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_hexdigit).
++
++### Why is this bad?
++`is_digit(..)` is slower and requires specifying the radix.
++
++### Example
++```
++let c: char = '6';
++c.is_digit(10);
++c.is_digit(16);
++```
++Use instead:
++```
++let c: char = '6';
++c.is_ascii_digit();
++c.is_ascii_hexdigit();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6fdfff50d20e40844dfd35f78c55b945b9af6661
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++### What it does
++Checks for items declared after some statement in a block.
++
++### Why is this bad?
++Items live for the entire scope they are declared
++in. But statements are processed in order. This might cause confusion as
++it's hard to figure out which item is meant in a statement.
++
++### Example
++```
++fn foo() {
++    println!("cake");
++}
++
++fn main() {
++    foo(); // prints "foo"
++    fn foo() {
++        println!("foo");
++    }
++    foo(); // prints "foo"
++}
++```
++
++Use instead:
++```
++fn foo() {
++    println!("cake");
++}
++
++fn main() {
++    fn foo() {
++        println!("foo");
++    }
++    foo(); // prints "foo"
++    foo(); // prints "foo"
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..90dc9ebb40f0b595825eb718cd380c1603613f69
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for the use of `.cloned().collect()` on slice to
++create a `Vec`.
++
++### Why is this bad?
++`.to_vec()` is clearer
++
++### Example
++```
++let s = [1, 2, 3, 4, 5];
++let s2: Vec<isize> = s[..].iter().cloned().collect();
++```
++The better use would be:
++```
++let s = [1, 2, 3, 4, 5];
++let s2: Vec<isize> = s.to_vec();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f3db4a26c2997f42ab673bf3112c6e1a448031f6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for the use of `.iter().count()`.
++
++### Why is this bad?
++`.len()` is more efficient and more
++readable.
++
++### Example
++```
++let some_vec = vec![0, 1, 2, 3];
++
++some_vec.iter().count();
++&some_vec[..].iter().count();
++```
++
++Use instead:
++```
++let some_vec = vec![0, 1, 2, 3];
++
++some_vec.len();
++&some_vec[..].len();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b33eb39d6e1d37529ed4969f94d4cf2c107d5042
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for loops on `x.next()`.
++
++### Why is this bad?
++`next()` returns either `Some(value)` if there was a
++value, or `None` otherwise. The insidious thing is that `Option<_>`
++implements `IntoIterator`, so that possibly one value will be iterated,
++leading to some hard to find bugs. No one will want to write such code
++[except to win an Underhanded Rust
++Contest](https://www.reddit.com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr).
++
++### Example
++```
++for x in y.next() {
++    ..
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1cea25eaf3017a7d5f6b8024ac886cad194182e2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for usage of `iter().next()` on a Slice or an Array
++
++### Why is this bad?
++These can be shortened into `.get()`
++
++### Example
++```
++a[2..].iter().next();
++b.iter().next();
++```
++should be written as:
++```
++a.get(2);
++b.get(0);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0ca862910a6f072a8199956be2a29db0ecfdc13d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Detects methods named `iter` or `iter_mut` that do not have a return type that implements `Iterator`.
++
++### Why is this bad?
++Methods named `iter` or `iter_mut` conventionally return an `Iterator`.
++
++### Example
++```
++// `String` does not implement `Iterator`
++struct Data {}
++impl Data {
++    fn iter(&self) -> String {
++        todo!()
++    }
++}
++```
++Use instead:
++```
++use std::str::Chars;
++struct Data {}
++impl Data {
++   fn iter(&self) -> Chars<'static> {
++       todo!()
++   }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3d67d583ffde387ca67137aec43d074eeccdfb92
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++### What it does
++Checks for use of `.iter().nth()` (and the related
++`.iter_mut().nth()`) on standard library types with *O*(1) element access.
++
++### Why is this bad?
++`.get()` and `.get_mut()` are more efficient and more
++readable.
++
++### Example
++```
++let some_vec = vec![0, 1, 2, 3];
++let bad_vec = some_vec.iter().nth(3);
++let bad_slice = &some_vec[..].iter().nth(3);
++```
++The correct use would be:
++```
++let some_vec = vec![0, 1, 2, 3];
++let bad_vec = some_vec.get(3);
++let bad_slice = &some_vec[..].get(3);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8efe47a16a100ab2b193b58f1a7d52747b3a2e57
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for the use of `iter.nth(0)`.
++
++### Why is this bad?
++`iter.next()` is equivalent to
++`iter.nth(0)`, as they both consume the next element,
++ but is more readable.
++
++### Example
++```
++let x = s.iter().nth(0);
++```
++
++Use instead:
++```
++let x = s.iter().next();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..87c4ec12afae71b4592015e135452701cf64f0c1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++### What it does
++
++Checks for calls to `iter`, `iter_mut` or `into_iter` on empty collections
++
++### Why is this bad?
++
++It is simpler to use the empty function from the standard library:
++
++### Example
++
++```
++use std::{slice, option};
++let a: slice::Iter<i32> = [].iter();
++let f: option::IntoIter<i32> = None.into_iter();
++```
++Use instead:
++```
++use std::iter;
++let a: iter::Empty<i32> = iter::empty();
++let b: iter::Empty<i32> = iter::empty();
++```
++
++### Known problems
++
++The type of the resulting iterator might become incompatible with its usage
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d0388f25d045e709a953550443b5fe6feac8a119
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++
++Checks for calls to `iter`, `iter_mut` or `into_iter` on collections containing a single item
++
++### Why is this bad?
++
++It is simpler to use the once function from the standard library:
++
++### Example
++
++```
++let a = [123].iter();
++let b = Some(123).into_iter();
++```
++Use instead:
++```
++use std::iter;
++let a = iter::once(&123);
++let b = iter::once(123);
++```
++
++### Known problems
++
++The type of the resulting iterator might become incompatible with its usage
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2f902a0c2db4d1c6295ef59e3a05f977eb07377e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for usage of `_.cloned().<func>()` where call to `.cloned()` can be postponed.
++
++### Why is this bad?
++It's often inefficient to clone all elements of an iterator, when eventually, only some
++of them will be consumed.
++
++### Known Problems
++This `lint` removes the side of effect of cloning items in the iterator.
++A code that relies on that side-effect could fail.
++
++### Examples
++```
++vec.iter().cloned().take(10);
++vec.iter().cloned().last();
++```
++
++Use instead:
++```
++vec.iter().take(10).cloned();
++vec.iter().last().cloned();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..da226b041cf23aaa89e417a05505015fc472a1e8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for use of `.skip(x).next()` on iterators.
++
++### Why is this bad?
++`.nth(x)` is cleaner
++
++### Example
++```
++let some_vec = vec![0, 1, 2, 3];
++let bad_vec = some_vec.iter().skip(3).next();
++let bad_slice = &some_vec[..].iter().skip(3).next();
++```
++The correct use would be:
++```
++let some_vec = vec![0, 1, 2, 3];
++let bad_vec = some_vec.iter().nth(3);
++let bad_slice = &some_vec[..].iter().nth(3);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2c52b99f7a5c5aceb533a8c43ba3594054767a20
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for use of `.drain(..)` on `Vec` and `VecDeque` for iteration.
++
++### Why is this bad?
++`.into_iter()` is simpler with better performance.
++
++### Example
++```
++let mut foo = vec![0, 1, 2, 3];
++let bar: HashSet<usize> = foo.drain(..).collect();
++```
++Use instead:
++```
++let foo = vec![0, 1, 2, 3];
++let bar: HashSet<usize> = foo.into_iter().collect();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..73ecc99acfcbcb53b8328d4f8f4ce551313dc9d1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++### What it does
++Checks for calling `.step_by(0)` on iterators which panics.
++
++### Why is this bad?
++This very much looks like an oversight. Use `panic!()` instead if you
++actually intend to panic.
++
++### Example
++```
++for x in (0..100).step_by(0) {
++    //..
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a8790bcf25be6175547827c0908cd0bce21616ad
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++### What it does
++Checks if you have variables whose name consists of just
++underscores and digits.
++
++### Why is this bad?
++It's hard to memorize what a variable means without a
++descriptive name.
++
++### Example
++```
++let _1 = 1;
++let ___1 = 1;
++let __1___2 = 11;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..71f67854f2a1afcd3dc1bb0e961000ca3059e8f3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for large `const` arrays that should
++be defined as `static` instead.
++
++### Why is this bad?
++Performance: const variables are inlined upon use.
++Static items result in only one instance and has a fixed location in memory.
++
++### Example
++```
++pub const a = [0u32; 1_000_000];
++```
++
++Use instead:
++```
++pub static a = [0u32; 1_000_000];
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f60b19345af44e86e6f74564c80d17d654f9311b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++### What it does
++Warns if the digits of an integral or floating-point
++constant are grouped into groups that
++are too large.
++
++### Why is this bad?
++Negatively impacts readability.
++
++### Example
++```
++let x: u64 = 6186491_8973511;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1f95430790d292b8fc2a574417597921bcac0acf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,41 @@@
++### What it does
++Checks for large size differences between variants on
++`enum`s.
++
++### Why is this bad?
++Enum size is bounded by the largest variant. Having one
++large variant can penalize the memory layout of that enum.
++
++### Known problems
++This lint obviously cannot take the distribution of
++variants in your running program into account. It is possible that the
++smaller variants make up less than 1% of all instances, in which case
++the overhead is negligible and the boxing is counter-productive. Always
++measure the change this lint suggests.
++
++For types that implement `Copy`, the suggestion to `Box` a variant's
++data would require removing the trait impl. The types can of course
++still be `Clone`, but that is worse ergonomically. Depending on the
++use case it may be possible to store the large data in an auxiliary
++structure (e.g. Arena or ECS).
++
++The lint will ignore the impact of generic types to the type layout by
++assuming every type parameter is zero-sized. Depending on your use case,
++this may lead to a false positive.
++
++### Example
++```
++enum Test {
++    A(i32),
++    B([i32; 8000]),
++}
++```
++
++Use instead:
++```
++// Possibly better
++enum Test2 {
++    A(i32),
++    B(Box<[i32; 8000]>),
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b2a54bd2eb5ca0f8f5ab0ffdc43e25e89ea58acb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checks for the inclusion of large files via `include_bytes!()`
++and `include_str!()`
++
++### Why is this bad?
++Including large files can increase the size of the binary
++
++### Example
++```
++let included_str = include_str!("very_large_file.txt");
++let included_bytes = include_bytes!("very_large_file.txt");
++```
++
++Use instead:
++```
++use std::fs;
++
++// You can load the file at runtime
++let string = fs::read_to_string("very_large_file.txt")?;
++let bytes = fs::read("very_large_file.txt")?;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4a6f34785b0ef802748ff039f44578ff4dda2ea4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++### What it does
++Checks for local arrays that may be too large.
++
++### Why is this bad?
++Large local arrays may cause stack overflow.
++
++### Example
++```
++let a = [0u32; 1_000_000];
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bca07f3ac61bbe56b2916ba79e839cad046d9b4a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++Checks for functions taking arguments by value, where
++the argument type is `Copy` and large enough to be worth considering
++passing by reference. Does not trigger if the function is being exported,
++because that might induce API breakage, if the parameter is declared as mutable,
++or if the argument is a `self`.
++
++### Why is this bad?
++Arguments passed by value might result in an unnecessary
++shallow copy, taking up more space in the stack and requiring a call to
++`memcpy`, which can be expensive.
++
++### Example
++```
++#[derive(Clone, Copy)]
++struct TooLarge([u8; 2048]);
++
++fn foo(v: TooLarge) {}
++```
++
++Use instead:
++```
++fn foo(v: &TooLarge) {}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..47a2e8575228542722423914d378f5d4cdbe436f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++Checks for items that implement `.len()` but not
++`.is_empty()`.
++
++### Why is this bad?
++It is good custom to have both methods, because for
++some data structures, asking about the length will be a costly operation,
++whereas `.is_empty()` can usually answer in constant time. Also it used to
++lead to false positives on the [`len_zero`](#len_zero) lint – currently that
++lint will ignore such entities.
++
++### Example
++```
++impl X {
++    pub fn len(&self) -> usize {
++        ..
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..664124bd391dfc719ed24e3220e7ee9407e8414d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++### What it does
++Checks for getting the length of something via `.len()`
++just to compare to zero, and suggests using `.is_empty()` where applicable.
++
++### Why is this bad?
++Some structures can answer `.is_empty()` much faster
++than calculating their length. So it is good to get into the habit of using
++`.is_empty()`, and having it is cheap.
++Besides, it makes the intent clearer than a manual comparison in some contexts.
++
++### Example
++```
++if x.len() == 0 {
++    ..
++}
++if y.len() != 0 {
++    ..
++}
++```
++instead use
++```
++if x.is_empty() {
++    ..
++}
++if !y.is_empty() {
++    ..
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eba5a90ddd66c57846806913fd0a81cb024df7ea
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checks for `let`-bindings, which are subsequently
++returned.
++
++### Why is this bad?
++It is just extraneous code. Remove it to make your code
++more rusty.
++
++### Example
++```
++fn foo() -> String {
++    let x = String::new();
++    x
++}
++```
++instead, use
++```
++fn foo() -> String {
++    String::new()
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..29ce9bf50ce6a61254591e93ee7dadb8a0bfdd9d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++### What it does
++Checks for `let _ = <expr>`
++where expr has a type that implements `Drop`
++
++### Why is this bad?
++This statement immediately drops the initializer
++expression instead of extending its lifetime to the end of the scope, which
++is often not intended. To extend the expression's lifetime to the end of the
++scope, use an underscore-prefixed name instead (i.e. _var). If you want to
++explicitly drop the expression, `std::mem::drop` conveys your intention
++better and is less error-prone.
++
++### Example
++```
++{
++    let _ = DroppableItem;
++    //                   ^ dropped here
++    /* more code */
++}
++```
++
++Use instead:
++```
++{
++    let _droppable = DroppableItem;
++    /* more code */
++    // dropped at end of scope
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bd8217fb58b3c19dce8e06a2cbd8ddf2f5639f00
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++### What it does
++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
++extending its lifetime to the end of the scope, which is often not intended.
++To extend lock lifetime to the end of the scope, use an underscore-prefixed
++name instead (i.e. _lock). If you want to explicitly drop the lock,
++`std::mem::drop` conveys your intention better and is less error-prone.
++
++### Example
++```
++let _ = mutex.lock();
++```
++
++Use instead:
++```
++let _lock = mutex.lock();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..270b81d9a4c414ec482301e1593042f89c922b43
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for `let _ = <expr>` where expr is `#[must_use]`
++
++### Why is this bad?
++It's better to explicitly handle the value of a `#[must_use]`
++expr
++
++### Example
++```
++fn f() -> Result<u32, u32> {
++    Ok(0)
++}
++
++let _ = f();
++// is_ok() is marked #[must_use]
++let _ = f().is_ok();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bc16d5b3d81beeb6880a1f44bf36c49d5b9bb867
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++### What it does
++Checks for binding a unit value.
++
++### Why is this bad?
++A unit value cannot usefully be used anywhere. So
++binding one is kind of pointless.
++
++### Example
++```
++let x = {
++    1;
++};
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..986ff1369e3c150f5a7d838a4f81922d920af44d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++### What it does
++Checks for usage of any `LinkedList`, suggesting to use a
++`Vec` or a `VecDeque` (formerly called `RingBuf`).
++
++### Why is this bad?
++Gankro says:
++
++> The TL;DR of `LinkedList` is that it's built on a massive amount of
++pointers and indirection.
++> It wastes memory, it has terrible cache locality, and is all-around slow.
++`RingBuf`, while
++> "only" amortized for push/pop, should be faster in the general case for
++almost every possible
++> workload, and isn't even amortized at all if you can predict the capacity
++you need.
++>
++> `LinkedList`s are only really good if you're doing a lot of merging or
++splitting of lists.
++> This is because they can just mangle some pointers instead of actually
++copying the data. Even
++> if you're doing a lot of insertion in the middle of the list, `RingBuf`
++can still be better
++> because of how expensive it is to seek to the middle of a `LinkedList`.
++
++### Known problems
++False positives – the instances where using a
++`LinkedList` makes sense are few and far between, but they can still happen.
++
++### Example
++```
++let x: LinkedList<usize> = LinkedList::new();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bbcb9115ea62a2f7ab2b608d1b56c48edc86cb48
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for whole number float literals that
++cannot be represented as the underlying type without loss.
++
++### Why is this bad?
++Rust will silently lose precision during
++conversion to a float.
++
++### Example
++```
++let _: f32 = 16_777_217.0; // 16_777_216.0
++```
++
++Use instead:
++```
++let _: f32 = 16_777_216.0;
++let _: f64 = 16_777_217.0;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6a8180a60bc6fec1bc63542b035d7fbab02897b7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++### What it does
++Checks for `#[macro_use] use...`.
++
++### Why is this bad?
++Since the Rust 2018 edition you can import
++macro's directly, this is considered idiomatic.
++
++### Example
++```
++#[macro_use]
++use some_macro;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e49becd15bbdb333909e3e2a2147cda2803367c5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++### What it does
++Checks for recursion using the entrypoint.
++
++### Why is this bad?
++Apart from special setups (which we could detect following attributes like #![no_std]),
++recursing into main() seems like an unintuitive anti-pattern we should be able to detect.
++
++### Example
++```
++fn main() {
++    main();
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..93653081a2ce08833423201991d8ee71b2bf1484
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Detects `if`-then-`panic!` that can be replaced with `assert!`.
++
++### Why is this bad?
++`assert!` is simpler than `if`-then-`panic!`.
++
++### Example
++```
++let sad_people: Vec<&str> = vec![];
++if !sad_people.is_empty() {
++    panic!("there are sad people: {:?}", sad_people);
++}
++```
++Use instead:
++```
++let sad_people: Vec<&str> = vec![];
++assert!(sad_people.is_empty(), "there are sad people: {:?}", sad_people);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d01ac402e0d21f99b370c33d681d5d53680ef015
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++It checks for manual implementations of `async` functions.
++
++### Why is this bad?
++It's more idiomatic to use the dedicated syntax.
++
++### Example
++```
++use std::future::Future;
++
++fn foo() -> impl Future<Output = i32> { async { 42 } }
++```
++Use instead:
++```
++async fn foo() -> i32 { 42 }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b96c2eb151d4161b67aa4bd2a093f95caa8b657a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks for uses of `std::mem::size_of::<T>() * 8` when
++`T::BITS` is available.
++
++### Why is this bad?
++Can be written as the shorter `T::BITS`.
++
++### Example
++```
++std::mem::size_of::<usize>() * 8;
++```
++Use instead:
++```
++usize::BITS as usize;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3b6860798ff58a54a569a0da8179a20219c7ad2c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++Checks for usage of `_.filter(_).map(_)` that can be written more simply
++as `filter_map(_)`.
++
++### Why is this bad?
++Redundant code in the `filter` and `map` operations is poor style and
++less performant.
++
++### Example
++```
++(0_i32..10)
++    .filter(|n| n.checked_add(1).is_some())
++    .map(|n| n.checked_add(1).unwrap());
++```
++
++Use instead:
++```
++(0_i32..10).filter_map(|n| n.checked_add(1));
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e3e07a2771f95ccc49ca7275e60c47c6edae29f2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++Check for manual implementations of Iterator::find
++
++### Why is this bad?
++It doesn't affect performance, but using `find` is shorter and easier to read.
++
++### Example
++
++```
++fn example(arr: Vec<i32>) -> Option<i32> {
++    for el in arr {
++        if el == 1 {
++            return Some(el);
++        }
++    }
++    None
++}
++```
++Use instead:
++```
++fn example(arr: Vec<i32>) -> Option<i32> {
++    arr.into_iter().find(|&el| el == 1)
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..83b22060c0e193a5510d103afdb7eb78f400b687
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++Checks for usage of `_.find(_).map(_)` that can be written more simply
++as `find_map(_)`.
++
++### Why is this bad?
++Redundant code in the `find` and `map` operations is poor style and
++less performant.
++
++### Example
++```
++(0_i32..10)
++    .find(|n| n.checked_add(1).is_some())
++    .map(|n| n.checked_add(1).unwrap());
++```
++
++Use instead:
++```
++(0_i32..10).find_map(|n| n.checked_add(1));
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..62d5f3ec9357659152c99389be9f436c791a6a8c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++### What it does
++Check for unnecessary `if let` usage in a for loop
++where only the `Some` or `Ok` variant of the iterator element is used.
++
++### Why is this bad?
++It is verbose and can be simplified
++by first calling the `flatten` method on the `Iterator`.
++
++### Example
++
++```
++let x = vec![Some(1), Some(2), Some(3)];
++for n in x {
++    if let Some(n) = n {
++        println!("{}", n);
++    }
++}
++```
++Use instead:
++```
++let x = vec![Some(1), Some(2), Some(3)];
++for n in x.into_iter().flatten() {
++    println!("{}", n);
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dde3d493c70d3806891006be2eb83c653ec046f9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Lints subtraction between `Instant::now()` and another `Instant`.
++
++### Why is this bad?
++It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns
++as `Instant` subtraction saturates.
++
++`prev_instant.elapsed()` also more clearly signals intention.
++
++### Example
++```
++use std::time::Instant;
++let prev_instant = Instant::now();
++let duration = Instant::now() - prev_instant;
++```
++Use instead:
++```
++use std::time::Instant;
++let prev_instant = Instant::now();
++let duration = prev_instant.elapsed();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7f68ccd1037e3fb4f89927bdb1d7f7d7ba7e5544
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for usages of `match` which could be implemented using `map`
++
++### Why is this bad?
++Using the `map` method is clearer and more concise.
++
++### Example
++```
++match Some(0) {
++    Some(x) => Some(x + 1),
++    None => None,
++};
++```
++Use instead:
++```
++Some(0).map(|x| x + 1);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d7690bf25866934959af7781dbae6cf525972e50
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for for-loops that manually copy items between
++slices that could be optimized by having a memcpy.
++
++### Why is this bad?
++It is not as fast as a memcpy.
++
++### Example
++```
++for i in 0..src.len() {
++    dst[i + 64] = src[i];
++}
++```
++
++Use instead:
++```
++dst[64..(src.len() + 64)].clone_from_slice(&src[..]);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fb021393bd7c935beea0436d35fd047cf4e5f7ff
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,41 @@@
++### What it does
++Checks for manual implementations of the non-exhaustive pattern.
++
++### Why is this bad?
++Using the #[non_exhaustive] attribute expresses better the intent
++and allows possible optimizations when applied to enums.
++
++### Example
++```
++struct S {
++    pub a: i32,
++    pub b: i32,
++    _c: (),
++}
++
++enum E {
++    A,
++    B,
++    #[doc(hidden)]
++    _C,
++}
++
++struct T(pub i32, pub i32, ());
++```
++Use instead:
++```
++#[non_exhaustive]
++struct S {
++    pub a: i32,
++    pub b: i32,
++}
++
++#[non_exhaustive]
++enum E {
++    A,
++    B,
++}
++
++#[non_exhaustive]
++struct T(pub i32, pub i32);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5accdf25965ab35f6d02cbf3d81d33483bef287a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++
++Finds patterns that reimplement `Option::ok_or`.
++
++### Why is this bad?
++
++Concise code helps focusing on behavior instead of boilerplate.
++
++### Examples
++```
++let foo: Option<i32> = None;
++foo.map_or(Err("error"), |v| Ok(v));
++```
++
++Use instead:
++```
++let foo: Option<i32> = None;
++foo.ok_or("error");
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0ade26951d38523180ef49c247037c993a07653c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++Checks for expressions like `x >= 3 && x < 8` that could
++be more readably expressed as `(3..8).contains(x)`.
++
++### Why is this bad?
++`contains` expresses the intent better and has less
++failure modes (such as fencepost errors or using `||` instead of `&&`).
++
++### Example
++```
++// given
++let x = 6;
++
++assert!(x >= 3 && x < 8);
++```
++Use instead:
++```
++assert!((3..8).contains(&x));
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d3bb8c61304e1cf513a41942d221d4e079f26171
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for an expression like `((x % 4) + 4) % 4` which is a common manual reimplementation
++of `x.rem_euclid(4)`.
++
++### Why is this bad?
++It's simpler and more readable.
++
++### Example
++```
++let x: i32 = 24;
++let rem = ((x % 4) + 4) % 4;
++```
++Use instead:
++```
++let x: i32 = 24;
++let rem = x.rem_euclid(4);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cd4f65a93fc33ea4bc8d5a7521dd8c581b9e544d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks for code to be replaced by `.retain()`.
++### Why is this bad?
++`.retain()` is simpler and avoids needless allocation.
++### Example
++```
++let mut vec = vec![0, 1, 2];
++vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect();
++vec = vec.into_iter().filter(|x| x % 2 == 0).collect();
++```
++Use instead:
++```
++let mut vec = vec![0, 1, 2];
++vec.retain(|x| x % 2 == 0);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d9f5d3d1187129a7fe8cc6fc8c598d8d8930962e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`.
++
++### Why is this bad?
++These can be written simply with `saturating_add/sub` methods.
++
++### Example
++```
++let add = x.checked_add(y).unwrap_or(u32::MAX);
++let sub = x.checked_sub(y).unwrap_or(u32::MIN);
++```
++
++can be written using dedicated methods for saturating addition/subtraction as:
++
++```
++let add = x.saturating_add(y);
++let sub = x.saturating_sub(y);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..291ae447de08a6b80afb6c4960050e27abd7dae3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++### What it does
++Checks for usages of `str::splitn(2, _)`
++
++### Why is this bad?
++`split_once` is both clearer in intent and slightly more efficient.
++
++### Example
++```
++let s = "key=value=add";
++let (key, value) = s.splitn(2, '=').next_tuple()?;
++let value = s.splitn(2, '=').nth(1)?;
++
++let mut parts = s.splitn(2, '=');
++let key = parts.next()?;
++let value = parts.next()?;
++```
++
++Use instead:
++```
++let s = "key=value=add";
++let (key, value) = s.split_once('=')?;
++let value = s.split_once('=')?.1;
++
++let (key, value) = s.split_once('=')?;
++```
++
++### Limitations
++The multiple statement variant currently only detects `iter.next()?`/`iter.next().unwrap()`
++in two separate `let` statements that immediately follow the `splitn()`
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1d4a7a48e203345b96f0ae4db4e243542693f327
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks for manual implementations of `str::repeat`
++
++### Why is this bad?
++These are both harder to read, as well as less performant.
++
++### Example
++```
++let x: String = std::iter::repeat('x').take(10).collect();
++```
++
++Use instead:
++```
++let x: String = "x".repeat(10);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4cbc43f8f8439b681a45c17adeb7f937d9fefb0d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++### What it does
++
++Checks for usage of `""` to create a `String`, such as `"".to_string()`, `"".to_owned()`,
++`String::from("")` and others.
++
++### Why is this bad?
++
++Different ways of creating an empty string makes your code less standardized, which can
++be confusing.
++
++### Example
++```
++let a = "".to_string();
++let b: String = "".into();
++```
++Use instead:
++```
++let a = String::new();
++let b = String::new();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f32d8e7a09bb327fb711f76df7222b1c2d18fdd7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using
++the pattern's length.
++
++### Why is this bad?
++Using `str:strip_{prefix,suffix}` is safer and may have better performance as there is no
++slicing which may panic and the compiler does not need to insert this panic code. It is
++also sometimes more readable as it removes the need for duplicating or storing the pattern
++used by `str::{starts,ends}_with` and in the slicing.
++
++### Example
++```
++let s = "hello, world!";
++if s.starts_with("hello, ") {
++    assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
++}
++```
++Use instead:
++```
++let s = "hello, world!";
++if let Some(end) = s.strip_prefix("hello, ") {
++    assert_eq!(end.to_uppercase(), "WORLD!");
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bd9526288e35b14bb806238a1a2163f6d672efe9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for manual swapping.
++
++### Why is this bad?
++The `std::mem::swap` function exposes the intent better
++without deinitializing or copying either variable.
++
++### Example
++```
++let mut a = 42;
++let mut b = 1337;
++
++let t = b;
++b = a;
++a = t;
++```
++Use std::mem::swap():
++```
++let mut a = 1;
++let mut b = 2;
++std::mem::swap(&mut a, &mut b);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1fd7d831bfca2a44771e5321e0679d7013698cdf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++### What it does
++Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`.
++
++### Why is this bad?
++Concise code helps focusing on behavior instead of boilerplate.
++
++### Example
++```
++let foo: Option<i32> = None;
++match foo {
++    Some(v) => v,
++    None => 1,
++};
++```
++
++Use instead:
++```
++let foo: Option<i32> = None;
++foo.unwrap_or(1);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..55ee5da5557443219b0f2a48a85f71d01af4c5f1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++### What it does
++Checks for too many variables whose name consists of a
++single character.
++
++### Why is this bad?
++It's hard to memorize what a variable means without a
++descriptive name.
++
++### Example
++```
++let (a, b, c, d, e, f, g) = (...);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3ee27f072ef3da18b536306083f16808618fa47b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for usage of `map(|x| x.clone())` or
++dereferencing closures for `Copy` types, on `Iterator` or `Option`,
++and suggests `cloned()` or `copied()` instead
++
++### Why is this bad?
++Readability, this can be written more concisely
++
++### Example
++```
++let x = vec![42, 43];
++let y = x.iter();
++let z = y.map(|i| *i);
++```
++
++The correct use would be:
++
++```
++let x = vec![42, 43];
++let y = x.iter();
++let z = y.cloned();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9b720612495ce1607d7bd8c7d529bee8f5abb496
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++### What it does
++Checks for usage of `_.map(_).collect::<Result<(), _>()`.
++
++### Why is this bad?
++Using `try_for_each` instead is more readable and idiomatic.
++
++### Example
++```
++(0..3).map(|t| Err(t)).collect::<Result<(), _>>();
++```
++Use instead:
++```
++(0..3).try_for_each(|t| Err(t));
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..20dba1798d0d5d17887fc745fe7b44537ec8059e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++### What it does
++Checks for uses of `contains_key` + `insert` on `HashMap`
++or `BTreeMap`.
++
++### Why is this bad?
++Using `entry` is more efficient.
++
++### Known problems
++The suggestion may have type inference errors in some cases. e.g.
++```
++let mut map = std::collections::HashMap::new();
++let _ = if !map.contains_key(&0) {
++    map.insert(0, 0)
++} else {
++    None
++};
++```
++
++### Example
++```
++if !map.contains_key(&k) {
++    map.insert(k, v);
++}
++```
++Use instead:
++```
++map.entry(k).or_insert(v);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2606c13a7afd9e1081559f167d501d98c986a69a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,93 @@@
++### What it does
++Checks for instances of `map_err(|_| Some::Enum)`
++
++### Why is this bad?
++This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error
++
++### Example
++Before:
++```
++use std::fmt;
++
++#[derive(Debug)]
++enum Error {
++    Indivisible,
++    Remainder(u8),
++}
++
++impl fmt::Display for Error {
++    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
++        match self {
++            Error::Indivisible => write!(f, "could not divide input by three"),
++            Error::Remainder(remainder) => write!(
++                f,
++                "input is not divisible by three, remainder = {}",
++                remainder
++            ),
++        }
++    }
++}
++
++impl std::error::Error for Error {}
++
++fn divisible_by_3(input: &str) -> Result<(), Error> {
++    input
++        .parse::<i32>()
++        .map_err(|_| Error::Indivisible)
++        .map(|v| v % 3)
++        .and_then(|remainder| {
++            if remainder == 0 {
++                Ok(())
++            } else {
++                Err(Error::Remainder(remainder as u8))
++            }
++        })
++}
++ ```
++
++ After:
++ ```rust
++use std::{fmt, num::ParseIntError};
++
++#[derive(Debug)]
++enum Error {
++    Indivisible(ParseIntError),
++    Remainder(u8),
++}
++
++impl fmt::Display for Error {
++    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
++        match self {
++            Error::Indivisible(_) => write!(f, "could not divide input by three"),
++            Error::Remainder(remainder) => write!(
++                f,
++                "input is not divisible by three, remainder = {}",
++                remainder
++            ),
++        }
++    }
++}
++
++impl std::error::Error for Error {
++    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
++        match self {
++            Error::Indivisible(source) => Some(source),
++            _ => None,
++        }
++    }
++}
++
++fn divisible_by_3(input: &str) -> Result<(), Error> {
++    input
++        .parse::<i32>()
++        .map_err(Error::Indivisible)
++        .map(|v| v % 3)
++        .and_then(|remainder| {
++            if remainder == 0 {
++                Ok(())
++            } else {
++                Err(Error::Remainder(remainder as u8))
++            }
++        })
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..73c0e51407f28e73e0914c7bae9ad8672f2f259b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checks for usage of `_.map(_).flatten(_)` on `Iterator` and `Option`
++
++### Why is this bad?
++Readability, this can be written more concisely as
++`_.flat_map(_)` for `Iterator` or `_.and_then(_)` for `Option`
++
++### Example
++```
++let vec = vec![vec![1]];
++let opt = Some(5);
++
++vec.iter().map(|x| x.iter()).flatten();
++opt.map(|x| Some(x * 2)).flatten();
++```
++
++Use instead:
++```
++vec.iter().flat_map(|x| x.iter());
++opt.and_then(|x| Some(x * 2));
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e2e7af0bed9b148d36e262f2c1cb913b65dc6b1c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for instances of `map(f)` where `f` is the identity function.
++
++### Why is this bad?
++It can be written more concisely without the call to `map`.
++
++### Example
++```
++let x = [1, 2, 3];
++let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
++```
++Use instead:
++```
++let x = [1, 2, 3];
++let y: Vec<_> = x.iter().map(|x| 2*x).collect();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..485b29f01b103e6cac338a0f0d7a2d03b62c5210
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or
++`result.map(_).unwrap_or_else(_)`.
++
++### Why is this bad?
++Readability, these can be written more concisely (resp.) as
++`option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`.
++
++### Known problems
++The order of the arguments is not in execution order
++
++### Examples
++```
++option.map(|a| a + 1).unwrap_or(0);
++result.map(|a| a + 1).unwrap_or_else(some_function);
++```
++
++Use instead:
++```
++option.map_or(0, |a| a + 1);
++result.map_or_else(some_function, |a| a + 1);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5e5f3d645a4e4f1da645bc5f2c868df6c6d77ee2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Checks for match which is used to add a reference to an
++`Option` value.
++
++### Why is this bad?
++Using `as_ref()` or `as_mut()` instead is shorter.
++
++### Example
++```
++let x: Option<()> = None;
++
++let r: Option<&()> = match x {
++    None => None,
++    Some(ref v) => Some(v),
++};
++```
++
++Use instead:
++```
++let x: Option<()> = None;
++
++let r: Option<&()> = x.as_ref();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..96f9e1f8b7d1bfb503d685bb7861da665d8616f2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++Checks for matches where match expression is a `bool`. It
++suggests to replace the expression with an `if...else` block.
++
++### Why is this bad?
++It makes the code less readable.
++
++### Example
++```
++let condition: bool = true;
++match condition {
++    true => foo(),
++    false => bar(),
++}
++```
++Use if/else instead:
++```
++let condition: bool = true;
++if condition {
++    foo();
++} else {
++    bar();
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..643e2ddc97ba0bfe03114790fe1af7e3bdf6527f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++### What it does
++Checks for `match`  or `if let` expressions producing a
++`bool` that could be written using `matches!`
++
++### Why is this bad?
++Readability and needless complexity.
++
++### Known problems
++This lint falsely triggers, if there are arms with
++`cfg` attributes that remove an arm evaluating to `false`.
++
++### Example
++```
++let x = Some(5);
++
++let a = match x {
++    Some(0) => true,
++    _ => false,
++};
++
++let a = if let Some(0) = x {
++    true
++} else {
++    false
++};
++```
++
++Use instead:
++```
++let x = Some(5);
++let a = matches!(x, Some(0));
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..981d18d0f9ed560919e29c4913a44773612f3adf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++### What it does
++Checks for `match vec[idx]` or `match vec[n..m]`.
++
++### Why is this bad?
++This can panic at runtime.
++
++### Example
++```
++let arr = vec![0, 1, 2, 3];
++let idx = 1;
++
++match arr[idx] {
++    0 => println!("{}", 0),
++    1 => println!("{}", 3),
++    _ => {},
++}
++```
++
++Use instead:
++```
++let arr = vec![0, 1, 2, 3];
++let idx = 1;
++
++match arr.get(idx) {
++    Some(0) => println!("{}", 0),
++    Some(1) => println!("{}", 3),
++    _ => {},
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..841c091bd5cad43216bef6f4c07c4d3f93facafe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for overlapping match arms.
++
++### Why is this bad?
++It is likely to be an error and if not, makes the code
++less obvious.
++
++### Example
++```
++let x = 5;
++match x {
++    1..=10 => println!("1 ... 10"),
++    5..=15 => println!("5 ... 15"),
++    _ => (),
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b1d9029950972cfb76a01aa79b408a8e57f2c767
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Checks for matches where all arms match a reference,
++suggesting to remove the reference and deref the matched expression
++instead. It also checks for `if let &foo = bar` blocks.
++
++### Why is this bad?
++It just makes the code less readable. That reference
++destructuring adds nothing to the code.
++
++### Example
++```
++match x {
++    &A(ref y) => foo(y),
++    &B => bar(),
++    _ => frob(&x),
++}
++```
++
++Use instead:
++```
++match *x {
++    A(ref y) => foo(y),
++    B => bar(),
++    _ => frob(x),
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eea7c8e00f1bb6b7bd0f3d7ccfe8bce8f0228914
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++### What it does
++Checks for unnecessary `ok()` in `while let`.
++
++### Why is this bad?
++Calling `ok()` in `while let` is unnecessary, instead match
++on `Ok(pat)`
++
++### Example
++```
++while let Some(value) = iter.next().ok() {
++    vec.push(value)
++}
++
++if let Some(value) = iter.next().ok() {
++    vec.push(value)
++}
++```
++Use instead:
++```
++while let Ok(value) = iter.next() {
++    vec.push(value)
++}
++
++if let Ok(value) = iter.next() {
++       vec.push(value)
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..14edf12032e0d31082449e2672ac8840f7c8fff8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,38 @@@
++### What it does
++Checks for `match` with identical arm bodies.
++
++### Why is this bad?
++This is probably a copy & paste error. If arm bodies
++are the same on purpose, you can factor them
++[using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
++
++### Known problems
++False positive possible with order dependent `match`
++(see issue
++[#860](https://github.com/rust-lang/rust-clippy/issues/860)).
++
++### Example
++```
++match foo {
++    Bar => bar(),
++    Quz => quz(),
++    Baz => bar(), // <= oops
++}
++```
++
++This should probably be
++```
++match foo {
++    Bar => bar(),
++    Quz => quz(),
++    Baz => baz(), // <= fixed
++}
++```
++
++or if the original code was not a typo:
++```
++match foo {
++    Bar | Baz => bar(), // <= shows the intent better
++    Quz => quz(),
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..67ded0bbd5534601fc2158b1eee91284bf00dd14
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Checks for useless match that binds to only one value.
++
++### Why is this bad?
++Readability and needless complexity.
++
++### Known problems
++ Suggested replacements may be incorrect when `match`
++is actually binding temporary value, bringing a 'dropped while borrowed' error.
++
++### Example
++```
++match (a, b) {
++    (c, d) => {
++        // useless match
++    }
++}
++```
++
++Use instead:
++```
++let (c, d) = (a, b);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..19e74c2084eae933025b46ed4bf441880b318b6f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for `match` expressions modifying the case of a string with non-compliant arms
++
++### Why is this bad?
++The arm is unreachable, which is likely a mistake
++
++### Example
++```
++match &*text.to_ascii_lowercase() {
++    "foo" => {},
++    "Bar" => {},
++    _ => {},
++}
++```
++Use instead:
++```
++match &*text.to_ascii_lowercase() {
++    "foo" => {},
++    "bar" => {},
++    _ => {},
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f89b3a23a1ca2052dc39371427aa3f7634f563f3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for arm which matches all errors with `Err(_)`
++and take drastic actions like `panic!`.
++
++### Why is this bad?
++It is generally a bad practice, similar to
++catching all exceptions in java with `catch(Exception)`
++
++### Example
++```
++let x: Result<i32, &str> = Ok(3);
++match x {
++    Ok(_) => println!("ok"),
++    Err(_) => panic!("err"),
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..25559b9ecdcff813cbc33e8c5fd28652d37bd327
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++### What it does
++Checks for wildcard enum matches for a single variant.
++
++### Why is this bad?
++New enum variants added by library updates can be missed.
++
++### Known problems
++Suggested replacements may not use correct path to enum
++if it's not present in the current scope.
++
++### Example
++```
++match x {
++    Foo::A => {},
++    Foo::B => {},
++    _ => {},
++}
++```
++
++Use instead:
++```
++match x {
++    Foo::A => {},
++    Foo::B => {},
++    Foo::C => {},
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1204a49b4668171c1df51234458547b3f439fe66
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for iteration that may be infinite.
++
++### Why is this bad?
++While there may be places where this is acceptable
++(e.g., in event streams), in most cases this is simply an error.
++
++### Known problems
++The code may have a condition to stop iteration, but
++this lint is not clever enough to analyze it.
++
++### Example
++```
++let infinite_iter = 0..;
++[0..].iter().zip(infinite_iter.take_while(|x| *x > 5));
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a6888c48fc33b1aa69c3aa63d812e530d1193b6e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++### What it does
++Checks for usage of `std::mem::forget(t)` where `t` is
++`Drop`.
++
++### Why is this bad?
++`std::mem::forget(t)` prevents `t` from running its
++destructor, possibly causing leaks.
++
++### Example
++```
++mem::forget(Rc::new(55))
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7f243d1c165658739add51f38587759f1287955e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checks for `mem::replace()` on an `Option` with
++`None`.
++
++### Why is this bad?
++`Option` already has the method `take()` for
++taking its current value (Some(..) or None) and replacing it with
++`None`.
++
++### Example
++```
++use std::mem;
++
++let mut an_option = Some(0);
++let replaced = mem::replace(&mut an_option, None);
++```
++Is better expressed with:
++```
++let mut an_option = Some(0);
++let taken = an_option.take();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..24e0913a30c920319bdfbc33b64c3beaf7f551f3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for `std::mem::replace` on a value of type
++`T` with `T::default()`.
++
++### Why is this bad?
++`std::mem` module already has the method `take` to
++take the current value and replace it with the default value of that type.
++
++### Example
++```
++let mut text = String::from("foo");
++let replaced = std::mem::replace(&mut text, String::default());
++```
++Is better expressed with:
++```
++let mut text = String::from("foo");
++let taken = std::mem::take(&mut text);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0bb483668abc10f2c4e2094a2758754b5675f5d9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++Checks for `mem::replace(&mut _, mem::uninitialized())`
++and `mem::replace(&mut _, mem::zeroed())`.
++
++### Why is this bad?
++This will lead to undefined behavior even if the
++value is overwritten later, because the uninitialized value may be
++observed in the case of a panic.
++
++### Example
++```
++use std::mem;
++
++#[allow(deprecated, invalid_value)]
++fn myfunc (v: &mut Vec<i32>) {
++    let taken_v = unsafe { mem::replace(v, mem::uninitialized()) };
++    let new_v = may_panic(taken_v); // undefined behavior on panic
++    mem::forget(mem::replace(v, new_v));
++}
++```
++
++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.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6acf0f932e986afb3e902030979226ae42f5f6db
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for expressions where `std::cmp::min` and `max` are
++used to clamp values, but switched so that the result is constant.
++
++### Why is this bad?
++This is in all probability not the intended outcome. At
++the least it hurts readability of the code.
++
++### Example
++```
++min(0, max(100, x))
++
++// or
++
++x.max(100).min(0)
++```
++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`.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..51e5ec6e7c5c4c7ad8ccc3016e6064bf07d4dcdf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++Checks for cfg attributes having operating systems used in target family position.
++
++### Why is this bad?
++The configuration option will not be recognised and the related item will not be included
++by the conditional compilation engine.
++
++### Example
++```
++#[cfg(linux)]
++fn conditional() { }
++```
++
++Use instead:
++```
++#[cfg(target_os = "linux")]
++fn conditional() { }
++
++// or
++
++#[cfg(unix)]
++fn conditional() { }
++```
++Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ffc7f32d0aad8ea2320dfba9b2058c8de82a6903
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++### What it does
++Checks for type parameters which are positioned inconsistently between
++a type definition and impl block. Specifically, a parameter in an impl
++block which has the same name as a parameter in the type def, but is in
++a different place.
++
++### Why is this bad?
++Type parameters are determined by their position rather than name.
++Naming type parameters inconsistently may cause you to refer to the
++wrong type parameter.
++
++### Limitations
++This lint only applies to impl blocks with simple generic params, e.g.
++`A`. If there is anything more complicated, such as a tuple, it will be
++ignored.
++
++### Example
++```
++struct Foo<A, B> {
++    x: A,
++    y: B,
++}
++// inside the impl, B refers to Foo::A
++impl<B, A> Foo<B, A> {}
++```
++Use instead:
++```
++struct Foo<A, B> {
++    x: A,
++    y: B,
++}
++impl<A, B> Foo<A, B> {}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3d691fe4178b50d187691ee896402e2a271a83fc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++### What it does
++Checks for `a op= a op b` or `a op= b op a` patterns.
++
++### Why is this bad?
++Most likely these are bugs where one meant to write `a
++op= b`.
++
++### Known problems
++Clippy cannot know for sure if `a op= a op b` should have
++been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore, it suggests both.
++If `a op= a op b` is really the correct behavior it should be
++written as `a = a op a op b` as it's less confusing.
++
++### Example
++```
++let mut a = 5;
++let b = 2;
++// ...
++a += a + b;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..067614d4c46bffe086a8fcb1055ed0566244c276
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++### What it does
++Suggests the use of `const` in functions and methods where possible.
++
++### Why is this bad?
++Not having the function const prevents callers of the function from being const as well.
++
++### Known problems
++Const functions are currently still being worked on, with some features only being available
++on nightly. This lint does not consider all edge cases currently and the suggestions may be
++incorrect if you are using this lint on stable.
++
++Also, the lint only runs one pass over the code. Consider these two non-const functions:
++
++```
++fn a() -> i32 {
++    0
++}
++fn b() -> i32 {
++    a()
++}
++```
++
++When running Clippy, the lint will only suggest to make `a` const, because `b` at this time
++can't be const as it calls a non-const function. Making `a` const and running Clippy again,
++will suggest to make `b` const, too.
++
++### Example
++```
++fn new() -> Self {
++    Self { random_number: 42 }
++}
++```
++
++Could be a const fn:
++
++```
++const fn new() -> Self {
++    Self { random_number: 42 }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5d37505bb1716288b6e16ac32c00bb008a85840c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++### What it does
++Warns if there is missing doc for any documentable item
++(public or private).
++
++### Why is this bad?
++Doc is good. *rustc* has a `MISSING_DOCS`
++allowed-by-default lint for
++public members, but has no way to enforce documentation of private items.
++This lint fixes that.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8f4649bd5923d40cb36b189fae53a67a5acb9f36
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for imports that do not rename the item as specified
++in the `enforce-import-renames` config option.
++
++### Why is this bad?
++Consistency is important, if a project has defined import
++renames they should be followed. More practically, some item names are too
++vague outside of their defining scope this can enforce a more meaningful naming.
++
++### Example
++An example clippy.toml configuration:
++```
++enforced-import-renames = [ { path = "serde_json::Value", rename = "JsonValue" }]
++```
++
++```
++use serde_json::Value;
++```
++Use instead:
++```
++use serde_json::Value as JsonValue;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..028778d85aeba6b8affa132c56b0db574d9cfaf0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checks the doc comments of publicly visible functions that
++return a `Result` type and warns if there is no `# Errors` section.
++
++### Why is this bad?
++Documenting the type of errors that can be returned from a
++function can help callers write code to handle the errors appropriately.
++
++### Examples
++Since the following function returns a `Result` it has an `# Errors` section in
++its doc comment:
++
++```
++/// # Errors
++///
++/// Will return `Err` if `filename` does not exist or the user does not have
++/// permission to read it.
++pub fn read(filename: String) -> io::Result<String> {
++    unimplemented!();
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d90c50fe7f9e822898d034c60d377d2e04da4c02
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,45 @@@
++### What it does
++It lints if an exported function, method, trait method with default impl,
++or trait method impl is not `#[inline]`.
++
++### Why is this bad?
++In general, it is not. Functions can be inlined across
++crates when that's profitable as long as any form of LTO is used. When LTO is disabled,
++functions that are not `#[inline]` cannot be inlined across crates. Certain types of crates
++might intend for most of the methods in their public API to be able to be inlined across
++crates even when LTO is disabled. For these types of crates, enabling this lint might make
++sense. It allows the crate to require all exported methods to be `#[inline]` by default, and
++then opt out for specific methods where this might not make sense.
++
++### Example
++```
++pub fn foo() {} // missing #[inline]
++fn ok() {} // ok
++#[inline] pub fn bar() {} // ok
++#[inline(always)] pub fn baz() {} // ok
++
++pub trait Bar {
++  fn bar(); // ok
++  fn def_bar() {} // missing #[inline]
++}
++
++struct Baz;
++impl Baz {
++   fn private() {} // ok
++}
++
++impl Bar for Baz {
++  fn bar() {} // ok - Baz is not exported
++}
++
++pub struct PubBaz;
++impl PubBaz {
++   fn private() {} // ok
++   pub fn not_private() {} // missing #[inline]
++}
++
++impl Bar for PubBaz {
++   fn bar() {} // missing #[inline]
++   fn def_bar() {} // missing #[inline]
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e5e39a824519b9f16755fe07560ae583db9fcd14
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++Checks the doc comments of publicly visible functions that
++may panic and warns if there is no `# Panics` section.
++
++### Why is this bad?
++Documenting the scenarios in which panicking occurs
++can help callers who do not want to panic to avoid those situations.
++
++### Examples
++Since the following function may panic it has a `# Panics` section in
++its doc comment:
++
++```
++/// # Panics
++///
++/// Will panic if y is 0
++pub fn divide_by(x: i32, y: i32) -> i32 {
++    if y == 0 {
++        panic!("Cannot divide by 0")
++    } else {
++        x / y
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6492eb84f63bf7328cd13496e0fcd5c98fca86e2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Checks for the doc comments of publicly visible
++unsafe functions and warns if there is no `# Safety` section.
++
++### Why is this bad?
++Unsafe functions should document their safety
++preconditions, so that users can be sure they are using them safely.
++
++### Examples
++```
++/// This function should really be documented
++pub unsafe fn start_apocalypse(u: &mut Universe) {
++    unimplemented!();
++}
++```
++
++At least write a line about safety:
++
++```
++/// # Safety
++///
++/// This function should not be called before the horsemen are ready.
++pub unsafe fn start_apocalypse(u: &mut Universe) {
++    unimplemented!();
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3a06a91d718355bc7bfe2c3084a87dedc51df29b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++### What it does
++Check for empty spin loops
++
++### Why is this bad?
++The loop body should have something like `thread::park()` or at least
++`std::hint::spin_loop()` to avoid needlessly burning cycles and conserve
++energy. Perhaps even better use an actual lock, if possible.
++
++### Known problems
++This lint doesn't currently trigger on `while let` or
++`loop { match .. { .. } }` loops, which would be considered idiomatic in
++combination with e.g. `AtomicBool::compare_exchange_weak`.
++
++### Example
++
++```
++use core::sync::atomic::{AtomicBool, Ordering};
++let b = AtomicBool::new(true);
++// give a ref to `b` to another thread,wait for it to become false
++while b.load(Ordering::Acquire) {};
++```
++Use instead:
++```
++while b.load(Ordering::Acquire) {
++    std::hint::spin_loop()
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1760fcbfeacc420ad2215ff7139aa8016439828a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Warns for mistyped suffix in literals
++
++### Why is this bad?
++This is most probably a typo
++
++### Known problems
++- Does not match on integers too large to fit in the corresponding unsigned type
++- Does not match on `_127` since that is a valid grouping for decimal and octal numbers
++
++### Example
++```
++`2_32` => `2_i32`
++`250_8 => `250_u8`
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d2d01e0c98eb49f8983ad0f5c0b908c69fc3b077
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Warns on hexadecimal literals with mixed-case letter
++digits.
++
++### Why is this bad?
++It looks confusing.
++
++### Example
++```
++0x1a9BAcD
++```
++
++Use instead:
++```
++0x1A9BACD
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..02d1c5d0525247843ca35f9ac5b6480eca74036e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++### What it does
++Checks for a read and a write to the same variable where
++whether the read occurs before or after the write depends on the evaluation
++order of sub-expressions.
++
++### Why is this bad?
++It is often confusing to read. As described [here](https://doc.rust-lang.org/reference/expressions.html?highlight=subexpression#evaluation-order-of-operands),
++the operands of these expressions are evaluated before applying the effects of the expression.
++
++### Known problems
++Code which intentionally depends on the evaluation
++order, or which is correct for any evaluation order.
++
++### Example
++```
++let mut x = 0;
++
++let a = {
++    x = 1;
++    1
++} + x;
++// Unclear whether a is 1 or 2.
++```
++
++Use instead:
++```
++let tmp = {
++    x = 1;
++    1
++};
++let a = tmp + x;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..95bca583afd3cedc315c9dd931acc81937ed7585
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks that module layout uses only self named module files, bans `mod.rs` files.
++
++### Why is this bad?
++Having multiple module layout styles in a project can be confusing.
++
++### Example
++```
++src/
++  stuff/
++    stuff_files.rs
++    mod.rs
++  lib.rs
++```
++Use instead:
++```
++src/
++  stuff/
++    stuff_files.rs
++  stuff.rs
++  lib.rs
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d80a1b8d8fe577e711bc488c8907d731845964e4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++Checks for modules that have the same name as their
++parent module
++
++### Why is this bad?
++A typical beginner mistake is to have `mod foo;` and
++again `mod foo { ..
++}` in `foo.rs`.
++The expectation is that items inside the inner `mod foo { .. }` are then
++available
++through `foo::x`, but they are only available through
++`foo::foo::x`.
++If this is done on purpose, it would be better to choose a more
++representative module name.
++
++### Example
++```
++// lib.rs
++mod foo;
++// foo.rs
++mod foo {
++    ...
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3bc05d02780cac386936f874084a313e4b965fcc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++### What it does
++Detects type names that are prefixed or suffixed by the
++containing module's name.
++
++### Why is this bad?
++It requires the user to type the module name twice.
++
++### Example
++```
++mod cake {
++    struct BlackForestCake;
++}
++```
++
++Use instead:
++```
++mod cake {
++    struct BlackForest;
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ff7296f3c5b8d52e0716bf1d96898a474ff3dce7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks for modulo arithmetic.
++
++### Why is this bad?
++The results of modulo (%) operation might differ
++depending on the language, when negative numbers are involved.
++If you interop with different languages it might be beneficial
++to double check all places that use modulo arithmetic.
++
++For example, in Rust `17 % -3 = 2`, but in Python `17 % -3 = -1`.
++
++### Example
++```
++let x = -17 % 3;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bc8f95b0be693ab26dd7e62a1a2e0480ddb6d2bb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for getting the remainder of a division by one or minus
++one.
++
++### Why is this bad?
++The result for a divisor of one can only ever be zero; for
++minus one it can cause panic/overflow (if the left operand is the minimal value of
++the respective integer type) or results in zero. No one will write such code
++deliberately, unless trying to win an Underhanded Rust Contest. Even for that
++contest, it's probably a bad idea. Use something more underhanded.
++
++### Example
++```
++let a = x % 1;
++let a = x % -1;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ed1f1b420cbd3363ef19d48ba1f76c862c0a5d0e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for nested assignments.
++
++### Why is this bad?
++While this is in most cases already a type mismatch,
++the result of an assignment being `()` can throw off people coming from languages like python or C,
++where such assignments return a copy of the assigned value.
++
++### Example
++```
++a = b = 42;
++```
++Use instead:
++```
++b = 42;
++a = b;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cf2d2c6abee3eaf045c1644246999589f4890a7d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++Checks to see if multiple versions of a crate are being
++used.
++
++### Why is this bad?
++This bloats the size of targets, and can lead to
++confusing error messages when structs or traits are used interchangeably
++between different versions of a crate.
++
++### Known problems
++Because this can be caused purely by the dependencies
++themselves, it's not always possible to fix this issue.
++
++### Example
++```
++[dependencies]
++ctrlc = "=3.1.0"
++ansi_term = "=0.11.0"
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9d42286560cef8fa7bdc3f3c43c297a7f9c2e8fa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Checks for multiple inherent implementations of a struct
++
++### Why is this bad?
++Splitting the implementation of a type makes the code harder to navigate.
++
++### Example
++```
++struct X;
++impl X {
++    fn one() {}
++}
++impl X {
++    fn other() {}
++}
++```
++
++Could be written:
++
++```
++struct X;
++impl X {
++    fn one() {}
++    fn other() {}
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..70890346fe6b8172e299238db33f394d8092d83b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Checks for public functions that have no
++`#[must_use]` attribute, but return something not already marked
++must-use, have no mutable arg and mutate no statics.
++
++### Why is this bad?
++Not bad at all, this lint just shows places where
++you could add the attribute.
++
++### Known problems
++The lint only checks the arguments for mutable
++types without looking if they are actually changed. On the other hand,
++it also ignores a broad range of potentially interesting side effects,
++because we cannot decide whether the programmer intends the function to
++be called for the side effect or the result. Expect many false
++positives. At least we don't lint if the result type is unit or already
++`#[must_use]`.
++
++### Examples
++```
++// this could be annotated with `#[must_use]`.
++fn id<T>(t: T) -> T { t }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cabbb23f8651ef4f262de1adf3cad75843582aca
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++### What it does
++Checks for a `#[must_use]` attribute on
++unit-returning functions and methods.
++
++### Why is this bad?
++Unit values are useless. The attribute is likely
++a remnant of a refactoring that removed the return type.
++
++### Examples
++```
++#[must_use]
++fn useless() { }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cc1da12549a5d90e7934c1077aaf16b945c07418
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++This lint checks for functions that take immutable references and return
++mutable ones. This will not trigger if no unsafe code exists as there
++are multiple safe functions which will do this transformation
++
++To be on the conservative side, if there's at least one mutable
++reference with the output lifetime, this lint will not trigger.
++
++### Why is this bad?
++Creating a mutable reference which can be repeatably derived from an
++immutable reference is unsound as it allows creating multiple live
++mutable references to the same object.
++
++This [error](https://github.com/rust-lang/rust/issues/39465) actually
++lead to an interim Rust release 1.15.1.
++
++### Known problems
++This pattern is used by memory allocators to allow allocating multiple
++objects while returning mutable references to each one. So long as
++different mutable references are returned each time such a function may
++be safe.
++
++### Example
++```
++fn foo(&Foo) -> &mut Bar { .. }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0bd34dd24b26a0c189d3d9ad3bf5453e048ae1d2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++### What it does
++Checks for instances of `mut mut` references.
++
++### Why is this bad?
++Multiple `mut`s don't add anything meaningful to the
++source. This is either a copy'n'paste error, or it shows a fundamental
++misunderstanding of references.
++
++### Example
++```
++let x = &mut &mut y;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5e9ad8a3f176a544d204f2ebd3b411910abe39f4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++### What it does
++Checks for `&mut Mutex::lock` calls
++
++### Why is this bad?
++`Mutex::lock` is less efficient than
++calling `Mutex::get_mut`. In addition you also have a statically
++guarantee that the mutex isn't locked, instead of just a runtime
++guarantee.
++
++### Example
++```
++use std::sync::{Arc, Mutex};
++
++let mut value_rc = Arc::new(Mutex::new(42_u8));
++let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
++
++let mut value = value_mutex.lock().unwrap();
++*value += 1;
++```
++Use instead:
++```
++use std::sync::{Arc, Mutex};
++
++let mut value_rc = Arc::new(Mutex::new(42_u8));
++let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
++
++let value = value_mutex.get_mut().unwrap();
++*value += 1;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e9c38a543b146169a0e3fc63fcd7ade333607a0e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++### What it does
++Checks for loops which have a range bound that is a mutable variable
++
++### Why is this bad?
++One might think that modifying the mutable variable changes the loop bounds
++
++### Known problems
++False positive when mutation is followed by a `break`, but the `break` is not immediately
++after the mutation:
++
++```
++let mut x = 5;
++for _ in 0..x {
++    x += 1; // x is a range bound that is mutated
++    ..; // some other expression
++    break; // leaves the loop, so mutation is not an issue
++}
++```
++
++False positive on nested loops ([#6072](https://github.com/rust-lang/rust-clippy/issues/6072))
++
++### Example
++```
++let mut foo = 42;
++for i in 0..foo {
++    foo -= 1;
++    println!("{}", i); // prints numbers from 0 to 42, not 0 to 21
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..15fe34f2bb5ec5ba6602802b5f16ce13ace34b17
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,61 @@@
++### What it does
++Checks for sets/maps with mutable key types.
++
++### Why is this bad?
++All of `HashMap`, `HashSet`, `BTreeMap` and
++`BtreeSet` rely on either the hash or the order of keys be unchanging,
++so having types with interior mutability is a bad idea.
++
++### Known problems
++
++#### False Positives
++It's correct to use a struct that contains interior mutability as a key, when its
++implementation of `Hash` or `Ord` doesn't access any of the interior mutable types.
++However, this lint is unable to recognize this, so it will often cause false positives in
++theses cases.  The `bytes` crate is a great example of this.
++
++#### False Negatives
++For custom `struct`s/`enum`s, this lint is unable to check for interior mutability behind
++indirection.  For example, `struct BadKey<'a>(&'a Cell<usize>)` will be seen as immutable
++and cause a false negative if its implementation of `Hash`/`Ord` accesses the `Cell`.
++
++This lint does check a few cases for indirection.  Firstly, using some standard library
++types (`Option`, `Result`, `Box`, `Rc`, `Arc`, `Vec`, `VecDeque`, `BTreeMap` and
++`BTreeSet`) directly as keys (e.g. in `HashMap<Box<Cell<usize>>, ()>`) **will** trigger the
++lint, because the impls of `Hash`/`Ord` for these types directly call `Hash`/`Ord` on their
++contained type.
++
++Secondly, the implementations of `Hash` and `Ord` for raw pointers (`*const T` or `*mut T`)
++apply only to the **address** of the contained value.  Therefore, interior mutability
++behind raw pointers (e.g. in `HashSet<*mut Cell<usize>>`) can't impact the value of `Hash`
++or `Ord`, and therefore will not trigger this link.  For more info, see issue
++[#6745](https://github.com/rust-lang/rust-clippy/issues/6745).
++
++### Example
++```
++use std::cmp::{PartialEq, Eq};
++use std::collections::HashSet;
++use std::hash::{Hash, Hasher};
++use std::sync::atomic::AtomicUsize;
++
++struct Bad(AtomicUsize);
++impl PartialEq for Bad {
++    fn eq(&self, rhs: &Self) -> bool {
++         ..
++; unimplemented!();
++    }
++}
++
++impl Eq for Bad {}
++
++impl Hash for Bad {
++    fn hash<H: Hasher>(&self, h: &mut H) {
++        ..
++; unimplemented!();
++    }
++}
++
++fn main() {
++    let _: HashSet<Bad> = HashSet::new();
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..062ac8b323b742fa430cfc3ce1c6f35a35acc292
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for usages of `Mutex<X>` where an atomic will do.
++
++### Why is this bad?
++Using a mutex just to make access to a plain bool or
++reference sequential is shooting flies with cannons.
++`std::sync::atomic::AtomicBool` and `std::sync::atomic::AtomicPtr` are leaner and
++faster.
++
++### Known problems
++This lint cannot detect if the mutex is actually used
++for waiting before a critical section.
++
++### Example
++```
++let x = Mutex::new(&y);
++```
++
++Use instead:
++```
++let x = AtomicBool::new(y);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f9dbdfb904c9a91a681451e936b3ec22a0321e58
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for usages of `Mutex<X>` where `X` is an integral
++type.
++
++### Why is this bad?
++Using a mutex just to make access to a plain integer
++sequential is
++shooting flies with cannons. `std::sync::atomic::AtomicUsize` is leaner and faster.
++
++### Known problems
++This lint cannot detect if the mutex is actually used
++for waiting before a critical section.
++
++### Example
++```
++let x = Mutex::new(0usize);
++```
++
++Use instead:
++```
++let x = AtomicUsize::new(0usize);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..24659dc79ae71882a097e6b5be3131b12ef30eaf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for naive byte counts
++
++### Why is this bad?
++The [`bytecount`](https://crates.io/crates/bytecount)
++crate has methods to count your bytes faster, especially for large slices.
++
++### Known problems
++If you have predominantly small slices, the
++`bytecount::count(..)` method may actually be slower. However, if you can
++ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be
++faster in those cases.
++
++### Example
++```
++let count = vec.iter().filter(|x| **x == 0u8).count();
++```
++
++Use instead:
++```
++let count = bytecount::count(&vec, 0u8);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8216a3a3fb6e58ebd703699948c3c28ea504e158
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,44 @@@
++### What it does
++The lint checks for `self` in fn parameters that
++specify the `Self`-type explicitly
++### Why is this bad?
++Increases the amount and decreases the readability of code
++
++### Example
++```
++enum ValType {
++    I32,
++    I64,
++    F32,
++    F64,
++}
++
++impl ValType {
++    pub fn bytes(self: Self) -> usize {
++        match self {
++            Self::I32 | Self::F32 => 4,
++            Self::I64 | Self::F64 => 8,
++        }
++    }
++}
++```
++
++Could be rewritten as
++
++```
++enum ValType {
++    I32,
++    I64,
++    F32,
++    F64,
++}
++
++impl ValType {
++    pub fn bytes(self) -> usize {
++        match self {
++            Self::I32 | Self::F32 => 4,
++            Self::I64 | Self::F64 => 8,
++        }
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fcd7b730aaae003ea7f6c88262dfdadc365bd634
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for uses of bitwise and/or operators between booleans, where performance may be improved by using
++a lazy and.
++
++### Why is this bad?
++The bitwise operators do not support short-circuiting, so it may hinder code performance.
++Additionally, boolean logic "masked" as bitwise logic is not caught by lints like `unnecessary_fold`
++
++### Known problems
++This lint evaluates only when the right side is determined to have no side effects. At this time, that
++determination is quite conservative.
++
++### Example
++```
++let (x,y) = (true, false);
++if x & !y {} // where both x and y are booleans
++```
++Use instead:
++```
++let (x,y) = (true, false);
++if x && !y {}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b5c78871f14f66dbceaf53473cbdbb261d4f5f8e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Checks for expressions of the form `if c { true } else {
++false }` (or vice versa) and suggests using the condition directly.
++
++### Why is this bad?
++Redundant code.
++
++### Known problems
++Maybe false positives: Sometimes, the two branches are
++painstakingly documented (which we, of course, do not detect), so they *may*
++have some value. Even then, the documentation can be rewritten to match the
++shorter code.
++
++### Example
++```
++if x {
++    false
++} else {
++    true
++}
++```
++
++Use instead:
++```
++!x
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4debcf473723a21f64f895cecb9c3111ea79772f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### 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
++```
++fn fun(_a: &i32) {}
++
++let x: &i32 = &&&&&&5;
++fun(&x);
++```
++
++Use instead:
++```
++let x: &i32 = &5;
++fun(x);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..55faa0cf5719569abb22f17a906fa9436f1428a1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++### What it does
++Checks for bindings that destructure a reference and borrow the inner
++value with `&ref`.
++
++### Why is this bad?
++This pattern has no effect in almost all cases.
++
++### Known problems
++In some cases, `&ref` is needed to avoid a lifetime mismatch error.
++Example:
++```
++fn foo(a: &Option<String>, b: &Option<String>) {
++    match (a, b) {
++        (None, &ref c) | (&ref c, None) => (),
++        (&Some(ref c), _) => (),
++    };
++}
++```
++
++### Example
++```
++let mut v = Vec::<String>::new();
++v.iter_mut().filter(|&ref a| a.is_empty());
++```
++
++Use instead:
++```
++let mut v = Vec::<String>::new();
++v.iter_mut().filter(|a| a.is_empty());
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..275c39afc9df1064a40fe5d2b7eae33bba0a3992
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++### What it does
++Checks for functions collecting an iterator when collect
++is not needed.
++
++### Why is this bad?
++`collect` causes the allocation of a new data structure,
++when this allocation may not be needed.
++
++### Example
++```
++let len = iterator.clone().collect::<Vec<_>>().len();
++// should be
++let len = iterator.count();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2cee621c1af035f4706c4ef4b9cbf979c7955587
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,61 @@@
++### What it does
++The lint checks for `if`-statements appearing in loops
++that contain a `continue` statement in either their main blocks or their
++`else`-blocks, when omitting the `else`-block possibly with some
++rearrangement of code can make the code easier to understand.
++
++### Why is this bad?
++Having explicit `else` blocks for `if` statements
++containing `continue` in their THEN branch adds unnecessary branching and
++nesting to the code. Having an else block containing just `continue` can
++also be better written by grouping the statements following the whole `if`
++statement within the THEN block and omitting the else block completely.
++
++### Example
++```
++while condition() {
++    update_condition();
++    if x {
++        // ...
++    } else {
++        continue;
++    }
++    println!("Hello, world");
++}
++```
++
++Could be rewritten as
++
++```
++while condition() {
++    update_condition();
++    if x {
++        // ...
++        println!("Hello, world");
++    }
++}
++```
++
++As another example, the following code
++
++```
++loop {
++    if waiting() {
++        continue;
++    } else {
++        // Do something useful
++    }
++    # break;
++}
++```
++Could be rewritten as
++
++```
++loop {
++    if waiting() {
++        continue;
++    }
++    // Do something useful
++    # break;
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8f91a7baa71510617ff72d15f710716b612a46ee
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for `fn main() { .. }` in doctests
++
++### Why is this bad?
++The test can be shorter (and likely more readable)
++if the `fn main()` is left implicit.
++
++### Examples
++```
++/// An example of a doctest with a `main()` function
++///
++/// # Examples
++///
++/// ```
++/// fn main() {
++///     // this needs not be in an `fn`
++/// }
++/// ```
++fn needless_main() {
++    unimplemented!();
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9ae6dd360c8f74ae53fa21249a1b5b178c973f12
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++Checks for usage of `for_each` that would be more simply written as a
++`for` loop.
++
++### Why is this bad?
++`for_each` may be used after applying iterator transformers like
++`filter` for better readability and performance. It may also be used to fit a simple
++operation on one line.
++But when none of these apply, a simple `for` loop is more idiomatic.
++
++### Example
++```
++let v = vec![0, 1, 2];
++v.iter().for_each(|elem| {
++    println!("{}", elem);
++})
++```
++Use instead:
++```
++let v = vec![0, 1, 2];
++for elem in v.iter() {
++    println!("{}", elem);
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9e7bbcea99825cca935a530cf0ec42a8064ab0e8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++### 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
++```
++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:
++```
++let a = 1;
++
++let b = match 3 {
++    0 => "zero",
++    1 => "one",
++    _ => "many",
++};
++
++let c = if true {
++    1
++} else {
++    -1
++};
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b280caa66b5833d7dc4c9509cfb03e5f0944243f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++### What it does
++Checks for lifetime annotations which can be removed by
++relying on lifetime elision.
++
++### Why is this bad?
++The additional lifetimes make the code look more
++complicated, while there is nothing out of the ordinary going on. Removing
++them leads to more readable code.
++
++### Known problems
++- We bail out if the function has a `where` clause where lifetimes
++are mentioned due to potential false positives.
++- Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the
++placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`.
++
++### Example
++```
++// Unnecessary lifetime annotations
++fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 {
++    x
++}
++```
++
++Use instead:
++```
++fn elided(x: &u8, y: u8) -> &u8 {
++    x
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..92b40a5df64889f7c89f284b954e2b160ea23ff3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++### What it does
++Checks for unnecessary `match` or match-like `if let` returns for `Option` and `Result`
++when function signatures are the same.
++
++### Why is this bad?
++This `match` block does nothing and might not be what the coder intended.
++
++### Example
++```
++fn foo() -> Result<(), i32> {
++    match result {
++        Ok(val) => Ok(val),
++        Err(err) => Err(err),
++    }
++}
++
++fn bar() -> Option<i32> {
++    if let Some(val) = option {
++        Some(val)
++    } else {
++        None
++    }
++}
++```
++
++Could be replaced as
++
++```
++fn foo() -> Result<(), i32> {
++    result
++}
++
++fn bar() -> Option<i32> {
++    option
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..226396c97ac49f8ce715dcee362d3ab644362143
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for no-op uses of `Option::{as_deref, as_deref_mut}`,
++for example, `Option<&T>::as_deref()` returns the same type.
++
++### Why is this bad?
++Redundant code and improving readability.
++
++### Example
++```
++let a = Some(&1);
++let b = a.as_deref(); // goes from Option<&i32> to Option<&i32>
++```
++
++Use instead:
++```
++let a = Some(&1);
++let b = a;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6bac65a13b5b883e20d79764769d6f38f0b521be
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for calling `take` function after `as_ref`.
++
++### Why is this bad?
++Redundant code. `take` writes `None` to its argument.
++In this case the modification is useless as it's a temporary that cannot be read from afterwards.
++
++### Example
++```
++let x = Some(3);
++x.as_ref().take();
++```
++Use instead:
++```
++let x = Some(3);
++x.as_ref();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..85fab10cb5f65ea1027802d820e6d52197c7831d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++The lint checks for parenthesis on literals in range statements that are
++superfluous.
++
++### Why is this bad?
++Having superfluous parenthesis makes the code less readable
++overhead when reading.
++
++### Example
++
++```
++for i in (0)..10 {
++  println!("{i}");
++}
++```
++
++Use instead:
++
++```
++for i in 0..10 {
++  println!("{i}");
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..58c420b19f628d373ac3b81781bbedf6895bd445
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++### What it does
++Checks for functions taking arguments by value, but not
++consuming them in its
++body.
++
++### Why is this bad?
++Taking arguments by reference is more flexible and can
++sometimes avoid
++unnecessary allocations.
++
++### Known problems
++* This lint suggests taking an argument by reference,
++however sometimes it is better to let users decide the argument type
++(by using `Borrow` trait, for example), depending on how the function is used.
++
++### Example
++```
++fn foo(v: Vec<i32>) {
++    assert_eq!(v.len(), 42);
++}
++```
++should be
++```
++fn foo(v: &[i32]) {
++    assert_eq!(v.len(), 42);
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..540739fd45fae05eafd471984a483ce5e991ee08
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,43 @@@
++### What it does
++Suggests alternatives for useless applications of `?` in terminating expressions
++
++### Why is this bad?
++There's no reason to use `?` to short-circuit when execution of the body will end there anyway.
++
++### Example
++```
++struct TO {
++    magic: Option<usize>,
++}
++
++fn f(to: TO) -> Option<usize> {
++    Some(to.magic?)
++}
++
++struct TR {
++    magic: Result<usize, bool>,
++}
++
++fn g(tr: Result<TR, bool>) -> Result<usize, bool> {
++    tr.and_then(|t| Ok(t.magic?))
++}
++
++```
++Use instead:
++```
++struct TO {
++    magic: Option<usize>,
++}
++
++fn f(to: TO) -> Option<usize> {
++   to.magic
++}
++
++struct TR {
++    magic: Result<usize, bool>,
++}
++
++fn g(tr: Result<TR, bool>) -> Result<usize, bool> {
++    tr.and_then(|t| t.magic)
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..583c09b284976e04960a0297612cd69e6e391669
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Checks for looping over the range of `0..len` of some
++collection just to get the values by index.
++
++### Why is this bad?
++Just iterating the collection itself makes the intent
++more clear and is probably faster.
++
++### Example
++```
++let vec = vec!['a', 'b', 'c'];
++for i in 0..vec.len() {
++    println!("{}", vec[i]);
++}
++```
++
++Use instead:
++```
++let vec = vec!['a', 'b', 'c'];
++for i in vec {
++    println!("{}", i);
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..48782cb0ca84dac7f4f65cfa0e71138d0ceaa531
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++Checks for return statements at the end of a block.
++
++### Why is this bad?
++Removing the `return` and semicolon will make the code
++more rusty.
++
++### Example
++```
++fn foo(x: usize) -> usize {
++    return x;
++}
++```
++simplify to
++```
++fn foo(x: usize) -> usize {
++    x
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b10a84fbc42d45c806a187a8ea5ba463abc75ef2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### 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
++```
++let str = "key=value=add";
++let _ = str.splitn(3, '=').next().unwrap();
++```
++
++Use instead:
++```
++let str = "key=value=add";
++let _ = str.split('=').next().unwrap();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..82adabf6482c3daf902ce0aa38b3f11c1b49d1c4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++### What it does
++Checks for needlessly including a base struct on update
++when all fields are changed anyway.
++
++This lint is not applied to structs marked with
++[non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html).
++
++### Why is this bad?
++This will cost resources (because the base has to be
++somewhere), and make the code less readable.
++
++### Example
++```
++Point {
++    x: 1,
++    y: 1,
++    z: 1,
++    ..zero_point
++};
++```
++
++Use instead:
++```
++// Missing field `z`
++Point {
++    x: 1,
++    y: 1,
++    ..zero_point
++};
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fa55c6cfd74b3990aea2bfee110ab50e8bd69212
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Checks for the usage of negated comparison operators on types which only implement
++`PartialOrd` (e.g., `f64`).
++
++### Why is this bad?
++These operators make it easy to forget that the underlying types actually allow not only three
++potential Orderings (Less, Equal, Greater) but also a fourth one (Uncomparable). This is
++especially easy to miss if the operator based comparison result is negated.
++
++### Example
++```
++let a = 1.0;
++let b = f64::NAN;
++
++let not_less_or_equal = !(a <= b);
++```
++
++Use instead:
++```
++use std::cmp::Ordering;
++
++let _not_less_or_equal = match a.partial_cmp(&b) {
++    None | Some(Ordering::Greater) => true,
++    _ => false,
++};
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4e8b096eb9cc74b90077cae1b66e7157e745f40a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for multiplication by -1 as a form of negation.
++
++### Why is this bad?
++It's more readable to just negate.
++
++### Known problems
++This only catches integers (for now).
++
++### Example
++```
++let a = x * -1;
++```
++
++Use instead:
++```
++let a = -x;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..01ee9efb318bee37f5bf1df26f271e4333757bb0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for negative feature names with prefix `no-` or `not-`
++
++### Why is this bad?
++Features are supposed to be additive, and negatively-named features violate it.
++
++### Example
++```
++[features]
++default = []
++no-abc = []
++not-def = []
++
++```
++Use instead:
++```
++[features]
++default = ["abc", "def"]
++abc = []
++def = []
++
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..737ccf415cb981cec2a5a132bcc17f46ebb69522
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks for loops that will always `break`, `return` or
++`continue` an outer loop.
++
++### Why is this bad?
++This loop never loops, all it does is obfuscating the
++code.
++
++### Example
++```
++loop {
++    ..;
++    break;
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..291bad24a643b13f1f4e09b58d35f999a7bcce7d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,47 @@@
++### What it does
++Checks for `new` not returning a type that contains `Self`.
++
++### Why is this bad?
++As a convention, `new` methods are used to make a new
++instance of a type.
++
++### Example
++In an impl block:
++```
++impl Foo {
++    fn new() -> NotAFoo {
++    }
++}
++```
++
++```
++struct Bar(Foo);
++impl Foo {
++    // Bad. The type name must contain `Self`
++    fn new() -> Bar {
++    }
++}
++```
++
++```
++impl Foo {
++    // Good. Return type contains `Self`
++    fn new() -> Result<Foo, FooError> {
++    }
++}
++```
++
++Or in a trait definition:
++```
++pub trait Trait {
++    // Bad. The type name must contain `Self`
++    fn new();
++}
++```
++
++```
++pub trait Trait {
++    // Good. Return type contains `Self`
++    fn new() -> Self;
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..662d39c8efdd2b6c311359de9e881ba1580cb9ce
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++### What it does
++Checks for public types with a `pub fn new() -> Self` method and no
++implementation of
++[`Default`](https://doc.rust-lang.org/std/default/trait.Default.html).
++
++### Why is this bad?
++The user might expect to be able to use
++[`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) as the
++type can be constructed without arguments.
++
++### Example
++```
++pub struct Foo(Bar);
++
++impl Foo {
++    pub fn new() -> Self {
++        Foo(Bar::new())
++    }
++}
++```
++
++To fix the lint, add a `Default` implementation that delegates to `new`:
++
++```
++pub struct Foo(Bar);
++
++impl Default for Foo {
++    fn default() -> Self {
++        Foo::new()
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d4cc08fa8a7d8fc7509cc77872957fdfd701d2e1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++### What it does
++Checks for statements which have no effect.
++
++### Why is this bad?
++Unlike dead code, these statements are actually
++executed. However, as they have no effect, all they do is make the code less
++readable.
++
++### Example
++```
++0;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..646d45287ef504630ec34cd59f6190ee8107466e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++### What it does
++Checks for `replace` statements which have no effect.
++
++### Why is this bad?
++It's either a mistake or confusing.
++
++### Example
++```
++"1234".replace("12", "12");
++"1234".replacen("12", "12", 1);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..972f60dd01e92ec61d71845199fa1e05dabcd75f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for binding to underscore prefixed variable without side-effects.
++
++### Why is this bad?
++Unlike dead code, these bindings are actually
++executed. However, as they have no effect and shouldn't be used further on, all they
++do is make the code less readable.
++
++### Known problems
++Further usage of this variable is not checked, which can lead to false positives if it is
++used later in the code.
++
++### Example
++```
++let _i_serve_no_purpose = 1;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..164902b4726e1011ba6890f64383ea3103be3121
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++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
++back. Even so, there still are editors and other programs out there that
++don't work well with Unicode. So if the code is meant to be used
++internationally, on multiple operating systems, or has other portability
++requirements, activating this lint could be useful.
++
++### Example
++```
++let x = String::from("€");
++```
++
++Use instead:
++```
++let x = String::from("\u{20ac}");
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4a468e94db1641f7a38aa37ee340e517c384268c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Checks for non-octal values used to set Unix file permissions.
++
++### Why is this bad?
++They will be converted into octal, creating potentially
++unintended file permissions.
++
++### Example
++```
++use std::fs::OpenOptions;
++use std::os::unix::fs::OpenOptionsExt;
++
++let mut options = OpenOptions::new();
++options.mode(644);
++```
++Use instead:
++```
++use std::fs::OpenOptions;
++use std::os::unix::fs::OpenOptionsExt;
++
++let mut options = OpenOptions::new();
++options.mode(0o644);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..11e6f6e162cf07e026ce7b570e2240de2ef2bc21
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++### What it does
++This lint warns about a `Send` implementation for a type that
++contains fields that are not safe to be sent across threads.
++It tries to detect fields that can cause a soundness issue
++when sent to another thread (e.g., `Rc`) while allowing `!Send` fields
++that are expected to exist in a `Send` type, such as raw pointers.
++
++### Why is this bad?
++Sending the struct to another thread effectively sends all of its fields,
++and the fields that do not implement `Send` can lead to soundness bugs
++such as data races when accessed in a thread
++that is different from the thread that created it.
++
++See:
++* [*The Rustonomicon* about *Send and Sync*](https://doc.rust-lang.org/nomicon/send-and-sync.html)
++* [The documentation of `Send`](https://doc.rust-lang.org/std/marker/trait.Send.html)
++
++### Known Problems
++This lint relies on heuristics to distinguish types that are actually
++unsafe to be sent across threads and `!Send` types that are expected to
++exist in  `Send` type. Its rule can filter out basic cases such as
++`Vec<*const T>`, but it's not perfect. Feel free to create an issue if
++you have a suggestion on how this heuristic can be improved.
++
++### Example
++```
++struct ExampleStruct<T> {
++    rc_is_not_send: Rc<String>,
++    unbounded_generic_field: T,
++}
++
++// This impl is unsound because it allows sending `!Send` types through `ExampleStruct`
++unsafe impl<T> Send for ExampleStruct<T> {}
++```
++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`).
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..488980ddf023934376fc1379c7695ebbdbe03a8a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Checks for boolean expressions that can be written more
++concisely.
++
++### Why is this bad?
++Readability of boolean expressions suffers from
++unnecessary duplication.
++
++### Known problems
++Ignores short circuiting behavior of `||` and
++`&&`. Ignores `|`, `&` and `^`.
++
++### Example
++```
++if a && true {}
++if !(a == b) {}
++```
++
++Use instead:
++```
++if a {}
++if a != b {}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7a95443b51a50088a4560099e7eef74625d321bc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++### What it does
++Checks for duplicate open options as well as combinations
++that make no sense.
++
++### Why is this bad?
++In the best case, the code will be harder to read than
++necessary. I don't know the worst case.
++
++### Example
++```
++use std::fs::OpenOptions;
++
++OpenOptions::new().read(true).truncate(true);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7e8d0d2d33bf1ceca2facfabca8cbdd127c2a8ea
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks that common macros are used with consistent bracing.
++
++### Why is this bad?
++This is mostly a consistency lint although using () or []
++doesn't give you a semicolon in item position, which can be unexpected.
++
++### Example
++```
++vec!{1, 2, 3};
++```
++Use instead:
++```
++vec![1, 2, 3];
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..31355fbb7b666c1117bb46eebb896c06df649d61
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++### What it does
++Checks for public functions that dereference raw pointer
++arguments but are not marked `unsafe`.
++
++### Why is this bad?
++The function should probably be marked `unsafe`, since
++for an arbitrary raw pointer, there is no way of telling for sure if it is
++valid.
++
++### Known problems
++* It does not check functions recursively so if the pointer is passed to a
++private non-`unsafe` function which does the dereferencing, the lint won't
++trigger.
++* It only checks for arguments whose type are raw pointers, not raw pointers
++got from an argument in some other way (`fn foo(bar: &[*const u8])` or
++`some_argument.get_raw_ptr()`).
++
++### Example
++```
++pub fn foo(x: *const u8) {
++    println!("{}", unsafe { *x });
++}
++```
++
++Use instead:
++```
++pub unsafe fn foo(x: *const u8) {
++    println!("{}", unsafe { *x });
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..638f63b0db5e4a6a59de5db19721beb6c113c78f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checks for usages of `.then_some(..).unwrap_or(..)`
++
++### Why is this bad?
++This can be written more clearly with `if .. else ..`
++
++### Limitations
++This lint currently only looks for usages of
++`.then_some(..).unwrap_or(..)`, but will be expanded
++to account for similar patterns.
++
++### Example
++```
++let x = true;
++x.then_some("a").unwrap_or("b");
++```
++Use instead:
++```
++let x = true;
++if x { "a" } else { "b" };
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eee820587158c0c37957874b744b8fcc47d0b98c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++### 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 unambiguous.
++
++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
++```
++let one = "\033[1m Bold? \033[0m";  // \033 intended as escape
++let two = "\033\0";                 // \033 intended as null-3-3
++```
++
++Use instead:
++```
++let one = "\x1b[1mWill this be bold?\x1b[0m";
++let two = "\x0033\x00";
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fd5205d49dc0e3becf694a8789849d7fdde002f0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++Checks for usage of `ok().expect(..)`.
++
++### Why is this bad?
++Because you usually call `expect()` on the `Result`
++directly to get a better error message.
++
++### Known problems
++The error type needs to implement `Debug`
++
++### Example
++```
++x.ok().expect("why did I do this again?");
++```
++
++Use instead:
++```
++x.expect("why did I do this again?");
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f19f47ff9eb50b7d6c6bae8054091794f287f9cb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++### What it does
++Checks for arguments that are only used in recursion with no side-effects.
++
++### Why is this bad?
++It could contain a useless calculation and can make function simpler.
++
++The arguments can be involved in calculations and assignments but as long as
++the calculations have no side-effects (function calls or mutating dereference)
++and the assigned variables are also only in recursion, it is useless.
++
++### Known problems
++Too many code paths in the linting code are currently untested and prone to produce false
++positives or are prone to have performance implications.
++
++In some cases, this would not catch all useless arguments.
++
++```
++fn foo(a: usize, b: usize) -> usize {
++    let f = |x| x + 1;
++
++    if a == 0 {
++        1
++    } else {
++        foo(a - 1, f(b))
++    }
++}
++```
++
++For example, the argument `b` is only used in recursion, but the lint would not catch it.
++
++List of some examples that can not be caught:
++- binary operation of non-primitive types
++- closure usage
++- some `break` relative operations
++- struct pattern binding
++
++Also, when you recurse the function name with path segments, it is not possible to detect.
++
++### Example
++```
++fn f(a: usize, b: usize) -> usize {
++    if a == 0 {
++        1
++    } else {
++        f(a - 1, b + 1)
++    }
++}
++```
++Use instead:
++```
++fn f(a: usize) -> usize {
++    if a == 0 {
++        1
++    } else {
++        f(a - 1)
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7a7ed1bc9badc1c215022b1a4afed49ee4967f49
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for arguments to `==` which have their address
++taken to satisfy a bound
++and suggests to dereference the other argument instead
++
++### Why is this bad?
++It is more idiomatic to dereference the other argument.
++
++### Example
++```
++&x == y
++```
++
++Use instead:
++```
++x == *y
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ad7411d3d4bd934b33f39e20f9fd71ead51fed14
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str).
++
++### Why is this bad?
++Readability, this can be written more concisely as
++`_.as_deref()`.
++
++### Example
++```
++opt.as_ref().map(String::as_str)
++```
++Can be written as
++```
++opt.as_deref()
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c952cba8e26151217cec474aef12d9c0f26e0d56
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++Checks for usage of `option_env!(...).unwrap()` and
++suggests usage of the `env!` macro.
++
++### Why is this bad?
++Unwrapping the result of `option_env!` will panic
++at run-time if the environment variable doesn't exist, whereas `env!`
++catches it at compile-time.
++
++### Example
++```
++let _ = option_env!("HOME").unwrap();
++```
++
++Is better expressed as:
++
++```
++let _ = env!("HOME");
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..25f7bde7b4d043766a0ad6762d37da02a34e3557
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks for indirect collection of populated `Option`
++
++### Why is this bad?
++`Option` is like a collection of 0-1 things, so `flatten`
++automatically does this without suspicious-looking `unwrap` calls.
++
++### Example
++```
++let _ = std::iter::empty::<Option<i32>>().filter(Option::is_some).map(Option::unwrap);
++```
++Use instead:
++```
++let _ = std::iter::empty::<Option<i32>>().flatten();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..43652db513b4c7dd8b69b0f11f4a4562d668f0bc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++### What it does
++Lints usage of `if let Some(v) = ... { y } else { x }` and
++`match .. { Some(v) => y, None/_ => x }` which are more
++idiomatically done with `Option::map_or` (if the else bit is a pure
++expression) or `Option::map_or_else` (if the else bit is an impure
++expression).
++
++### Why is this bad?
++Using the dedicated functions of the `Option` type is clearer and
++more concise than an `if let` expression.
++
++### Known problems
++This lint uses a deliberately conservative metric for checking
++if the inside of either body contains breaks or continues which will
++cause it to not suggest a fix if either block contains a loop with
++continues or breaks contained within the loop.
++
++### Example
++```
++let _ = if let Some(foo) = optional {
++    foo
++} else {
++    5
++};
++let _ = match optional {
++    Some(val) => val + 1,
++    None => 5
++};
++let _ = if let Some(foo) = optional {
++    foo
++} else {
++    let y = do_complicated_function();
++    y*y
++};
++```
++
++should be
++
++```
++let _ = optional.map_or(5, |foo| foo);
++let _ = optional.map_or(5, |val| val + 1);
++let _ = optional.map_or_else(||{
++    let y = do_complicated_function();
++    y*y
++}, |foo| foo);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c86c65215f01c2c255cf97153ac72ed454e9d3a9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++Checks for usage of `_.map_or(None, _)`.
++
++### Why is this bad?
++Readability, this can be written more concisely as
++`_.and_then(_)`.
++
++### Known problems
++The order of the arguments is not in execution order.
++
++### Example
++```
++opt.map_or(None, |a| Some(a + 1));
++```
++
++Use instead:
++```
++opt.and_then(|a| Some(a + 1));
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fc4b528f0923e843a31eaa75187860b9dd14c380
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++### What it does
++Checks for usage of `option.map(f)` where f is a function
++or closure that returns the unit type `()`.
++
++### Why is this bad?
++Readability, this can be written more clearly with
++an if let statement
++
++### Example
++```
++let x: Option<String> = do_stuff();
++x.map(log_err_msg);
++x.map(|msg| log_err_msg(format_msg(msg)));
++```
++
++The correct use would be:
++
++```
++let x: Option<String> = do_stuff();
++if let Some(msg) = x {
++    log_err_msg(msg);
++}
++
++if let Some(msg) = x {
++    log_err_msg(format_msg(msg));
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b4324bd83990a80d438be00bbef771a6c7a9a912
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++### What it does
++Checks for use of `Option<Option<_>>` in function signatures and type
++definitions
++
++### Why is this bad?
++`Option<_>` represents an optional value. `Option<Option<_>>`
++represents an optional optional value which is logically the same thing as an optional
++value but has an unneeded extra level of wrapping.
++
++If you have a case where `Some(Some(_))`, `Some(None)` and `None` are distinct cases,
++consider a custom `enum` instead, with clear names for each case.
++
++### Example
++```
++fn get_data() -> Option<Option<u32>> {
++    None
++}
++```
++
++Better:
++
++```
++pub enum Contents {
++    Data(Vec<u8>), // Was Some(Some(Vec<u8>))
++    NotYetFetched, // Was Some(None)
++    None,          // Was None
++}
++
++fn get_data() -> Contents {
++    Contents::None
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6ce77cc268c51fcf20a56449031f51f80282a7bc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++### What it does
++Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`,
++`.or_insert(foo(..))` etc., and suggests to use `.or_else(|| foo(..))`,
++`.unwrap_or_else(|| foo(..))`, `.unwrap_or_default()` or `.or_default()`
++etc. instead.
++
++### Why is this bad?
++The function will always be called and potentially
++allocate an object acting as the default.
++
++### Known problems
++If the function has side-effects, not calling it will
++change the semantic of the program, but you shouldn't rely on that anyway.
++
++### Example
++```
++foo.unwrap_or(String::new());
++```
++
++Use instead:
++```
++foo.unwrap_or_else(String::new);
++
++// or
++
++foo.unwrap_or_default();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..64ac53749e8ad92353deb102446b4334aecde272
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for `.or(…).unwrap()` calls to Options and Results.
++
++### Why is this bad?
++You should use `.unwrap_or(…)` instead for clarity.
++
++### Example
++```
++// Result
++let value = result.or::<Error>(Ok(fallback)).unwrap();
++
++// Option
++let value = option.or(Some(fallback)).unwrap();
++```
++Use instead:
++```
++// Result
++let value = result.unwrap_or(fallback);
++
++// Option
++let value = option.unwrap_or(fallback);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5802eea29966f3c930583e73760c0521e7a05ad8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for out of bounds array indexing with a constant
++index.
++
++### Why is this bad?
++This will always panic at runtime.
++
++### Example
++```
++let x = [1, 2, 3, 4];
++
++x[9];
++&x[2..9];
++```
++
++Use instead:
++```
++// Index within bounds
++
++x[0];
++x[3];
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a09cc18a0bcc568daf9db5ab42fbcf30890578de
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++### What it does
++Detects classic underflow/overflow checks.
++
++### Why is this bad?
++Most classic C underflow/overflow checks will fail in
++Rust. Users can use functions like `overflowing_*` and `wrapping_*` instead.
++
++### Example
++```
++a + b < a;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..65ca18392e756d60c0d68a947dd4aa0c47dd1bb1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++### What it does
++Checks for boolean expressions that contain terminals that
++can be eliminated.
++
++### Why is this bad?
++This is most likely a logic bug.
++
++### Known problems
++Ignores short circuiting behavior.
++
++### Example
++```
++// The `b` is unnecessary, the expression is equivalent to `if a`.
++if a && b || a { ... }
++```
++
++Use instead:
++```
++if a {}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f9bdc6e87ccf944a14773aa695e3660c3e629cb2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++### What it does
++Checks for usage of `panic!`.
++
++### Why is this bad?
++`panic!` will stop the execution of the executable
++
++### Example
++```
++panic!("even with a good reason");
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..51c2f8ae5a30e6eb219979faf68683d7bc552e4d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result.
++
++### Why is this bad?
++For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence panicking macros should be avoided.
++
++### Known problems
++Functions called from a function returning a `Result` may invoke a panicking macro. This is not checked.
++
++### Example
++```
++fn result_with_panic() -> Result<bool, String>
++{
++    panic!("error");
++}
++```
++Use instead:
++```
++fn result_without_panic() -> Result<bool, String> {
++    Err(String::from("error"))
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1fbc245c8ec386ba87fdb7075e49c511a8fd5b02
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for calls of `unwrap[_err]()` that will always fail.
++
++### Why is this bad?
++If panicking is desired, an explicit `panic!()` should be used.
++
++### Known problems
++This lint only checks `if` conditions not assignments.
++So something like `let x: Option<()> = None; x.unwrap();` will not be recognized.
++
++### Example
++```
++if option.is_none() {
++    do_something_with(option.unwrap())
++}
++```
++
++This code will always panic. The if condition should probably be inverted.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..78f55188bab816f7f0b7a2f76b1378db9226ab7f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for manual re-implementations of `PartialEq::ne`.
++
++### Why is this bad?
++`PartialEq::ne` is required to always return the
++negated result of `PartialEq::eq`, which is exactly what the default
++implementation does. Therefore, there should never be any need to
++re-implement it.
++
++### Example
++```
++struct Foo;
++
++impl PartialEq for Foo {
++   fn eq(&self, other: &Foo) -> bool { true }
++   fn ne(&self, other: &Foo) -> bool { !(self == other) }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5cc07bf88435c46bf9e73e9816e743cb75962047
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++
++Checks for binary comparisons to a literal `Option::None`.
++
++### Why is this bad?
++
++A programmer checking if some `foo` is `None` via a comparison `foo == None`
++is usually inspired from other programming languages (e.g. `foo is None`
++in Python).
++Checking if a value of type `Option<T>` is (not) equal to `None` in that
++way relies on `T: PartialEq` to do the comparison, which is unneeded.
++
++### Example
++```
++fn foo(f: Option<u32>) -> &'static str {
++    if f != None { "yay" } else { "nay" }
++}
++```
++Use instead:
++```
++fn foo(f: Option<u32>) -> &'static str {
++    if f.is_some() { "yay" } else { "nay" }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..34f8901da2396d5a981816ade1656918d1a5f9cb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++### What it does
++* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push)
++calls on `PathBuf` that can cause overwrites.
++
++### Why is this bad?
++Calling `push` with a root path at the start can overwrite the
++previous defined path.
++
++### Example
++```
++use std::path::PathBuf;
++
++let mut x = PathBuf::from("/foo");
++x.push("/bar");
++assert_eq!(x, PathBuf::from("/bar"));
++```
++Could be written:
++
++```
++use std::path::PathBuf;
++
++let mut x = PathBuf::from("/foo");
++x.push("bar");
++assert_eq!(x, PathBuf::from("/foo/bar"));
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..64da881d592a8401cf3c1ebd7a5b57860ee52d82
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,64 @@@
++### What it does
++Checks for patterns that aren't exact representations of the types
++they are applied to.
++
++To satisfy this lint, you will have to adjust either the expression that is matched
++against or the pattern itself, as well as the bindings that are introduced by the
++adjusted patterns. For matching you will have to either dereference the expression
++with the `*` operator, or amend the patterns to explicitly match against `&<pattern>`
++or `&mut <pattern>` depending on the reference mutability. For the bindings you need
++to use the inverse. You can leave them as plain bindings if you wish for the value
++to be copied, but you must use `ref mut <variable>` or `ref <variable>` to construct
++a reference into the matched structure.
++
++If you are looking for a way to learn about ownership semantics in more detail, it
++is recommended to look at IDE options available to you to highlight types, lifetimes
++and reference semantics in your code. The available tooling would expose these things
++in a general way even outside of the various pattern matching mechanics. Of course
++this lint can still be used to highlight areas of interest and ensure a good understanding
++of ownership semantics.
++
++### Why is this bad?
++It isn't bad in general. But in some contexts it can be desirable
++because it increases ownership hints in the code, and will guard against some changes
++in ownership.
++
++### Example
++This example shows the basic adjustments necessary to satisfy the lint. Note how
++the matched expression is explicitly dereferenced with `*` and the `inner` variable
++is bound to a shared borrow via `ref inner`.
++
++```
++// Bad
++let value = &Some(Box::new(23));
++match value {
++    Some(inner) => println!("{}", inner),
++    None => println!("none"),
++}
++
++// Good
++let value = &Some(Box::new(23));
++match *value {
++    Some(ref inner) => println!("{}", inner),
++    None => println!("none"),
++}
++```
++
++The following example demonstrates one of the advantages of the more verbose style.
++Note how the second version uses `ref mut a` to explicitly declare `a` a shared mutable
++borrow, while `b` is simply taken by value. This ensures that the loop body cannot
++accidentally modify the wrong part of the structure.
++
++```
++// Bad
++let mut values = vec![(2, 3), (3, 4)];
++for (a, b) in &mut values {
++    *a += *b;
++}
++
++// Good
++let mut values = vec![(2, 3), (3, 4)];
++for &mut (ref mut a, b) in &mut values {
++    *a += b;
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e391d2406677d5bd66cd6fec02ae92ed85a634e3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++This lint warns when a named parameter in a format string is used as a positional one.
++
++### Why is this bad?
++It may be confused for an assignment and obfuscates which parameter is being used.
++
++### Example
++```
++println!("{}", x = 10);
++```
++
++Use instead:
++```
++println!("{x}", x = 10);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5d92f4cae91e0bdf28b0295381976f9d33ca69fe
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++### What it does
++Checks for possible missing comma in an array. It lints if
++an array element is a binary operator expression and it lies on two lines.
++
++### Why is this bad?
++This could lead to unexpected results.
++
++### Example
++```
++let a = &[
++    -1, -2, -3 // <= no comma here
++    -4, -5, -6
++];
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fda0b831f335f845b67837b3e497634adfb5f95c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for operations where precedence may be unclear
++and suggests to add parentheses. Currently it catches the following:
++* mixed usage of arithmetic and bit shifting/combining operators without
++parentheses
++* a "negative" numeric literal (which is really a unary `-` followed by a
++numeric literal)
++  followed by a method call
++
++### Why is this bad?
++Not everyone knows the precedence of those operators by
++heart, so expressions like these may trip others trying to reason about the
++code.
++
++### Example
++* `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7
++* `-1i32.abs()` equals -1, while `(-1i32).abs()` equals 1
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..140d23d6faab4d09ab1adc214323e6cff1bc5eb1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++### What it does
++Checks for use of `println`, `print`, `eprintln` or `eprint` in an
++implementation of a formatting trait.
++
++### Why is this bad?
++Using a print macro is likely unintentional since formatting traits
++should write to the `Formatter`, not stdout/stderr.
++
++### Example
++```
++use std::fmt::{Display, Error, Formatter};
++
++struct S;
++impl Display for S {
++    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
++        println!("S");
++
++        Ok(())
++    }
++}
++```
++Use instead:
++```
++use std::fmt::{Display, Error, Formatter};
++
++struct S;
++impl Display for S {
++    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
++        writeln!(f, "S");
++
++        Ok(())
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..160073414f9af6a5529173affa132f27a6d97661
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++### What it does
++This lint warns about the use of literals as `print!`/`println!` args.
++
++### Why is this bad?
++Using literals as `println!` args is inefficient
++(c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
++(i.e., just put the literal in the format string)
++
++### Known problems
++Will also warn with macro calls as arguments that expand to literals
++-- e.g., `println!("{}", env!("FOO"))`.
++
++### Example
++```
++println!("{}", "foo");
++```
++use the literal without formatting:
++```
++println!("foo");
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fc14511cd6a6569f6076cd9dafce259cde18656e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checks for printing on *stderr*. The purpose of this lint
++is to catch debugging remnants.
++
++### Why is this bad?
++People often print on *stderr* while debugging an
++application and might forget to remove those prints afterward.
++
++### Known problems
++* 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
++```
++eprintln!("Hello world!");
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6c9a4b98e1e6685b28c944ca436a17ffadc83d84
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checks for printing on *stdout*. The purpose of this lint
++is to catch debugging remnants.
++
++### Why is this bad?
++People often print on *stdout* while debugging an
++application and might forget to remove those prints afterward.
++
++### Known problems
++* 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
++```
++println!("Hello world!");
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..640323e822dbf5c529d4cdd7e56fc7d5459a5e27
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++This lint warns when you use `print!()` with a format
++string that ends in a newline.
++
++### Why is this bad?
++You should use `println!()` instead, which appends the
++newline.
++
++### Example
++```
++print!("Hello {}!\n", name);
++```
++use println!() instead
++```
++println!("Hello {}!", name);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b980413022cab084167d94e2c8fe726d1971561e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++This lint warns when you use `println!("")` to
++print a newline.
++
++### Why is this bad?
++You should use `println!()`, which is simpler.
++
++### Example
++```
++println!("");
++```
++
++Use instead:
++```
++println!();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..796b0a65b717fadc75f9ad0d29cc3cb6a128c75b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++### What it does
++This lint checks for function arguments of type `&String`, `&Vec`,
++`&PathBuf`, and `Cow<_>`. It will also suggest you replace `.clone()` calls
++with the appropriate `.to_owned()`/`to_string()` calls.
++
++### Why is this bad?
++Requiring the argument to be of the specific size
++makes the function less useful for no benefit; slices in the form of `&[T]`
++or `&str` usually suffice and can be obtained from other types, too.
++
++### Known problems
++There may be `fn(&Vec)`-typed references pointing to your function.
++If you have them, you will get a compiler error after applying this lint's
++suggestions. You then have the choice to undo your changes or change the
++type of the reference.
++
++Note that if the function is part of your public interface, there may be
++other crates referencing it, of which you may not be aware. Carefully
++deprecate the function before applying the lint suggestions in this case.
++
++### Example
++```
++fn foo(&Vec<u32>) { .. }
++```
++
++Use instead:
++```
++fn foo(&[u32]) { .. }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8fb35c4aae8f1cf9b4170c5ab90979811e5f3c5c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for `as` casts between raw pointers without changing its mutability,
++namely `*const T` to `*const U` and `*mut T` to `*mut U`.
++
++### Why is this bad?
++Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because
++it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`.
++
++### Example
++```
++let ptr: *const u32 = &42_u32;
++let mut_ptr: *mut u32 = &mut 42_u32;
++let _ = ptr as *const i32;
++let _ = mut_ptr as *mut i32;
++```
++Use instead:
++```
++let ptr: *const u32 = &42_u32;
++let mut_ptr: *mut u32 = &mut 42_u32;
++let _ = ptr.cast::<i32>();
++let _ = mut_ptr.cast::<i32>();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..06b36ca55e691bce02217011d11dc57e4b3ba8b8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Use `std::ptr::eq` when applicable
++
++### Why is this bad?
++`ptr::eq` can be used to compare `&T` references
++(which coerce to `*const T` implicitly) by their address rather than
++comparing the values they point to.
++
++### Example
++```
++let a = &[1, 2, 3];
++let b = &[1, 2, 3];
++
++assert!(a as *const _ as usize == b as *const _ as usize);
++```
++Use instead:
++```
++let a = &[1, 2, 3];
++let b = &[1, 2, 3];
++
++assert!(std::ptr::eq(a, b));
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f204e769bf4b8ca3a228a8396e45d705cef71ade
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++### What it does
++Checks for usage of the `offset` pointer method with a `usize` casted to an
++`isize`.
++
++### Why is this bad?
++If we’re always increasing the pointer address, we can avoid the numeric
++cast by using the `add` method instead.
++
++### Example
++```
++let vec = vec![b'a', b'b', b'c'];
++let ptr = vec.as_ptr();
++let offset = 1_usize;
++
++unsafe {
++    ptr.offset(offset as isize);
++}
++```
++
++Could be written:
++
++```
++let vec = vec![b'a', b'b', b'c'];
++let ptr = vec.as_ptr();
++let offset = 1_usize;
++
++unsafe {
++    ptr.add(offset);
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..407cafa01903fef9334a91eb2df45ada4138c8da
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++### What it does
++
++Restricts the usage of `pub use ...`
++
++### Why is this bad?
++
++`pub use` is usually fine, but a project may wish to limit `pub use` instances to prevent
++unintentional exports or to encourage placing exported items directly in public modules
++
++### Example
++```
++pub mod outer {
++    mod inner {
++        pub struct Test {}
++    }
++    pub use inner::Test;
++}
++
++use outer::Test;
++```
++Use instead:
++```
++pub mod outer {
++    pub struct Test {}
++}
++
++use outer::Test;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4dc987be881321ef35c6a602aacc9bb101aac56c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for expressions that could be replaced by the question mark operator.
++
++### Why is this bad?
++Question mark usage is more idiomatic.
++
++### Example
++```
++if option.is_none() {
++    return None;
++}
++```
++
++Could be written:
++
++```
++option?;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fcb96dcc34edf2c695ef68751e8b146c2b2908bc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++### What it does
++Checks for inclusive ranges where 1 is subtracted from
++the upper bound, e.g., `x..=(y-1)`.
++
++### Why is this bad?
++The code is more readable with an exclusive range
++like `x..y`.
++
++### Known problems
++This will cause a warning that cannot be fixed if
++the consumer of the range only accepts a specific range type, instead of
++the generic `RangeBounds` trait
++([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).
++
++### Example
++```
++for i in x..=(y-1) {
++    // ..
++}
++```
++
++Use instead:
++```
++for i in x..y {
++    // ..
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..193c85f9cbc72ad844634760867283c0ef93724a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++### What it does
++Checks for exclusive ranges where 1 is added to the
++upper bound, e.g., `x..(y+1)`.
++
++### Why is this bad?
++The code is more readable with an inclusive range
++like `x..=y`.
++
++### Known problems
++Will add unnecessary pair of parentheses when the
++expression is not wrapped in a pair but starts with an opening parenthesis
++and ends with a closing one.
++I.e., `let _ = (f()+1)..(f()+1)` results in `let _ = ((f()+1)..=f())`.
++
++Also in many cases, inclusive ranges are still slower to run than
++exclusive ranges, because they essentially add an extra branch that
++LLVM may fail to hoist out of the loop.
++
++This will cause a warning that cannot be fixed if the consumer of the
++range only accepts a specific range type, instead of the generic
++`RangeBounds` trait
++([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).
++
++### Example
++```
++for i in x..(y+1) {
++    // ..
++}
++```
++
++Use instead:
++```
++for i in x..=y {
++    // ..
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..24c1efec789a98c093a44a42b789b744d2a94b9e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for zipping a collection with the range of
++`0.._.len()`.
++
++### Why is this bad?
++The code is better expressed with `.enumerate()`.
++
++### Example
++```
++let _ = x.iter().zip(0..x.len());
++```
++
++Use instead:
++```
++let _ = x.iter().enumerate();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..82ac58eeb3081e574d65e971489fb07984ee46d3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++### What it does
++Checks for `Rc<T>` and `Arc<T>` when `T` is a mutable buffer type such as `String` or `Vec`.
++
++### Why is this bad?
++Expressions such as `Rc<String>` usually have no advantage over `Rc<str>`, since
++it is larger and involves an extra level of indirection, and doesn't implement `Borrow<str>`.
++
++While mutating a buffer type would still be possible with `Rc::get_mut()`, it only
++works if there are no additional references yet, which usually defeats the purpose of
++enclosing it in a shared ownership type. Instead, additionally wrapping the inner
++type with an interior mutable container (such as `RefCell` or `Mutex`) would normally
++be used.
++
++### Known problems
++This pattern can be desirable to avoid the overhead of a `RefCell` or `Mutex` for
++cases where mutation only happens before there are any additional references.
++
++### Example
++```
++fn foo(interned: Rc<String>) { ... }
++```
++
++Better:
++
++```
++fn foo(interned: Rc<str>) { ... }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6fc08aaf9ab57141dd81585151c2e6eedea87271
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++### What it does
++Checks for reference-counted pointers (`Arc`, `Rc`, `rc::Weak`, and `sync::Weak`)
++in `vec![elem; len]`
++
++### Why is this bad?
++This will create `elem` once and clone it `len` times - doing so with `Arc`/`Rc`/`Weak`
++is a bit misleading, as it will create references to the same pointer, rather
++than different instances.
++
++### Example
++```
++let v = vec![std::sync::Arc::new("some data".to_string()); 100];
++// or
++let v = vec![std::rc::Rc::new("some data".to_string()); 100];
++```
++Use instead:
++```
++// Initialize each value separately:
++let mut data = Vec::with_capacity(100);
++for _ in 0..100 {
++    data.push(std::rc::Rc::new("some data".to_string()));
++}
++
++// Or if you want clones of the same reference,
++// Create the reference beforehand to clarify that
++// it should be cloned for each value
++let data = std::rc::Rc::new("some data".to_string());
++let v = vec![data; 100];
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ed7a1e344d086a496d7d9c975e0468ddd80769d1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Checks for `Rc<Mutex<T>>`.
++
++### Why is this bad?
++`Rc` is used in single thread and `Mutex` is used in multi thread.
++Consider using `Rc<RefCell<T>>` in single thread or `Arc<Mutex<T>>` in multi thread.
++
++### Known problems
++Sometimes combining generic types can lead to the requirement that a
++type use Rc in conjunction with Mutex. We must consider those cases false positives, but
++alas they are quite hard to rule out. Luckily they are also rare.
++
++### Example
++```
++use std::rc::Rc;
++use std::sync::Mutex;
++fn foo(interned: Rc<Mutex<i32>>) { ... }
++```
++
++Better:
++
++```
++use std::rc::Rc;
++use std::cell::RefCell
++fn foo(interned: Rc<RefCell<i32>>) { ... }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cef5604e01c08b08a2bd2c614bc3dd3e1c63264f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++### What it does
++This lint catches reads into a zero-length `Vec`.
++Especially in the case of a call to `with_capacity`, this lint warns that read
++gets the number of bytes from the `Vec`'s length, not its capacity.
++
++### Why is this bad?
++Reading zero bytes is almost certainly not the intended behavior.
++
++### Known problems
++In theory, a very unusual read implementation could assign some semantic meaning
++to zero-byte reads. But it seems exceptionally unlikely that code intending to do
++a zero-byte read would allocate a `Vec` for it.
++
++### Example
++```
++use std::io;
++fn foo<F: io::Read>(mut f: F) {
++    let mut data = Vec::with_capacity(100);
++    f.read(&mut data).unwrap();
++}
++```
++Use instead:
++```
++use std::io;
++fn foo<F: io::Read>(mut f: F) {
++    let mut data = Vec::with_capacity(100);
++    data.resize(100, 0);
++    f.read(&mut data).unwrap();
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..32fffd84cf43240dcecfab5e30cbdac3432641f8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++### What it does
++Checks for format trait implementations (e.g. `Display`) with a recursive call to itself
++which uses `self` as a parameter.
++This is typically done indirectly with the `write!` macro or with `to_string()`.
++
++### Why is this bad?
++This will lead to infinite recursion and a stack overflow.
++
++### Example
++
++```
++use std::fmt;
++
++struct Structure(i32);
++impl fmt::Display for Structure {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        write!(f, "{}", self.to_string())
++    }
++}
++
++```
++Use instead:
++```
++use std::fmt;
++
++struct Structure(i32);
++impl fmt::Display for Structure {
++    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++        write!(f, "{}", self.0)
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..86bf51e8dfec7e546ad57eee5154dfb4b6b510b5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for use of redundant allocations anywhere in the code.
++
++### Why is this bad?
++Expressions such as `Rc<&T>`, `Rc<Rc<T>>`, `Rc<Arc<T>>`, `Rc<Box<T>>`, `Arc<&T>`, `Arc<Rc<T>>`,
++`Arc<Arc<T>>`, `Arc<Box<T>>`, `Box<&T>`, `Box<Rc<T>>`, `Box<Arc<T>>`, `Box<Box<T>>`, add an unnecessary level of indirection.
++
++### Example
++```
++fn foo(bar: Rc<&usize>) {}
++```
++
++Better:
++
++```
++fn foo(bar: &usize) {}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b29aed0b5e7796280ce7ec70956799536a5d19ff
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Checks for a redundant `clone()` (and its relatives) which clones an owned
++value that is going to be dropped without further use.
++
++### Why is this bad?
++It is not always possible for the compiler to eliminate useless
++allocations and deallocations generated by redundant `clone()`s.
++
++### Known problems
++False-negatives: analysis performed by this lint is conservative and limited.
++
++### Example
++```
++{
++    let x = Foo::new();
++    call(x.clone());
++    call(x.clone()); // this can just pass `x`
++}
++
++["lorem", "ipsum"].join(" ").to_string();
++
++Path::new("/a/b").join("c").to_path_buf();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0faa9513f97f2dd5862d45f413d22c2d4a09cb6a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++### What it does
++Checks for closures which just call another function where
++the function can be called directly. `unsafe` functions or calls where types
++get adjusted are ignored.
++
++### Why is this bad?
++Needlessly creating a closure adds code for no benefit
++and gives the optimizer more work.
++
++### Known problems
++If creating the closure inside the closure has a side-
++effect then moving the closure creation out will change when that side-
++effect runs.
++See [#1439](https://github.com/rust-lang/rust-clippy/issues/1439) for more details.
++
++### Example
++```
++xs.map(|x| foo(x))
++```
++
++Use instead:
++```
++// where `foo(_)` is a plain function that takes the exact argument type of `x`.
++xs.map(foo)
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..913d1a705e294d361f84c5392d49cf256e9acb7d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Detects closures called in the same expression where they
++are defined.
++
++### Why is this bad?
++It is unnecessarily adding to the expression's
++complexity.
++
++### Example
++```
++let a = (|| 42)();
++```
++
++Use instead:
++```
++let a = 42;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..865510e14755a0c7b29c00711ea47dbd38534c86
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks for closures which only invoke a method on the closure
++argument and can be replaced by referencing the method directly.
++
++### Why is this bad?
++It's unnecessary to create the closure.
++
++### Example
++```
++Some('a').map(|s| s.to_uppercase());
++```
++may be rewritten as
++```
++Some('a').map(char::to_uppercase);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3f4e86917603c283e2d42730cace41f9f4bd1bac
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++### What it does
++Checks for `else` blocks that can be removed without changing semantics.
++
++### Why is this bad?
++The `else` block adds unnecessary indentation and verbosity.
++
++### Known problems
++Some may prefer to keep the `else` block for clarity.
++
++### Example
++```
++fn my_func(count: u32) {
++    if count == 0 {
++        print!("Nothing to do");
++        return;
++    } else {
++        print!("Moving on...");
++    }
++}
++```
++Use instead:
++```
++fn my_func(count: u32) {
++    if count == 0 {
++        print!("Nothing to do");
++        return;
++    }
++    print!("Moving on...");
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5bd6925ed47d0e047f56d989ab22344cd7061ece
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Checks for feature names with prefix `use-`, `with-` or suffix `-support`
++
++### Why is this bad?
++These prefixes and suffixes have no significant meaning.
++
++### Example
++```
++[features]
++default = ["use-abc", "with-def", "ghi-support"]
++use-abc = []  // redundant
++with-def = []   // redundant
++ghi-support = []   // redundant
++```
++
++Use instead:
++```
++[features]
++default = ["abc", "def", "ghi"]
++abc = []
++def = []
++ghi = []
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..35f20a466b3785dfcb7a5126e727d0de110f0d19
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for fields in struct literals where shorthands
++could be used.
++
++### Why is this bad?
++If the field and variable names are the same,
++the field name is redundant.
++
++### Example
++```
++let bar: u8 = 123;
++
++struct Foo {
++    bar: u8,
++}
++
++let foo = Foo { bar: bar };
++```
++the last line can be simplified to
++```
++let foo = Foo { bar };
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..45f6cfc86702c08afa235c8771990bd63d5727ee
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for patterns in the form `name @ _`.
++
++### Why is this bad?
++It's almost always more readable to just use direct
++bindings.
++
++### Example
++```
++match v {
++    Some(x) => (),
++    y @ _ => (),
++}
++```
++
++Use instead:
++```
++match v {
++    Some(x) => (),
++    y => (),
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..77b1021e0db3bfc35d4b3fae407b66d25a060550
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,45 @@@
++### What it does
++Lint for redundant pattern matching over `Result`, `Option`,
++`std::task::Poll` or `std::net::IpAddr`
++
++### Why is this bad?
++It's more concise and clear to just use the proper
++utility function
++
++### Known problems
++This will change the drop order for the matched type. Both `if let` and
++`while let` will drop the value at the end of the block, both `if` and `while` will drop the
++value before entering the block. For most types this change will not matter, but for a few
++types this will not be an acceptable change (e.g. locks). See the
++[reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about
++drop order.
++
++### Example
++```
++if let Ok(_) = Ok::<i32, i32>(42) {}
++if let Err(_) = Err::<i32, i32>(42) {}
++if let None = None::<()> {}
++if let Some(_) = Some(42) {}
++if let Poll::Pending = Poll::Pending::<()> {}
++if let Poll::Ready(_) = Poll::Ready(42) {}
++if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {}
++if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {}
++match Ok::<i32, i32>(42) {
++    Ok(_) => true,
++    Err(_) => false,
++};
++```
++
++The more idiomatic use would be:
++
++```
++if Ok::<i32, i32>(42).is_ok() {}
++if Err::<i32, i32>(42).is_err() {}
++if None::<()>.is_none() {}
++if Some(42).is_some() {}
++if Poll::Pending::<()>.is_pending() {}
++if Poll::Ready(42).is_ready() {}
++if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {}
++if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
++Ok::<i32, i32>(42).is_ok();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a527bb5acda1e23304eeb027347e2376c36ed3e4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checks for items declared `pub(crate)` that are not crate visible because they
++are inside a private module.
++
++### Why is this bad?
++Writing `pub(crate)` is misleading when it's redundant due to the parent
++module's visibility.
++
++### Example
++```
++mod internal {
++    pub(crate) fn internal_fn() { }
++}
++```
++This function is not visible outside the module and it can be declared with `pub` or
++private visibility
++```
++mod internal {
++    pub fn internal_fn() { }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6798911ed76c08b5da77afdbf12c8c7f809fb5fb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++Checks for redundant slicing expressions which use the full range, and
++do not change the type.
++
++### Why is this bad?
++It unnecessarily adds complexity to the expression.
++
++### Known problems
++If the type being sliced has an implementation of `Index<RangeFull>`
++that actually changes anything then it can't be removed. However, this would be surprising
++to people reading the code and should have a note with it.
++
++### Example
++```
++fn get_slice(x: &[u32]) -> &[u32] {
++    &x[..]
++}
++```
++Use instead:
++```
++fn get_slice(x: &[u32]) -> &[u32] {
++    x
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..edb8e7b5c62bfb8c02e00a84b9ba951281898b09
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++Checks for constants and statics with an explicit `'static` lifetime.
++
++### Why is this bad?
++Adding `'static` to every reference can create very
++complicated types.
++
++### Example
++```
++const FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =
++&[...]
++static FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =
++&[...]
++```
++This code can be rewritten as
++```
++ const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
++ static FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dc391cd988e587d66b20a9bd3def86dcafcc6b53
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### 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
++```
++let x = Some("");
++if let Some(ref x) = x {
++    // use `x` here
++}
++```
++
++Use instead:
++```
++let x = Some("");
++if let Some(x) = x {
++    // use `&x` here
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..951c7bd7f7ec6c61810e334d03c128cf74b1771a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++Checks for usage of `&Option<&T>`.
++
++### Why is this bad?
++Since `&` is Copy, it's useless to have a
++reference on `Option<&T>`.
++
++### Known problems
++It may be irrelevant to use this lint on
++public API code as it will make a breaking change to apply it.
++
++### Example
++```
++let x: &Option<&u32> = &Some(&0u32);
++```
++Use instead:
++```
++let x: Option<&u32> = Some(&0u32);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3ba189c1945d7c35e97c1f4cb9ccfdc0015cdca2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++### What it does
++Checks for usage of `.repeat(1)` and suggest the following method for each types.
++- `.to_string()` for `str`
++- `.clone()` for `String`
++- `.to_vec()` for `slice`
++
++The lint will evaluate constant expressions and values as arguments of `.repeat(..)` and emit a message if
++they are equivalent to `1`. (Related discussion in [rust-clippy#7306](https://github.com/rust-lang/rust-clippy/issues/7306))
++
++### Why is this bad?
++For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning
++the string is the intention behind this, `clone()` should be used.
++
++### Example
++```
++fn main() {
++    let x = String::from("hello world").repeat(1);
++}
++```
++Use instead:
++```
++fn main() {
++    let x = String::from("hello world").clone();
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..40ebbe754a3723bb819fb31376b067f6af3cf1d0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.
++
++### Why is this bad?
++Correctness and readability. It's like having a wildcard pattern after
++matching all enum variants explicitly.
++
++### Example
++```
++let a = A { a: 5 };
++
++match a {
++    A { a: 5, .. } => {},
++    _ => {},
++}
++```
++
++Use instead:
++```
++match a {
++    A { a: 5 } => {},
++    _ => {},
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e5fab3c5cfcfabfe813d427c01113ec5936b7a34
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++### What it does
++Checks for functions that return `Result` with an unusually large
++`Err`-variant.
++
++### Why is this bad?
++A `Result` is at least as large as the `Err`-variant. While we
++expect that variant to be seldomly used, the compiler needs to reserve
++and move that much memory every single time.
++
++### Known problems
++The size determined by Clippy is platform-dependent.
++
++### Examples
++```
++pub enum ParseError {
++    UnparsedBytes([u8; 512]),
++    UnexpectedEof,
++}
++
++// The `Result` has at least 512 bytes, even in the `Ok`-case
++pub fn parse() -> Result<(), ParseError> {
++    Ok(())
++}
++```
++should be
++```
++pub enum ParseError {
++    UnparsedBytes(Box<[u8; 512]>),
++    UnexpectedEof,
++}
++
++// The `Result` is slightly larger than a pointer
++pub fn parse() -> Result<(), ParseError> {
++    Ok(())
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..899d98c307c8f463605fba0b1110a65d86a6413f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for usage of `_.map_or(None, Some)`.
++
++### Why is this bad?
++Readability, this can be written more concisely as
++`_.ok()`.
++
++### Example
++```
++assert_eq!(Some(1), r.map_or(None, Some));
++```
++
++Use instead:
++```
++assert_eq!(Some(1), r.ok());
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3455c5c1f9b8a0c4eea5f13fdabee13fde2348c7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Checks for usage of `result.map(f)` where f is a function
++or closure that returns the unit type `()`.
++
++### Why is this bad?
++Readability, this can be written more clearly with
++an if let statement
++
++### Example
++```
++let x: Result<String, String> = do_stuff();
++x.map(log_err_msg);
++x.map(|msg| log_err_msg(format_msg(msg)));
++```
++
++The correct use would be:
++
++```
++let x: Result<String, String> = do_stuff();
++if let Ok(msg) = x {
++    log_err_msg(msg);
++};
++if let Ok(msg) = x {
++    log_err_msg(format_msg(msg));
++};
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7c8ec2ffcf9485f792780a79cfddea9bced2e56e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++### What it does
++Checks for public functions that return a `Result`
++with an `Err` type of `()`. It suggests using a custom type that
++implements `std::error::Error`.
++
++### Why is this bad?
++Unit does not implement `Error` and carries no
++further information about what went wrong.
++
++### Known problems
++Of course, this lint assumes that `Result` is used
++for a fallible operation (which is after all the intended use). However
++code may opt to (mis)use it as a basic two-variant-enum. In that case,
++the suggestion is misguided, and the code should use a custom enum
++instead.
++
++### Examples
++```
++pub fn read_u8() -> Result<u8, ()> { Err(()) }
++```
++should become
++```
++use std::fmt;
++
++#[derive(Debug)]
++pub struct EndOfStream;
++
++impl fmt::Display for EndOfStream {
++    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
++        write!(f, "End of Stream")
++    }
++}
++
++impl std::error::Error for EndOfStream { }
++
++pub fn read_u8() -> Result<u8, EndOfStream> { Err(EndOfStream) }
++```
++
++Note that there are crates that simplify creating the error type, e.g.
++[`thiserror`](https://docs.rs/thiserror).
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4a4fd2c6e517b86dafd0f253542318eccc9bfede
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++### What it does
++This lint warns when a method returning `Self` doesn't have the `#[must_use]` attribute.
++
++### Why is this bad?
++Methods returning `Self` often create new values, having the `#[must_use]` attribute
++prevents users from "forgetting" to use the newly created value.
++
++The `#[must_use]` attribute can be added to the type itself to ensure that instances
++are never forgotten. Functions returning a type marked with `#[must_use]` will not be
++linted, as the usage is already enforced by the type attribute.
++
++### Limitations
++This lint is only applied on methods taking a `self` argument. It would be mostly noise
++if it was added on constructors for example.
++
++### Example
++```
++pub struct Bar;
++impl Bar {
++    // Missing attribute
++    pub fn bar(&self) -> Self {
++        Self
++    }
++}
++```
++
++Use instead:
++```
++// It's better to have the `#[must_use]` attribute on the method like this:
++pub struct Bar;
++impl Bar {
++    #[must_use]
++    pub fn bar(&self) -> Self {
++        Self
++    }
++}
++
++// Or on the type definition like this:
++#[must_use]
++pub struct Bar;
++impl Bar {
++    pub fn bar(&self) -> Self {
++        Self
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..39f481193866861d6f8fa68e96437c819e0fb273
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Checks for range expressions `x..y` where both `x` and `y`
++are constant and `x` is greater or equal to `y`.
++
++### Why is this bad?
++Empty ranges yield no values so iterating them is a no-op.
++Moreover, trying to use a reversed range to index a slice will panic at run-time.
++
++### Example
++```
++fn main() {
++    (10..=0).for_each(|x| println!("{}", x));
++
++    let arr = [1, 2, 3, 4, 5];
++    let sub = &arr[3..1];
++}
++```
++Use instead:
++```
++fn main() {
++    (0..=10).rev().for_each(|x| println!("{}", x));
++
++    let arr = [1, 2, 3, 4, 5];
++    let sub = &arr[1..3];
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a0a90eec681e6dfcb69400b8fa7c9df233d14ab9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,41 @@@
++### What it does
++Checks for consecutive `if`s with the same function call.
++
++### Why is this bad?
++This is probably a copy & paste error.
++Despite the fact that function can have side effects and `if` works as
++intended, such an approach is implicit and can be considered a "code smell".
++
++### Example
++```
++if foo() == bar {
++    …
++} else if foo() == bar {
++    …
++}
++```
++
++This probably should be:
++```
++if foo() == bar {
++    …
++} else if foo() == baz {
++    …
++}
++```
++
++or if the original code was not a typo and called function mutates a state,
++consider move the mutation out of the `if` condition to avoid similarity to
++a copy & paste error:
++
++```
++let first = foo();
++if first == bar {
++    …
++} else {
++    let second = foo();
++    if second == bar {
++    …
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7e724073f8aa7aa9a5cc4d1916903352fe74c835
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++### What it does
++Checks whether a for loop is being used to push a constant
++value into a Vec.
++
++### Why is this bad?
++This kind of operation can be expressed more succinctly with
++`vec![item; SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also
++have better performance.
++
++### Example
++```
++let item1 = 2;
++let item2 = 3;
++let mut vec: Vec<u8> = Vec::new();
++for _ in 0..20 {
++   vec.push(item1);
++}
++for _ in 0..30 {
++    vec.push(item2);
++}
++```
++
++Use instead:
++```
++let item1 = 2;
++let item2 = 3;
++let mut vec: Vec<u8> = vec![item1; 20];
++vec.resize(20 + 30, item2);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..792dd717fc6450cdf836fa5f560bd1c34a85270e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++It lints if a struct has two methods with the same name:
++one from a trait, another not from trait.
++
++### Why is this bad?
++Confusing.
++
++### Example
++```
++trait T {
++    fn foo(&self) {}
++}
++
++struct S;
++
++impl T for S {
++    fn foo(&self) {}
++}
++
++impl S {
++    fn foo(&self) {}
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..67292d54585f2bfdaa4fb054dd99ab9ad28a2a41
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++Checks for an iterator or string search (such as `find()`,
++`position()`, or `rposition()`) followed by a call to `is_some()` or `is_none()`.
++
++### Why is this bad?
++Readability, this can be written more concisely as:
++* `_.any(_)`, or `_.contains(_)` for `is_some()`,
++* `!_.any(_)`, or `!_.contains(_)` for `is_none()`.
++
++### Example
++```
++let vec = vec![1];
++vec.iter().find(|x| **x == 0).is_some();
++
++"hello world".find("world").is_none();
++```
++
++Use instead:
++```
++let vec = vec![1];
++vec.iter().any(|x| *x == 0);
++
++!"hello world".contains("world");
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ea60ea077ab5f320ca82ecec32b57b35a0182ddb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++### What it does
++Checks for explicit self-assignments.
++
++### Why is this bad?
++Self-assignments are redundant and unlikely to be
++intentional.
++
++### Known problems
++If expression contains any deref coercions or
++indexing operations they are assumed not to have any side effects.
++
++### Example
++```
++struct Event {
++    x: i32,
++}
++
++fn copy_position(a: &mut Event, b: &Event) {
++    a.x = a.x;
++}
++```
++
++Should be:
++```
++struct Event {
++    x: i32,
++}
++
++fn copy_position(a: &mut Event, b: &Event) {
++    a.x = b.x;
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a01669a84542d8f55b00ac49975722fc4e7281fd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Warns when constructors have the same name as their types.
++
++### Why is this bad?
++Repeating the name of the type is redundant.
++
++### Example
++```
++struct Foo {}
++
++impl Foo {
++    pub fn foo() -> Foo {
++        Foo {}
++    }
++}
++```
++Use instead:
++```
++struct Foo {}
++
++impl Foo {
++    pub fn new() -> Foo {
++        Foo {}
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..73e8051362883d3ba4707d97504a3cf29f345ac8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks that module layout uses only `mod.rs` files.
++
++### Why is this bad?
++Having multiple module layout styles in a project can be confusing.
++
++### Example
++```
++src/
++  stuff/
++    stuff_files.rs
++  stuff.rs
++  lib.rs
++```
++Use instead:
++```
++src/
++  stuff/
++    stuff_files.rs
++    mod.rs
++  lib.rs
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..30c963ca211b3a7a6ecd2c5359a91a6284156774
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++### What it does
++Looks for blocks of expressions and fires if the last expression returns
++`()` but is not followed by a semicolon.
++
++### Why is this bad?
++The semicolon might be optional but when extending the block with new
++code, it doesn't require a change in previous last line.
++
++### Example
++```
++fn main() {
++    println!("Hello world")
++}
++```
++Use instead:
++```
++fn main() {
++    println!("Hello world");
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..226a6b8a98766da0de8855d7d786a847f51269a1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Warns if literal suffixes are separated by an underscore.
++To enforce separated literal suffix style,
++see the `unseparated_literal_suffix` lint.
++
++### Why is this bad?
++Suffix style should be consistent.
++
++### Example
++```
++123832_i32
++```
++
++Use instead:
++```
++123832i32
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8a3c89ac11adcb69d49d95a82bc6db32006fb9b1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++### What it does
++Checks for mis-uses of the serde API.
++
++### Why is this bad?
++Serde is very finnicky about how its API should be
++used, but the type system can't be used to enforce it (yet?).
++
++### Example
++Implementing `Visitor::visit_string` but not
++`Visitor::visit_str`.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9eb8e7ad164bbe5c501309a8633080ac369b018f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++### What it does
++Checks for bindings that shadow other bindings already in
++scope, while reusing the original value.
++
++### Why is this bad?
++Not too much, in fact it's a common pattern in Rust
++code. Still, some argue that name shadowing like this hurts readability,
++because a value may be bound to different things depending on position in
++the code.
++
++### Example
++```
++let x = 2;
++let x = x + 1;
++```
++use different variable name:
++```
++let x = 2;
++let y = x + 1;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3cd96f560a5e2cc792ef3e6a3e4635a959eb9115
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for bindings that shadow other bindings already in
++scope, while just changing reference level or mutability.
++
++### Why is this bad?
++Not much, in fact it's a very common pattern in Rust
++code. Still, some may opt to avoid it in their code base, they can set this
++lint to `Warn`.
++
++### Example
++```
++let x = &x;
++```
++
++Use instead:
++```
++let y = &x; // use different variable name
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..436251c520a9810848ffd831a446ab565483dacd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for bindings that shadow other bindings already in
++scope, either without an initialization or with one that does not even use
++the original value.
++
++### Why is this bad?
++Name shadowing can hurt readability, especially in
++large code bases, because it is easy to lose track of the active binding at
++any place in the code. This can be alleviated by either giving more specific
++names to bindings or introducing more scopes to contain the bindings.
++
++### Example
++```
++let x = y;
++let x = z; // shadows the earlier binding
++```
++
++Use instead:
++```
++let x = y;
++let w = z; // use different variable name
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..31492bed03d4bfc725ef4005e0939e97e59e18ae
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++### What it does
++Checks for the use of short circuit boolean conditions as
++a
++statement.
++
++### Why is this bad?
++Using a short circuit boolean condition as a statement
++may hide the fact that the second part is executed or not depending on the
++outcome of the first part.
++
++### Example
++```
++f() && g(); // We should write `if f() { g(); }`.
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..02e74751ae034ee573789f03b41b6cd4c176f0ba
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for methods that should live in a trait
++implementation of a `std` trait (see [llogiq's blog
++post](http://llogiq.github.io/2015/07/30/traits.html) for further
++information) instead of an inherent implementation.
++
++### Why is this bad?
++Implementing the traits improve ergonomics for users of
++the code, often with very little cost. Also people seeing a `mul(...)`
++method
++may expect `*` to work equally, so you should have good reason to disappoint
++them.
++
++### Example
++```
++struct X;
++impl X {
++    fn add(&self, other: &X) -> X {
++        // ..
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f869def0ddb7c28eec21f8eb08b381f48ca33cdc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,43 @@@
++### What it does
++Check for temporaries returned from function calls in a match scrutinee that have the
++`clippy::has_significant_drop` attribute.
++
++### Why is this bad?
++The `clippy::has_significant_drop` attribute can be added to types whose Drop impls have
++an important side-effect, such as unlocking a mutex, making it important for users to be
++able to accurately understand their lifetimes. When a temporary is returned in a function
++call in a match scrutinee, its lifetime lasts until the end of the match block, which may
++be surprising.
++
++For `Mutex`es this can lead to a deadlock. This happens when the match scrutinee uses a
++function call that returns a `MutexGuard` and then tries to lock again in one of the match
++arms. In that case the `MutexGuard` in the scrutinee will not be dropped until the end of
++the match block and thus will not unlock.
++
++### Example
++```
++let mutex = Mutex::new(State {});
++
++match mutex.lock().unwrap().foo() {
++    true => {
++        mutex.lock().unwrap().bar(); // Deadlock!
++    }
++    false => {}
++};
++
++println!("All done!");
++```
++Use instead:
++```
++let mutex = Mutex::new(State {});
++
++let is_foo = mutex.lock().unwrap().foo();
++match is_foo {
++    true => {
++        mutex.lock().unwrap().bar();
++    }
++    false => {}
++};
++
++println!("All done!");
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..13aca9c0bb75bae2d36608350dbc947a3da15f9f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++### What it does
++Checks for names that are very similar and thus confusing.
++
++### Why is this bad?
++It's hard to distinguish between names that differ only
++by a single character.
++
++### Example
++```
++let checked_exp = something;
++let checked_expr = something_else;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cf23dc0c89bbb2fddc71affd85cf9a528cc6432e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Warns when using `push_str`/`insert_str` with a single-character string literal
++where `push`/`insert` with a `char` would work fine.
++
++### Why is this bad?
++It's less clear that we are pushing a single character.
++
++### Example
++```
++string.insert_str(0, "R");
++string.push_str("R");
++```
++
++Use instead:
++```
++string.insert(0, 'R');
++string.push('R');
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..92dd24bf247ec7840cc58ba4cbf636fee230fcc5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++### What it does
++Checks for lifetimes with names which are one character
++long.
++
++### Why is this bad?
++A single character is likely not enough to express the
++purpose of a lifetime. Using a longer name can make code
++easier to understand, especially for those who are new to
++Rust.
++
++### Known problems
++Rust programmers and learning resources tend to use single
++character lifetimes, so this lint is at odds with the
++ecosystem at large. In addition, the lifetime's purpose may
++be obvious or, rarely, expressible in one character.
++
++### Example
++```
++struct DiagnosticCtx<'a> {
++    source: &'a str,
++}
++```
++Use instead:
++```
++struct DiagnosticCtx<'src> {
++    source: &'src str,
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9e5ad1e7d6395a9726543b6ad15f33536b2e5753
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++### What it does
++Checks for string methods that receive a single-character
++`str` as an argument, e.g., `_.split("x")`.
++
++### Why is this bad?
++Performing these methods using a `char` is faster than
++using a `str`.
++
++### Known problems
++Does not catch multi-byte unicode characters.
++
++### Example
++```
++_.split("x");
++```
++
++Use instead:
++```
++_.split('x');
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3a026388183e1cb754e65d398323f932543a6034
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checking for imports with single component use path.
++
++### Why is this bad?
++Import with single component use path such as `use cratename;`
++is not necessary, and thus should be removed.
++
++### Example
++```
++use regex;
++
++fn main() {
++    regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
++}
++```
++Better as
++```
++fn main() {
++    regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6f0c15a85f7749176320797287631a87305b0ead
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checks whether a for loop has a single element.
++
++### Why is this bad?
++There is no reason to have a loop of a
++single element.
++
++### Example
++```
++let item1 = 2;
++for item in &[item1] {
++    println!("{}", item);
++}
++```
++
++Use instead:
++```
++let item1 = 2;
++let item = &item1;
++println!("{}", item);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..31dde4da8489cece5111dfcf9d01a8d7df48854d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checks for matches with a single arm where an `if let`
++will usually suffice.
++
++### Why is this bad?
++Just readability – `if let` nests less than a `match`.
++
++### Example
++```
++match x {
++    Some(ref foo) => bar(foo),
++    _ => (),
++}
++```
++
++Use instead:
++```
++if let Some(ref foo) = x {
++    bar(foo);
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..29a447af09b86f49aa81052bc32a52f5516d9367
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++### What it does
++Checks for matches with two arms where an `if let else` will
++usually suffice.
++
++### Why is this bad?
++Just readability – `if let` nests less than a `match`.
++
++### Known problems
++Personal style preferences may differ.
++
++### Example
++Using `match`:
++
++```
++match x {
++    Some(ref foo) => bar(foo),
++    _ => bar(&other_ref),
++}
++```
++
++Using `if let` with `else`:
++
++```
++if let Some(ref foo) = x {
++    bar(foo);
++} else {
++    bar(&other_ref);
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d893ec6a2a014ef486b78836a216072f7f84d7fa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Detects expressions where
++`size_of::<T>` or `size_of_val::<T>` is used as a
++count of elements of type `T`
++
++### Why is this bad?
++These functions expect a count
++of `T` and not a number of bytes
++
++### Example
++```
++const SIZE: usize = 128;
++let x = [2u8; SIZE];
++let mut y = [2u8; SIZE];
++unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>() * SIZE) };
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1ec8a3a28d5b03c1f8aa110be33b077a4605a17a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for usage of `_.skip_while(condition).next()`.
++
++### Why is this bad?
++Readability, this can be written more concisely as
++`_.find(!condition)`.
++
++### Example
++```
++vec.iter().skip_while(|x| **x == 0).next();
++```
++
++Use instead:
++```
++vec.iter().find(|x| **x != 0);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..53442e1796541b476163a6750b778535dacbd5c6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++Checks slow zero-filled vector initialization
++
++### Why is this bad?
++These structures are non-idiomatic and less efficient than simply using
++`vec![0; len]`.
++
++### Example
++```
++let mut vec1 = Vec::with_capacity(len);
++vec1.resize(len, 0);
++
++let mut vec1 = Vec::with_capacity(len);
++vec1.resize(vec1.capacity(), 0);
++
++let mut vec2 = Vec::with_capacity(len);
++vec2.extend(repeat(0).take(len));
++```
++
++Use instead:
++```
++let mut vec1 = vec![0; len];
++let mut vec2 = vec![0; len];
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6465dbee46b10d39e18bce8efe2a8e9778611165
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++### What it does
++When sorting primitive values (integers, bools, chars, as well
++as arrays, slices, and tuples of such items), it is typically better to
++use an unstable sort than a stable sort.
++
++### Why is this bad?
++Typically, using a stable sort consumes more memory and cpu cycles.
++Because values which compare equal are identical, preserving their
++relative order (the guarantee that a stable sort provides) means
++nothing, while the extra costs still apply.
++
++### Known problems
++
++As pointed out in
++[issue #8241](https://github.com/rust-lang/rust-clippy/issues/8241),
++a stable sort can instead be significantly faster for certain scenarios
++(eg. when a sorted vector is extended with new data and resorted).
++
++For more information and benchmarking results, please refer to the
++issue linked above.
++
++### Example
++```
++let mut vec = vec![2, 1, 3];
++vec.sort();
++```
++Use instead:
++```
++let mut vec = vec![2, 1, 3];
++vec.sort_unstable();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c2d32704e50551a82049d700a1a18ea77dfdefd0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++
++Finds items imported through `std` when available through `alloc`.
++
++### Why is this bad?
++
++Crates which have `no_std` compatibility and require alloc may wish to ensure types are imported from
++alloc to ensure disabling `std` does not cause the crate to fail to compile. This lint is also useful
++for crates migrating to become `no_std` compatible.
++
++### Example
++```
++use std::vec::Vec;
++```
++Use instead:
++```
++use alloc::vec::Vec;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f1e1518c6a62a955209f225d543ac100b411ce98
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++
++Finds items imported through `std` when available through `core`.
++
++### Why is this bad?
++
++Crates which have `no_std` compatibility may wish to ensure types are imported from core to ensure
++disabling `std` does not cause the crate to fail to compile. This lint is also useful for crates
++migrating to become `no_std` compatible.
++
++### Example
++```
++use std::hash::Hasher;
++```
++Use instead:
++```
++use core::hash::Hasher;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a24755223747b9189e880a850867190a25e3ca1b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++This lint checks for `.to_string()` method calls on values of type `&str`.
++
++### Why is this bad?
++The `to_string` method is also used on other types to convert them to a string.
++When called on a `&str` it turns the `&str` into the owned variant `String`, which can be better
++expressed with `.to_owned()`.
++
++### Example
++```
++// example code where clippy issues a warning
++let _ = "str".to_string();
++```
++Use instead:
++```
++// example code which does not raise clippy warning
++let _ = "str".to_owned();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..23dafd0d0333dbf5397ab640869a27637c8bfa5e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++### What it does
++Checks for all instances of `x + _` where `x` is of type
++`String`, but only if [`string_add_assign`](#string_add_assign) does *not*
++match.
++
++### Why is this bad?
++It's not bad in and of itself. However, this particular
++`Add` implementation is asymmetric (the other operand need not be `String`,
++but `x` does), while addition as mathematically defined is symmetric, also
++the `String::push_str(_)` function is a perfectly good replacement.
++Therefore, some dislike it and wish not to have it in their code.
++
++That said, other people think that string addition, having a long tradition
++in other languages is actually fine, which is why we decided to make this
++particular lint `allow` by default.
++
++### Example
++```
++let x = "Hello".to_owned();
++x + ", World";
++```
++
++Use instead:
++```
++let mut x = "Hello".to_owned();
++x.push_str(", World");
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7438be855db8c3485249cc47b66589ab0f97134e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for string appends of the form `x = x + y` (without
++`let`!).
++
++### Why is this bad?
++It's not really bad, but some people think that the
++`.push_str(_)` method is more readable.
++
++### Example
++```
++let mut x = "Hello".to_owned();
++x = x + ", World";
++
++// More readable
++x += ", World";
++x.push_str(", World");
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..619ea3e1186735933992f8176f573e07f81642db
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Checks for the use of `.extend(s.chars())` where s is a
++`&str` or `String`.
++
++### Why is this bad?
++`.push_str(s)` is clearer
++
++### Example
++```
++let abc = "abc";
++let def = String::from("def");
++let mut s = String::new();
++s.extend(abc.chars());
++s.extend(def.chars());
++```
++The correct use would be:
++```
++let abc = "abc";
++let def = String::from("def");
++let mut s = String::new();
++s.push_str(abc);
++s.push_str(&def);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9102d73471cb27dd6e97cfc858f27e07682934f4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Check if the string is transformed to byte array and casted back to string.
++
++### Why is this bad?
++It's unnecessary, the string can be used directly.
++
++### Example
++```
++std::str::from_utf8(&"Hello World!".as_bytes()[6..11]).unwrap();
++```
++
++Use instead:
++```
++&"Hello World!"[6..11];
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a125b97ed6567d3791131e6b94fd682f40d2316d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,39 @@@
++### What it does
++Checks for the `as_bytes` method called on string literals
++that contain only ASCII characters.
++
++### Why is this bad?
++Byte string literals (e.g., `b"foo"`) can be used
++instead. They are shorter but less discoverable than `as_bytes()`.
++
++### Known problems
++`"str".as_bytes()` and the suggested replacement of `b"str"` are not
++equivalent because they have different types. The former is `&[u8]`
++while the latter is `&[u8; 3]`. That means in general they will have a
++different set of methods and different trait implementations.
++
++```
++fn f(v: Vec<u8>) {}
++
++f("...".as_bytes().to_owned()); // works
++f(b"...".to_owned()); // does not work, because arg is [u8; 3] not Vec<u8>
++
++fn g(r: impl std::io::Read) {}
++
++g("...".as_bytes()); // works
++g(b"..."); // does not work
++```
++
++The actual equivalent of `"str".as_bytes()` with the same type is not
++`b"str"` but `&b"str"[..]`, which is a great deal of punctuation and not
++more readable than a function call.
++
++### Example
++```
++let bstr = "a byte string".as_bytes();
++```
++
++Use instead:
++```
++let bstr = b"a byte string";
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3d9e49dd39eb781c0945ddb1eb33a2be7f34ce54
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for slice operations on strings
++
++### Why is this bad?
++UTF-8 characters span multiple bytes, and it is easy to inadvertently confuse character
++counts and string indices. This may lead to panics, and should warrant some test cases
++containing wide UTF-8 characters. This lint is most useful in code that should avoid
++panics at all costs.
++
++### Known problems
++Probably lots of false positives. If an index comes from a known valid position (e.g.
++obtained via `char_indices` over the same string), it is totally OK.
++
++# Example
++```
++&"Ölkanne"[1..];
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..deb7eebe7842c5177dffd094e61b5befdd861dac
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++This lint checks for `.to_string()` method calls on values of type `String`.
++
++### Why is this bad?
++The `to_string` method is also used on other types to convert them to a string.
++When called on a `String` it only clones the `String`, which can be better expressed with `.clone()`.
++
++### Example
++```
++// example code where clippy issues a warning
++let msg = String::from("Hello World");
++let _ = msg.to_string();
++```
++Use instead:
++```
++// example code which does not raise clippy warning
++let msg = String::from("Hello World");
++let _ = msg.clone();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0454abf55a20481fded0fad25014db3f0f7b121f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++### What it does
++Checks for usage of `libc::strlen` on a `CString` or `CStr` value,
++and suggest calling `as_bytes().len()` or `to_bytes().len()` respectively instead.
++
++### Why is this bad?
++This avoids calling an unsafe `libc` function.
++Currently, it also avoids calculating the length.
++
++### Example
++```
++use std::ffi::CString;
++let cstring = CString::new("foo").expect("CString::new failed");
++let len = unsafe { libc::strlen(cstring.as_ptr()) };
++```
++Use instead:
++```
++use std::ffi::CString;
++let cstring = CString::new("foo").expect("CString::new failed");
++let len = cstring.as_bytes().len();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9e197c786201d23538234caccc473070e3fe8275
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++### What it does
++Checks for excessive
++use of bools in structs.
++
++### Why is this bad?
++Excessive bools in a struct
++is often a sign that it's used as a state machine,
++which is much better implemented as an enum.
++If it's not the case, excessive bools usually benefit
++from refactoring into two-variant enums for better
++readability and API.
++
++### Example
++```
++struct S {
++    is_pending: bool,
++    is_processing: bool,
++    is_finished: bool,
++}
++```
++
++Use instead:
++```
++enum S {
++    Pending,
++    Processing,
++    Finished,
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f1c9c665b085d8cf2879f65578932bbe7764fc72
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,50 @@@
++### What it does
++Looks for floating-point expressions that
++can be expressed using built-in methods to improve both
++accuracy and performance.
++
++### Why is this bad?
++Negatively impacts accuracy and performance.
++
++### Example
++```
++use std::f32::consts::E;
++
++let a = 3f32;
++let _ = (2f32).powf(a);
++let _ = E.powf(a);
++let _ = a.powf(1.0 / 2.0);
++let _ = a.log(2.0);
++let _ = a.log(10.0);
++let _ = a.log(E);
++let _ = a.powf(2.0);
++let _ = a * 2.0 + 4.0;
++let _ = if a < 0.0 {
++    -a
++} else {
++    a
++};
++let _ = if a < 0.0 {
++    a
++} else {
++    -a
++};
++```
++
++is better expressed as
++
++```
++use std::f32::consts::E;
++
++let a = 3f32;
++let _ = a.exp2();
++let _ = a.exp();
++let _ = a.sqrt();
++let _ = a.log2();
++let _ = a.log10();
++let _ = a.ln();
++let _ = a.powi(2);
++let _ = a.mul_add(2.0, 4.0);
++let _ = a.abs();
++let _ = -a.abs();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d67ff279346dc2cf5dd7d56292c7f00016c64f80
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Lints for suspicious operations in impls of arithmetic operators, e.g.
++subtracting elements in an Add impl.
++
++### Why is this bad?
++This is probably a typo or copy-and-paste error and not intended.
++
++### Example
++```
++impl Add for Foo {
++    type Output = Foo;
++
++    fn add(self, other: Foo) -> Foo {
++        Foo(self.0 - other.0)
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b889827cdf27fc926ebcf8286a2da63389d4ecee
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++### What it does
++Checks for use of the non-existent `=*`, `=!` and `=-`
++operators.
++
++### Why is this bad?
++This is either a typo of `*=`, `!=` or `-=` or
++confusing.
++
++### Example
++```
++a =- 42; // confusing, should it be `a -= 42` or `a = -42`?
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3cf2f74868ebfb4e5faed85fef5190dd50e588c0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++### What it does
++Checks for formatting of `else`. It lints if the `else`
++is followed immediately by a newline or the `else` seems to be missing.
++
++### Why is this bad?
++This is probably some refactoring remnant, even if the
++code is correct, it might look confusing.
++
++### Example
++```
++if foo {
++} { // looks like an `else` is missing here
++}
++
++if foo {
++} if bar { // looks like an `else` is missing here
++}
++
++if foo {
++} else
++
++{ // this is the `else` block of the previous `if`, but should it be?
++}
++
++if foo {
++} else
++
++if bar { // this is the `else` block of the previous `if`, but should it be?
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d8fa52c43fb0af231e545dccdc067114714cf31a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++### What it does
++Checks for calls to `map` followed by a `count`.
++
++### Why is this bad?
++It looks suspicious. Maybe `map` was confused with `filter`.
++If the `map` call is intentional, this should be rewritten
++using `inspect`. Or, if you intend to drive the iterator to
++completion, you can just use `for_each` instead.
++
++### Example
++```
++let _ = (0..3).map(|x| x + 2).count();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..81abfbecae073e9864a2fc8e4e8b3fbe83c1106a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Lints for suspicious operations in impls of OpAssign, e.g.
++subtracting elements in an AddAssign impl.
++
++### Why is this bad?
++This is probably a typo or copy-and-paste error and not intended.
++
++### Example
++```
++impl AddAssign for Foo {
++    fn add_assign(&mut self, other: Foo) {
++        *self = *self - other;
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..81ede5d3da575271f8fd5569d6f8c262c4887700
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,41 @@@
++### What it does
++Checks for unlikely usages of binary operators that are almost
++certainly typos and/or copy/paste errors, given the other usages
++of binary operators nearby.
++
++### Why is this bad?
++They are probably bugs and if they aren't then they look like bugs
++and you should add a comment explaining why you are doing such an
++odd set of operations.
++
++### Known problems
++There may be some false positives if you are trying to do something
++unusual that happens to look like a typo.
++
++### Example
++```
++struct Vec3 {
++    x: f64,
++    y: f64,
++    z: f64,
++}
++
++impl Eq for Vec3 {}
++
++impl PartialEq for Vec3 {
++    fn eq(&self, other: &Self) -> bool {
++        // This should trigger the lint because `self.x` is compared to `other.y`
++        self.x == other.y && self.y == other.y && self.z == other.z
++    }
++}
++```
++Use instead:
++```
++// same as above except:
++impl PartialEq for Vec3 {
++    fn eq(&self, other: &Self) -> bool {
++        // Note we now compare other.x to self.x
++        self.x == other.x && self.y == other.y && self.z == other.z
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..79a3dbfa6f4a7371577f1b264574561ff7fd74dc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for calls to [`splitn`]
++(https://doc.rust-lang.org/std/primitive.str.html#method.splitn) and
++related functions with either zero or one splits.
++
++### Why is this bad?
++These calls don't actually split the value and are
++likely to be intended as a different number.
++
++### Example
++```
++for x in s.splitn(1, ":") {
++    // ..
++}
++```
++
++Use instead:
++```
++for x in s.splitn(2, ":") {
++    // ..
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8cbf61dc7175beff26516bc6237ba917328be94f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,39 @@@
++### What it does
++Checks for the usage of `_.to_owned()`, on a `Cow<'_, _>`.
++
++### Why is this bad?
++Calling `to_owned()` on a `Cow` creates a clone of the `Cow`
++itself, without taking ownership of the `Cow` contents (i.e.
++it's equivalent to calling `Cow::clone`).
++The similarly named `into_owned` method, on the other hand,
++clones the `Cow` contents, effectively turning any `Cow::Borrowed`
++into a `Cow::Owned`.
++
++Given the potential ambiguity, consider replacing `to_owned`
++with `clone` for better readability or, if getting a `Cow::Owned`
++was the original intent, using `into_owned` instead.
++
++### Example
++```
++let s = "Hello world!";
++let cow = Cow::Borrowed(s);
++
++let data = cow.to_owned();
++assert!(matches!(data, Cow::Borrowed(_)))
++```
++Use instead:
++```
++let s = "Hello world!";
++let cow = Cow::Borrowed(s);
++
++let data = cow.clone();
++assert!(matches!(data, Cow::Borrowed(_)))
++```
++or
++```
++let s = "Hello world!";
++let cow = Cow::Borrowed(s);
++
++let data = cow.into_owned();
++assert!(matches!(data, String))
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..06fb09db76d0c3ebc057d55deeb1741b3ecc2e99
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks the formatting of a unary operator on the right hand side
++of a binary operator. It lints if there is no space between the binary and unary operators,
++but there is a space between the unary and its operand.
++
++### Why is this bad?
++This is either a typo in the binary operator or confusing.
++
++### Example
++```
++// &&! looks like a different operator
++if foo &&! bar {}
++```
++
++Use instead:
++```
++if foo && !bar {}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0215d1e8a42153cd03c6c6ae9beb9a9e2ff0215b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Checks for calls to `core::mem::swap` where either parameter is derived from a pointer
++
++### Why is this bad?
++When at least one parameter to `swap` is derived from a pointer it may overlap with the
++other. This would then lead to undefined behavior.
++
++### Example
++```
++unsafe fn swap(x: &[*mut u32], y: &[*mut u32]) {
++    for (&x, &y) in x.iter().zip(y) {
++        core::mem::swap(&mut *x, &mut *y);
++    }
++}
++```
++Use instead:
++```
++unsafe fn swap(x: &[*mut u32], y: &[*mut u32]) {
++    for (&x, &y) in x.iter().zip(y) {
++        core::ptr::swap(x, y);
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f83dbe2b73cb71e0329a2b8a9bebedec2efded74
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,44 @@@
++### What it does
++Checks doc comments for usage of tab characters.
++
++### Why is this bad?
++The rust style-guide promotes spaces instead of tabs for indentation.
++To keep a consistent view on the source, also doc comments should not have tabs.
++Also, explaining ascii-diagrams containing tabs can get displayed incorrectly when the
++display settings of the author and reader differ.
++
++### Example
++```
++///
++/// Struct to hold two strings:
++///   - first         one
++///   - second        one
++pub struct DoubleString {
++   ///
++   ///        - First String:
++   ///                - needs to be inside here
++   first_string: String,
++   ///
++   ///        - Second String:
++   ///                - needs to be inside here
++   second_string: String,
++}
++```
++
++Will be converted to:
++```
++///
++/// Struct to hold two strings:
++///     - first        one
++///     - second    one
++pub struct DoubleString {
++   ///
++   ///     - First String:
++   ///         - needs to be inside here
++   first_string: String,
++   ///
++   ///     - Second String:
++   ///         - needs to be inside here
++   second_string: String,
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..195b42cf0d492fc95d375594b1f8aad81238bab1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++### What it does
++Checks for construction of a structure or tuple just to
++assign a value in it.
++
++### Why is this bad?
++Readability. If the structure is only created to be
++updated, why not write the structure you want in the first place?
++
++### Example
++```
++(0, 0).0 = 1
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eee8375adf7aee3912818f6c74e3ce7058a24d0b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks for `.to_digit(..).is_some()` on `char`s.
++
++### Why is this bad?
++This is a convoluted way of checking if a `char` is a digit. It's
++more straight forward to use the dedicated `is_digit` method.
++
++### Example
++```
++let is_digit = c.to_digit(radix).is_some();
++```
++can be written as:
++```
++let is_digit = c.is_digit(radix);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..34b20583585ad0fb8fe26778ebb5a816cf4fe825
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for [`ToString::to_string`](https://doc.rust-lang.org/std/string/trait.ToString.html#tymethod.to_string)
++applied to a type that implements [`Display`](https://doc.rust-lang.org/std/fmt/trait.Display.html)
++in a macro that does formatting.
++
++### Why is this bad?
++Since the type implements `Display`, the use of `to_string` is
++unnecessary.
++
++### Example
++```
++println!("error: something failed at {}", Location::caller().to_string());
++```
++Use instead:
++```
++println!("error: something failed at {}", Location::caller());
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..661eb1ac5cff403772dd7bcb2cf08a79e71de86c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++### What it does
++Checks for usage of `todo!`.
++
++### Why is this bad?
++This macro should not be present in production code
++
++### Example
++```
++todo!();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4669f9f82e66126504a0a9df835c73f34573152a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++### What it does
++Checks for functions with too many parameters.
++
++### Why is this bad?
++Functions with lots of parameters are considered bad
++style and reduce readability (“what does the 5th parameter mean?”). Consider
++grouping some parameters into a new type.
++
++### Example
++```
++fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {
++    // ..
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..425db348bbd75f38b399d59a1eaf305eda54fddd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for functions with a large amount of lines.
++
++### Why is this bad?
++Functions with a lot of lines are harder to understand
++due to having to look at a larger amount of code to understand what the
++function is doing. Consider splitting the body of the function into
++multiple functions.
++
++### Example
++```
++fn im_too_long() {
++    println!("");
++    // ... 100 more LoC
++    println!("");
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..96a9e2db8b7e721153ce9e05d38a048f924e3cff
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++### What it does
++Checks for function arguments and let bindings denoted as
++`ref`.
++
++### Why is this bad?
++The `ref` declaration makes the function take an owned
++value, but turns the argument into a reference (which means that the value
++is destroyed when exiting the function). This adds not much value: either
++take a reference type, or take an owned value and create references in the
++body.
++
++For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The
++type of `x` is more obvious with the former.
++
++### Known problems
++If the argument is dereferenced within the function,
++removing the `ref` will lead to errors. This can be fixed by removing the
++dereferences, e.g., changing `*x` to `x` within the function.
++
++### Example
++```
++fn foo(ref _x: u8) {}
++```
++
++Use instead:
++```
++fn foo(_x: &u8) {}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..db1908cc96d03d479fd557796bd9f95b61af60e9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Displays a warning when a struct with a trailing zero-sized array is declared without a `repr` attribute.
++
++### Why is this bad?
++Zero-sized arrays aren't very useful in Rust itself, so such a struct is likely being created to pass to C code or in some other situation where control over memory layout matters (for example, in conjunction with manual allocation to make it easy to compute the offset of the array). Either way, `#[repr(C)]` (or another `repr` attribute) is needed.
++
++### Example
++```
++struct RarelyUseful {
++    some_field: u32,
++    last: [u32; 0],
++}
++```
++
++Use instead:
++```
++#[repr(C)]
++struct MoreOftenUseful {
++    some_field: usize,
++    last: [u32; 0],
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..509736bb36444a10131be93a173eef72903ffb9b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++### What it does
++Checks for cases where generics are being used and multiple
++syntax specifications for trait bounds are used simultaneously.
++
++### Why is this bad?
++Duplicate bounds makes the code
++less readable than specifying them only once.
++
++### Example
++```
++fn func<T: Clone + Default>(arg: T) where T: Clone + Default {}
++```
++
++Use instead:
++```
++fn func<T: Clone + Default>(arg: T) {}
++
++// or
++
++fn func<T>(arg: T) where T: Clone + Default {}
++```
++
++```
++fn foo<T: Default + Default>(bar: T) {}
++```
++Use instead:
++```
++fn foo<T: Default>(bar: T) {}
++```
++
++```
++fn foo<T>(bar: T) where T: Default + Default {}
++```
++Use instead:
++```
++fn foo<T>(bar: T) where T: Default {}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..75889b9c73ae3335fb22e034c08ca2f03112ad1e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++### What it does
++Checks for transmutes from a `&[u8]` to a `&str`.
++
++### Why is this bad?
++Not every byte slice is a valid UTF-8 string.
++
++### Known problems
++- [`from_utf8`] which this lint suggests using is slower than `transmute`
++as it needs to validate the input.
++If you are certain that the input is always a valid UTF-8,
++use [`from_utf8_unchecked`] which is as fast as `transmute`
++but has a semantically meaningful name.
++- You might want to handle errors returned from [`from_utf8`] instead of calling `unwrap`.
++
++[`from_utf8`]: https://doc.rust-lang.org/std/str/fn.from_utf8.html
++[`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html
++
++### Example
++```
++let b: &[u8] = &[1_u8, 2_u8];
++unsafe {
++    let _: &str = std::mem::transmute(b); // where b: &[u8]
++}
++
++// should be:
++let _ = std::str::from_utf8(b).unwrap();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1877e5a465a4e647109046845814004e2b9de50b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for transmutes from a float to an integer.
++
++### Why is this bad?
++Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive
++and safe.
++
++### Example
++```
++unsafe {
++    let _: u32 = std::mem::transmute(1f32);
++}
++
++// should be:
++let _: u32 = 1f32.to_bits();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..07c10f8d0bca7bb6d516ef45f74ecab1829bbdca
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for transmutes from an integer to a `bool`.
++
++### Why is this bad?
++This might result in an invalid in-memory representation of a `bool`.
++
++### Example
++```
++let x = 1_u8;
++unsafe {
++    let _: bool = std::mem::transmute(x); // where x: u8
++}
++
++// should be:
++let _: bool = x != 0;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..836d22d3f99c05888f1748b50265aa766b448fba
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++### What it does
++Checks for transmutes from an integer to a `char`.
++
++### Why is this bad?
++Not every integer is a Unicode scalar value.
++
++### Known problems
++- [`from_u32`] which this lint suggests using is slower than `transmute`
++as it needs to validate the input.
++If you are certain that the input is always a valid Unicode scalar value,
++use [`from_u32_unchecked`] which is as fast as `transmute`
++but has a semantically meaningful name.
++- You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`.
++
++[`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html
++[`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html
++
++### Example
++```
++let x = 1_u32;
++unsafe {
++    let _: char = std::mem::transmute(x); // where x: u32
++}
++
++// should be:
++let _ = std::char::from_u32(x).unwrap();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..75cdc62e972758cd808225d5370f62e8be3ddf29
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for transmutes from an integer to a float.
++
++### Why is this bad?
++Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive
++and safe.
++
++### Example
++```
++unsafe {
++    let _: f32 = std::mem::transmute(1_u32); // where x: u32
++}
++
++// should be:
++let _: f32 = f32::from_bits(1_u32);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a2c39a1b947e31966e0a8d12409333be8b99da62
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for transmutes from a number to an array of `u8`
++
++### Why this is bad?
++Transmutes are dangerous and error-prone, whereas `to_ne_bytes`
++is intuitive and safe.
++
++### Example
++```
++unsafe {
++    let x: [u8; 8] = std::mem::transmute(1i64);
++}
++
++// should be
++let x: [u8; 8] = 0i64.to_ne_bytes();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..65777db98618640fc51882194a9859fa45945979
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checks for transmutes from a pointer to a pointer, or
++from a reference to a reference.
++
++### Why is this bad?
++Transmutes are dangerous, and these can instead be
++written as casts.
++
++### Example
++```
++let ptr = &1u32 as *const u32;
++unsafe {
++    // pointer-to-pointer transmute
++    let _: *const f32 = std::mem::transmute(ptr);
++    // ref-ref transmute
++    let _: &f32 = std::mem::transmute(&1u32);
++}
++// These can be respectively written:
++let _ = ptr as *const f32;
++let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..aca550f5036ecc0f7803a801a4130861bcc5b2a8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Checks for transmutes from a pointer to a reference.
++
++### Why is this bad?
++This can always be rewritten with `&` and `*`.
++
++### Known problems
++- `mem::transmute` in statics and constants is stable from Rust 1.46.0,
++while dereferencing raw pointer is not stable yet.
++If you need to do this in those places,
++you would have to use `transmute` instead.
++
++### Example
++```
++unsafe {
++    let _: &T = std::mem::transmute(p); // where p: *const T
++}
++
++// can be written:
++let _: &T = &*p;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5ee6aaf4ca92cbfab88569eea2188d8a0087ea26
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for transmutes between types which do not have a representation defined relative to
++each other.
++
++### Why is this bad?
++The results of such a transmute are not defined.
++
++### Known problems
++This lint has had multiple problems in the past and was moved to `nursery`. See issue
++[#8496](https://github.com/rust-lang/rust-clippy/issues/8496) for more details.
++
++### Example
++```
++struct Foo<T>(u32, T);
++let _ = unsafe { core::mem::transmute::<Foo<u32>, Foo<i32>>(Foo(0u32, 0u32)) };
++```
++Use instead:
++```
++#[repr(C)]
++struct Foo<T>(u32, T);
++let _ = unsafe { core::mem::transmute::<Foo<u32>, Foo<i32>>(Foo(0u32, 0u32)) };
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b68a8cda9c74bdabbbd6c61bac838ac187540b82
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Checks for transmutes that could be a pointer cast.
++
++### Why is this bad?
++Readability. The code tricks people into thinking that
++something complex is going on.
++
++### Example
++
++```
++unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) };
++```
++Use instead:
++```
++p as *const [u16];
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f8bacfc0b90b45d5b36a692e4039e87dfb5837d9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks for transmute calls which would receive a null pointer.
++
++### Why is this bad?
++Transmuting a null pointer is undefined behavior.
++
++### Known problems
++Not all cases can be detected at the moment of this writing.
++For example, variables which hold a null pointer and are then fed to a `transmute`
++call, aren't detectable yet.
++
++### Example
++```
++let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f7e3e7858f9497aac91658d454db8e3631faedcd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++### What it does
++Warns about calling `str::trim` (or variants) before `str::split_whitespace`.
++
++### Why is this bad?
++`split_whitespace` already ignores leading and trailing whitespace.
++
++### Example
++```
++" A B C ".trim().split_whitespace();
++```
++Use instead:
++```
++" A B C ".split_whitespace();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f71d667fd771166a388b402e806f77f071b9df1f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for trivial [regex](https://crates.io/crates/regex)
++creation (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`).
++
++### Why is this bad?
++Matching the regex can likely be replaced by `==` or
++`str::starts_with`, `str::ends_with` or `std::contains` or other `str`
++methods.
++
++### Known problems
++If the same regex is going to be applied to multiple
++inputs, the precomputations done by `Regex` construction can give
++significantly better performance than any of the `str`-based methods.
++
++### Example
++```
++Regex::new("^foobar")
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f54cce5e2bd76e82def56243c0caa3e084a4cc5a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,43 @@@
++### What it does
++Checks for functions taking arguments by reference, where
++the argument type is `Copy` and small enough to be more efficient to always
++pass by value.
++
++### Why is this bad?
++In many calling conventions instances of structs will
++be passed through registers if they fit into two or less general purpose
++registers.
++
++### Known problems
++This lint is target register size dependent, it is
++limited to 32-bit to try and reduce portability problems between 32 and
++64-bit, but if you are compiling for 8 or 16-bit targets then the limit
++will be different.
++
++The configuration option `trivial_copy_size_limit` can be set to override
++this limit for a project.
++
++This lint attempts to allow passing arguments by reference if a reference
++to that argument is returned. This is implemented by comparing the lifetime
++of the argument and return value for equality. However, this can cause
++false positives in cases involving multiple lifetimes that are bounded by
++each other.
++
++Also, it does not take account of other similar cases where getting memory addresses
++matters; namely, returning the pointer to the argument in question,
++and passing the argument, as both references and pointers,
++to a function that needs the memory address. For further details, refer to
++[this issue](https://github.com/rust-lang/rust-clippy/issues/5953)
++that explains a real case in which this false positive
++led to an **undefined behavior** introduced with unsafe code.
++
++### Example
++
++```
++fn foo(v: &u32) {}
++```
++
++Use instead:
++```
++fn foo(v: u32) {}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e3d4ef3a09df1e3fa3de4f9c1e15a8b7b7861b2d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++### What it does
++Checks for usages of `Err(x)?`.
++
++### Why is this bad?
++The `?` operator is designed to allow calls that
++can fail to be easily chained. For example, `foo()?.bar()` or
++`foo(bar()?)`. Because `Err(x)?` can't be used that way (it will
++always return), it is more clear to write `return Err(x)`.
++
++### Example
++```
++fn foo(fail: bool) -> Result<i32, String> {
++    if fail {
++      Err("failed")?;
++    }
++    Ok(0)
++}
++```
++Could be written:
++
++```
++fn foo(fail: bool) -> Result<i32, String> {
++    if fail {
++      return Err("failed".into());
++    }
++    Ok(0)
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..69cd8750050431897a45dd430aca4296b274a1b4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++### What it does
++Checks for types used in structs, parameters and `let`
++declarations above a certain complexity threshold.
++
++### Why is this bad?
++Too complex types make the code less readable. Consider
++using a `type` definition to simplify them.
++
++### Example
++```
++struct Foo {
++    inner: Rc<Vec<Vec<Box<(u32, u32, u32, u32)>>>>,
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..18ed372fd13e374d907980b097407c8c1b5789a5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++This lint warns about unnecessary type repetitions in trait bounds
++
++### Why is this bad?
++Repeating the type for every bound makes the code
++less readable than combining the bounds
++
++### Example
++```
++pub fn foo<T>(t: T) where T: Copy, T: Clone {}
++```
++
++Use instead:
++```
++pub fn foo<T>(t: T) where T: Copy + Clone {}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f3af4753c5f75c8143c1e7e6a771dd5d58149f7b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,43 @@@
++### What it does
++Checks for `unsafe` blocks and impls without a `// SAFETY: ` comment
++explaining why the unsafe operations performed inside
++the block are safe.
++
++Note the comment must appear on the line(s) preceding the unsafe block
++with nothing appearing in between. The following is ok:
++```
++foo(
++    // SAFETY:
++    // This is a valid safety comment
++    unsafe { *x }
++)
++```
++But neither of these are:
++```
++// SAFETY:
++// This is not a valid safety comment
++foo(
++    /* SAFETY: Neither is this */ unsafe { *x },
++);
++```
++
++### Why is this bad?
++Undocumented unsafe blocks and impls can make it difficult to
++read and maintain code, as well as uncover unsoundness
++and bugs.
++
++### Example
++```
++use std::ptr::NonNull;
++let a = &mut 42;
++
++let ptr = unsafe { NonNull::new_unchecked(a) };
++```
++Use instead:
++```
++use std::ptr::NonNull;
++let a = &mut 42;
++
++// SAFETY: references are guaranteed to be non-null.
++let ptr = unsafe { NonNull::new_unchecked(a) };
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..85e3ec56653c9736daa7964a113e7c50879403e9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`.
++
++### Why is this bad?
++The safe `drop` function does not drop the inner value of a `ManuallyDrop`.
++
++### Known problems
++Does not catch cases if the user binds `std::mem::drop`
++to a different name and calls it that way.
++
++### Example
++```
++struct S;
++drop(std::mem::ManuallyDrop::new(S));
++```
++Use instead:
++```
++struct S;
++unsafe {
++    std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c660c51dadb26d44ddbbbababa6f749abd2ad3f1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++### What it does
++Checks for string literals that contain Unicode in a form
++that is not equal to its
++[NFC-recomposition](http://www.unicode.org/reports/tr15/#Norm_Forms).
++
++### Why is this bad?
++If such a string is compared to another, the results
++may be surprising.
++
++### 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}"`.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7095594fb2e7371c73c0adf15caab8359d03e850
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++### What it does
++Checks for usage of `unimplemented!`.
++
++### Why is this bad?
++This macro should not be present in production code
++
++### Example
++```
++unimplemented!();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cca24093d40df18204fc5403e74a9801767fa76c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++### What it does
++Checks for `MaybeUninit::uninit().assume_init()`.
++
++### Why is this bad?
++For most types, this is undefined behavior.
++
++### Known problems
++For now, we accept empty tuples and tuples / arrays
++of `MaybeUninit`. There may be other types that allow uninitialized
++data, but those are not yet rigorously defined.
++
++### Example
++```
++// Beware the UB
++use std::mem::MaybeUninit;
++
++let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
++```
++
++Note that the following is OK:
++
++```
++use std::mem::MaybeUninit;
++
++let _: [MaybeUninit<bool>; 5] = unsafe {
++    MaybeUninit::uninit().assume_init()
++};
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cd50afe78f6f28feefea4d27b13fa72123882ce7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,41 @@@
++### What it does
++Checks for `set_len()` call that creates `Vec` with uninitialized elements.
++This is commonly caused by calling `set_len()` right after allocating or
++reserving a buffer with `new()`, `default()`, `with_capacity()`, or `reserve()`.
++
++### Why is this bad?
++It creates a `Vec` with uninitialized data, which leads to
++undefined behavior with most safe operations. Notably, uninitialized
++`Vec<u8>` must not be used with generic `Read`.
++
++Moreover, calling `set_len()` on a `Vec` created with `new()` or `default()`
++creates out-of-bound values that lead to heap memory corruption when used.
++
++### Known Problems
++This lint only checks directly adjacent statements.
++
++### Example
++```
++let mut vec: Vec<u8> = Vec::with_capacity(1000);
++unsafe { vec.set_len(1000); }
++reader.read(&mut vec); // undefined behavior!
++```
++
++### How to fix?
++1. Use an initialized buffer:
++   ```rust,ignore
++   let mut vec: Vec<u8> = vec![0; 1000];
++   reader.read(&mut vec);
++   ```
++2. Wrap the content in `MaybeUninit`:
++   ```rust,ignore
++   let mut vec: Vec<MaybeUninit<T>> = Vec::with_capacity(1000);
++   vec.set_len(1000);  // `MaybeUninit` can be uninitialized
++   ```
++3. If you are on 1.60.0 or later, `Vec::spare_capacity_mut()` is available:
++   ```rust,ignore
++   let mut vec: Vec<u8> = Vec::with_capacity(1000);
++   let remaining = vec.spare_capacity_mut();  // `&mut [MaybeUninit<u8>]`
++   // perform initialization with `remaining`
++   vec.set_len(...);  // Safe to call `set_len()` on initialized part
++   ```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eb83403bb275c0b078b4cee4a94e20d1d4a89e8a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++### What it does
++Checks for passing a unit value as an argument to a function without using a
++unit literal (`()`).
++
++### Why is this bad?
++This is likely the result of an accidental semicolon.
++
++### Example
++```
++foo({
++    let a = bar();
++    baz(a);
++})
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6f3d62010dc51ab1ee3a5207b961c67077c9ac28
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,33 @@@
++### What it does
++Checks for comparisons to unit. This includes all binary
++comparisons (like `==` and `<`) and asserts.
++
++### Why is this bad?
++Unit is always equal to itself, and thus is just a
++clumsily written constant. Mostly this happens when someone accidentally
++adds semicolons at the end of the operands.
++
++### Example
++```
++if {
++    foo();
++} == {
++    bar();
++} {
++    baz();
++}
++```
++is equal to
++```
++{
++    foo();
++    bar();
++    baz();
++}
++```
++
++For asserts:
++```
++assert_eq!({ foo(); }, { bar(); });
++```
++will always succeed
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a22d2994602ac67fcd2c8c050088906baf0ff64d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++### What it does
++Detects `().hash(_)`.
++
++### Why is this bad?
++Hashing a unit value doesn't do anything as the implementation of `Hash` for `()` is a no-op.
++
++### Example
++```
++match my_enum {
++      Empty => ().hash(&mut state),
++      WithValue(x) => x.hash(&mut state),
++}
++```
++Use instead:
++```
++match my_enum {
++      Empty => 0_u8.hash(&mut state),
++      WithValue(x) => x.hash(&mut state),
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..781feac5afcfc972a17657970fe9a75ea990c594
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++### What it does
++Checks for functions that expect closures of type
++Fn(...) -> Ord where the implemented closure returns the unit type.
++The lint also suggests to remove the semi-colon at the end of the statement if present.
++
++### Why is this bad?
++Likely, returning the unit type is unintentional, and
++could simply be caused by an extra semi-colon. Since () implements Ord
++it doesn't cause a compilation error.
++This is the same reasoning behind the unit_cmp lint.
++
++### Known problems
++If returning unit is intentional, then there is no
++way of specifying this without triggering needless_return lint
++
++### Example
++```
++let mut twins = vec!((1, 1), (2, 2));
++twins.sort_by_key(|x| { x.1; });
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..603f2606099c8317e4c0ffda8d0c86e58565768e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++Checks for casts to the same type, casts of int literals to integer types
++and casts of float literals to float types.
++
++### Why is this bad?
++It's just unnecessary.
++
++### Example
++```
++let _ = 2i32 as i32;
++let _ = 0.5 as f32;
++```
++
++Better:
++
++```
++let _ = 2_i32;
++let _ = 0.5_f32;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b19341ecf660e52a9c6bac109c57355607a904a4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Checks for `filter_map` calls that could be replaced by `filter` or `map`.
++More specifically it checks if the closure provided is only performing one of the
++filter or map operations and suggests the appropriate option.
++
++### Why is this bad?
++Complexity. The intent is also clearer if only a single
++operation is being performed.
++
++### Example
++```
++let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None });
++
++// As there is no transformation of the argument this could be written as:
++let _ = (0..3).filter(|&x| x > 2);
++```
++
++```
++let _ = (0..4).filter_map(|x| Some(x + 1));
++
++// As there is no conditional check on the argument this could be written as:
++let _ = (0..4).map(|x| x + 1);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f9444dc48ad6ad1e44db3fa046d89bf78de11df4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Checks for `find_map` calls that could be replaced by `find` or `map`. More
++specifically it checks if the closure provided is only performing one of the
++find or map operations and suggests the appropriate option.
++
++### Why is this bad?
++Complexity. The intent is also clearer if only a single
++operation is being performed.
++
++### Example
++```
++let _ = (0..3).find_map(|x| if x > 2 { Some(x) } else { None });
++
++// As there is no transformation of the argument this could be written as:
++let _ = (0..3).find(|&x| x > 2);
++```
++
++```
++let _ = (0..4).find_map(|x| Some(x + 1));
++
++// As there is no conditional check on the argument this could be written as:
++let _ = (0..4).map(|x| x + 1).next();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e1b0e65f51971971a41a2495cc0493924ddaa664
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for using `fold` when a more succinct alternative exists.
++Specifically, this checks for `fold`s which could be replaced by `any`, `all`,
++`sum` or `product`.
++
++### Why is this bad?
++Readability.
++
++### Example
++```
++(0..3).fold(false, |acc, x| acc || x > 2);
++```
++
++Use instead:
++```
++(0..3).any(|x| x > 2);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ee4e78601f847ac4d225a795ef9ea74da44dd3d1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++### What it does
++Checks for use of `.collect::<Vec<String>>().join("")` on iterators.
++
++### Why is this bad?
++`.collect::<String>()` is more concise and might be more performant
++
++### Example
++```
++let vector = vec!["hello",  "world"];
++let output = vector.iter().map(|item| item.to_uppercase()).collect::<Vec<String>>().join("");
++println!("{}", output);
++```
++The correct use would be:
++```
++let vector = vec!["hello",  "world"];
++let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
++println!("{}", output);
++```
++### Known problems
++While `.collect::<String>()` is sometimes more performant, there are cases where
++using `.collect::<String>()` over `.collect::<Vec<String>>().join("")`
++will prevent loop unrolling and will result in a negative performance impact.
++
++Additionally, differences have been observed between aarch64 and x86_64 assembly output,
++with aarch64 tending to producing faster assembly in more cases when using `.collect::<String>()`
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..208188ce971a5827930587d7bbe1de91a6a2b95f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++### What it does
++As the counterpart to `or_fun_call`, this lint looks for unnecessary
++lazily evaluated closures on `Option` and `Result`.
++
++This lint suggests changing the following functions, when eager evaluation results in
++simpler code:
++ - `unwrap_or_else` to `unwrap_or`
++ - `and_then` to `and`
++ - `or_else` to `or`
++ - `get_or_insert_with` to `get_or_insert`
++ - `ok_or_else` to `ok_or`
++
++### Why is this bad?
++Using eager evaluation is shorter and simpler in some cases.
++
++### Known problems
++It is possible, but not recommended for `Deref` and `Index` to have
++side effects. Eagerly evaluating them can change the semantics of the program.
++
++### Example
++```
++// example code where clippy issues a warning
++let opt: Option<u32> = None;
++
++opt.unwrap_or_else(|| 42);
++```
++Use instead:
++```
++let opt: Option<u32> = None;
++
++opt.unwrap_or(42);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2f8bdd113dfdb7d8570448abcf67396751add24a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Detects passing a mutable reference to a function that only
++requires an immutable reference.
++
++### Why is this bad?
++The mutable reference rules out all other references to
++the value. Also the code misleads about the intent of the call site.
++
++### Example
++```
++vec.push(&mut value);
++```
++
++Use instead:
++```
++vec.push(&value);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7f455e264cb30cd2fb033fc174ebe6dc36202b66
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++### What it does
++Checks for expression statements that can be reduced to a
++sub-expression.
++
++### Why is this bad?
++Expressions by themselves often have no side-effects.
++Having such expressions reduces readability.
++
++### Example
++```
++compute_array()[0];
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8cd9fba603e06246db75052b9d0d043f2ce55c6d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++
++Detects cases of owned empty strings being passed as an argument to a function expecting `&str`
++
++### Why is this bad?
++
++This results in longer and less readable code
++
++### Example
++```
++vec!["1", "2", "3"].join(&String::new());
++```
++Use instead:
++```
++vec!["1", "2", "3"].join("");
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b909cd5a76dae930ac7370bc45d4eb1205cb75b8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++Checks for imports ending in `::{self}`.
++
++### Why is this bad?
++In most cases, this can be written much more cleanly by omitting `::{self}`.
++
++### Known problems
++Removing `::{self}` will cause any non-module items at the same path to also be imported.
++This might cause a naming conflict (https://github.com/rust-lang/rustfmt/issues/3568). This lint makes no attempt
++to detect this scenario and that is why it is a restriction lint.
++
++### Example
++```
++use std::io::{self};
++```
++Use instead:
++```
++use std::io;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6913b62c48eb4b9445095309c6290ba3d125f943
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++Detects uses of `Vec::sort_by` passing in a closure
++which compares the two arguments, either directly or indirectly.
++
++### Why is this bad?
++It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
++possible) than to use `Vec::sort_by` and a more complicated
++closure.
++
++### Known problems
++If the suggested `Vec::sort_by_key` uses Reverse and it isn't already
++imported by a use statement, then it will need to be added manually.
++
++### Example
++```
++vec.sort_by(|a, b| a.foo().cmp(&b.foo()));
++```
++Use instead:
++```
++vec.sort_by_key(|a| a.foo());
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5d4213bdaf8994797437acbd2ef924d98f6e2c9d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++Checks for unnecessary calls to [`ToOwned::to_owned`](https://doc.rust-lang.org/std/borrow/trait.ToOwned.html#tymethod.to_owned)
++and other `to_owned`-like functions.
++
++### Why is this bad?
++The unnecessary calls result in useless allocations.
++
++### Known problems
++`unnecessary_to_owned` can falsely trigger if `IntoIterator::into_iter` is applied to an
++owned copy of a resource and the resource is later used mutably. See
++[#8148](https://github.com/rust-lang/rust-clippy/issues/8148).
++
++### Example
++```
++let path = std::path::Path::new("x");
++foo(&path.to_string_lossy().to_string());
++fn foo(s: &str) {}
++```
++Use instead:
++```
++let path = std::path::Path::new("x");
++foo(&path.to_string_lossy());
++fn foo(s: &str) {}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..50ae845bb44f783952708296f98531b5a2d0f2e1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++### What it does
++Checks for calls of `unwrap[_err]()` that cannot fail.
++
++### Why is this bad?
++Using `if let` or `match` is more idiomatic.
++
++### Example
++```
++if option.is_some() {
++    do_something_with(option.unwrap())
++}
++```
++
++Could be written:
++
++```
++if let Some(value) = option {
++    do_something_with(value)
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c0a23d492889a003c9b4a5380e97a267fdb5e309
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++### What it does
++Checks for private functions that only return `Ok` or `Some`.
++
++### Why is this bad?
++It is not meaningful to wrap values when no `None` or `Err` is returned.
++
++### Known problems
++There can be false positives if the function signature is designed to
++fit some external requirement.
++
++### Example
++```
++fn get_cool_number(a: bool, b: bool) -> Option<i32> {
++    if a && b {
++        return Some(50);
++    }
++    if a {
++        Some(0)
++    } else {
++        Some(10)
++    }
++}
++```
++Use instead:
++```
++fn get_cool_number(a: bool, b: bool) -> i32 {
++    if a && b {
++        return 50;
++    }
++    if a {
++        0
++    } else {
++        10
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3cd00a0f3e3843e15e7ba7d4d75a06786618c5ac
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Checks for structure field patterns bound to wildcards.
++
++### Why is this bad?
++Using `..` instead is shorter and leaves the focus on
++the fields that are actually bound.
++
++### Example
++```
++let f = Foo { a: 0, b: 0, c: 0 };
++
++match f {
++    Foo { a: _, b: 0, .. } => {},
++    Foo { a: _, b: _, c: _ } => {},
++}
++```
++
++Use instead:
++```
++let f = Foo { a: 0, b: 0, c: 0 };
++
++match f {
++    Foo { b: 0, .. } => {},
++    Foo { .. } => {},
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..817061efd1622bd388d649ec41f3faeb8154c87a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++### What it does
++Checks for tuple patterns with a wildcard
++pattern (`_`) is next to a rest pattern (`..`).
++
++_NOTE_: While `_, ..` means there is at least one element left, `..`
++means there are 0 or more elements left. This can make a difference
++when refactoring, but shouldn't result in errors in the refactored code,
++since the wildcard pattern isn't used anyway.
++
++### Why is this bad?
++The wildcard pattern is unneeded as the rest pattern
++can match that element as well.
++
++### Example
++```
++match t {
++    TupleStruct(0, .., _) => (),
++    _ => (),
++}
++```
++
++Use instead:
++```
++match t {
++    TupleStruct(0, ..) => (),
++    _ => (),
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..49c45d4ee5e9ce753c4085c5505434a92a8240e7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for unnested or-patterns, e.g., `Some(0) | Some(2)` and
++suggests replacing the pattern with a nested one, `Some(0 | 2)`.
++
++Another way to think of this is that it rewrites patterns in
++*disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*.
++
++### Why is this bad?
++In the example above, `Some` is repeated, which unnecessarily complicates the pattern.
++
++### Example
++```
++fn main() {
++    if let Some(0) | Some(2) = Some(0) {}
++}
++```
++Use instead:
++```
++fn main() {
++    if let Some(0 | 2) = Some(0) {}
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..10469ca77454f4588425b0b008a60c8a38e1068b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++### What it does
++Checks for usage of `unreachable!`.
++
++### Why is this bad?
++This macro can cause code to panic
++
++### Example
++```
++unreachable!();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e168f90a84c19a56a23eeeffc1db1a2b3b0b254e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Warns if a long integral or floating-point constant does
++not contain underscores.
++
++### Why is this bad?
++Reading long numbers is difficult without separators.
++
++### Example
++```
++61864918973511
++```
++
++Use instead:
++```
++61_864_918_973_511
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f56c48044f4fa9d21e623a2f7062e9bf4d59b6f6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++### What it does
++Checks for deriving `serde::Deserialize` on a type that
++has methods using `unsafe`.
++
++### Why is this bad?
++Deriving `serde::Deserialize` will create a constructor
++that may violate invariants hold by another constructor.
++
++### Example
++```
++use serde::Deserialize;
++
++#[derive(Deserialize)]
++pub struct Foo {
++    // ..
++}
++
++impl Foo {
++    pub fn new() -> Self {
++        // setup here ..
++    }
++
++    pub unsafe fn parts() -> (&str, &str) {
++        // assumes invariants hold
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6f55c1815dd65001fb98d508923d3ac403bacc9f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks for imports that remove "unsafe" from an item's
++name.
++
++### Why is this bad?
++Renaming makes it less clear which traits and
++structures are unsafe.
++
++### Example
++```
++use std::cell::{UnsafeCell as TotallySafeCell};
++
++extern crate crossbeam;
++use crossbeam::{spawn_unsafe as spawn};
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d80248e34d0c7f880b429fd21fdbdde0e5b0127a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Warns if literal suffixes are not separated by an
++underscore.
++To enforce unseparated literal suffix style,
++see the `separated_literal_suffix` lint.
++
++### Why is this bad?
++Suffix style should be consistent.
++
++### Example
++```
++123832i32
++```
++
++Use instead:
++```
++123832_i32
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..29db9258e83fb3cd17c4c00ab011997000b06c41
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++### What it does
++Checks for transmutes between collections whose
++types have different ABI, size or alignment.
++
++### Why is this bad?
++This is undefined behavior.
++
++### Known problems
++Currently, we cannot know whether a type is a
++collection, so we just lint the ones that come with `std`.
++
++### Example
++```
++// different size, therefore likely out-of-bounds memory access
++// You absolutely do not want this in your code!
++unsafe {
++    std::mem::transmute::<_, Vec<u32>>(vec![2_u16])
++};
++```
++
++You must always iterate, map and collect the values:
++
++```
++vec![2_u16].into_iter().map(u32::from).collect::<Vec<_>>();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..26def11aa17a51a3f903834a8afda06294b4cc44
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Checks for functions that are declared `async` but have no `.await`s inside of them.
++
++### Why is this bad?
++Async functions with no async code create overhead, both mentally and computationally.
++Callers of async methods either need to be calling from an async function themselves or run it on an executor, both of which
++causes runtime overhead and hassle for the caller.
++
++### Example
++```
++async fn get_random_number() -> i64 {
++    4 // Chosen by fair dice roll. Guaranteed to be random.
++}
++let number_future = get_random_number();
++```
++
++Use instead:
++```
++fn get_random_number_improved() -> i64 {
++    4 // Chosen by fair dice roll. Guaranteed to be random.
++}
++let number_future = async { get_random_number_improved() };
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fbc4c299c7bb747a611720c57697cc49973dc190
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++### What it does
++Checks for unused written/read amount.
++
++### Why is this bad?
++`io::Write::write(_vectored)` and
++`io::Read::read(_vectored)` are not guaranteed to
++process the entire buffer. They return how many bytes were processed, which
++might be smaller
++than a given buffer's length. If you don't need to deal with
++partial-write/read, use
++`write_all`/`read_exact` instead.
++
++When working with asynchronous code (either with the `futures`
++crate or with `tokio`), a similar issue exists for
++`AsyncWriteExt::write()` and `AsyncReadExt::read()` : these
++functions are also not guaranteed to process the entire
++buffer.  Your code should either handle partial-writes/reads, or
++call the `write_all`/`read_exact` methods on those traits instead.
++
++### Known problems
++Detects only common patterns.
++
++### Examples
++```
++use std::io;
++fn foo<W: io::Write>(w: &mut W) -> io::Result<()> {
++    // must be `w.write_all(b"foo")?;`
++    w.write(b"foo")?;
++    Ok(())
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..268de1ce3bec33e43891ad3178d59cf6cf135431
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Checks for the creation of a `peekable` iterator that is never `.peek()`ed
++
++### Why is this bad?
++Creating a peekable iterator without using any of its methods is likely a mistake,
++or just a leftover after a refactor.
++
++### Example
++```
++let collection = vec![1, 2, 3];
++let iter = collection.iter().peekable();
++
++for item in iter {
++    // ...
++}
++```
++
++Use instead:
++```
++let collection = vec![1, 2, 3];
++let iter = collection.iter();
++
++for item in iter {
++    // ...
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..70947aceee77b7e4ed3590f178c539cbe09febd2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++
++Detects cases where a whole-number literal float is being rounded, using
++the `floor`, `ceil`, or `round` methods.
++
++### Why is this bad?
++
++This is unnecessary and confusing to the reader. Doing this is probably a mistake.
++
++### Example
++```
++let x = 1f32.ceil();
++```
++Use instead:
++```
++let x = 1f32;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a8d0fc7598954e911a026def6fd14f5f55ade3a8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Checks methods that contain a `self` argument but don't use it
++
++### Why is this bad?
++It may be clearer to define the method as an associated function instead
++of an instance method if it doesn't require `self`.
++
++### Example
++```
++struct A;
++impl A {
++    fn method(&self) {}
++}
++```
++
++Could be written:
++
++```
++struct A;
++impl A {
++    fn method() {}
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..48d16ca655232600b9bfc489bf78319ab468441b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for unit (`()`) expressions that can be removed.
++
++### Why is this bad?
++Such expressions add no value, but can make the code
++less readable. Depending on formatting they can make a `break` or `return`
++statement look like a function call.
++
++### Example
++```
++fn return_unit() -> () {
++    ()
++}
++```
++is equivalent to
++```
++fn return_unit() {}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9a1f132a6112fb335b129dfb1fdf14ff892b4719
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++### What it does
++Warns if hexadecimal or binary literals are not grouped
++by nibble or byte.
++
++### Why is this bad?
++Negatively impacts readability.
++
++### Example
++```
++let x: u32 = 0xFFF_FFF;
++let y: u8 = 0b01_011_101;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7497dd863d3545889b9291749340e83e77071d3a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,39 @@@
++### What it does
++Checks for functions of type `Result` that contain `expect()` or `unwrap()`
++
++### Why is this bad?
++These functions promote recoverable errors to non-recoverable errors which may be undesirable in code bases which wish to avoid panics.
++
++### Known problems
++This can cause false positives in functions that handle both recoverable and non recoverable errors.
++
++### Example
++Before:
++```
++fn divisible_by_3(i_str: String) -> Result<(), String> {
++    let i = i_str
++        .parse::<i32>()
++        .expect("cannot divide the input by three");
++
++    if i % 3 != 0 {
++        Err("Number is not divisible by 3")?
++    }
++
++    Ok(())
++}
++```
++
++After:
++```
++fn divisible_by_3(i_str: String) -> Result<(), String> {
++    let i = i_str
++        .parse::<i32>()
++        .map_err(|e| format!("cannot divide the input by three: {}", e))?;
++
++    if i % 3 != 0 {
++        Err("Number is not divisible by 3")?
++    }
++
++    Ok(())
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..34b4cf088581fdc98130541a17fbe9a8a0317c86
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for usages of `_.unwrap_or_else(Default::default)` on `Option` and
++`Result` values.
++
++### Why is this bad?
++Readability, these can be written as `_.unwrap_or_default`, which is
++simpler and more concise.
++
++### Examples
++```
++x.unwrap_or_else(Default::default);
++x.unwrap_or_else(u32::default);
++```
++
++Use instead:
++```
++x.unwrap_or_default();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9b4713df515d65e2bdf91498d9d2577ff12a6635
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++### What it does
++Checks for `.unwrap()` or `.unwrap_err()` calls on `Result`s and `.unwrap()` call on `Option`s.
++
++### Why is this bad?
++It is better to handle the `None` or `Err` case,
++or at least call `.expect(_)` with a more helpful message. Still, for a lot of
++quick-and-dirty code, `unwrap` is a good choice, which is why this lint is
++`Allow` by default.
++
++`result.unwrap()` will let the thread panic on `Err` values.
++Normally, you want to implement more sophisticated error handling,
++and propagate errors upwards with `?` operator.
++
++Even if you want to panic on errors, not all `Error`s implement good
++messages on display. Therefore, it may be beneficial to look at the places
++where they may get displayed. Activate this lint to do just that.
++
++### Examples
++```
++option.unwrap();
++result.unwrap();
++```
++
++Use instead:
++```
++option.expect("more helpful message");
++result.expect("more helpful message");
++```
++
++If [expect_used](#expect_used) is enabled, instead:
++```
++option?;
++
++// or
++
++result?;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a1e39c7e10c6e8529c4ccc6d332c5a981e68ce70
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++### What it does
++Checks for fully capitalized names and optionally names containing a capitalized acronym.
++
++### Why is this bad?
++In CamelCase, acronyms count as one word.
++See [naming conventions](https://rust-lang.github.io/api-guidelines/naming.html#casing-conforms-to-rfc-430-c-case)
++for more.
++
++By default, the lint only triggers on fully-capitalized names.
++You can use the `upper-case-acronyms-aggressive: true` config option to enable linting
++on all camel case names
++
++### Known problems
++When two acronyms are contiguous, the lint can't tell where
++the first acronym ends and the second starts, so it suggests to lowercase all of
++the letters in the second acronym.
++
++### Example
++```
++struct HTTPResponse;
++```
++Use instead:
++```
++struct HttpResponse;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..94d4a6fd29ae5d2b7c99c1eef0e4575f75538087
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++### What it does
++Checks for use of `Debug` formatting. The purpose of this
++lint is to catch debugging remnants.
++
++### Why is this bad?
++The purpose of the `Debug` trait is to facilitate
++debugging Rust code. It should not be used in user-facing output.
++
++### Example
++```
++println!("{:?}", foo);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bd37ed1e0025634ec48508fc2e9e3f01529ec94d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++### What it does
++Checks for unnecessary repetition of structure name when a
++replacement with `Self` is applicable.
++
++### Why is this bad?
++Unnecessary repetition. Mixed use of `Self` and struct
++name
++feels inconsistent.
++
++### Known problems
++- Unaddressed false negative in fn bodies of trait implementations
++- False positive with associated types in traits (#4140)
++
++### Example
++```
++struct Foo;
++impl Foo {
++    fn new() -> Foo {
++        Foo {}
++    }
++}
++```
++could be
++```
++struct Foo;
++impl Foo {
++    fn new() -> Self {
++        Self {}
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ed67c41eb0dbf2f935273adb96ddd14579c604dc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++### What it does
++Checks for the use of bindings with a single leading
++underscore.
++
++### Why is this bad?
++A single leading underscore is usually used to indicate
++that a binding will not be used. Using such a binding breaks this
++expectation.
++
++### Known problems
++The lint does not work properly with desugaring and
++macro, it has been allowed in the mean time.
++
++### Example
++```
++let _x = 0;
++let y = _x + 1; // Here we are using `_x`, even though it has a leading
++                // underscore. We should rename `_x` to `x`
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f777cd3775edf5fc17526b681e2f274c36c094e5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for usage of `.as_ref()` or `.as_mut()` where the
++types before and after the call are the same.
++
++### Why is this bad?
++The call is unnecessary.
++
++### Example
++```
++let x: &[i32] = &[1, 2, 3, 4, 5];
++do_stuff(x.as_ref());
++```
++The correct use would be:
++```
++let x: &[i32] = &[1, 2, 3, 4, 5];
++do_stuff(x);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e02d4c90789841e64801ded5d4a3a51d2ad4f0c3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++### What it does
++Checks for `extern crate` and `use` items annotated with
++lint attributes.
++
++This lint permits lint attributes for lints emitted on the items themself.
++For `use` items these lints are:
++* deprecated
++* unreachable_pub
++* unused_imports
++* clippy::enum_glob_use
++* clippy::macro_use_imports
++* clippy::wildcard_imports
++
++For `extern crate` items these lints are:
++* `unused_imports` on items with `#[macro_use]`
++
++### Why is this bad?
++Lint attributes have no effect on crate imports. Most
++likely a `!` was forgotten.
++
++### Example
++```
++#[deny(dead_code)]
++extern crate foo;
++#[forbid(dead_code)]
++use foo::bar;
++```
++
++Use instead:
++```
++#[allow(unused_imports)]
++use foo::baz;
++#[allow(unused_imports)]
++#[macro_use]
++extern crate baz;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..06000a7ad98d5199cb5f41fdc590d49baaf3aa02
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` calls
++which uselessly convert to the same type.
++
++### Why is this bad?
++Redundant code.
++
++### Example
++```
++// format!() returns a `String`
++let s: String = format!("hello").into();
++```
++
++Use instead:
++```
++let s: String = format!("hello");
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eb4819da1e95aa6064c50f8c4022b5ec948c0193
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for the use of `format!("string literal with no
++argument")` and `format!("{}", foo)` where `foo` is a string.
++
++### Why is this bad?
++There is no point of doing that. `format!("foo")` can
++be replaced by `"foo".to_owned()` if you really need a `String`. The even
++worse `&format!("foo")` is often encountered in the wild. `format!("{}",
++foo)` can be replaced by `foo.clone()` if `foo: String` or `foo.to_owned()`
++if `foo: &str`.
++
++### Examples
++```
++let foo = "foo";
++format!("{}", foo);
++```
++
++Use instead:
++```
++let foo = "foo";
++foo.to_owned();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c6dcd57eb2e2dacc8d13a22434880329ca74df73
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,39 @@@
++### What it does
++Checks for variable declarations immediately followed by a
++conditional affectation.
++
++### Why is this bad?
++This is not idiomatic Rust.
++
++### Example
++```
++let foo;
++
++if bar() {
++    foo = 42;
++} else {
++    foo = 0;
++}
++
++let mut baz = None;
++
++if bar() {
++    baz = Some(42);
++}
++```
++
++should be written
++
++```
++let foo = if bar() {
++    42
++} else {
++    0
++};
++
++let baz = if bar() {
++    Some(42)
++} else {
++    None
++};
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1d3a1758814558519be3e95fecba2b229541fbc0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++### What it does
++Checks for transmutes to the original type of the object
++and transmutes that could be a cast.
++
++### Why is this bad?
++Readability. The code tricks people into thinking that
++something complex is going on.
++
++### Example
++```
++core::intrinsics::transmute(t); // where the result type is the same as `t`'s
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ee5afc99e4bf274ebf0c71ba4a0677f4e7f7bffb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++Checks for usage of `&vec![..]` when using `&[..]` would
++be possible.
++
++### Why is this bad?
++This is less efficient.
++
++### Example
++```
++fn foo(_x: &[u8]) {}
++
++foo(&vec![1, 2]);
++```
++
++Use instead:
++```
++foo(&[1, 2]);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..701b1c9ce9b94d035bee84864b05b6b7853b3c99
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++### What it does
++Checks for use of `Vec<Box<T>>` where T: Sized anywhere in the code.
++Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
++
++### Why is this bad?
++`Vec` already keeps its contents in a separate area on
++the heap. So if you `Box` its contents, you just add another level of indirection.
++
++### Known problems
++Vec<Box<T: Sized>> makes sense if T is a large type (see [#3530](https://github.com/rust-lang/rust-clippy/issues/3530),
++1st comment).
++
++### Example
++```
++struct X {
++    values: Vec<Box<i32>>,
++}
++```
++
++Better:
++
++```
++struct X {
++    values: Vec<i32>,
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..445f2874796bbd81fd1c6872cdcc6bad812c6ff8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,23 @@@
++### What it does
++Checks for calls to `push` immediately after creating a new `Vec`.
++
++If the `Vec` is created using `with_capacity` this will only lint if the capacity is a
++constant and the number of pushes is greater than or equal to the initial capacity.
++
++If the `Vec` is extended after the initial sequence of pushes and it was default initialized
++then this will only lint after there were at least four pushes. This number may change in
++the future.
++
++### Why is this bad?
++The `vec![]` macro is both more performant and easier to read than
++multiple `push` calls.
++
++### Example
++```
++let mut v = Vec::new();
++v.push(0);
++```
++Use instead:
++```
++let v = vec![0];
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0b92686772bb089fdc6b1c48f10f93c24b5f3a3f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Finds occurrences of `Vec::resize(0, an_int)`
++
++### Why is this bad?
++This is probably an argument inversion mistake.
++
++### Example
++```
++vec!(1, 2, 3, 4, 5).resize(0, 5)
++```
++
++Use instead:
++```
++vec!(1, 2, 3, 4, 5).clear()
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..87a84702925eda9254d39c70c1d1364dd185fe4a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks for bit masks that can be replaced by a call
++to `trailing_zeros`
++
++### Why is this bad?
++`x.trailing_zeros() > 4` is much clearer than `x & 15
++== 0`
++
++### Known problems
++llvm generates better code for `x & 15 == 0` on x86
++
++### Example
++```
++if x & 0b1111 == 0 { }
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9703df423a57574e9500815719f0eda1c12b0330
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for use of File::read_to_end and File::read_to_string.
++
++### Why is this bad?
++`fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values.
++See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html)
++
++### Example
++```
++let mut f = File::open("foo.txt").unwrap();
++let mut bytes = Vec::new();
++f.read_to_end(&mut bytes).unwrap();
++```
++Can be written more concisely as
++```
++let mut bytes = fs::read("foo.txt").unwrap();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4a34e4ba78ef4cf5d448a637d9022698ecb13857
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++### What it does
++Checks for comparisons with an address of a trait vtable.
++
++### Why is this bad?
++Comparing trait objects pointers compares an vtable addresses which
++are not guaranteed to be unique and could vary between different code generation units.
++Furthermore vtables for different types could have the same address after being merged
++together.
++
++### Example
++```
++let a: Rc<dyn Trait> = ...
++let b: Rc<dyn Trait> = ...
++if Rc::ptr_eq(&a, &b) {
++    ...
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..71800701f489717154a3f9dedf896b1a34cad092
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++### What it does
++Checks whether variables used within while loop condition
++can be (and are) mutated in the body.
++
++### Why is this bad?
++If the condition is unchanged, entering the body of the loop
++will lead to an infinite loop.
++
++### Known problems
++If the `while`-loop is in a closure, the check for mutation of the
++condition variables in the body can cause false negatives. For example when only `Upvar` `a` is
++in the condition and only `Upvar` `b` gets mutated in the body, the lint will not trigger.
++
++### Example
++```
++let i = 0;
++while i > 10 {
++    println!("let me loop forever!");
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ab7bf60975ec563f5904bcb1d525fcc64b202dfb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++### What it does
++Detects `loop + match` combinations that are easier
++written as a `while let` loop.
++
++### Why is this bad?
++The `while let` loop is usually shorter and more
++readable.
++
++### Known problems
++Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)).
++
++### Example
++```
++loop {
++    let x = match y {
++        Some(x) => x,
++        None => break,
++    };
++    // .. do something with x
++}
++// is easier written as
++while let Some(x) = y {
++    // .. do something with x
++};
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..af053c541199d0e62c4165410f5cd4859de11bb7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++### What it does
++Checks for `while let` expressions on iterators.
++
++### Why is this bad?
++Readability. A simple `for` loop is shorter and conveys
++the intent better.
++
++### Example
++```
++while let Some(val) = iter.next() {
++    ..
++}
++```
++
++Use instead:
++```
++for val in &mut iter {
++    ..
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2affaf9740d9f1c9fb8d1287d19c1636fdb82d96
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++### What it does
++Checks for wildcard dependencies in the `Cargo.toml`.
++
++### Why is this bad?
++[As the edition guide says](https://rust-lang-nursery.github.io/edition-guide/rust-2018/cargo-and-crates-io/crates-io-disallows-wildcard-dependencies.html),
++it is highly unlikely that you work with any possible version of your dependency,
++and wildcard dependencies would cause unnecessary breakage in the ecosystem.
++
++### Example
++```
++[dependencies]
++regex = "*"
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..09807c01c652b9635eefb8c775bf98449fee81f8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++### What it does
++Checks for wildcard enum matches using `_`.
++
++### Why is this bad?
++New enum variants added by library updates can be missed.
++
++### Known problems
++Suggested replacements may be incorrect if guards exhaustively cover some
++variants, and also may not use correct path to enum if it's not present in the current scope.
++
++### Example
++```
++match x {
++    Foo::A(_) => {},
++    _ => {},
++}
++```
++
++Use instead:
++```
++match x {
++    Foo::A(_) => {},
++    Foo::B(_) => {},
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bd56aa5b082820168b277d9af2f47916c402c86d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,45 @@@
++### What it does
++Checks for wildcard imports `use _::*`.
++
++### Why is this bad?
++wildcard imports can pollute the namespace. This is especially bad if
++you try to import something through a wildcard, that already has been imported by name from
++a different source:
++
++```
++use crate1::foo; // Imports a function named foo
++use crate2::*; // Has a function named foo
++
++foo(); // Calls crate1::foo
++```
++
++This can lead to confusing error messages at best and to unexpected behavior at worst.
++
++### Exceptions
++Wildcard imports are allowed from modules named `prelude`. Many crates (including the standard library)
++provide modules named "prelude" specifically designed for wildcard import.
++
++`use super::*` is allowed in test modules. This is defined as any module with "test" in the name.
++
++These exceptions can be disabled using the `warn-on-all-wildcard-imports` configuration flag.
++
++### Known problems
++If macros are imported through the wildcard, this macro is not included
++by the suggestion and has to be added by hand.
++
++Applying the suggestion when explicit imports of the things imported with a glob import
++exist, may result in `unused_imports` warnings.
++
++### Example
++```
++use crate1::*;
++
++foo();
++```
++
++Use instead:
++```
++use crate1::foo;
++
++foo();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..70468ca41e0b8b91f0ce6def20b2601bd243381f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++### What it does
++Checks for wildcard pattern used with others patterns in same match arm.
++
++### Why is this bad?
++Wildcard pattern already covers any other pattern as it will match anyway.
++It makes the code less readable, especially to spot wildcard pattern use in match arm.
++
++### Example
++```
++match s {
++    "a" => {},
++    "bar" | _ => {},
++}
++```
++
++Use instead:
++```
++match s {
++    "a" => {},
++    _ => {},
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9c41a48f9f73134c7aa1113aae76fa62f79db36c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,21 @@@
++### What it does
++This lint warns about the use of literals as `write!`/`writeln!` args.
++
++### Why is this bad?
++Using literals as `writeln!` args is inefficient
++(c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
++(i.e., just put the literal in the format string)
++
++### Known problems
++Will also warn with macro calls as arguments that expand to literals
++-- e.g., `writeln!(buf, "{}", env!("FOO"))`.
++
++### Example
++```
++writeln!(buf, "{}", "foo");
++```
++
++Use instead:
++```
++writeln!(buf, "foo");
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..22845fd6515bdb88841396bfbd64aa2a3b107c15
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++### What it does
++This lint warns when you use `write!()` with a format
++string that
++ends in a newline.
++
++### Why is this bad?
++You should use `writeln!()` instead, which appends the
++newline.
++
++### Example
++```
++write!(buf, "Hello {}!\n", name);
++```
++
++Use instead:
++```
++writeln!(buf, "Hello {}!", name);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3b3aeb79a48bce1fd5e072c60907ae34c2378d75
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++This lint warns when you use `writeln!(buf, "")` to
++print a newline.
++
++### Why is this bad?
++You should use `writeln!(buf)`, which is simpler.
++
++### Example
++```
++writeln!(buf, "");
++```
++
++Use instead:
++```
++writeln!(buf);
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d6b69ab87f862d8335e8e81e91ebc592dc5d6fc2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,39 @@@
++### What it does
++Checks for methods with certain name prefixes and which
++doesn't match how self is taken. The actual rules are:
++
++|Prefix |Postfix     |`self` taken                   | `self` type  |
++|-------|------------|-------------------------------|--------------|
++|`as_`  | none       |`&self` or `&mut self`         | any          |
++|`from_`| none       | none                          | any          |
++|`into_`| none       |`self`                         | any          |
++|`is_`  | none       |`&mut self` or `&self` or none | any          |
++|`to_`  | `_mut`     |`&mut self`                    | any          |
++|`to_`  | not `_mut` |`self`                         | `Copy`       |
++|`to_`  | not `_mut` |`&self`                        | not `Copy`   |
++
++Note: Clippy doesn't trigger methods with `to_` prefix in:
++- Traits definition.
++Clippy can not tell if a type that implements a trait is `Copy` or not.
++- Traits implementation, when `&self` is taken.
++The method signature is controlled by the trait and often `&self` is required for all types that implement the trait
++(see e.g. the `std::string::ToString` trait).
++
++Clippy allows `Pin<&Self>` and `Pin<&mut Self>` if `&self` and `&mut self` is required.
++
++Please find more info here:
++https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv
++
++### Why is this bad?
++Consistency breeds readability. If you follow the
++conventions, your users won't be surprised that they, e.g., need to supply a
++mutable reference to a `as_..` function.
++
++### Example
++```
++impl X {
++    fn as_str(self) -> &'static str {
++        // ..
++    }
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9fc71e0e382e3410834cba1cab328de604f4cf7a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks for transmutes that can't ever be correct on any
++architecture.
++
++### Why is this bad?
++It's basically guaranteed to be undefined behavior.
++
++### Known problems
++When accessing C, users might want to store pointer
++sized objects in `extradata` arguments to save an allocation.
++
++### Example
++```
++let ptr: *const T = core::intrinsics::transmute('x')
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..394de20c0c0c68e59fee4aa54c1ad1393790780b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++### What it does
++Checks for `0.0 / 0.0`.
++
++### Why is this bad?
++It's less readable than `f32::NAN` or `f64::NAN`.
++
++### Example
++```
++let nan = 0.0f32 / 0.0;
++```
++
++Use instead:
++```
++let nan = f32::NAN;
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5c5588725363e5b26a1c45454a790b92949301d7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++### What it does
++Warns if an integral constant literal starts with `0`.
++
++### Why is this bad?
++In some languages (including the infamous C language
++and most of its
++family), this marks an octal constant. In Rust however, this is a decimal
++constant. This could
++be confusing for both the writer and a reader of the constant.
++
++### Example
++
++In Rust:
++```
++fn main() {
++    let a = 0123;
++    println!("{}", a);
++}
++```
++
++prints `123`, while in C:
++
++```
++#include <stdio.h>
++
++int main() {
++    int a = 0123;
++    printf("%d\n", a);
++}
++```
++
++prints `83` (as `83 == 0o123` while `123 == 0o173`).
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e768a02366009d0086fdc2ea89896430d7e6cfb0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++### What it does
++Catch casts from `0` to some pointer type
++
++### Why is this bad?
++This generally means `null` and is better expressed as
++{`std`, `core`}`::ptr::`{`null`, `null_mut`}.
++
++### Example
++```
++let a = 0 as *const u32;
++```
++
++Use instead:
++```
++let a = std::ptr::null::<u32>();
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0502bdbf3950ee6ce1e54669b36e0659ba4625d2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++### What it does
++Checks for maps with zero-sized value types anywhere in the code.
++
++### Why is this bad?
++Since there is only a single value for a zero-sized type, a map
++containing zero sized values is effectively a set. Using a set in that case improves
++readability and communicates intent more clearly.
++
++### Known problems
++* A zero-sized type cannot be recovered later if it contains private fields.
++* This lints the signature of public items
++
++### Example
++```
++fn unique_words(text: &str) -> HashMap<&str, ()> {
++    todo!();
++}
++```
++Use instead:
++```
++fn unique_words(text: &str) -> HashSet<&str> {
++    todo!();
++}
++```
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5810455ee9550196aa5d32be49f06639723aefee
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++### What it does
++Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to
++zero-sized types
++
++### Why is this bad?
++This is a no-op, and likely unintended
++
++### Example
++```
++unsafe { (&() as *const ()).offset(1) };
++```
index 9ee4a40cbf2424715733eeab4e156055f52932e5,0000000000000000000000000000000000000000..4a32e0e54a81b3fdbe579249771fb0c5123e14a2
mode 100644,000000..100644
--- /dev/null
@@@ -1,194 -1,0 +1,207 @@@
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +
 +use rustc_tools_util::VersionInfo;
 +use std::env;
 +use std::path::PathBuf;
 +use std::process::{self, Command};
 +
++mod docs;
++
 +const CARGO_CLIPPY_HELP: &str = r#"Checks a package to catch common mistakes and improve your Rust code.
 +
 +Usage:
 +    cargo clippy [options] [--] [<opts>...]
 +
 +Common options:
 +    --no-deps                Run Clippy only on the given crate, without linting the dependencies
 +    --fix                    Automatically apply lint suggestions. This flag implies `--no-deps`
 +    -h, --help               Print this message
 +    -V, --version            Print version info and exit
++    --explain LINT           Print the documentation for a given lint
 +
 +Other options are the same as `cargo check`.
 +
 +To allow or deny a lint from the command line you can use `cargo clippy --`
 +with:
 +
 +    -W --warn OPT       Set lint warnings
 +    -A --allow OPT      Set lint allowed
 +    -D --deny OPT       Set lint denied
 +    -F --forbid OPT     Set lint forbidden
 +
 +You can use tool lints to allow or deny lints from your code, eg.:
 +
 +    #[allow(clippy::needless_lifetimes)]
 +"#;
 +
 +fn show_help() {
 +    println!("{}", CARGO_CLIPPY_HELP);
 +}
 +
 +fn show_version() {
 +    let version_info = rustc_tools_util::get_version_info!();
 +    println!("{}", version_info);
 +}
 +
 +pub fn main() {
 +    // Check for version and help flags even when invoked as 'cargo-clippy'
 +    if env::args().any(|a| a == "--help" || a == "-h") {
 +        show_help();
 +        return;
 +    }
 +
 +    if env::args().any(|a| a == "--version" || a == "-V") {
 +        show_version();
 +        return;
 +    }
 +
++    if let Some(pos) = env::args().position(|a| a == "--explain") {
++        if let Some(mut lint) = env::args().nth(pos + 1) {
++            lint.make_ascii_lowercase();
++            docs::explain(&lint.strip_prefix("clippy::").unwrap_or(&lint).replace('-', "_"));
++        } else {
++            show_help();
++        }
++        return;
++    }
++
 +    if let Err(code) = process(env::args().skip(2)) {
 +        process::exit(code);
 +    }
 +}
 +
 +struct ClippyCmd {
 +    cargo_subcommand: &'static str,
 +    args: Vec<String>,
 +    clippy_args: Vec<String>,
 +}
 +
 +impl ClippyCmd {
 +    fn new<I>(mut old_args: I) -> Self
 +    where
 +        I: Iterator<Item = String>,
 +    {
 +        let mut cargo_subcommand = "check";
 +        let mut args = vec![];
 +        let mut clippy_args: Vec<String> = vec![];
 +
 +        for arg in old_args.by_ref() {
 +            match arg.as_str() {
 +                "--fix" => {
 +                    cargo_subcommand = "fix";
 +                    continue;
 +                },
 +                "--no-deps" => {
 +                    clippy_args.push("--no-deps".into());
 +                    continue;
 +                },
 +                "--" => break,
 +                _ => {},
 +            }
 +
 +            args.push(arg);
 +        }
 +
 +        clippy_args.append(&mut (old_args.collect()));
 +        if cargo_subcommand == "fix" && !clippy_args.iter().any(|arg| arg == "--no-deps") {
 +            clippy_args.push("--no-deps".into());
 +        }
 +
 +        Self {
 +            cargo_subcommand,
 +            args,
 +            clippy_args,
 +        }
 +    }
 +
 +    fn path() -> PathBuf {
 +        let mut path = env::current_exe()
 +            .expect("current executable path invalid")
 +            .with_file_name("clippy-driver");
 +
 +        if cfg!(windows) {
 +            path.set_extension("exe");
 +        }
 +
 +        path
 +    }
 +
 +    fn into_std_cmd(self) -> Command {
 +        let mut cmd = Command::new("cargo");
 +        let clippy_args: String = self
 +            .clippy_args
 +            .iter()
 +            .map(|arg| format!("{}__CLIPPY_HACKERY__", arg))
 +            .collect();
 +
 +        // Currently, `CLIPPY_TERMINAL_WIDTH` is used only to format "unknown field" error messages.
 +        let terminal_width = termize::dimensions().map_or(0, |(w, _)| w);
 +
 +        cmd.env("RUSTC_WORKSPACE_WRAPPER", Self::path())
 +            .env("CLIPPY_ARGS", clippy_args)
 +            .env("CLIPPY_TERMINAL_WIDTH", terminal_width.to_string())
 +            .arg(self.cargo_subcommand)
 +            .args(&self.args);
 +
 +        cmd
 +    }
 +}
 +
 +fn process<I>(old_args: I) -> Result<(), i32>
 +where
 +    I: Iterator<Item = String>,
 +{
 +    let cmd = ClippyCmd::new(old_args);
 +
 +    let mut cmd = cmd.into_std_cmd();
 +
 +    let exit_status = cmd
 +        .spawn()
 +        .expect("could not run cargo")
 +        .wait()
 +        .expect("failed to wait for cargo?");
 +
 +    if exit_status.success() {
 +        Ok(())
 +    } else {
 +        Err(exit_status.code().unwrap_or(-1))
 +    }
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use super::ClippyCmd;
 +
 +    #[test]
 +    fn fix() {
 +        let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string);
 +        let cmd = ClippyCmd::new(args);
 +        assert_eq!("fix", cmd.cargo_subcommand);
 +        assert!(!cmd.args.iter().any(|arg| arg.ends_with("unstable-options")));
 +    }
 +
 +    #[test]
 +    fn fix_implies_no_deps() {
 +        let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string);
 +        let cmd = ClippyCmd::new(args);
 +        assert!(cmd.clippy_args.iter().any(|arg| arg == "--no-deps"));
 +    }
 +
 +    #[test]
 +    fn no_deps_not_duplicated_with_fix() {
 +        let args = "cargo clippy --fix -- --no-deps"
 +            .split_whitespace()
 +            .map(ToString::to_string);
 +        let cmd = ClippyCmd::new(args);
 +        assert_eq!(cmd.clippy_args.iter().filter(|arg| *arg == "--no-deps").count(), 1);
 +    }
 +
 +    #[test]
 +    fn check() {
 +        let args = "cargo clippy".split_whitespace().map(ToString::to_string);
 +        let cmd = ClippyCmd::new(args);
 +        assert_eq!("check", cmd.cargo_subcommand);
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1aed09b7c7bd4bc7dcdb4462191df436c0ba52ee
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++#![warn(clippy::arithmetic_side_effects)]
++
++use core::ops::Add;
++
++#[derive(Clone, Copy)]
++struct Point {
++    x: i32,
++    y: i32,
++}
++
++impl Add for Point {
++    type Output = Self;
++
++    fn add(self, other: Self) -> Self {
++        todo!()
++    }
++}
++
++fn main() {
++    let _ = Point { x: 1, y: 0 } + Point { x: 2, y: 3 };
++
++    let point: Point = Point { x: 1, y: 0 };
++    let _ = point + point;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e736256f29a475564c327ccdab471bec05993d49
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++arithmetic-side-effects-allowed = ["Point"]
index a52a0b5289fe4839f22bc68f9ec1bbfb206fc6e2,0000000000000000000000000000000000000000..f27f78d15d3a04d2d8f17dec0061f8154c2e8477
mode 100644,000000..100644
--- /dev/null
@@@ -1,47 -1,0 +1,47 @@@
-            arithmetic-allowed
 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of
 +           allow-dbg-in-tests
 +           allow-expect-in-tests
 +           allow-unwrap-in-tests
 +           allowed-scripts
++           arithmetic-side-effects-allowed
 +           array-size-threshold
 +           avoid-breaking-exported-api
 +           await-holding-invalid-types
 +           blacklisted-names
 +           cargo-ignore-publish
 +           cognitive-complexity-threshold
 +           cyclomatic-complexity-threshold
 +           disallowed-methods
 +           disallowed-names
 +           disallowed-types
 +           doc-valid-idents
 +           enable-raw-pointer-heuristic-for-send
 +           enforced-import-renames
 +           enum-variant-name-threshold
 +           enum-variant-size-threshold
 +           large-error-threshold
 +           literal-representation-threshold
 +           max-fn-params-bools
 +           max-include-file-size
 +           max-struct-bools
 +           max-suggested-slice-pattern-length
 +           max-trait-bounds
 +           msrv
 +           pass-by-value-size-limit
 +           single-char-binding-names-threshold
 +           standard-macro-braces
 +           third-party
 +           too-large-for-stack
 +           too-many-arguments-threshold
 +           too-many-lines-threshold
 +           trivial-copy-size-limit
 +           type-complexity-threshold
 +           unreadable-literal-lint-fractions
 +           upper-case-acronyms-aggressive
 +           vec-box-size-threshold
 +           verbose-bit-mask-threshold
 +           warn-on-all-wildcard-imports
 +       at line 5 column 1
 +
 +error: aborting due to previous error
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f5390c7464250dea1bf9456618e2e06457362f46
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,57 @@@
++#![allow(clippy::assign_op_pattern, clippy::unnecessary_owned_empty_strings)]
++#![feature(inline_const, saturating_int_impl)]
++#![warn(clippy::arithmetic_side_effects)]
++
++use core::num::{Saturating, Wrapping};
++
++pub fn hard_coded_allowed() {
++    let _ = 1f32 + 1f32;
++    let _ = 1f64 + 1f64;
++
++    let _ = Saturating(0u32) + Saturating(0u32);
++    let _ = String::new() + "";
++    let _ = Wrapping(0u32) + Wrapping(0u32);
++
++    let saturating: Saturating<u32> = Saturating(0u32);
++    let string: String = String::new();
++    let wrapping: Wrapping<u32> = Wrapping(0u32);
++
++    let inferred_saturating = saturating + saturating;
++    let inferred_string = string + "";
++    let inferred_wrapping = wrapping + wrapping;
++
++    let _ = inferred_saturating + inferred_saturating;
++    let _ = inferred_string + "";
++    let _ = inferred_wrapping + inferred_wrapping;
++}
++
++#[rustfmt::skip]
++pub fn non_overflowing_ops() {
++    const _: i32 = { let mut n = 1; n += 1; n };
++    let _ = const { let mut n = 1; n += 1; n };
++
++    const _: i32 = { let mut n = 1; n = n + 1; n };
++    let _ = const { let mut n = 1; n = n + 1; n };
++
++    const _: i32 = { let mut n = 1; n = 1 + n; n };
++    let _ = const { let mut n = 1; n = 1 + n; n };
++
++    const _: i32 = 1 + 1;
++    let _ = 1 + 1;
++    let _ = const { 1 + 1 };
++
++    let mut _a = 1;
++    _a *= 1;
++    _a /= 1;
++}
++
++#[rustfmt::skip]
++pub fn overflowing_ops() {
++    let mut _a = 1; _a += 1;
++
++    let mut _b = 1; _b = _b + 1;
++
++    let mut _c = 1; _c = 1 + _c;
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6c4c8bdec0f0589ded293437129264d1bb412166
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: arithmetic detected
++  --> $DIR/arithmetic_side_effects.rs:50:21
++   |
++LL |     let mut _a = 1; _a += 1;
++   |                     ^^^^^^^
++   |
++   = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings`
++
++error: arithmetic detected
++  --> $DIR/arithmetic_side_effects.rs:52:26
++   |
++LL |     let mut _b = 1; _b = _b + 1;
++   |                          ^^^^^^
++
++error: arithmetic detected
++  --> $DIR/arithmetic_side_effects.rs:54:26
++   |
++LL |     let mut _c = 1; _c = 1 + _c;
++   |                          ^^^^^^
++
++error: aborting due to 3 previous errors
++
index 5fdf3433a3702de7b324580e6fd4b8f386403914,0000000000000000000000000000000000000000..a99bdfc1313792999485940405d71c418a1aa2bd
mode 100644,000000..100644
--- /dev/null
@@@ -1,40 -1,0 +1,45 @@@
- #[allow(clippy::unnecessary_operation, clippy::single_match)]
++#![allow(
++    clippy::unnecessary_operation,
++    clippy::single_match,
++    clippy::no_effect,
++    clippy::bool_to_int_with_if
++)]
 +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();
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9c1098dc4c17aa10ea216d3502f8b763b461eb70
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,85 @@@
++// run-rustfix
++
++#![warn(clippy::bool_to_int_with_if)]
++#![allow(unused, dead_code, clippy::unnecessary_operation, clippy::no_effect)]
++
++fn main() {
++    let a = true;
++    let b = false;
++
++    let x = 1;
++    let y = 2;
++
++    // Should lint
++    // precedence
++    i32::from(a);
++    i32::from(!a);
++    i32::from(a || b);
++    i32::from(cond(a, b));
++    i32::from(x + y < 4);
++
++    // if else if
++    if a {
++        123
++    } else {i32::from(b)};
++
++    // Shouldn't lint
++
++    if a {
++        1
++    } else if b {
++        0
++    } else {
++        3
++    };
++
++    if a {
++        3
++    } else if b {
++        1
++    } else {
++        -2
++    };
++
++    if a {
++        3
++    } else {
++        0
++    };
++    if a {
++        side_effect();
++        1
++    } else {
++        0
++    };
++    if a {
++        1
++    } else {
++        side_effect();
++        0
++    };
++
++    // multiple else ifs
++    if a {
++        123
++    } else if b {
++        1
++    } else if a | b {
++        0
++    } else {
++        123
++    };
++
++    some_fn(a);
++}
++
++// Lint returns and type inference
++fn some_fn(a: bool) -> u8 {
++    u8::from(a)
++}
++
++fn side_effect() {}
++
++fn cond(a: bool, b: bool) -> bool {
++    a || b
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0c967dac6e2dd906155338b065ab573dca4e5018
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,109 @@@
++// run-rustfix
++
++#![warn(clippy::bool_to_int_with_if)]
++#![allow(unused, dead_code, clippy::unnecessary_operation, clippy::no_effect)]
++
++fn main() {
++    let a = true;
++    let b = false;
++
++    let x = 1;
++    let y = 2;
++
++    // Should lint
++    // precedence
++    if a {
++        1
++    } else {
++        0
++    };
++    if !a {
++        1
++    } else {
++        0
++    };
++    if a || b {
++        1
++    } else {
++        0
++    };
++    if cond(a, b) {
++        1
++    } else {
++        0
++    };
++    if x + y < 4 {
++        1
++    } else {
++        0
++    };
++
++    // if else if
++    if a {
++        123
++    } else if b {
++        1
++    } else {
++        0
++    };
++
++    // Shouldn't lint
++
++    if a {
++        1
++    } else if b {
++        0
++    } else {
++        3
++    };
++
++    if a {
++        3
++    } else if b {
++        1
++    } else {
++        -2
++    };
++
++    if a {
++        3
++    } else {
++        0
++    };
++    if a {
++        side_effect();
++        1
++    } else {
++        0
++    };
++    if a {
++        1
++    } else {
++        side_effect();
++        0
++    };
++
++    // multiple else ifs
++    if a {
++        123
++    } else if b {
++        1
++    } else if a | b {
++        0
++    } else {
++        123
++    };
++
++    some_fn(a);
++}
++
++// Lint returns and type inference
++fn some_fn(a: bool) -> u8 {
++    if a { 1 } else { 0 }
++}
++
++fn side_effect() {}
++
++fn cond(a: bool, b: bool) -> bool {
++    a || b
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8647a9cffbede3e289c312d1d26f2831031b795d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,84 @@@
++error: boolean to int conversion using if
++  --> $DIR/bool_to_int_with_if.rs:15:5
++   |
++LL | /     if a {
++LL | |         1
++LL | |     } else {
++LL | |         0
++LL | |     };
++   | |_____^ help: replace with from: `i32::from(a)`
++   |
++   = note: `-D clippy::bool-to-int-with-if` implied by `-D warnings`
++   = note: `a as i32` or `a.into()` can also be valid options
++
++error: boolean to int conversion using if
++  --> $DIR/bool_to_int_with_if.rs:20:5
++   |
++LL | /     if !a {
++LL | |         1
++LL | |     } else {
++LL | |         0
++LL | |     };
++   | |_____^ help: replace with from: `i32::from(!a)`
++   |
++   = note: `!a as i32` or `!a.into()` can also be valid options
++
++error: boolean to int conversion using if
++  --> $DIR/bool_to_int_with_if.rs:25:5
++   |
++LL | /     if a || b {
++LL | |         1
++LL | |     } else {
++LL | |         0
++LL | |     };
++   | |_____^ help: replace with from: `i32::from(a || b)`
++   |
++   = note: `(a || b) as i32` or `(a || b).into()` can also be valid options
++
++error: boolean to int conversion using if
++  --> $DIR/bool_to_int_with_if.rs:30:5
++   |
++LL | /     if cond(a, b) {
++LL | |         1
++LL | |     } else {
++LL | |         0
++LL | |     };
++   | |_____^ help: replace with from: `i32::from(cond(a, b))`
++   |
++   = note: `cond(a, b) as i32` or `cond(a, b).into()` can also be valid options
++
++error: boolean to int conversion using if
++  --> $DIR/bool_to_int_with_if.rs:35:5
++   |
++LL | /     if x + y < 4 {
++LL | |         1
++LL | |     } else {
++LL | |         0
++LL | |     };
++   | |_____^ help: replace with from: `i32::from(x + y < 4)`
++   |
++   = note: `(x + y < 4) as i32` or `(x + y < 4).into()` can also be valid options
++
++error: boolean to int conversion using if
++  --> $DIR/bool_to_int_with_if.rs:44:12
++   |
++LL |       } else if b {
++   |  ____________^
++LL | |         1
++LL | |     } else {
++LL | |         0
++LL | |     };
++   | |_____^ help: replace with from: `{i32::from(b)}`
++   |
++   = note: `b as i32` or `b.into()` can also be valid options
++
++error: boolean to int conversion using if
++  --> $DIR/bool_to_int_with_if.rs:102:5
++   |
++LL |     if a { 1 } else { 0 }
++   |     ^^^^^^^^^^^^^^^^^^^^^ help: replace with from: `u8::from(a)`
++   |
++   = note: `a as u8` or `a.into()` can also be valid options
++
++error: aborting due to 7 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e2d274aeb0442700d650ff49b659548689030b4b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++#![warn(clippy::useless_format)]
++#![allow(clippy::print_literal)]
++
++fn main() {
++    println!(
++        "\
++
++            {}",
++        "multiple skipped lines"
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9a6e410f21ea8871b61fc315bfbe20a91faaceec
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11 @@@
++warning: multiple lines skipped by escaped newline
++  --> $DIR/ice-9405.rs:6:10
++   |
++LL |           "/
++   |  __________^
++LL | |
++LL | |             {}",
++   | |____________^ skipping everything up to and including this point
++
++warning: 1 warning emitted
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..02cf5d5c240fa131765f8a9e77cbbfc36f6d336b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++#![warn(clippy::result_large_err)]
++
++trait T {}
++fn f(_: &u32) -> Result<(), *const (dyn '_ + T)> {
++    Ok(())
++}
++
++fn main() {}
index 7efe10a10f9e9809db283ef8bf73378a16bbe31a,0000000000000000000000000000000000000000..e7ef45634dff4e0bd4e2d7d17915b8bcc0f64b6b
mode 100644,000000..100644
--- /dev/null
@@@ -1,45 -1,0 +1,50 @@@
 +// run-rustfix
 +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
 +
 +fn main() {
 +    let x = 3f32;
 +    let _ = x.exp2();
 +    let _ = 3.1f32.exp2();
 +    let _ = (-3.1f32).exp2();
 +    let _ = x.exp();
 +    let _ = 3.1f32.exp();
 +    let _ = (-3.1f32).exp();
 +    let _ = x.sqrt();
 +    let _ = x.cbrt();
 +    let _ = (x as f32).cbrt();
 +    let _ = x.powi(3);
 +    let _ = x.powi(-2);
 +    let _ = x.powi(16_777_215);
 +    let _ = x.powi(-16_777_215);
 +    let _ = (x as f32).powi(-16_777_215);
 +    let _ = (x as f32).powi(3);
++    let _ = (1.5_f32 + 1.0).cbrt();
++    let _ = 1.5_f64.cbrt();
++    let _ = 1.5_f64.sqrt();
++    let _ = 1.5_f64.powi(3);
++
 +    // Cases where the lint shouldn't be applied
 +    let _ = x.powf(2.1);
 +    let _ = x.powf(-2.1);
 +    let _ = x.powf(16_777_216.0);
 +    let _ = x.powf(-16_777_216.0);
 +
 +    let x = 3f64;
 +    let _ = x.exp2();
 +    let _ = 3.1f64.exp2();
 +    let _ = (-3.1f64).exp2();
 +    let _ = x.exp();
 +    let _ = 3.1f64.exp();
 +    let _ = (-3.1f64).exp();
 +    let _ = x.sqrt();
 +    let _ = x.cbrt();
 +    let _ = x.powi(3);
 +    let _ = x.powi(-2);
 +    let _ = x.powi(-2_147_483_648);
 +    let _ = x.powi(2_147_483_647);
 +    // Cases where the lint shouldn't be applied
 +    let _ = x.powf(2.1);
 +    let _ = x.powf(-2.1);
 +    let _ = x.powf(-2_147_483_649.0);
 +    let _ = x.powf(2_147_483_648.0);
 +}
index 445080417f2ed8c2a0e5b7f1f1a799286077c2f8,0000000000000000000000000000000000000000..d749aa2d48a418d79552a77eb44f403738217d0a
mode 100644,000000..100644
--- /dev/null
@@@ -1,45 -1,0 +1,50 @@@
 +// run-rustfix
 +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
 +
 +fn main() {
 +    let x = 3f32;
 +    let _ = 2f32.powf(x);
 +    let _ = 2f32.powf(3.1);
 +    let _ = 2f32.powf(-3.1);
 +    let _ = std::f32::consts::E.powf(x);
 +    let _ = std::f32::consts::E.powf(3.1);
 +    let _ = std::f32::consts::E.powf(-3.1);
 +    let _ = x.powf(1.0 / 2.0);
 +    let _ = x.powf(1.0 / 3.0);
 +    let _ = (x as f32).powf(1.0 / 3.0);
 +    let _ = x.powf(3.0);
 +    let _ = x.powf(-2.0);
 +    let _ = x.powf(16_777_215.0);
 +    let _ = x.powf(-16_777_215.0);
 +    let _ = (x as f32).powf(-16_777_215.0);
 +    let _ = (x as f32).powf(3.0);
++    let _ = (1.5_f32 + 1.0).powf(1.0 / 3.0);
++    let _ = 1.5_f64.powf(1.0 / 3.0);
++    let _ = 1.5_f64.powf(1.0 / 2.0);
++    let _ = 1.5_f64.powf(3.0);
++
 +    // Cases where the lint shouldn't be applied
 +    let _ = x.powf(2.1);
 +    let _ = x.powf(-2.1);
 +    let _ = x.powf(16_777_216.0);
 +    let _ = x.powf(-16_777_216.0);
 +
 +    let x = 3f64;
 +    let _ = 2f64.powf(x);
 +    let _ = 2f64.powf(3.1);
 +    let _ = 2f64.powf(-3.1);
 +    let _ = std::f64::consts::E.powf(x);
 +    let _ = std::f64::consts::E.powf(3.1);
 +    let _ = std::f64::consts::E.powf(-3.1);
 +    let _ = x.powf(1.0 / 2.0);
 +    let _ = x.powf(1.0 / 3.0);
 +    let _ = x.powf(3.0);
 +    let _ = x.powf(-2.0);
 +    let _ = x.powf(-2_147_483_648.0);
 +    let _ = x.powf(2_147_483_647.0);
 +    // Cases where the lint shouldn't be applied
 +    let _ = x.powf(2.1);
 +    let _ = x.powf(-2.1);
 +    let _ = x.powf(-2_147_483_649.0);
 +    let _ = x.powf(2_147_483_648.0);
 +}
index 6ee696e6ada5ffda8d5580258d2ec7074bcfee3a,0000000000000000000000000000000000000000..e9693de8fc9090f47802fd465fd5aa5f66c91b07
mode 100644,000000..100644
--- /dev/null
@@@ -1,168 -1,0 +1,192 @@@
-   --> $DIR/floating_point_powf.rs:28:13
 +error: exponent for bases 2 and e can be computed more accurately
 +  --> $DIR/floating_point_powf.rs:6:13
 +   |
 +LL |     let _ = 2f32.powf(x);
 +   |             ^^^^^^^^^^^^ help: consider using: `x.exp2()`
 +   |
 +   = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
 +
 +error: exponent for bases 2 and e can be computed more accurately
 +  --> $DIR/floating_point_powf.rs:7:13
 +   |
 +LL |     let _ = 2f32.powf(3.1);
 +   |             ^^^^^^^^^^^^^^ help: consider using: `3.1f32.exp2()`
 +
 +error: exponent for bases 2 and e can be computed more accurately
 +  --> $DIR/floating_point_powf.rs:8:13
 +   |
 +LL |     let _ = 2f32.powf(-3.1);
 +   |             ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f32).exp2()`
 +
 +error: exponent for bases 2 and e can be computed more accurately
 +  --> $DIR/floating_point_powf.rs:9:13
 +   |
 +LL |     let _ = std::f32::consts::E.powf(x);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()`
 +
 +error: exponent for bases 2 and e can be computed more accurately
 +  --> $DIR/floating_point_powf.rs:10:13
 +   |
 +LL |     let _ = std::f32::consts::E.powf(3.1);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f32.exp()`
 +
 +error: exponent for bases 2 and e can be computed more accurately
 +  --> $DIR/floating_point_powf.rs:11:13
 +   |
 +LL |     let _ = std::f32::consts::E.powf(-3.1);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f32).exp()`
 +
 +error: square-root of a number can be computed more efficiently and accurately
 +  --> $DIR/floating_point_powf.rs:12:13
 +   |
 +LL |     let _ = x.powf(1.0 / 2.0);
 +   |             ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()`
 +
 +error: cube-root of a number can be computed more accurately
 +  --> $DIR/floating_point_powf.rs:13:13
 +   |
 +LL |     let _ = x.powf(1.0 / 3.0);
 +   |             ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()`
 +   |
 +   = note: `-D clippy::imprecise-flops` implied by `-D warnings`
 +
 +error: cube-root of a number can be computed more accurately
 +  --> $DIR/floating_point_powf.rs:14:13
 +   |
 +LL |     let _ = (x as f32).powf(1.0 / 3.0);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).cbrt()`
 +
 +error: exponentiation with integer powers can be computed more efficiently
 +  --> $DIR/floating_point_powf.rs:15:13
 +   |
 +LL |     let _ = x.powf(3.0);
 +   |             ^^^^^^^^^^^ help: consider using: `x.powi(3)`
 +
 +error: exponentiation with integer powers can be computed more efficiently
 +  --> $DIR/floating_point_powf.rs:16:13
 +   |
 +LL |     let _ = x.powf(-2.0);
 +   |             ^^^^^^^^^^^^ help: consider using: `x.powi(-2)`
 +
 +error: exponentiation with integer powers can be computed more efficiently
 +  --> $DIR/floating_point_powf.rs:17:13
 +   |
 +LL |     let _ = x.powf(16_777_215.0);
 +   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(16_777_215)`
 +
 +error: exponentiation with integer powers can be computed more efficiently
 +  --> $DIR/floating_point_powf.rs:18:13
 +   |
 +LL |     let _ = x.powf(-16_777_215.0);
 +   |             ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-16_777_215)`
 +
 +error: exponentiation with integer powers can be computed more efficiently
 +  --> $DIR/floating_point_powf.rs:19:13
 +   |
 +LL |     let _ = (x as f32).powf(-16_777_215.0);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).powi(-16_777_215)`
 +
 +error: exponentiation with integer powers can be computed more efficiently
 +  --> $DIR/floating_point_powf.rs:20:13
 +   |
 +LL |     let _ = (x as f32).powf(3.0);
 +   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).powi(3)`
 +
++error: cube-root of a number can be computed more accurately
++  --> $DIR/floating_point_powf.rs:21:13
++   |
++LL |     let _ = (1.5_f32 + 1.0).powf(1.0 / 3.0);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(1.5_f32 + 1.0).cbrt()`
++
++error: cube-root of a number can be computed more accurately
++  --> $DIR/floating_point_powf.rs:22:13
++   |
++LL |     let _ = 1.5_f64.powf(1.0 / 3.0);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.5_f64.cbrt()`
++
++error: square-root of a number can be computed more efficiently and accurately
++  --> $DIR/floating_point_powf.rs:23:13
++   |
++LL |     let _ = 1.5_f64.powf(1.0 / 2.0);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.5_f64.sqrt()`
++
++error: exponentiation with integer powers can be computed more efficiently
++  --> $DIR/floating_point_powf.rs:24:13
++   |
++LL |     let _ = 1.5_f64.powf(3.0);
++   |             ^^^^^^^^^^^^^^^^^ help: consider using: `1.5_f64.powi(3)`
++
 +error: exponent for bases 2 and e can be computed more accurately
-   --> $DIR/floating_point_powf.rs:29:13
++  --> $DIR/floating_point_powf.rs:33:13
 +   |
 +LL |     let _ = 2f64.powf(x);
 +   |             ^^^^^^^^^^^^ help: consider using: `x.exp2()`
 +
 +error: exponent for bases 2 and e can be computed more accurately
-   --> $DIR/floating_point_powf.rs:30:13
++  --> $DIR/floating_point_powf.rs:34:13
 +   |
 +LL |     let _ = 2f64.powf(3.1);
 +   |             ^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp2()`
 +
 +error: exponent for bases 2 and e can be computed more accurately
-   --> $DIR/floating_point_powf.rs:31:13
++  --> $DIR/floating_point_powf.rs:35:13
 +   |
 +LL |     let _ = 2f64.powf(-3.1);
 +   |             ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp2()`
 +
 +error: exponent for bases 2 and e can be computed more accurately
-   --> $DIR/floating_point_powf.rs:32:13
++  --> $DIR/floating_point_powf.rs:36:13
 +   |
 +LL |     let _ = std::f64::consts::E.powf(x);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()`
 +
 +error: exponent for bases 2 and e can be computed more accurately
-   --> $DIR/floating_point_powf.rs:33:13
++  --> $DIR/floating_point_powf.rs:37:13
 +   |
 +LL |     let _ = std::f64::consts::E.powf(3.1);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp()`
 +
 +error: exponent for bases 2 and e can be computed more accurately
-   --> $DIR/floating_point_powf.rs:34:13
++  --> $DIR/floating_point_powf.rs:38:13
 +   |
 +LL |     let _ = std::f64::consts::E.powf(-3.1);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp()`
 +
 +error: square-root of a number can be computed more efficiently and accurately
-   --> $DIR/floating_point_powf.rs:35:13
++  --> $DIR/floating_point_powf.rs:39:13
 +   |
 +LL |     let _ = x.powf(1.0 / 2.0);
 +   |             ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()`
 +
 +error: cube-root of a number can be computed more accurately
-   --> $DIR/floating_point_powf.rs:36:13
++  --> $DIR/floating_point_powf.rs:40:13
 +   |
 +LL |     let _ = x.powf(1.0 / 3.0);
 +   |             ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()`
 +
 +error: exponentiation with integer powers can be computed more efficiently
-   --> $DIR/floating_point_powf.rs:37:13
++  --> $DIR/floating_point_powf.rs:41:13
 +   |
 +LL |     let _ = x.powf(3.0);
 +   |             ^^^^^^^^^^^ help: consider using: `x.powi(3)`
 +
 +error: exponentiation with integer powers can be computed more efficiently
-   --> $DIR/floating_point_powf.rs:38:13
++  --> $DIR/floating_point_powf.rs:42:13
 +   |
 +LL |     let _ = x.powf(-2.0);
 +   |             ^^^^^^^^^^^^ help: consider using: `x.powi(-2)`
 +
 +error: exponentiation with integer powers can be computed more efficiently
-   --> $DIR/floating_point_powf.rs:39:13
++  --> $DIR/floating_point_powf.rs:43:13
 +   |
 +LL |     let _ = x.powf(-2_147_483_648.0);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-2_147_483_648)`
 +
 +error: exponentiation with integer powers can be computed more efficiently
- error: aborting due to 27 previous errors
++  --> $DIR/floating_point_powf.rs:44:13
 +   |
 +LL |     let _ = x.powf(2_147_483_647.0);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2_147_483_647)`
 +
++error: aborting due to 31 previous errors
 +
index 23152a13322e82ab3b73d7bb4481db22cc1f9668,0000000000000000000000000000000000000000..717009e4c4ccadce5a5c5c93aa1607435c906edd
mode 100644,000000..100644
--- /dev/null
@@@ -1,135 -1,0 +1,159 @@@
 +// aux-build:macro_rules.rs
 +
 +#![allow(dead_code)]
 +#![allow(unused_variables)]
 +#![warn(clippy::large_enum_variant)]
 +
 +#[macro_use]
 +extern crate macro_rules;
 +
 +enum LargeEnum {
 +    A(i32),
 +    B([i32; 8000]),
 +}
 +
 +enum GenericEnumOk<T> {
 +    A(i32),
 +    B([T; 8000]),
 +}
 +
 +enum GenericEnum2<T> {
 +    A(i32),
 +    B([i32; 8000]),
 +    C(T, [i32; 8000]),
 +}
 +
 +trait SomeTrait {
 +    type Item;
 +}
 +
 +enum LargeEnumGeneric<A: SomeTrait> {
 +    Var(A::Item),
 +}
 +
 +enum LargeEnum2 {
 +    VariantOk(i32, u32),
 +    ContainingLargeEnum(LargeEnum),
 +}
 +
 +enum LargeEnum3 {
 +    ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]),
 +    VoidVariant,
 +    StructLikeLittle { x: i32, y: i32 },
 +}
 +
 +enum LargeEnum4 {
 +    VariantOk(i32, u32),
 +    StructLikeLarge { x: [i32; 8000], y: i32 },
 +}
 +
 +enum LargeEnum5 {
 +    VariantOk(i32, u32),
 +    StructLikeLarge2 { x: [i32; 8000] },
 +}
 +
 +enum LargeEnumOk {
 +    LargeA([i32; 8000]),
 +    LargeB([i32; 8001]),
 +}
 +
 +enum LargeEnum6 {
 +    A,
 +    B([u8; 255]),
 +    C([u8; 200]),
 +}
 +
 +enum LargeEnum7 {
 +    A,
 +    B([u8; 1255]),
 +    C([u8; 200]),
 +}
 +
 +enum LargeEnum8 {
 +    VariantOk(i32, u32),
 +    ContainingMoreThanOneField([i32; 8000], [i32; 2], [i32; 9500], [i32; 30]),
 +}
 +
 +enum LargeEnum9 {
 +    A(Struct<()>),
 +    B(Struct2),
 +}
 +
 +enum LargeEnumOk2<T> {
 +    A(T),
 +    B(Struct2),
 +}
 +
 +enum LargeEnumOk3<T> {
 +    A(Struct<T>),
 +    B(Struct2),
 +}
 +
 +struct Struct<T> {
 +    a: i32,
 +    t: T,
 +}
 +
 +struct Struct2 {
 +    a: [i32; 8000],
 +}
 +
 +#[derive(Copy, Clone)]
 +enum CopyableLargeEnum {
 +    A(bool),
 +    B([u128; 4000]),
 +}
 +
 +enum ManuallyCopyLargeEnum {
 +    A(bool),
 +    B([u128; 4000]),
 +}
 +
 +impl Clone for ManuallyCopyLargeEnum {
 +    fn clone(&self) -> Self {
 +        *self
 +    }
 +}
 +
 +impl Copy for ManuallyCopyLargeEnum {}
 +
 +enum SomeGenericPossiblyCopyEnum<T> {
 +    A(bool, std::marker::PhantomData<T>),
 +    B([u64; 4000]),
 +}
 +
 +impl<T: Copy> Clone for SomeGenericPossiblyCopyEnum<T> {
 +    fn clone(&self) -> Self {
 +        *self
 +    }
 +}
 +
 +impl<T: Copy> Copy for SomeGenericPossiblyCopyEnum<T> {}
 +
++enum LargeEnumWithGenerics<T> {
++    Small,
++    Large((T, [u8; 512])),
++}
++
++struct Foo<T> {
++    foo: T,
++}
++
++enum WithGenerics {
++    Large([Foo<u64>; 64]),
++    Small(u8),
++}
++
++enum PossiblyLargeEnumWithConst<const U: usize> {
++    SmallBuffer([u8; 4]),
++    MightyBuffer([u16; U]),
++}
++
++enum LargeEnumOfConst {
++    Ok,
++    Error(PossiblyLargeEnumWithConst<256>),
++}
++
 +fn main() {
 +    large_enum_variant!();
 +}
index 0248327262da0f63852ecfa684f6b38c058670f7,0000000000000000000000000000000000000000..c6ed97487c0e3a27aab80d8e6564b57bf00c8561
mode 100644,000000..100644
--- /dev/null
@@@ -1,197 -1,0 +1,279 @@@
-   --> $DIR/large_enum_variant.rs:12:5
 +error: large size difference between variants
- LL |     B([i32; 8000]),
-    |     ^^^^^^^^^^^^^^ this variant is 32000 bytes
++  --> $DIR/large_enum_variant.rs:10:1
 +   |
- note: and the second-largest variant is 4 bytes:
-   --> $DIR/large_enum_variant.rs:11:5
-    |
- LL |     A(i32),
-    |     ^^^^^^
++LL | / enum LargeEnum {
++LL | |     A(i32),
++   | |     ------ the second-largest variant contains at least 4 bytes
++LL | |     B([i32; 8000]),
++   | |     -------------- the largest variant contains at least 32000 bytes
++LL | | }
++   | |_^ the entire enum is at least 32004 bytes
 +   |
 +   = note: `-D clippy::large-enum-variant` implied by `-D warnings`
-   --> $DIR/large_enum_variant.rs:36:5
-    |
- LL |     ContainingLargeEnum(LargeEnum),
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes
 +help: consider boxing the large fields to reduce the total size of the enum
 +   |
 +LL |     B(Box<[i32; 8000]>),
 +   |       ~~~~~~~~~~~~~~~~
 +
 +error: large size difference between variants
- note: and the second-largest variant is 8 bytes:
-   --> $DIR/large_enum_variant.rs:35:5
++  --> $DIR/large_enum_variant.rs:34:1
 +   |
- LL |     VariantOk(i32, u32),
-    |     ^^^^^^^^^^^^^^^^^^^
++LL | / enum LargeEnum2 {
++LL | |     VariantOk(i32, u32),
++   | |     ------------------- the second-largest variant contains at least 8 bytes
++LL | |     ContainingLargeEnum(LargeEnum),
++   | |     ------------------------------ the largest variant contains at least 32004 bytes
++LL | | }
++   | |_^ the entire enum is at least 32004 bytes
 +   |
-   --> $DIR/large_enum_variant.rs:40:5
-    |
- LL |     ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]),
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 70004 bytes
 +help: consider boxing the large fields to reduce the total size of the enum
 +   |
 +LL |     ContainingLargeEnum(Box<LargeEnum>),
 +   |                         ~~~~~~~~~~~~~~
 +
 +error: large size difference between variants
- note: and the second-largest variant is 8 bytes:
-   --> $DIR/large_enum_variant.rs:42:5
++  --> $DIR/large_enum_variant.rs:39:1
 +   |
- LL |     StructLikeLittle { x: i32, y: i32 },
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++LL | / enum LargeEnum3 {
++LL | |     ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]),
++   | |     --------------------------------------------------------- the largest variant contains at least 70004 bytes
++LL | |     VoidVariant,
++LL | |     StructLikeLittle { x: i32, y: i32 },
++   | |     ----------------------------------- the second-largest variant contains at least 8 bytes
++LL | | }
++   | |_^ the entire enum is at least 70008 bytes
 +   |
-   --> $DIR/large_enum_variant.rs:47:5
 +help: consider boxing the large fields to reduce the total size of the enum
 +   |
 +LL |     ContainingMoreThanOneField(i32, Box<[i32; 8000]>, Box<[i32; 9500]>),
 +   |                                     ~~~~~~~~~~~~~~~~  ~~~~~~~~~~~~~~~~
 +
 +error: large size difference between variants
- LL |     StructLikeLarge { x: [i32; 8000], y: i32 },
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes
++  --> $DIR/large_enum_variant.rs:45:1
 +   |
- note: and the second-largest variant is 8 bytes:
-   --> $DIR/large_enum_variant.rs:46:5
-    |
- LL |     VariantOk(i32, u32),
-    |     ^^^^^^^^^^^^^^^^^^^
++LL | / enum LargeEnum4 {
++LL | |     VariantOk(i32, u32),
++   | |     ------------------- the second-largest variant contains at least 8 bytes
++LL | |     StructLikeLarge { x: [i32; 8000], y: i32 },
++   | |     ------------------------------------------ the largest variant contains at least 32004 bytes
++LL | | }
++   | |_^ the entire enum is at least 32008 bytes
 +   |
-   --> $DIR/large_enum_variant.rs:52:5
-    |
- LL |     StructLikeLarge2 { x: [i32; 8000] },
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32000 bytes
 +help: consider boxing the large fields to reduce the total size of the enum
 +   |
 +LL |     StructLikeLarge { x: Box<[i32; 8000]>, y: i32 },
 +   |                          ~~~~~~~~~~~~~~~~
 +
 +error: large size difference between variants
- note: and the second-largest variant is 8 bytes:
-   --> $DIR/large_enum_variant.rs:51:5
++  --> $DIR/large_enum_variant.rs:50:1
 +   |
- LL |     VariantOk(i32, u32),
-    |     ^^^^^^^^^^^^^^^^^^^
++LL | / enum LargeEnum5 {
++LL | |     VariantOk(i32, u32),
++   | |     ------------------- the second-largest variant contains at least 8 bytes
++LL | |     StructLikeLarge2 { x: [i32; 8000] },
++   | |     ----------------------------------- the largest variant contains at least 32000 bytes
++LL | | }
++   | |_^ the entire enum is at least 32004 bytes
 +   |
-   --> $DIR/large_enum_variant.rs:68:5
-    |
- LL |     B([u8; 1255]),
-    |     ^^^^^^^^^^^^^ this variant is 1255 bytes
 +help: consider boxing the large fields to reduce the total size of the enum
 +   |
 +LL |     StructLikeLarge2 { x: Box<[i32; 8000]> },
 +   |                           ~~~~~~~~~~~~~~~~
 +
 +error: large size difference between variants
- note: and the second-largest variant is 200 bytes:
-   --> $DIR/large_enum_variant.rs:69:5
++  --> $DIR/large_enum_variant.rs:66:1
 +   |
- LL |     C([u8; 200]),
-    |     ^^^^^^^^^^^^
++LL | / enum LargeEnum7 {
++LL | |     A,
++LL | |     B([u8; 1255]),
++   | |     ------------- the largest variant contains at least 1255 bytes
++LL | |     C([u8; 200]),
++   | |     ------------ the second-largest variant contains at least 200 bytes
++LL | | }
++   | |_^ the entire enum is at least 1256 bytes
 +   |
-   --> $DIR/large_enum_variant.rs:74:5
 +help: consider boxing the large fields to reduce the total size of the enum
 +   |
 +LL |     B(Box<[u8; 1255]>),
 +   |       ~~~~~~~~~~~~~~~
 +
 +error: large size difference between variants
- LL |     ContainingMoreThanOneField([i32; 8000], [i32; 2], [i32; 9500], [i32; 30]),
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 70128 bytes
++  --> $DIR/large_enum_variant.rs:72:1
 +   |
- note: and the second-largest variant is 8 bytes:
-   --> $DIR/large_enum_variant.rs:73:5
-    |
- LL |     VariantOk(i32, u32),
-    |     ^^^^^^^^^^^^^^^^^^^
++LL | / enum LargeEnum8 {
++LL | |     VariantOk(i32, u32),
++   | |     ------------------- the second-largest variant contains at least 8 bytes
++LL | |     ContainingMoreThanOneField([i32; 8000], [i32; 2], [i32; 9500], [i32; 30]),
++   | |     ------------------------------------------------------------------------- the largest variant contains at least 70128 bytes
++LL | | }
++   | |_^ the entire enum is at least 70132 bytes
 +   |
-   --> $DIR/large_enum_variant.rs:79:5
 +help: consider boxing the large fields to reduce the total size of the enum
 +   |
 +LL |     ContainingMoreThanOneField(Box<[i32; 8000]>, [i32; 2], Box<[i32; 9500]>, [i32; 30]),
 +   |                                ~~~~~~~~~~~~~~~~            ~~~~~~~~~~~~~~~~
 +
 +error: large size difference between variants
- LL |     B(Struct2),
-    |     ^^^^^^^^^^ this variant is 32000 bytes
++  --> $DIR/large_enum_variant.rs:77:1
++   |
++LL | / enum LargeEnum9 {
++LL | |     A(Struct<()>),
++   | |     ------------- the second-largest variant contains at least 4 bytes
++LL | |     B(Struct2),
++   | |     ---------- the largest variant contains at least 32000 bytes
++LL | | }
++   | |_^ the entire enum is at least 32004 bytes
++   |
++help: consider boxing the large fields to reduce the total size of the enum
 +   |
- note: and the second-largest variant is 4 bytes:
-   --> $DIR/large_enum_variant.rs:78:5
++LL |     B(Box<Struct2>),
++   |       ~~~~~~~~~~~~
++
++error: large size difference between variants
++  --> $DIR/large_enum_variant.rs:82:1
 +   |
- LL |     A(Struct<()>),
-    |     ^^^^^^^^^^^^^
++LL | / enum LargeEnumOk2<T> {
++LL | |     A(T),
++   | |     ---- the second-largest variant contains at least 0 bytes
++LL | |     B(Struct2),
++   | |     ---------- the largest variant contains at least 32000 bytes
++LL | | }
++   | |_^ the entire enum is at least 32000 bytes
 +   |
-   --> $DIR/large_enum_variant.rs:104:5
 +help: consider boxing the large fields to reduce the total size of the enum
 +   |
 +LL |     B(Box<Struct2>),
 +   |       ~~~~~~~~~~~~
 +
 +error: large size difference between variants
- LL |     B([u128; 4000]),
-    |     ^^^^^^^^^^^^^^^ this variant is 64000 bytes
++  --> $DIR/large_enum_variant.rs:87:1
 +   |
- note: and the second-largest variant is 1 bytes:
-   --> $DIR/large_enum_variant.rs:103:5
++LL | / enum LargeEnumOk3<T> {
++LL | |     A(Struct<T>),
++   | |     ------------ the second-largest variant contains at least 4 bytes
++LL | |     B(Struct2),
++   | |     ---------- the largest variant contains at least 32000 bytes
++LL | | }
++   | |_^ the entire enum is at least 32000 bytes
++   |
++help: consider boxing the large fields to reduce the total size of the enum
 +   |
- LL |     A(bool),
-    |     ^^^^^^^
++LL |     B(Box<Struct2>),
++   |       ~~~~~~~~~~~~
++
++error: large size difference between variants
++  --> $DIR/large_enum_variant.rs:102:1
++   |
++LL | / enum CopyableLargeEnum {
++LL | |     A(bool),
++   | |     ------- the second-largest variant contains at least 1 bytes
++LL | |     B([u128; 4000]),
++   | |     --------------- the largest variant contains at least 64000 bytes
++LL | | }
++   | |_^ the entire enum is at least 64008 bytes
 +   |
-   --> $DIR/large_enum_variant.rs:109:5
 +note: boxing a variant would require the type no longer be `Copy`
 +  --> $DIR/large_enum_variant.rs:102:6
 +   |
 +LL | enum CopyableLargeEnum {
 +   |      ^^^^^^^^^^^^^^^^^
 +help: consider boxing the large fields to reduce the total size of the enum
 +  --> $DIR/large_enum_variant.rs:104:5
 +   |
 +LL |     B([u128; 4000]),
 +   |     ^^^^^^^^^^^^^^^
 +
 +error: large size difference between variants
- LL |     B([u128; 4000]),
-    |     ^^^^^^^^^^^^^^^ this variant is 64000 bytes
-    |
- note: and the second-largest variant is 1 bytes:
-   --> $DIR/large_enum_variant.rs:108:5
++  --> $DIR/large_enum_variant.rs:107:1
 +   |
- LL |     A(bool),
-    |     ^^^^^^^
++LL | / enum ManuallyCopyLargeEnum {
++LL | |     A(bool),
++   | |     ------- the second-largest variant contains at least 1 bytes
++LL | |     B([u128; 4000]),
++   | |     --------------- the largest variant contains at least 64000 bytes
++LL | | }
++   | |_^ the entire enum is at least 64008 bytes
 +   |
-   --> $DIR/large_enum_variant.rs:122:5
 +note: boxing a variant would require the type no longer be `Copy`
 +  --> $DIR/large_enum_variant.rs:107:6
 +   |
 +LL | enum ManuallyCopyLargeEnum {
 +   |      ^^^^^^^^^^^^^^^^^^^^^
 +help: consider boxing the large fields to reduce the total size of the enum
 +  --> $DIR/large_enum_variant.rs:109:5
 +   |
 +LL |     B([u128; 4000]),
 +   |     ^^^^^^^^^^^^^^^
 +
 +error: large size difference between variants
- LL |     B([u64; 4000]),
-    |     ^^^^^^^^^^^^^^ this variant is 32000 bytes
-    |
- note: and the second-largest variant is 1 bytes:
-   --> $DIR/large_enum_variant.rs:121:5
++  --> $DIR/large_enum_variant.rs:120:1
 +   |
- LL |     A(bool, std::marker::PhantomData<T>),
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++LL | / enum SomeGenericPossiblyCopyEnum<T> {
++LL | |     A(bool, std::marker::PhantomData<T>),
++   | |     ------------------------------------ the second-largest variant contains at least 1 bytes
++LL | |     B([u64; 4000]),
++   | |     -------------- the largest variant contains at least 32000 bytes
++LL | | }
++   | |_^ the entire enum is at least 32008 bytes
 +   |
- error: aborting due to 11 previous errors
 +note: boxing a variant would require the type no longer be `Copy`
 +  --> $DIR/large_enum_variant.rs:120:6
 +   |
 +LL | enum SomeGenericPossiblyCopyEnum<T> {
 +   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +help: consider boxing the large fields to reduce the total size of the enum
 +  --> $DIR/large_enum_variant.rs:122:5
 +   |
 +LL |     B([u64; 4000]),
 +   |     ^^^^^^^^^^^^^^
 +
++error: large size difference between variants
++  --> $DIR/large_enum_variant.rs:133:1
++   |
++LL | / enum LargeEnumWithGenerics<T> {
++LL | |     Small,
++   | |     ----- the second-largest variant carries no data at all
++LL | |     Large((T, [u8; 512])),
++   | |     --------------------- the largest variant contains at least 512 bytes
++LL | | }
++   | |_^ the entire enum is at least 512 bytes
++   |
++help: consider boxing the large fields to reduce the total size of the enum
++   |
++LL |     Large(Box<(T, [u8; 512])>),
++   |           ~~~~~~~~~~~~~~~~~~~
++
++error: large size difference between variants
++  --> $DIR/large_enum_variant.rs:142:1
++   |
++LL | / enum WithGenerics {
++LL | |     Large([Foo<u64>; 64]),
++   | |     --------------------- the largest variant contains at least 512 bytes
++LL | |     Small(u8),
++   | |     --------- the second-largest variant contains at least 1 bytes
++LL | | }
++   | |_^ the entire enum is at least 520 bytes
++   |
++help: consider boxing the large fields to reduce the total size of the enum
++   |
++LL |     Large(Box<[Foo<u64>; 64]>),
++   |           ~~~~~~~~~~~~~~~~~~~
++
++error: large size difference between variants
++  --> $DIR/large_enum_variant.rs:152:1
++   |
++LL | / enum LargeEnumOfConst {
++LL | |     Ok,
++   | |     -- the second-largest variant carries no data at all
++LL | |     Error(PossiblyLargeEnumWithConst<256>),
++   | |     -------------------------------------- the largest variant contains at least 514 bytes
++LL | | }
++   | |_^ the entire enum is at least 514 bytes
++   |
++help: consider boxing the large fields to reduce the total size of the enum
++   |
++LL |     Error(Box<PossiblyLargeEnumWithConst<256>>),
++   |           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++
++error: aborting due to 16 previous errors
 +
index 2a4012039ba97a62b993f0234906695186293e7f,0000000000000000000000000000000000000000..2d66daea8046e15c67bb4a3d2d4b7ea00c19eb21
mode 100644,000000..100644
--- /dev/null
@@@ -1,35 -1,0 +1,35 @@@
-    = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
 +error: `Err(_)` matches all errors
 +  --> $DIR/match_wild_err_arm.rs:14:9
 +   |
 +LL |         Err(_) => panic!("err"),
 +   |         ^^^^^^
 +   |
 +   = note: `-D clippy::match-wild-err-arm` implied by `-D warnings`
-    = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
++   = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable
 +
 +error: `Err(_)` matches all errors
 +  --> $DIR/match_wild_err_arm.rs:20:9
 +   |
 +LL |         Err(_) => panic!(),
 +   |         ^^^^^^
 +   |
-    = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
++   = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable
 +
 +error: `Err(_)` matches all errors
 +  --> $DIR/match_wild_err_arm.rs:26:9
 +   |
 +LL |         Err(_) => {
 +   |         ^^^^^^
 +   |
-    = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
++   = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable
 +
 +error: `Err(_e)` matches all errors
 +  --> $DIR/match_wild_err_arm.rs:34:9
 +   |
 +LL |         Err(_e) => panic!(),
 +   |         ^^^^^^^
 +   |
++   = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable
 +
 +error: aborting due to 4 previous errors
 +
index 2a4012039ba97a62b993f0234906695186293e7f,0000000000000000000000000000000000000000..2d66daea8046e15c67bb4a3d2d4b7ea00c19eb21
mode 100644,000000..100644
--- /dev/null
@@@ -1,35 -1,0 +1,35 @@@
-    = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
 +error: `Err(_)` matches all errors
 +  --> $DIR/match_wild_err_arm.rs:14:9
 +   |
 +LL |         Err(_) => panic!("err"),
 +   |         ^^^^^^
 +   |
 +   = note: `-D clippy::match-wild-err-arm` implied by `-D warnings`
-    = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
++   = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable
 +
 +error: `Err(_)` matches all errors
 +  --> $DIR/match_wild_err_arm.rs:20:9
 +   |
 +LL |         Err(_) => panic!(),
 +   |         ^^^^^^
 +   |
-    = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
++   = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable
 +
 +error: `Err(_)` matches all errors
 +  --> $DIR/match_wild_err_arm.rs:26:9
 +   |
 +LL |         Err(_) => {
 +   |         ^^^^^^
 +   |
-    = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
++   = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable
 +
 +error: `Err(_e)` matches all errors
 +  --> $DIR/match_wild_err_arm.rs:34:9
 +   |
 +LL |         Err(_e) => panic!(),
 +   |         ^^^^^^^
 +   |
++   = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable
 +
 +error: aborting due to 4 previous errors
 +
index 36bc52e3374e1a9747ddfdcad7da7306963bb092,0000000000000000000000000000000000000000..ecad10a82903884a182a13a46df9922e4a9896b0
mode 100644,000000..100644
--- /dev/null
@@@ -1,21 -1,0 +1,28 @@@
 +// run-rustfix
 +#![allow(dead_code, unused_mut)]
 +#![warn(clippy::mut_mutex_lock)]
 +
 +use std::sync::{Arc, Mutex};
 +
 +fn mut_mutex_lock() {
 +    let mut value_rc = Arc::new(Mutex::new(42_u8));
 +    let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
 +
 +    let mut value = value_mutex.get_mut().unwrap();
 +    *value += 1;
 +}
 +
 +fn no_owned_mutex_lock() {
 +    let mut value_rc = Arc::new(Mutex::new(42_u8));
 +    let mut value = value_rc.lock().unwrap();
 +    *value += 1;
 +}
 +
++fn issue9415() {
++    let mut arc_mutex = Arc::new(Mutex::new(42_u8));
++    let arc_mutex: &mut Arc<Mutex<u8>> = &mut arc_mutex;
++    let mut guard = arc_mutex.lock().unwrap();
++    *guard += 1;
++}
++
 +fn main() {}
index ea60df5ae1bbc92cda19453e4756aefea50833b3,0000000000000000000000000000000000000000..f2b1d6fbfbc3e1f4ca237404f4106a5fd2d8f5e7
mode 100644,000000..100644
--- /dev/null
@@@ -1,21 -1,0 +1,28 @@@
 +// run-rustfix
 +#![allow(dead_code, unused_mut)]
 +#![warn(clippy::mut_mutex_lock)]
 +
 +use std::sync::{Arc, Mutex};
 +
 +fn mut_mutex_lock() {
 +    let mut value_rc = Arc::new(Mutex::new(42_u8));
 +    let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
 +
 +    let mut value = value_mutex.lock().unwrap();
 +    *value += 1;
 +}
 +
 +fn no_owned_mutex_lock() {
 +    let mut value_rc = Arc::new(Mutex::new(42_u8));
 +    let mut value = value_rc.lock().unwrap();
 +    *value += 1;
 +}
 +
++fn issue9415() {
++    let mut arc_mutex = Arc::new(Mutex::new(42_u8));
++    let arc_mutex: &mut Arc<Mutex<u8>> = &mut arc_mutex;
++    let mut guard = arc_mutex.lock().unwrap();
++    *guard += 1;
++}
++
 +fn main() {}
index 18ea4e550292a4b3ddd5a729f4ba946afe2f4d13,0000000000000000000000000000000000000000..5991188ab6372389d50ed92e46c479aa0a6a3274
mode 100644,000000..100644
--- /dev/null
@@@ -1,229 -1,0 +1,229 @@@
-     map.entry(42).or_insert(String::new());
 +// run-rustfix
 +
 +#![warn(clippy::or_fun_call)]
 +#![allow(dead_code)]
 +#![allow(clippy::unnecessary_wraps, clippy::borrow_as_ptr)]
 +
 +use std::collections::BTreeMap;
 +use std::collections::HashMap;
 +use std::time::Duration;
 +
 +/// Checks implementation of the `OR_FUN_CALL` lint.
 +fn or_fun_call() {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn new() -> Foo {
 +            Foo
 +        }
 +    }
 +
 +    struct FakeDefault;
 +    impl FakeDefault {
 +        fn default() -> Self {
 +            FakeDefault
 +        }
 +    }
 +
 +    impl Default for FakeDefault {
 +        fn default() -> Self {
 +            FakeDefault
 +        }
 +    }
 +
 +    enum Enum {
 +        A(i32),
 +    }
 +
 +    fn make<T>() -> T {
 +        unimplemented!();
 +    }
 +
 +    let with_enum = Some(Enum::A(1));
 +    with_enum.unwrap_or(Enum::A(5));
 +
 +    let with_const_fn = Some(Duration::from_secs(1));
 +    with_const_fn.unwrap_or(Duration::from_secs(5));
 +
 +    let with_constructor = Some(vec![1]);
 +    with_constructor.unwrap_or_else(make);
 +
 +    let with_new = Some(vec![1]);
 +    with_new.unwrap_or_default();
 +
 +    let with_const_args = Some(vec![1]);
 +    with_const_args.unwrap_or_else(|| Vec::with_capacity(12));
 +
 +    let with_err: Result<_, ()> = Ok(vec![1]);
 +    with_err.unwrap_or_else(|_| make());
 +
 +    let with_err_args: Result<_, ()> = Ok(vec![1]);
 +    with_err_args.unwrap_or_else(|_| Vec::with_capacity(12));
 +
 +    let with_default_trait = Some(1);
 +    with_default_trait.unwrap_or_default();
 +
 +    let with_default_type = Some(1);
 +    with_default_type.unwrap_or_default();
 +
 +    let self_default = None::<FakeDefault>;
 +    self_default.unwrap_or_else(<FakeDefault>::default);
 +
 +    let real_default = None::<FakeDefault>;
 +    real_default.unwrap_or_default();
 +
 +    let with_vec = Some(vec![1]);
 +    with_vec.unwrap_or_default();
 +
 +    let without_default = Some(Foo);
 +    without_default.unwrap_or_else(Foo::new);
 +
 +    let mut map = HashMap::<u64, String>::new();
-     map_vec.entry(42).or_insert(vec![]);
++    map.entry(42).or_default();
 +
 +    let mut map_vec = HashMap::<u64, Vec<i32>>::new();
-     btree.entry(42).or_insert(String::new());
++    map_vec.entry(42).or_default();
 +
 +    let mut btree = BTreeMap::<u64, String>::new();
-     btree_vec.entry(42).or_insert(vec![]);
++    btree.entry(42).or_default();
 +
 +    let mut btree_vec = BTreeMap::<u64, Vec<i32>>::new();
++    btree_vec.entry(42).or_default();
 +
 +    let stringy = Some(String::new());
 +    let _ = stringy.unwrap_or_default();
 +
 +    let opt = Some(1);
 +    let hello = "Hello";
 +    let _ = opt.ok_or(format!("{} world.", hello));
 +
 +    // index
 +    let map = HashMap::<u64, u64>::new();
 +    let _ = Some(1).unwrap_or_else(|| map[&1]);
 +    let map = BTreeMap::<u64, u64>::new();
 +    let _ = Some(1).unwrap_or_else(|| map[&1]);
 +    // don't lint index vec
 +    let vec = vec![1];
 +    let _ = Some(1).unwrap_or(vec[1]);
 +}
 +
 +struct Foo(u8);
 +struct Bar(String, Duration);
 +#[rustfmt::skip]
 +fn test_or_with_ctors() {
 +    let opt = Some(1);
 +    let opt_opt = Some(Some(1));
 +    // we also test for const promotion, this makes sure we don't hit that
 +    let two = 2;
 +
 +    let _ = opt_opt.unwrap_or(Some(2));
 +    let _ = opt_opt.unwrap_or(Some(two));
 +    let _ = opt.ok_or(Some(2));
 +    let _ = opt.ok_or(Some(two));
 +    let _ = opt.ok_or(Foo(2));
 +    let _ = opt.ok_or(Foo(two));
 +    let _ = opt.or(Some(2));
 +    let _ = opt.or(Some(two));
 +
 +    let _ = Some("a".to_string()).or_else(|| Some("b".to_string()));
 +
 +    let b = "b".to_string();
 +    let _ = Some(Bar("a".to_string(), Duration::from_secs(1)))
 +        .or(Some(Bar(b, Duration::from_secs(2))));
 +
 +    let vec = vec!["foo"];
 +    let _ = opt.ok_or(vec.len());
 +
 +    let array = ["foo"];
 +    let _ = opt.ok_or(array.len());
 +
 +    let slice = &["foo"][..];
 +    let _ = opt.ok_or(slice.len());
 +
 +    let string = "foo";
 +    let _ = opt.ok_or(string.len());
 +}
 +
 +// Issue 4514 - early return
 +fn f() -> Option<()> {
 +    let a = Some(1);
 +    let b = 1i32;
 +
 +    let _ = a.unwrap_or(b.checked_mul(3)?.min(240));
 +
 +    Some(())
 +}
 +
 +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 s = "test".to_owned();
 +        let s = &s as *const _;
 +        None.unwrap_or_else(|| ptr_to_ref(s));
 +    }
 +
 +    fn bar() {
 +        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 { ptr_to_ref(s) });
 +    }
 +}
 +
 +mod issue8239 {
 +    fn more_than_max_suggestion_highest_lines_0() {
 +        let frames = Vec::new();
 +        frames
 +            .iter()
 +            .map(|f: &String| f.to_lowercase())
 +            .reduce(|mut acc, f| {
 +                acc.push_str(&f);
 +                acc
 +            })
 +            .unwrap_or_default();
 +    }
 +
 +    fn more_to_max_suggestion_highest_lines_1() {
 +        let frames = Vec::new();
 +        let iter = frames.iter();
 +        iter.map(|f: &String| f.to_lowercase())
 +            .reduce(|mut acc, f| {
 +                let _ = "";
 +                let _ = "";
 +                acc.push_str(&f);
 +                acc
 +            })
 +            .unwrap_or_default();
 +    }
 +
 +    fn equal_to_max_suggestion_highest_lines() {
 +        let frames = Vec::new();
 +        let iter = frames.iter();
 +        iter.map(|f: &String| f.to_lowercase())
 +            .reduce(|mut acc, f| {
 +                let _ = "";
 +                acc.push_str(&f);
 +                acc
 +            })
 +            .unwrap_or_default();
 +    }
 +
 +    fn less_than_max_suggestion_highest_lines() {
 +        let frames = Vec::new();
 +        let iter = frames.iter();
 +        let map = iter.map(|f: &String| f.to_lowercase());
 +        map.reduce(|mut acc, f| {
 +            acc.push_str(&f);
 +            acc
 +        })
 +        .unwrap_or_default();
 +    }
 +}
 +
 +fn main() {}
index 887f23ac9761dfd7167f44f269d3224cb6d75e75,0000000000000000000000000000000000000000..e3dab4cb14778c06579fe88223a0ee5d8c7d2c12
mode 100644,000000..100644
--- /dev/null
@@@ -1,136 -1,0 +1,160 @@@
- error: aborting due to 22 previous errors
 +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:14
 +   |
 +LL |     with_new.unwrap_or(Vec::new());
 +   |              ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:55:21
 +   |
 +LL |     with_const_args.unwrap_or(Vec::with_capacity(12));
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Vec::with_capacity(12))`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:58:14
 +   |
 +LL |     with_err.unwrap_or(make());
 +   |              ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| make())`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:61:19
 +   |
 +LL |     with_err_args.unwrap_or(Vec::with_capacity(12));
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| Vec::with_capacity(12))`
 +
 +error: use of `unwrap_or` followed by a call to `default`
 +  --> $DIR/or_fun_call.rs:64:24
 +   |
 +LL |     with_default_trait.unwrap_or(Default::default());
 +   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a call to `default`
 +  --> $DIR/or_fun_call.rs:67:23
 +   |
 +LL |     with_default_type.unwrap_or(u64::default());
 +   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:70:18
 +   |
 +LL |     self_default.unwrap_or(<FakeDefault>::default());
 +   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(<FakeDefault>::default)`
 +
 +error: use of `unwrap_or` followed by a call to `default`
 +  --> $DIR/or_fun_call.rs:73:18
 +   |
 +LL |     real_default.unwrap_or(<FakeDefault as Default>::default());
 +   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a call to `new`
 +  --> $DIR/or_fun_call.rs:76:14
 +   |
 +LL |     with_vec.unwrap_or(vec![]);
 +   |              ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:79:21
 +   |
 +LL |     without_default.unwrap_or(Foo::new());
 +   |                     ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)`
 +
++error: use of `or_insert` followed by a call to `new`
++  --> $DIR/or_fun_call.rs:82:19
++   |
++LL |     map.entry(42).or_insert(String::new());
++   |                   ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_default()`
++
++error: use of `or_insert` followed by a call to `new`
++  --> $DIR/or_fun_call.rs:85:23
++   |
++LL |     map_vec.entry(42).or_insert(vec![]);
++   |                       ^^^^^^^^^^^^^^^^^ help: try this: `or_default()`
++
++error: use of `or_insert` followed by a call to `new`
++  --> $DIR/or_fun_call.rs:88:21
++   |
++LL |     btree.entry(42).or_insert(String::new());
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_default()`
++
++error: use of `or_insert` followed by a call to `new`
++  --> $DIR/or_fun_call.rs:91:25
++   |
++LL |     btree_vec.entry(42).or_insert(vec![]);
++   |                         ^^^^^^^^^^^^^^^^^ help: try this: `or_default()`
++
 +error: use of `unwrap_or` followed by a call to `new`
 +  --> $DIR/or_fun_call.rs:94:21
 +   |
 +LL |     let _ = stringy.unwrap_or(String::new());
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:102:21
 +   |
 +LL |     let _ = Some(1).unwrap_or(map[&1]);
 +   |                     ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:104:21
 +   |
 +LL |     let _ = Some(1).unwrap_or(map[&1]);
 +   |                     ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])`
 +
 +error: use of `or` followed by a function call
 +  --> $DIR/or_fun_call.rs:128:35
 +   |
 +LL |     let _ = Some("a".to_string()).or(Some("b".to_string()));
 +   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:167:14
 +   |
 +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:173:14
 +   |
 +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:175:14
 +   |
 +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 call to `new`
 +  --> $DIR/or_fun_call.rs:189:14
 +   |
 +LL |             .unwrap_or(String::new());
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a call to `new`
 +  --> $DIR/or_fun_call.rs:202:14
 +   |
 +LL |             .unwrap_or(String::new());
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a call to `new`
 +  --> $DIR/or_fun_call.rs:214:14
 +   |
 +LL |             .unwrap_or(String::new());
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a call to `new`
 +  --> $DIR/or_fun_call.rs:225:10
 +   |
 +LL |         .unwrap_or(String::new());
 +   |          ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
++error: aborting due to 26 previous errors
 +
index 40d7791df281a67f7700871529ed5d0aa96e8b16,0000000000000000000000000000000000000000..a16a3e54d45eadaaf8025b6eadb94b4a2847076d
mode 100644,000000..100644
--- /dev/null
@@@ -1,42 -1,0 +1,61 @@@
 +// run-rustfix
 +
 +#![allow(unused_parens)]
 +#![allow(clippy::iter_with_drain)]
 +fn f() -> usize {
 +    42
 +}
 +
++macro_rules! macro_plus_one {
++    ($m: literal) => {
++        for i in 0..$m + 1 {
++            println!("{}", i);
++        }
++    };
++}
++
++macro_rules! macro_minus_one {
++    ($m: literal) => {
++        for i in 0..=$m - 1 {
++            println!("{}", i);
++        }
++    };
++}
++
 +#[warn(clippy::range_plus_one)]
 +#[warn(clippy::range_minus_one)]
 +fn main() {
 +    for _ in 0..2 {}
 +    for _ in 0..=2 {}
 +
 +    for _ in 0..=3 {}
 +    for _ in 0..=3 + 1 {}
 +
 +    for _ in 0..=5 {}
 +    for _ in 0..=1 + 5 {}
 +
 +    for _ in 1..=1 {}
 +    for _ in 1..=1 + 1 {}
 +
 +    for _ in 0..13 + 13 {}
 +    for _ in 0..=13 - 7 {}
 +
 +    for _ in 0..=f() {}
 +    for _ in 0..=(1 + f()) {}
 +
 +    let _ = ..11 - 1;
 +    let _ = ..11;
 +    let _ = ..11;
 +    let _ = (1..=11);
 +    let _ = ((f() + 1)..=f());
 +
 +    const ONE: usize = 1;
 +    // integer consts are linted, too
 +    for _ in 1..=ONE {}
 +
 +    let mut vec: Vec<()> = std::vec::Vec::new();
 +    vec.drain(..);
++
++    macro_plus_one!(5);
++    macro_minus_one!(5);
 +}
index a8ddd9b5f751b36ccb2fb065bd291ee0b2956328,0000000000000000000000000000000000000000..bd6cb4d21be51b653e54e6c42eb2148688175729
mode 100644,000000..100644
--- /dev/null
@@@ -1,42 -1,0 +1,61 @@@
 +// run-rustfix
 +
 +#![allow(unused_parens)]
 +#![allow(clippy::iter_with_drain)]
 +fn f() -> usize {
 +    42
 +}
 +
++macro_rules! macro_plus_one {
++    ($m: literal) => {
++        for i in 0..$m + 1 {
++            println!("{}", i);
++        }
++    };
++}
++
++macro_rules! macro_minus_one {
++    ($m: literal) => {
++        for i in 0..=$m - 1 {
++            println!("{}", i);
++        }
++    };
++}
++
 +#[warn(clippy::range_plus_one)]
 +#[warn(clippy::range_minus_one)]
 +fn main() {
 +    for _ in 0..2 {}
 +    for _ in 0..=2 {}
 +
 +    for _ in 0..3 + 1 {}
 +    for _ in 0..=3 + 1 {}
 +
 +    for _ in 0..1 + 5 {}
 +    for _ in 0..=1 + 5 {}
 +
 +    for _ in 1..1 + 1 {}
 +    for _ in 1..=1 + 1 {}
 +
 +    for _ in 0..13 + 13 {}
 +    for _ in 0..=13 - 7 {}
 +
 +    for _ in 0..(1 + f()) {}
 +    for _ in 0..=(1 + f()) {}
 +
 +    let _ = ..11 - 1;
 +    let _ = ..=11 - 1;
 +    let _ = ..=(11 - 1);
 +    let _ = (1..11 + 1);
 +    let _ = (f() + 1)..(f() + 1);
 +
 +    const ONE: usize = 1;
 +    // integer consts are linted, too
 +    for _ in 1..ONE + ONE {}
 +
 +    let mut vec: Vec<()> = std::vec::Vec::new();
 +    vec.drain(..);
++
++    macro_plus_one!(5);
++    macro_minus_one!(5);
 +}
index fb4f1658597a585de8f220e9255e0f5c57388ad5,0000000000000000000000000000000000000000..0223696243b20c75e41e68840d3e05be1f3a83c7
mode 100644,000000..100644
--- /dev/null
@@@ -1,60 -1,0 +1,60 @@@
-   --> $DIR/range_plus_minus_one.rs:15:14
 +error: an inclusive range would be more readable
-   --> $DIR/range_plus_minus_one.rs:18:14
++  --> $DIR/range_plus_minus_one.rs:31:14
 +   |
 +LL |     for _ in 0..3 + 1 {}
 +   |              ^^^^^^^^ help: use: `0..=3`
 +   |
 +   = note: `-D clippy::range-plus-one` implied by `-D warnings`
 +
 +error: an inclusive range would be more readable
-   --> $DIR/range_plus_minus_one.rs:21:14
++  --> $DIR/range_plus_minus_one.rs:34:14
 +   |
 +LL |     for _ in 0..1 + 5 {}
 +   |              ^^^^^^^^ help: use: `0..=5`
 +
 +error: an inclusive range would be more readable
-   --> $DIR/range_plus_minus_one.rs:27:14
++  --> $DIR/range_plus_minus_one.rs:37:14
 +   |
 +LL |     for _ in 1..1 + 1 {}
 +   |              ^^^^^^^^ help: use: `1..=1`
 +
 +error: an inclusive range would be more readable
-   --> $DIR/range_plus_minus_one.rs:31:13
++  --> $DIR/range_plus_minus_one.rs:43:14
 +   |
 +LL |     for _ in 0..(1 + f()) {}
 +   |              ^^^^^^^^^^^^ help: use: `0..=f()`
 +
 +error: an exclusive range would be more readable
-   --> $DIR/range_plus_minus_one.rs:32:13
++  --> $DIR/range_plus_minus_one.rs:47:13
 +   |
 +LL |     let _ = ..=11 - 1;
 +   |             ^^^^^^^^^ help: use: `..11`
 +   |
 +   = note: `-D clippy::range-minus-one` implied by `-D warnings`
 +
 +error: an exclusive range would be more readable
-   --> $DIR/range_plus_minus_one.rs:33:13
++  --> $DIR/range_plus_minus_one.rs:48:13
 +   |
 +LL |     let _ = ..=(11 - 1);
 +   |             ^^^^^^^^^^^ help: use: `..11`
 +
 +error: an inclusive range would be more readable
-   --> $DIR/range_plus_minus_one.rs:34:13
++  --> $DIR/range_plus_minus_one.rs:49:13
 +   |
 +LL |     let _ = (1..11 + 1);
 +   |             ^^^^^^^^^^^ help: use: `(1..=11)`
 +
 +error: an inclusive range would be more readable
-   --> $DIR/range_plus_minus_one.rs:38:14
++  --> $DIR/range_plus_minus_one.rs:50:13
 +   |
 +LL |     let _ = (f() + 1)..(f() + 1);
 +   |             ^^^^^^^^^^^^^^^^^^^^ help: use: `((f() + 1)..=f())`
 +
 +error: an inclusive range would be more readable
++  --> $DIR/range_plus_minus_one.rs:54:14
 +   |
 +LL |     for _ in 1..ONE + ONE {}
 +   |              ^^^^^^^^^^^^ help: use: `1..=ONE`
 +
 +error: aborting due to 9 previous errors
 +
index 78d8f76fe669fb32926d4459252735166997062d,0000000000000000000000000000000000000000..f7df3b85655013f4b02e712354355cf7abbae0d5
mode 100644,000000..100644
--- /dev/null
@@@ -1,98 -1,0 +1,99 @@@
 +#![warn(clippy::result_large_err)]
++#![allow(clippy::large_enum_variant)]
 +
 +pub fn small_err() -> Result<(), u128> {
 +    Ok(())
 +}
 +
 +pub fn large_err() -> Result<(), [u8; 512]> {
 +    Ok(())
 +}
 +
 +pub struct FullyDefinedLargeError {
 +    _foo: u128,
 +    _bar: [u8; 100],
 +    _foobar: [u8; 120],
 +}
 +
 +impl FullyDefinedLargeError {
 +    pub fn ret() -> Result<(), Self> {
 +        Ok(())
 +    }
 +}
 +
 +pub fn struct_error() -> Result<(), FullyDefinedLargeError> {
 +    Ok(())
 +}
 +
 +type Fdlr<T> = std::result::Result<T, FullyDefinedLargeError>;
 +pub fn large_err_via_type_alias<T>(x: T) -> Fdlr<T> {
 +    Ok(x)
 +}
 +
 +pub fn param_small_error<R>() -> Result<(), (R, u128)> {
 +    Ok(())
 +}
 +
 +pub fn param_large_error<R>() -> Result<(), (u128, R, FullyDefinedLargeError)> {
 +    Ok(())
 +}
 +
 +pub enum LargeErrorVariants<T> {
 +    _Small(u8),
 +    _Omg([u8; 512]),
 +    _Param(T),
 +}
 +
 +impl LargeErrorVariants<()> {
 +    pub fn large_enum_error() -> Result<(), Self> {
 +        Ok(())
 +    }
 +}
 +
 +trait TraitForcesLargeError {
 +    fn large_error() -> Result<(), [u8; 512]> {
 +        Ok(())
 +    }
 +}
 +
 +struct TraitImpl;
 +
 +impl TraitForcesLargeError for TraitImpl {
 +    // Should not lint
 +    fn large_error() -> Result<(), [u8; 512]> {
 +        Ok(())
 +    }
 +}
 +
 +pub union FullyDefinedUnionError {
 +    _maybe: u8,
 +    _or_even: [[u8; 16]; 32],
 +}
 +
 +pub fn large_union_err() -> Result<(), FullyDefinedUnionError> {
 +    Ok(())
 +}
 +
 +pub union UnionError<T: Copy> {
 +    _maybe: T,
 +    _or_perhaps_even: (T, [u8; 512]),
 +}
 +
 +pub fn param_large_union<T: Copy>() -> Result<(), UnionError<T>> {
 +    Ok(())
 +}
 +
 +pub struct ArrayError<T, U> {
 +    _large_array: [T; 32],
 +    _other_stuff: U,
 +}
 +
 +pub fn array_error_subst<U>() -> Result<(), ArrayError<i32, U>> {
 +    Ok(())
 +}
 +
 +pub fn array_error<T, U>() -> Result<(), ArrayError<(i32, T), U>> {
 +    Ok(())
 +}
 +
 +fn main() {}
index 0f1f39d72cba187cff97f89762840cc32023c90c,0000000000000000000000000000000000000000..ef19f2854ab12e731663126183c1d8f0795e48bb
mode 100644,000000..100644
--- /dev/null
@@@ -1,91 -1,0 +1,91 @@@
-   --> $DIR/result_large_err.rs:7:23
 +error: the `Err`-variant returned from this function is very large
-   --> $DIR/result_large_err.rs:18:21
++  --> $DIR/result_large_err.rs:8:23
 +   |
 +LL | pub fn large_err() -> Result<(), [u8; 512]> {
 +   |                       ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
 +   |
 +   = note: `-D clippy::result-large-err` implied by `-D warnings`
 +   = help: try reducing the size of `[u8; 512]`, for example by boxing large elements or replacing it with `Box<[u8; 512]>`
 +
 +error: the `Err`-variant returned from this function is very large
-   --> $DIR/result_large_err.rs:23:26
++  --> $DIR/result_large_err.rs:19:21
 +   |
 +LL |     pub fn ret() -> Result<(), Self> {
 +   |                     ^^^^^^^^^^^^^^^^ the `Err`-variant is at least 240 bytes
 +   |
 +   = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box<FullyDefinedLargeError>`
 +
 +error: the `Err`-variant returned from this function is very large
-   --> $DIR/result_large_err.rs:28:45
++  --> $DIR/result_large_err.rs:24:26
 +   |
 +LL | pub fn struct_error() -> Result<(), FullyDefinedLargeError> {
 +   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 240 bytes
 +   |
 +   = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box<FullyDefinedLargeError>`
 +
 +error: the `Err`-variant returned from this function is very large
-   --> $DIR/result_large_err.rs:36:34
++  --> $DIR/result_large_err.rs:29:45
 +   |
 +LL | pub fn large_err_via_type_alias<T>(x: T) -> Fdlr<T> {
 +   |                                             ^^^^^^^ the `Err`-variant is at least 240 bytes
 +   |
 +   = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box<FullyDefinedLargeError>`
 +
 +error: the `Err`-variant returned from this function is very large
-   --> $DIR/result_large_err.rs:47:34
++  --> $DIR/result_large_err.rs:37:34
 +   |
 +LL | pub fn param_large_error<R>() -> Result<(), (u128, R, FullyDefinedLargeError)> {
 +   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 256 bytes
 +   |
 +   = help: try reducing the size of `(u128, R, FullyDefinedLargeError)`, for example by boxing large elements or replacing it with `Box<(u128, R, FullyDefinedLargeError)>`
 +
 +error: the `Err`-variant returned from this function is very large
-   --> $DIR/result_large_err.rs:53:25
++  --> $DIR/result_large_err.rs:48:34
 +   |
 +LL |     pub fn large_enum_error() -> Result<(), Self> {
 +   |                                  ^^^^^^^^^^^^^^^^ the `Err`-variant is at least 513 bytes
 +   |
 +   = help: try reducing the size of `LargeErrorVariants<()>`, for example by boxing large elements or replacing it with `Box<LargeErrorVariants<()>>`
 +
 +error: the `Err`-variant returned from this function is very large
-   --> $DIR/result_large_err.rs:72:29
++  --> $DIR/result_large_err.rs:54:25
 +   |
 +LL |     fn large_error() -> Result<(), [u8; 512]> {
 +   |                         ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
 +   |
 +   = help: try reducing the size of `[u8; 512]`, for example by boxing large elements or replacing it with `Box<[u8; 512]>`
 +
 +error: the `Err`-variant returned from this function is very large
-   --> $DIR/result_large_err.rs:81:40
++  --> $DIR/result_large_err.rs:73:29
 +   |
 +LL | pub fn large_union_err() -> Result<(), FullyDefinedUnionError> {
 +   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
 +   |
 +   = help: try reducing the size of `FullyDefinedUnionError`, for example by boxing large elements or replacing it with `Box<FullyDefinedUnionError>`
 +
 +error: the `Err`-variant returned from this function is very large
-   --> $DIR/result_large_err.rs:90:34
++  --> $DIR/result_large_err.rs:82:40
 +   |
 +LL | pub fn param_large_union<T: Copy>() -> Result<(), UnionError<T>> {
 +   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
 +   |
 +   = help: try reducing the size of `UnionError<T>`, for example by boxing large elements or replacing it with `Box<UnionError<T>>`
 +
 +error: the `Err`-variant returned from this function is very large
-   --> $DIR/result_large_err.rs:94:31
++  --> $DIR/result_large_err.rs:91:34
 +   |
 +LL | pub fn array_error_subst<U>() -> Result<(), ArrayError<i32, U>> {
 +   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes
 +   |
 +   = help: try reducing the size of `ArrayError<i32, U>`, for example by boxing large elements or replacing it with `Box<ArrayError<i32, U>>`
 +
 +error: the `Err`-variant returned from this function is very large
++  --> $DIR/result_large_err.rs:95:31
 +   |
 +LL | pub fn array_error<T, U>() -> Result<(), ArrayError<(i32, T), U>> {
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes
 +   |
 +   = help: try reducing the size of `ArrayError<(i32, T), U>`, for example by boxing large elements or replacing it with `Box<ArrayError<(i32, T), U>>`
 +
 +error: aborting due to 11 previous errors
 +
index 9cd5bc73b1ec51852af3c2bbeae84cb5ddf05594,0000000000000000000000000000000000000000..a920c63b199c1e194697c33066be47f19c88e78c
mode 100644,000000..100644
--- /dev/null
@@@ -1,359 -1,0 +1,419 @@@
 +// run-rustfix
 +
 +#![allow(clippy::ptr_arg)]
 +#![warn(clippy::unnecessary_to_owned)]
 +#![feature(custom_inner_attributes)]
 +
 +use std::borrow::Cow;
 +use std::ffi::{CStr, CString, OsStr, OsString};
 +use std::ops::Deref;
 +
 +#[derive(Clone)]
 +struct X(String);
 +
 +impl Deref for X {
 +    type Target = [u8];
 +    fn deref(&self) -> &[u8] {
 +        self.0.as_bytes()
 +    }
 +}
 +
 +impl AsRef<str> for X {
 +    fn as_ref(&self) -> &str {
 +        self.0.as_str()
 +    }
 +}
 +
 +impl ToString for X {
 +    fn to_string(&self) -> String {
 +        self.0.to_string()
 +    }
 +}
 +
 +impl X {
 +    fn join(&self, other: impl AsRef<str>) -> Self {
 +        let mut s = self.0.clone();
 +        s.push_str(other.as_ref());
 +        Self(s)
 +    }
 +}
 +
 +#[allow(dead_code)]
 +#[derive(Clone)]
 +enum FileType {
 +    Account,
 +    PrivateKey,
 +    Certificate,
 +}
 +
 +fn main() {
 +    let c_str = CStr::from_bytes_with_nul(&[0]).unwrap();
 +    let os_str = OsStr::new("x");
 +    let path = std::path::Path::new("x");
 +    let s = "x";
 +    let array = ["x"];
 +    let array_ref = &["x"];
 +    let slice = &["x"][..];
 +    let x = X(String::from("x"));
 +    let x_ref = &x;
 +
 +    require_c_str(&Cow::from(c_str));
 +    require_c_str(c_str);
 +
 +    require_os_str(os_str);
 +    require_os_str(&Cow::from(os_str));
 +    require_os_str(os_str);
 +
 +    require_path(path);
 +    require_path(&Cow::from(path));
 +    require_path(path);
 +
 +    require_str(s);
 +    require_str(&Cow::from(s));
 +    require_str(s);
 +    require_str(x_ref.as_ref());
 +
 +    require_slice(slice);
 +    require_slice(&Cow::from(slice));
 +    require_slice(array.as_ref());
 +    require_slice(array_ref.as_ref());
 +    require_slice(slice);
 +    require_slice(&x_ref.to_owned()); // No longer flagged because of #8759.
 +
 +    require_x(&Cow::<X>::Owned(x.clone()));
 +    require_x(&x_ref.to_owned()); // No longer flagged because of #8759.
 +
 +    require_deref_c_str(c_str);
 +    require_deref_os_str(os_str);
 +    require_deref_path(path);
 +    require_deref_str(s);
 +    require_deref_slice(slice);
 +
 +    require_impl_deref_c_str(c_str);
 +    require_impl_deref_os_str(os_str);
 +    require_impl_deref_path(path);
 +    require_impl_deref_str(s);
 +    require_impl_deref_slice(slice);
 +
 +    require_deref_str_slice(s, slice);
 +    require_deref_slice_str(slice, s);
 +
 +    require_as_ref_c_str(c_str);
 +    require_as_ref_os_str(os_str);
 +    require_as_ref_path(path);
 +    require_as_ref_str(s);
 +    require_as_ref_str(&x);
 +    require_as_ref_slice(array);
 +    require_as_ref_slice(array_ref);
 +    require_as_ref_slice(slice);
 +
 +    require_impl_as_ref_c_str(c_str);
 +    require_impl_as_ref_os_str(os_str);
 +    require_impl_as_ref_path(path);
 +    require_impl_as_ref_str(s);
 +    require_impl_as_ref_str(&x);
 +    require_impl_as_ref_slice(array);
 +    require_impl_as_ref_slice(array_ref);
 +    require_impl_as_ref_slice(slice);
 +
 +    require_as_ref_str_slice(s, array);
 +    require_as_ref_str_slice(s, array_ref);
 +    require_as_ref_str_slice(s, slice);
 +    require_as_ref_slice_str(array, s);
 +    require_as_ref_slice_str(array_ref, s);
 +    require_as_ref_slice_str(slice, s);
 +
 +    let _ = x.join(x_ref);
 +
 +    let _ = slice.iter().copied();
 +    let _ = slice.iter().copied();
 +    let _ = [std::path::PathBuf::new()][..].iter().cloned();
 +    let _ = [std::path::PathBuf::new()][..].iter().cloned();
 +
 +    let _ = slice.iter().copied();
 +    let _ = slice.iter().copied();
 +    let _ = [std::path::PathBuf::new()][..].iter().cloned();
 +    let _ = [std::path::PathBuf::new()][..].iter().cloned();
 +
 +    let _ = check_files(&[FileType::Account]);
 +
 +    // negative tests
 +    require_string(&s.to_string());
 +    require_string(&Cow::from(s).into_owned());
 +    require_string(&s.to_owned());
 +    require_string(&x_ref.to_string());
 +
 +    // `X` isn't copy.
 +    require_slice(&x.to_owned());
 +    require_deref_slice(x.to_owned());
 +
 +    // The following should be flagged by `redundant_clone`, but not by this lint.
 +    require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap());
 +    require_os_str(&OsString::from("x"));
 +    require_path(&std::path::PathBuf::from("x"));
 +    require_str(&String::from("x"));
 +    require_slice(&[String::from("x")]);
 +}
 +
 +fn require_c_str(_: &CStr) {}
 +fn require_os_str(_: &OsStr) {}
 +fn require_path(_: &std::path::Path) {}
 +fn require_str(_: &str) {}
 +fn require_slice<T>(_: &[T]) {}
 +fn require_x(_: &X) {}
 +
 +fn require_deref_c_str<T: Deref<Target = CStr>>(_: T) {}
 +fn require_deref_os_str<T: Deref<Target = OsStr>>(_: T) {}
 +fn require_deref_path<T: Deref<Target = std::path::Path>>(_: T) {}
 +fn require_deref_str<T: Deref<Target = str>>(_: T) {}
 +fn require_deref_slice<T, U: Deref<Target = [T]>>(_: U) {}
 +
 +fn require_impl_deref_c_str(_: impl Deref<Target = CStr>) {}
 +fn require_impl_deref_os_str(_: impl Deref<Target = OsStr>) {}
 +fn require_impl_deref_path(_: impl Deref<Target = std::path::Path>) {}
 +fn require_impl_deref_str(_: impl Deref<Target = str>) {}
 +fn require_impl_deref_slice<T>(_: impl Deref<Target = [T]>) {}
 +
 +fn require_deref_str_slice<T: Deref<Target = str>, U, V: Deref<Target = [U]>>(_: T, _: V) {}
 +fn require_deref_slice_str<T, U: Deref<Target = [T]>, V: Deref<Target = str>>(_: U, _: V) {}
 +
 +fn require_as_ref_c_str<T: AsRef<CStr>>(_: T) {}
 +fn require_as_ref_os_str<T: AsRef<OsStr>>(_: T) {}
 +fn require_as_ref_path<T: AsRef<std::path::Path>>(_: T) {}
 +fn require_as_ref_str<T: AsRef<str>>(_: T) {}
 +fn require_as_ref_slice<T, U: AsRef<[T]>>(_: U) {}
 +
 +fn require_impl_as_ref_c_str(_: impl AsRef<CStr>) {}
 +fn require_impl_as_ref_os_str(_: impl AsRef<OsStr>) {}
 +fn require_impl_as_ref_path(_: impl AsRef<std::path::Path>) {}
 +fn require_impl_as_ref_str(_: impl AsRef<str>) {}
 +fn require_impl_as_ref_slice<T>(_: impl AsRef<[T]>) {}
 +
 +fn require_as_ref_str_slice<T: AsRef<str>, U, V: AsRef<[U]>>(_: T, _: V) {}
 +fn require_as_ref_slice_str<T, U: AsRef<[T]>, V: AsRef<str>>(_: U, _: V) {}
 +
 +// `check_files` is based on:
 +// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262
 +fn check_files(file_types: &[FileType]) -> bool {
 +    for t in file_types {
 +        let path = match get_file_path(t) {
 +            Ok(p) => p,
 +            Err(_) => {
 +                return false;
 +            },
 +        };
 +        if !path.is_file() {
 +            return false;
 +        }
 +    }
 +    true
 +}
 +
 +fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::Error> {
 +    Ok(std::path::PathBuf::new())
 +}
 +
 +fn require_string(_: &String) {}
 +
 +fn _msrv_1_35() {
 +    #![clippy::msrv = "1.35"]
 +    // `copied` was stabilized in 1.36, so clippy should use `cloned`.
 +    let _ = &["x"][..].iter().cloned();
 +}
 +
 +fn _msrv_1_36() {
 +    #![clippy::msrv = "1.36"]
 +    let _ = &["x"][..].iter().copied();
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/8507
 +mod issue_8507 {
 +    #![allow(dead_code)]
 +
 +    struct Opaque<P>(P);
 +
 +    pub trait Abstracted {}
 +
 +    impl<P> Abstracted for Opaque<P> {}
 +
 +    fn build<P>(p: P) -> Opaque<P>
 +    where
 +        P: AsRef<str>,
 +    {
 +        Opaque(p)
 +    }
 +
 +    // Should not lint.
 +    fn test_str(s: &str) -> Box<dyn Abstracted> {
 +        Box::new(build(s.to_string()))
 +    }
 +
 +    // Should not lint.
 +    fn test_x(x: super::X) -> Box<dyn Abstracted> {
 +        Box::new(build(x))
 +    }
 +
 +    #[derive(Clone, Copy)]
 +    struct Y(&'static str);
 +
 +    impl AsRef<str> for Y {
 +        fn as_ref(&self) -> &str {
 +            self.0
 +        }
 +    }
 +
 +    impl ToString for Y {
 +        fn to_string(&self) -> String {
 +            self.0.to_string()
 +        }
 +    }
 +
 +    // Should lint because Y is copy.
 +    fn test_y(y: Y) -> Box<dyn Abstracted> {
 +        Box::new(build(y))
 +    }
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/8759
 +mod issue_8759 {
 +    #![allow(dead_code)]
 +
 +    #[derive(Default)]
 +    struct View {}
 +
 +    impl std::borrow::ToOwned for View {
 +        type Owned = View;
 +        fn to_owned(&self) -> Self::Owned {
 +            View {}
 +        }
 +    }
 +
 +    #[derive(Default)]
 +    struct RenderWindow {
 +        default_view: View,
 +    }
 +
 +    impl RenderWindow {
 +        fn default_view(&self) -> &View {
 +            &self.default_view
 +        }
 +        fn set_view(&mut self, _view: &View) {}
 +    }
 +
 +    fn main() {
 +        let mut rw = RenderWindow::default();
 +        rw.set_view(&rw.default_view().to_owned());
 +    }
 +}
 +
 +mod issue_8759_variant {
 +    #![allow(dead_code)]
 +
 +    #[derive(Clone, Default)]
 +    struct View {}
 +
 +    #[derive(Default)]
 +    struct RenderWindow {
 +        default_view: View,
 +    }
 +
 +    impl RenderWindow {
 +        fn default_view(&self) -> &View {
 +            &self.default_view
 +        }
 +        fn set_view(&mut self, _view: &View) {}
 +    }
 +
 +    fn main() {
 +        let mut rw = RenderWindow::default();
 +        rw.set_view(&rw.default_view().to_owned());
 +    }
 +}
 +
 +mod issue_9317 {
 +    #![allow(dead_code)]
 +
 +    struct Bytes {}
 +
 +    impl ToString for Bytes {
 +        fn to_string(&self) -> String {
 +            "123".to_string()
 +        }
 +    }
 +
 +    impl AsRef<[u8]> for Bytes {
 +        fn as_ref(&self) -> &[u8] {
 +            &[1, 2, 3]
 +        }
 +    }
 +
 +    fn consume<C: AsRef<[u8]>>(c: C) {
 +        let _ = c;
 +    }
 +
 +    pub fn main() {
 +        let b = Bytes {};
 +        // Should not lint.
 +        consume(b.to_string());
 +    }
 +}
++
++mod issue_9351 {
++    #![allow(dead_code)]
++
++    use std::ops::Deref;
++    use std::path::{Path, PathBuf};
++
++    fn require_deref_path<T: Deref<Target = std::path::Path>>(x: T) -> T {
++        x
++    }
++
++    fn generic_arg_used_elsewhere<T: AsRef<Path>>(_x: T, _y: T) {}
++
++    fn id<T: AsRef<str>>(x: T) -> T {
++        x
++    }
++
++    fn predicates_are_satisfied(_x: impl std::fmt::Write) {}
++
++    // Should lint
++    fn single_return() -> impl AsRef<str> {
++        id("abc")
++    }
++
++    // Should not lint
++    fn multiple_returns(b: bool) -> impl AsRef<str> {
++        if b {
++            return String::new();
++        }
++
++        id("abc".to_string())
++    }
++
++    struct S1(String);
++
++    // Should not lint
++    fn fields1() -> S1 {
++        S1(id("abc".to_string()))
++    }
++
++    struct S2 {
++        s: String,
++    }
++
++    // Should not lint
++    fn fields2() {
++        let mut s = S2 { s: "abc".into() };
++        s.s = id("abc".to_string());
++    }
++
++    pub fn main() {
++        let path = std::path::Path::new("x");
++        let path_buf = path.to_owned();
++
++        // Should not lint.
++        let _x: PathBuf = require_deref_path(path.to_owned());
++        generic_arg_used_elsewhere(path.to_owned(), path_buf);
++        predicates_are_satisfied(id("abc".to_string()));
++    }
++}
index 7f62ba3ab5d559ea0d8a60da23f977389044f054,0000000000000000000000000000000000000000..2128bdacddadf03fa7736e020583e5912b00a6e7
mode 100644,000000..100644
--- /dev/null
@@@ -1,359 -1,0 +1,419 @@@
 +// run-rustfix
 +
 +#![allow(clippy::ptr_arg)]
 +#![warn(clippy::unnecessary_to_owned)]
 +#![feature(custom_inner_attributes)]
 +
 +use std::borrow::Cow;
 +use std::ffi::{CStr, CString, OsStr, OsString};
 +use std::ops::Deref;
 +
 +#[derive(Clone)]
 +struct X(String);
 +
 +impl Deref for X {
 +    type Target = [u8];
 +    fn deref(&self) -> &[u8] {
 +        self.0.as_bytes()
 +    }
 +}
 +
 +impl AsRef<str> for X {
 +    fn as_ref(&self) -> &str {
 +        self.0.as_str()
 +    }
 +}
 +
 +impl ToString for X {
 +    fn to_string(&self) -> String {
 +        self.0.to_string()
 +    }
 +}
 +
 +impl X {
 +    fn join(&self, other: impl AsRef<str>) -> Self {
 +        let mut s = self.0.clone();
 +        s.push_str(other.as_ref());
 +        Self(s)
 +    }
 +}
 +
 +#[allow(dead_code)]
 +#[derive(Clone)]
 +enum FileType {
 +    Account,
 +    PrivateKey,
 +    Certificate,
 +}
 +
 +fn main() {
 +    let c_str = CStr::from_bytes_with_nul(&[0]).unwrap();
 +    let os_str = OsStr::new("x");
 +    let path = std::path::Path::new("x");
 +    let s = "x";
 +    let array = ["x"];
 +    let array_ref = &["x"];
 +    let slice = &["x"][..];
 +    let x = X(String::from("x"));
 +    let x_ref = &x;
 +
 +    require_c_str(&Cow::from(c_str).into_owned());
 +    require_c_str(&c_str.to_owned());
 +
 +    require_os_str(&os_str.to_os_string());
 +    require_os_str(&Cow::from(os_str).into_owned());
 +    require_os_str(&os_str.to_owned());
 +
 +    require_path(&path.to_path_buf());
 +    require_path(&Cow::from(path).into_owned());
 +    require_path(&path.to_owned());
 +
 +    require_str(&s.to_string());
 +    require_str(&Cow::from(s).into_owned());
 +    require_str(&s.to_owned());
 +    require_str(&x_ref.to_string());
 +
 +    require_slice(&slice.to_vec());
 +    require_slice(&Cow::from(slice).into_owned());
 +    require_slice(&array.to_owned());
 +    require_slice(&array_ref.to_owned());
 +    require_slice(&slice.to_owned());
 +    require_slice(&x_ref.to_owned()); // No longer flagged because of #8759.
 +
 +    require_x(&Cow::<X>::Owned(x.clone()).into_owned());
 +    require_x(&x_ref.to_owned()); // No longer flagged because of #8759.
 +
 +    require_deref_c_str(c_str.to_owned());
 +    require_deref_os_str(os_str.to_owned());
 +    require_deref_path(path.to_owned());
 +    require_deref_str(s.to_owned());
 +    require_deref_slice(slice.to_owned());
 +
 +    require_impl_deref_c_str(c_str.to_owned());
 +    require_impl_deref_os_str(os_str.to_owned());
 +    require_impl_deref_path(path.to_owned());
 +    require_impl_deref_str(s.to_owned());
 +    require_impl_deref_slice(slice.to_owned());
 +
 +    require_deref_str_slice(s.to_owned(), slice.to_owned());
 +    require_deref_slice_str(slice.to_owned(), s.to_owned());
 +
 +    require_as_ref_c_str(c_str.to_owned());
 +    require_as_ref_os_str(os_str.to_owned());
 +    require_as_ref_path(path.to_owned());
 +    require_as_ref_str(s.to_owned());
 +    require_as_ref_str(x.to_owned());
 +    require_as_ref_slice(array.to_owned());
 +    require_as_ref_slice(array_ref.to_owned());
 +    require_as_ref_slice(slice.to_owned());
 +
 +    require_impl_as_ref_c_str(c_str.to_owned());
 +    require_impl_as_ref_os_str(os_str.to_owned());
 +    require_impl_as_ref_path(path.to_owned());
 +    require_impl_as_ref_str(s.to_owned());
 +    require_impl_as_ref_str(x.to_owned());
 +    require_impl_as_ref_slice(array.to_owned());
 +    require_impl_as_ref_slice(array_ref.to_owned());
 +    require_impl_as_ref_slice(slice.to_owned());
 +
 +    require_as_ref_str_slice(s.to_owned(), array.to_owned());
 +    require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
 +    require_as_ref_str_slice(s.to_owned(), slice.to_owned());
 +    require_as_ref_slice_str(array.to_owned(), s.to_owned());
 +    require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
 +    require_as_ref_slice_str(slice.to_owned(), s.to_owned());
 +
 +    let _ = x.join(&x_ref.to_string());
 +
 +    let _ = slice.to_vec().into_iter();
 +    let _ = slice.to_owned().into_iter();
 +    let _ = [std::path::PathBuf::new()][..].to_vec().into_iter();
 +    let _ = [std::path::PathBuf::new()][..].to_owned().into_iter();
 +
 +    let _ = IntoIterator::into_iter(slice.to_vec());
 +    let _ = IntoIterator::into_iter(slice.to_owned());
 +    let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec());
 +    let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned());
 +
 +    let _ = check_files(&[FileType::Account]);
 +
 +    // negative tests
 +    require_string(&s.to_string());
 +    require_string(&Cow::from(s).into_owned());
 +    require_string(&s.to_owned());
 +    require_string(&x_ref.to_string());
 +
 +    // `X` isn't copy.
 +    require_slice(&x.to_owned());
 +    require_deref_slice(x.to_owned());
 +
 +    // The following should be flagged by `redundant_clone`, but not by this lint.
 +    require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
 +    require_os_str(&OsString::from("x").to_os_string());
 +    require_path(&std::path::PathBuf::from("x").to_path_buf());
 +    require_str(&String::from("x").to_string());
 +    require_slice(&[String::from("x")].to_owned());
 +}
 +
 +fn require_c_str(_: &CStr) {}
 +fn require_os_str(_: &OsStr) {}
 +fn require_path(_: &std::path::Path) {}
 +fn require_str(_: &str) {}
 +fn require_slice<T>(_: &[T]) {}
 +fn require_x(_: &X) {}
 +
 +fn require_deref_c_str<T: Deref<Target = CStr>>(_: T) {}
 +fn require_deref_os_str<T: Deref<Target = OsStr>>(_: T) {}
 +fn require_deref_path<T: Deref<Target = std::path::Path>>(_: T) {}
 +fn require_deref_str<T: Deref<Target = str>>(_: T) {}
 +fn require_deref_slice<T, U: Deref<Target = [T]>>(_: U) {}
 +
 +fn require_impl_deref_c_str(_: impl Deref<Target = CStr>) {}
 +fn require_impl_deref_os_str(_: impl Deref<Target = OsStr>) {}
 +fn require_impl_deref_path(_: impl Deref<Target = std::path::Path>) {}
 +fn require_impl_deref_str(_: impl Deref<Target = str>) {}
 +fn require_impl_deref_slice<T>(_: impl Deref<Target = [T]>) {}
 +
 +fn require_deref_str_slice<T: Deref<Target = str>, U, V: Deref<Target = [U]>>(_: T, _: V) {}
 +fn require_deref_slice_str<T, U: Deref<Target = [T]>, V: Deref<Target = str>>(_: U, _: V) {}
 +
 +fn require_as_ref_c_str<T: AsRef<CStr>>(_: T) {}
 +fn require_as_ref_os_str<T: AsRef<OsStr>>(_: T) {}
 +fn require_as_ref_path<T: AsRef<std::path::Path>>(_: T) {}
 +fn require_as_ref_str<T: AsRef<str>>(_: T) {}
 +fn require_as_ref_slice<T, U: AsRef<[T]>>(_: U) {}
 +
 +fn require_impl_as_ref_c_str(_: impl AsRef<CStr>) {}
 +fn require_impl_as_ref_os_str(_: impl AsRef<OsStr>) {}
 +fn require_impl_as_ref_path(_: impl AsRef<std::path::Path>) {}
 +fn require_impl_as_ref_str(_: impl AsRef<str>) {}
 +fn require_impl_as_ref_slice<T>(_: impl AsRef<[T]>) {}
 +
 +fn require_as_ref_str_slice<T: AsRef<str>, U, V: AsRef<[U]>>(_: T, _: V) {}
 +fn require_as_ref_slice_str<T, U: AsRef<[T]>, V: AsRef<str>>(_: U, _: V) {}
 +
 +// `check_files` is based on:
 +// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262
 +fn check_files(file_types: &[FileType]) -> bool {
 +    for t in file_types.to_vec() {
 +        let path = match get_file_path(&t) {
 +            Ok(p) => p,
 +            Err(_) => {
 +                return false;
 +            },
 +        };
 +        if !path.is_file() {
 +            return false;
 +        }
 +    }
 +    true
 +}
 +
 +fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::Error> {
 +    Ok(std::path::PathBuf::new())
 +}
 +
 +fn require_string(_: &String) {}
 +
 +fn _msrv_1_35() {
 +    #![clippy::msrv = "1.35"]
 +    // `copied` was stabilized in 1.36, so clippy should use `cloned`.
 +    let _ = &["x"][..].to_vec().into_iter();
 +}
 +
 +fn _msrv_1_36() {
 +    #![clippy::msrv = "1.36"]
 +    let _ = &["x"][..].to_vec().into_iter();
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/8507
 +mod issue_8507 {
 +    #![allow(dead_code)]
 +
 +    struct Opaque<P>(P);
 +
 +    pub trait Abstracted {}
 +
 +    impl<P> Abstracted for Opaque<P> {}
 +
 +    fn build<P>(p: P) -> Opaque<P>
 +    where
 +        P: AsRef<str>,
 +    {
 +        Opaque(p)
 +    }
 +
 +    // Should not lint.
 +    fn test_str(s: &str) -> Box<dyn Abstracted> {
 +        Box::new(build(s.to_string()))
 +    }
 +
 +    // Should not lint.
 +    fn test_x(x: super::X) -> Box<dyn Abstracted> {
 +        Box::new(build(x))
 +    }
 +
 +    #[derive(Clone, Copy)]
 +    struct Y(&'static str);
 +
 +    impl AsRef<str> for Y {
 +        fn as_ref(&self) -> &str {
 +            self.0
 +        }
 +    }
 +
 +    impl ToString for Y {
 +        fn to_string(&self) -> String {
 +            self.0.to_string()
 +        }
 +    }
 +
 +    // Should lint because Y is copy.
 +    fn test_y(y: Y) -> Box<dyn Abstracted> {
 +        Box::new(build(y.to_string()))
 +    }
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/8759
 +mod issue_8759 {
 +    #![allow(dead_code)]
 +
 +    #[derive(Default)]
 +    struct View {}
 +
 +    impl std::borrow::ToOwned for View {
 +        type Owned = View;
 +        fn to_owned(&self) -> Self::Owned {
 +            View {}
 +        }
 +    }
 +
 +    #[derive(Default)]
 +    struct RenderWindow {
 +        default_view: View,
 +    }
 +
 +    impl RenderWindow {
 +        fn default_view(&self) -> &View {
 +            &self.default_view
 +        }
 +        fn set_view(&mut self, _view: &View) {}
 +    }
 +
 +    fn main() {
 +        let mut rw = RenderWindow::default();
 +        rw.set_view(&rw.default_view().to_owned());
 +    }
 +}
 +
 +mod issue_8759_variant {
 +    #![allow(dead_code)]
 +
 +    #[derive(Clone, Default)]
 +    struct View {}
 +
 +    #[derive(Default)]
 +    struct RenderWindow {
 +        default_view: View,
 +    }
 +
 +    impl RenderWindow {
 +        fn default_view(&self) -> &View {
 +            &self.default_view
 +        }
 +        fn set_view(&mut self, _view: &View) {}
 +    }
 +
 +    fn main() {
 +        let mut rw = RenderWindow::default();
 +        rw.set_view(&rw.default_view().to_owned());
 +    }
 +}
 +
 +mod issue_9317 {
 +    #![allow(dead_code)]
 +
 +    struct Bytes {}
 +
 +    impl ToString for Bytes {
 +        fn to_string(&self) -> String {
 +            "123".to_string()
 +        }
 +    }
 +
 +    impl AsRef<[u8]> for Bytes {
 +        fn as_ref(&self) -> &[u8] {
 +            &[1, 2, 3]
 +        }
 +    }
 +
 +    fn consume<C: AsRef<[u8]>>(c: C) {
 +        let _ = c;
 +    }
 +
 +    pub fn main() {
 +        let b = Bytes {};
 +        // Should not lint.
 +        consume(b.to_string());
 +    }
 +}
++
++mod issue_9351 {
++    #![allow(dead_code)]
++
++    use std::ops::Deref;
++    use std::path::{Path, PathBuf};
++
++    fn require_deref_path<T: Deref<Target = std::path::Path>>(x: T) -> T {
++        x
++    }
++
++    fn generic_arg_used_elsewhere<T: AsRef<Path>>(_x: T, _y: T) {}
++
++    fn id<T: AsRef<str>>(x: T) -> T {
++        x
++    }
++
++    fn predicates_are_satisfied(_x: impl std::fmt::Write) {}
++
++    // Should lint
++    fn single_return() -> impl AsRef<str> {
++        id("abc".to_string())
++    }
++
++    // Should not lint
++    fn multiple_returns(b: bool) -> impl AsRef<str> {
++        if b {
++            return String::new();
++        }
++
++        id("abc".to_string())
++    }
++
++    struct S1(String);
++
++    // Should not lint
++    fn fields1() -> S1 {
++        S1(id("abc".to_string()))
++    }
++
++    struct S2 {
++        s: String,
++    }
++
++    // Should not lint
++    fn fields2() {
++        let mut s = S2 { s: "abc".into() };
++        s.s = id("abc".to_string());
++    }
++
++    pub fn main() {
++        let path = std::path::Path::new("x");
++        let path_buf = path.to_owned();
++
++        // Should not lint.
++        let _x: PathBuf = require_deref_path(path.to_owned());
++        generic_arg_used_elsewhere(path.to_owned(), path_buf);
++        predicates_are_satisfied(id("abc".to_string()));
++    }
++}
index 243b4599dba427ccc2c657014b473aef7a4ed750,0000000000000000000000000000000000000000..7deb90b06f3b7bd6e73cfed25eef9481e69e3c05
mode 100644,000000..100644
--- /dev/null
@@@ -1,513 -1,0 +1,519 @@@
- error: aborting due to 78 previous errors
 +error: redundant clone
 +  --> $DIR/unnecessary_to_owned.rs:151:64
 +   |
 +LL |     require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
 +   |                                                                ^^^^^^^^^^^ help: remove this
 +   |
 +   = note: `-D clippy::redundant-clone` implied by `-D warnings`
 +note: this value is dropped without further use
 +  --> $DIR/unnecessary_to_owned.rs:151:20
 +   |
 +LL |     require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
 +   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: redundant clone
 +  --> $DIR/unnecessary_to_owned.rs:152:40
 +   |
 +LL |     require_os_str(&OsString::from("x").to_os_string());
 +   |                                        ^^^^^^^^^^^^^^^ help: remove this
 +   |
 +note: this value is dropped without further use
 +  --> $DIR/unnecessary_to_owned.rs:152:21
 +   |
 +LL |     require_os_str(&OsString::from("x").to_os_string());
 +   |                     ^^^^^^^^^^^^^^^^^^^
 +
 +error: redundant clone
 +  --> $DIR/unnecessary_to_owned.rs:153:48
 +   |
 +LL |     require_path(&std::path::PathBuf::from("x").to_path_buf());
 +   |                                                ^^^^^^^^^^^^^^ help: remove this
 +   |
 +note: this value is dropped without further use
 +  --> $DIR/unnecessary_to_owned.rs:153:19
 +   |
 +LL |     require_path(&std::path::PathBuf::from("x").to_path_buf());
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: redundant clone
 +  --> $DIR/unnecessary_to_owned.rs:154:35
 +   |
 +LL |     require_str(&String::from("x").to_string());
 +   |                                   ^^^^^^^^^^^^ help: remove this
 +   |
 +note: this value is dropped without further use
 +  --> $DIR/unnecessary_to_owned.rs:154:18
 +   |
 +LL |     require_str(&String::from("x").to_string());
 +   |                  ^^^^^^^^^^^^^^^^^
 +
 +error: redundant clone
 +  --> $DIR/unnecessary_to_owned.rs:155:39
 +   |
 +LL |     require_slice(&[String::from("x")].to_owned());
 +   |                                       ^^^^^^^^^^^ help: remove this
 +   |
 +note: this value is dropped without further use
 +  --> $DIR/unnecessary_to_owned.rs:155:20
 +   |
 +LL |     require_slice(&[String::from("x")].to_owned());
 +   |                    ^^^^^^^^^^^^^^^^^^^
 +
 +error: unnecessary use of `into_owned`
 +  --> $DIR/unnecessary_to_owned.rs:60:36
 +   |
 +LL |     require_c_str(&Cow::from(c_str).into_owned());
 +   |                                    ^^^^^^^^^^^^^ help: remove this
 +   |
 +   = note: `-D clippy::unnecessary-to-owned` implied by `-D warnings`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:61:19
 +   |
 +LL |     require_c_str(&c_str.to_owned());
 +   |                   ^^^^^^^^^^^^^^^^^ help: use: `c_str`
 +
 +error: unnecessary use of `to_os_string`
 +  --> $DIR/unnecessary_to_owned.rs:63:20
 +   |
 +LL |     require_os_str(&os_str.to_os_string());
 +   |                    ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str`
 +
 +error: unnecessary use of `into_owned`
 +  --> $DIR/unnecessary_to_owned.rs:64:38
 +   |
 +LL |     require_os_str(&Cow::from(os_str).into_owned());
 +   |                                      ^^^^^^^^^^^^^ help: remove this
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:65:20
 +   |
 +LL |     require_os_str(&os_str.to_owned());
 +   |                    ^^^^^^^^^^^^^^^^^^ help: use: `os_str`
 +
 +error: unnecessary use of `to_path_buf`
 +  --> $DIR/unnecessary_to_owned.rs:67:18
 +   |
 +LL |     require_path(&path.to_path_buf());
 +   |                  ^^^^^^^^^^^^^^^^^^^ help: use: `path`
 +
 +error: unnecessary use of `into_owned`
 +  --> $DIR/unnecessary_to_owned.rs:68:34
 +   |
 +LL |     require_path(&Cow::from(path).into_owned());
 +   |                                  ^^^^^^^^^^^^^ help: remove this
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:69:18
 +   |
 +LL |     require_path(&path.to_owned());
 +   |                  ^^^^^^^^^^^^^^^^ help: use: `path`
 +
 +error: unnecessary use of `to_string`
 +  --> $DIR/unnecessary_to_owned.rs:71:17
 +   |
 +LL |     require_str(&s.to_string());
 +   |                 ^^^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `into_owned`
 +  --> $DIR/unnecessary_to_owned.rs:72:30
 +   |
 +LL |     require_str(&Cow::from(s).into_owned());
 +   |                              ^^^^^^^^^^^^^ help: remove this
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:73:17
 +   |
 +LL |     require_str(&s.to_owned());
 +   |                 ^^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_string`
 +  --> $DIR/unnecessary_to_owned.rs:74:17
 +   |
 +LL |     require_str(&x_ref.to_string());
 +   |                 ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()`
 +
 +error: unnecessary use of `to_vec`
 +  --> $DIR/unnecessary_to_owned.rs:76:19
 +   |
 +LL |     require_slice(&slice.to_vec());
 +   |                   ^^^^^^^^^^^^^^^ help: use: `slice`
 +
 +error: unnecessary use of `into_owned`
 +  --> $DIR/unnecessary_to_owned.rs:77:36
 +   |
 +LL |     require_slice(&Cow::from(slice).into_owned());
 +   |                                    ^^^^^^^^^^^^^ help: remove this
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:78:19
 +   |
 +LL |     require_slice(&array.to_owned());
 +   |                   ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:79:19
 +   |
 +LL |     require_slice(&array_ref.to_owned());
 +   |                   ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:80:19
 +   |
 +LL |     require_slice(&slice.to_owned());
 +   |                   ^^^^^^^^^^^^^^^^^ help: use: `slice`
 +
 +error: unnecessary use of `into_owned`
 +  --> $DIR/unnecessary_to_owned.rs:83:42
 +   |
 +LL |     require_x(&Cow::<X>::Owned(x.clone()).into_owned());
 +   |                                          ^^^^^^^^^^^^^ help: remove this
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:86:25
 +   |
 +LL |     require_deref_c_str(c_str.to_owned());
 +   |                         ^^^^^^^^^^^^^^^^ help: use: `c_str`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:87:26
 +   |
 +LL |     require_deref_os_str(os_str.to_owned());
 +   |                          ^^^^^^^^^^^^^^^^^ help: use: `os_str`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:88:24
 +   |
 +LL |     require_deref_path(path.to_owned());
 +   |                        ^^^^^^^^^^^^^^^ help: use: `path`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:89:23
 +   |
 +LL |     require_deref_str(s.to_owned());
 +   |                       ^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:90:25
 +   |
 +LL |     require_deref_slice(slice.to_owned());
 +   |                         ^^^^^^^^^^^^^^^^ help: use: `slice`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:92:30
 +   |
 +LL |     require_impl_deref_c_str(c_str.to_owned());
 +   |                              ^^^^^^^^^^^^^^^^ help: use: `c_str`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:93:31
 +   |
 +LL |     require_impl_deref_os_str(os_str.to_owned());
 +   |                               ^^^^^^^^^^^^^^^^^ help: use: `os_str`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:94:29
 +   |
 +LL |     require_impl_deref_path(path.to_owned());
 +   |                             ^^^^^^^^^^^^^^^ help: use: `path`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:95:28
 +   |
 +LL |     require_impl_deref_str(s.to_owned());
 +   |                            ^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:96:30
 +   |
 +LL |     require_impl_deref_slice(slice.to_owned());
 +   |                              ^^^^^^^^^^^^^^^^ help: use: `slice`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:98:29
 +   |
 +LL |     require_deref_str_slice(s.to_owned(), slice.to_owned());
 +   |                             ^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:98:43
 +   |
 +LL |     require_deref_str_slice(s.to_owned(), slice.to_owned());
 +   |                                           ^^^^^^^^^^^^^^^^ help: use: `slice`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:99:29
 +   |
 +LL |     require_deref_slice_str(slice.to_owned(), s.to_owned());
 +   |                             ^^^^^^^^^^^^^^^^ help: use: `slice`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:99:47
 +   |
 +LL |     require_deref_slice_str(slice.to_owned(), s.to_owned());
 +   |                                               ^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:101:26
 +   |
 +LL |     require_as_ref_c_str(c_str.to_owned());
 +   |                          ^^^^^^^^^^^^^^^^ help: use: `c_str`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:102:27
 +   |
 +LL |     require_as_ref_os_str(os_str.to_owned());
 +   |                           ^^^^^^^^^^^^^^^^^ help: use: `os_str`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:103:25
 +   |
 +LL |     require_as_ref_path(path.to_owned());
 +   |                         ^^^^^^^^^^^^^^^ help: use: `path`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:104:24
 +   |
 +LL |     require_as_ref_str(s.to_owned());
 +   |                        ^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:105:24
 +   |
 +LL |     require_as_ref_str(x.to_owned());
 +   |                        ^^^^^^^^^^^^ help: use: `&x`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:106:26
 +   |
 +LL |     require_as_ref_slice(array.to_owned());
 +   |                          ^^^^^^^^^^^^^^^^ help: use: `array`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:107:26
 +   |
 +LL |     require_as_ref_slice(array_ref.to_owned());
 +   |                          ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:108:26
 +   |
 +LL |     require_as_ref_slice(slice.to_owned());
 +   |                          ^^^^^^^^^^^^^^^^ help: use: `slice`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:110:31
 +   |
 +LL |     require_impl_as_ref_c_str(c_str.to_owned());
 +   |                               ^^^^^^^^^^^^^^^^ help: use: `c_str`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:111:32
 +   |
 +LL |     require_impl_as_ref_os_str(os_str.to_owned());
 +   |                                ^^^^^^^^^^^^^^^^^ help: use: `os_str`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:112:30
 +   |
 +LL |     require_impl_as_ref_path(path.to_owned());
 +   |                              ^^^^^^^^^^^^^^^ help: use: `path`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:113:29
 +   |
 +LL |     require_impl_as_ref_str(s.to_owned());
 +   |                             ^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:114:29
 +   |
 +LL |     require_impl_as_ref_str(x.to_owned());
 +   |                             ^^^^^^^^^^^^ help: use: `&x`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:115:31
 +   |
 +LL |     require_impl_as_ref_slice(array.to_owned());
 +   |                               ^^^^^^^^^^^^^^^^ help: use: `array`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:116:31
 +   |
 +LL |     require_impl_as_ref_slice(array_ref.to_owned());
 +   |                               ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:117:31
 +   |
 +LL |     require_impl_as_ref_slice(slice.to_owned());
 +   |                               ^^^^^^^^^^^^^^^^ help: use: `slice`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:119:30
 +   |
 +LL |     require_as_ref_str_slice(s.to_owned(), array.to_owned());
 +   |                              ^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:119:44
 +   |
 +LL |     require_as_ref_str_slice(s.to_owned(), array.to_owned());
 +   |                                            ^^^^^^^^^^^^^^^^ help: use: `array`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:120:30
 +   |
 +LL |     require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
 +   |                              ^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:120:44
 +   |
 +LL |     require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
 +   |                                            ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:121:30
 +   |
 +LL |     require_as_ref_str_slice(s.to_owned(), slice.to_owned());
 +   |                              ^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:121:44
 +   |
 +LL |     require_as_ref_str_slice(s.to_owned(), slice.to_owned());
 +   |                                            ^^^^^^^^^^^^^^^^ help: use: `slice`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:122:30
 +   |
 +LL |     require_as_ref_slice_str(array.to_owned(), s.to_owned());
 +   |                              ^^^^^^^^^^^^^^^^ help: use: `array`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:122:48
 +   |
 +LL |     require_as_ref_slice_str(array.to_owned(), s.to_owned());
 +   |                                                ^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:123:30
 +   |
 +LL |     require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
 +   |                              ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:123:52
 +   |
 +LL |     require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
 +   |                                                    ^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:124:30
 +   |
 +LL |     require_as_ref_slice_str(slice.to_owned(), s.to_owned());
 +   |                              ^^^^^^^^^^^^^^^^ help: use: `slice`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:124:48
 +   |
 +LL |     require_as_ref_slice_str(slice.to_owned(), s.to_owned());
 +   |                                                ^^^^^^^^^^^^ help: use: `s`
 +
 +error: unnecessary use of `to_string`
 +  --> $DIR/unnecessary_to_owned.rs:126:20
 +   |
 +LL |     let _ = x.join(&x_ref.to_string());
 +   |                    ^^^^^^^^^^^^^^^^^^ help: use: `x_ref`
 +
 +error: unnecessary use of `to_vec`
 +  --> $DIR/unnecessary_to_owned.rs:128:13
 +   |
 +LL |     let _ = slice.to_vec().into_iter();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:129:13
 +   |
 +LL |     let _ = slice.to_owned().into_iter();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 +
 +error: unnecessary use of `to_vec`
 +  --> $DIR/unnecessary_to_owned.rs:130:13
 +   |
 +LL |     let _ = [std::path::PathBuf::new()][..].to_vec().into_iter();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:131:13
 +   |
 +LL |     let _ = [std::path::PathBuf::new()][..].to_owned().into_iter();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 +
 +error: unnecessary use of `to_vec`
 +  --> $DIR/unnecessary_to_owned.rs:133:13
 +   |
 +LL |     let _ = IntoIterator::into_iter(slice.to_vec());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:134:13
 +   |
 +LL |     let _ = IntoIterator::into_iter(slice.to_owned());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 +
 +error: unnecessary use of `to_vec`
 +  --> $DIR/unnecessary_to_owned.rs:135:13
 +   |
 +LL |     let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 +
 +error: unnecessary use of `to_owned`
 +  --> $DIR/unnecessary_to_owned.rs:136:13
 +   |
 +LL |     let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 +
 +error: unnecessary use of `to_vec`
 +  --> $DIR/unnecessary_to_owned.rs:198:14
 +   |
 +LL |     for t in file_types.to_vec() {
 +   |              ^^^^^^^^^^^^^^^^^^^
 +   |
 +help: use
 +   |
 +LL |     for t in file_types {
 +   |              ~~~~~~~~~~
 +help: remove this `&`
 +   |
 +LL -         let path = match get_file_path(&t) {
 +LL +         let path = match get_file_path(t) {
 +   |
 +
 +error: unnecessary use of `to_vec`
 +  --> $DIR/unnecessary_to_owned.rs:221:14
 +   |
 +LL |     let _ = &["x"][..].to_vec().into_iter();
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()`
 +
 +error: unnecessary use of `to_vec`
 +  --> $DIR/unnecessary_to_owned.rs:226:14
 +   |
 +LL |     let _ = &["x"][..].to_vec().into_iter();
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()`
 +
 +error: unnecessary use of `to_string`
 +  --> $DIR/unnecessary_to_owned.rs:273:24
 +   |
 +LL |         Box::new(build(y.to_string()))
 +   |                        ^^^^^^^^^^^^^ help: use: `y`
 +
++error: unnecessary use of `to_string`
++  --> $DIR/unnecessary_to_owned.rs:381:12
++   |
++LL |         id("abc".to_string())
++   |            ^^^^^^^^^^^^^^^^^ help: use: `"abc"`
++
++error: aborting due to 79 previous errors
 +
index c2b9bd2c881fe916b8e2f789dcf3bce7de18b1ee,0000000000000000000000000000000000000000..84f779569ff9dd7e863d37e33411a608e05e9196
mode 100644,000000..100644
--- /dev/null
@@@ -1,74 -1,0 +1,77 @@@
 +// run-rustfix
 +
 +#![warn(clippy::unwrap_or_else_default)]
 +#![allow(dead_code)]
 +#![allow(clippy::unnecessary_wraps)]
 +
 +/// Checks implementation of the `UNWRAP_OR_ELSE_DEFAULT` lint.
 +fn unwrap_or_else_default() {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn new() -> Foo {
 +            Foo
 +        }
 +
 +        // fake default, we should not trigger on this
 +        fn default() -> Foo {
 +            Foo
 +        }
 +    }
 +
 +    struct HasDefaultAndDuplicate;
 +
 +    impl HasDefaultAndDuplicate {
 +        fn default() -> Self {
 +            HasDefaultAndDuplicate
 +        }
 +    }
 +
 +    impl Default for HasDefaultAndDuplicate {
 +        fn default() -> Self {
 +            HasDefaultAndDuplicate
 +        }
 +    }
 +
 +    enum Enum {
 +        A(),
 +    }
 +
 +    fn make<T, V>(_: V) -> T {
 +        unimplemented!();
 +    }
 +
 +    let with_enum = Some(Enum::A());
 +    with_enum.unwrap_or_else(Enum::A);
 +
 +    let with_new = Some(vec![1]);
 +    with_new.unwrap_or_default();
 +
 +    let with_err: Result<_, ()> = Ok(vec![1]);
 +    with_err.unwrap_or_else(make);
 +
 +    // should not be changed
 +    let with_fake_default = None::<Foo>;
 +    with_fake_default.unwrap_or_else(Foo::default);
 +
 +    // should not be changed
 +    let with_fake_default2 = None::<HasDefaultAndDuplicate>;
 +    with_fake_default2.unwrap_or_else(<HasDefaultAndDuplicate>::default);
 +
 +    let with_real_default = None::<HasDefaultAndDuplicate>;
 +    with_real_default.unwrap_or_default();
 +
 +    let with_default_trait = Some(1);
 +    with_default_trait.unwrap_or_default();
 +
 +    let with_default_type = Some(1);
 +    with_default_type.unwrap_or_default();
 +
 +    let with_default_type: Option<Vec<u64>> = None;
 +    with_default_type.unwrap_or_default();
++
++    let empty_string = None::<String>;
++    empty_string.unwrap_or_default();
 +}
 +
 +fn main() {}
index d55664990aeb96f6dbbcf30869fc602538604329,0000000000000000000000000000000000000000..1735bd5808e5a94713e2593874ff8305a73f6cb1
mode 100644,000000..100644
--- /dev/null
@@@ -1,74 -1,0 +1,77 @@@
 +// run-rustfix
 +
 +#![warn(clippy::unwrap_or_else_default)]
 +#![allow(dead_code)]
 +#![allow(clippy::unnecessary_wraps)]
 +
 +/// Checks implementation of the `UNWRAP_OR_ELSE_DEFAULT` lint.
 +fn unwrap_or_else_default() {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn new() -> Foo {
 +            Foo
 +        }
 +
 +        // fake default, we should not trigger on this
 +        fn default() -> Foo {
 +            Foo
 +        }
 +    }
 +
 +    struct HasDefaultAndDuplicate;
 +
 +    impl HasDefaultAndDuplicate {
 +        fn default() -> Self {
 +            HasDefaultAndDuplicate
 +        }
 +    }
 +
 +    impl Default for HasDefaultAndDuplicate {
 +        fn default() -> Self {
 +            HasDefaultAndDuplicate
 +        }
 +    }
 +
 +    enum Enum {
 +        A(),
 +    }
 +
 +    fn make<T, V>(_: V) -> T {
 +        unimplemented!();
 +    }
 +
 +    let with_enum = Some(Enum::A());
 +    with_enum.unwrap_or_else(Enum::A);
 +
 +    let with_new = Some(vec![1]);
 +    with_new.unwrap_or_else(Vec::new);
 +
 +    let with_err: Result<_, ()> = Ok(vec![1]);
 +    with_err.unwrap_or_else(make);
 +
 +    // should not be changed
 +    let with_fake_default = None::<Foo>;
 +    with_fake_default.unwrap_or_else(Foo::default);
 +
 +    // should not be changed
 +    let with_fake_default2 = None::<HasDefaultAndDuplicate>;
 +    with_fake_default2.unwrap_or_else(<HasDefaultAndDuplicate>::default);
 +
 +    let with_real_default = None::<HasDefaultAndDuplicate>;
 +    with_real_default.unwrap_or_else(<HasDefaultAndDuplicate as Default>::default);
 +
 +    let with_default_trait = Some(1);
 +    with_default_trait.unwrap_or_else(Default::default);
 +
 +    let with_default_type = Some(1);
 +    with_default_type.unwrap_or_else(u64::default);
 +
 +    let with_default_type: Option<Vec<u64>> = None;
 +    with_default_type.unwrap_or_else(Vec::new);
++
++    let empty_string = None::<String>;
++    empty_string.unwrap_or_else(|| "".to_string());
 +}
 +
 +fn main() {}
index 53e31d85edfca277bab0061c7815eb5c18199c6d,0000000000000000000000000000000000000000..d2b9212223f7795957c33f5549a84e9af0a74f53
mode 100644,000000..100644
--- /dev/null
@@@ -1,34 -1,0 +1,40 @@@
- error: aborting due to 5 previous errors
 +error: use of `.unwrap_or_else(..)` to construct default value
 +  --> $DIR/unwrap_or_else_default.rs:48:5
 +   |
 +LL |     with_new.unwrap_or_else(Vec::new);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_new.unwrap_or_default()`
 +   |
 +   = note: `-D clippy::unwrap-or-else-default` implied by `-D warnings`
 +
 +error: use of `.unwrap_or_else(..)` to construct default value
 +  --> $DIR/unwrap_or_else_default.rs:62:5
 +   |
 +LL |     with_real_default.unwrap_or_else(<HasDefaultAndDuplicate as Default>::default);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_real_default.unwrap_or_default()`
 +
 +error: use of `.unwrap_or_else(..)` to construct default value
 +  --> $DIR/unwrap_or_else_default.rs:65:5
 +   |
 +LL |     with_default_trait.unwrap_or_else(Default::default);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_default_trait.unwrap_or_default()`
 +
 +error: use of `.unwrap_or_else(..)` to construct default value
 +  --> $DIR/unwrap_or_else_default.rs:68:5
 +   |
 +LL |     with_default_type.unwrap_or_else(u64::default);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_default_type.unwrap_or_default()`
 +
 +error: use of `.unwrap_or_else(..)` to construct default value
 +  --> $DIR/unwrap_or_else_default.rs:71:5
 +   |
 +LL |     with_default_type.unwrap_or_else(Vec::new);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_default_type.unwrap_or_default()`
 +
++error: use of `.unwrap_or_else(..)` to construct default value
++  --> $DIR/unwrap_or_else_default.rs:74:5
++   |
++LL |     empty_string.unwrap_or_else(|| "".to_string());
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `empty_string.unwrap_or_default()`
++
++error: aborting due to 6 previous errors
 +
index 531745424a7d02b77c8503480b81eb9b72ad2103,0000000000000000000000000000000000000000..8dd098a5b5405ab33c57d72c7a5c339f838b4a21
mode 100644,000000..100644
--- /dev/null
@@@ -1,106 -1,0 +1,112 @@@
 +#![allow(unused_variables)]
 +#![warn(clippy::vec_init_then_push)]
 +
 +fn main() {
 +    let mut def_err: Vec<u32> = Default::default();
 +    def_err.push(0);
 +
 +    let mut new_err = Vec::<u32>::new();
 +    new_err.push(1);
 +
 +    let mut cap_err = Vec::with_capacity(2);
 +    cap_err.push(0);
 +    cap_err.push(1);
 +    cap_err.push(2);
 +    if true {
 +        // don't include this one
 +        cap_err.push(3);
 +    }
 +
 +    let mut cap_ok = Vec::with_capacity(10);
 +    cap_ok.push(0);
 +
 +    new_err = Vec::new();
 +    new_err.push(0);
 +
 +    let mut vec = Vec::new();
 +    // control flow at block final expression
 +    if true {
 +        // no lint
 +        vec.push(1);
 +    }
 +
 +    let mut vec = Vec::with_capacity(5);
 +    vec.push(1);
 +    vec.push(2);
 +    vec.push(3);
 +    vec.push(4);
 +}
 +
 +pub fn no_lint() -> Vec<i32> {
 +    let mut p = Some(1);
 +    let mut vec = Vec::new();
 +    loop {
 +        match p {
 +            None => return vec,
 +            Some(i) => {
 +                vec.push(i);
 +                p = None;
 +            },
 +        }
 +    }
 +}
 +
 +fn _from_iter(items: impl Iterator<Item = u32>) -> Vec<u32> {
 +    let mut v = Vec::new();
 +    v.push(0);
 +    v.push(1);
 +    v.extend(items);
 +    v
 +}
 +
 +fn _cond_push(x: bool) -> Vec<u32> {
 +    let mut v = Vec::new();
 +    v.push(0);
 +    if x {
 +        v.push(1);
 +    }
 +    v.push(2);
 +    v
 +}
 +
 +fn _push_then_edit(x: u32) -> Vec<u32> {
 +    let mut v = Vec::new();
 +    v.push(x);
 +    v.push(1);
 +    v[0] = v[1] + 5;
 +    v
 +}
 +
 +fn _cond_push_with_large_start(x: bool) -> Vec<u32> {
 +    let mut v = Vec::new();
 +    v.push(0);
 +    v.push(1);
 +    v.push(0);
 +    v.push(1);
 +    v.push(0);
 +    v.push(0);
 +    v.push(1);
 +    v.push(0);
 +    if x {
 +        v.push(1);
 +    }
 +
 +    let mut v2 = Vec::new();
 +    v2.push(0);
 +    v2.push(1);
 +    v2.push(0);
 +    v2.push(1);
 +    v2.push(0);
 +    v2.push(0);
 +    v2.push(1);
 +    v2.push(0);
 +    v2.extend(&v);
 +
 +    v2
 +}
++
++fn f() {
++    let mut v = Vec::new();
++    v.push((0i32, 0i32));
++    let y = v[0].0.abs();
++}
index 50b029fc33727a2a23351099f8beb60ac88271f1,0000000000000000000000000000000000000000..a9da1c520197d3af1705e30e2371d29ba44d7f47
mode 100644,000000..100644
--- /dev/null
@@@ -1,66 -1,0 +1,73 @@@
- error: aborting due to 7 previous errors
 +error: calls to `push` immediately after creation
 +  --> $DIR/vec_init_then_push.rs:5:5
 +   |
 +LL | /     let mut def_err: Vec<u32> = Default::default();
 +LL | |     def_err.push(0);
 +   | |____________________^ help: consider using the `vec![]` macro: `let def_err: Vec<u32> = vec![..];`
 +   |
 +   = note: `-D clippy::vec-init-then-push` implied by `-D warnings`
 +
 +error: calls to `push` immediately after creation
 +  --> $DIR/vec_init_then_push.rs:8:5
 +   |
 +LL | /     let mut new_err = Vec::<u32>::new();
 +LL | |     new_err.push(1);
 +   | |____________________^ help: consider using the `vec![]` macro: `let mut new_err = vec![..];`
 +
 +error: calls to `push` immediately after creation
 +  --> $DIR/vec_init_then_push.rs:11:5
 +   |
 +LL | /     let mut cap_err = Vec::with_capacity(2);
 +LL | |     cap_err.push(0);
 +LL | |     cap_err.push(1);
 +LL | |     cap_err.push(2);
 +   | |____________________^ help: consider using the `vec![]` macro: `let mut cap_err = vec![..];`
 +
 +error: calls to `push` immediately after creation
 +  --> $DIR/vec_init_then_push.rs:23:5
 +   |
 +LL | /     new_err = Vec::new();
 +LL | |     new_err.push(0);
 +   | |____________________^ help: consider using the `vec![]` macro: `new_err = vec![..];`
 +
 +error: calls to `push` immediately after creation
 +  --> $DIR/vec_init_then_push.rs:73:5
 +   |
 +LL | /     let mut v = Vec::new();
 +LL | |     v.push(x);
 +LL | |     v.push(1);
 +   | |______________^ help: consider using the `vec![]` macro: `let mut v = vec![..];`
 +
 +error: calls to `push` immediately after creation
 +  --> $DIR/vec_init_then_push.rs:81:5
 +   |
 +LL | /     let mut v = Vec::new();
 +LL | |     v.push(0);
 +LL | |     v.push(1);
 +LL | |     v.push(0);
 +...  |
 +LL | |     v.push(1);
 +LL | |     v.push(0);
 +   | |______________^ help: consider using the `vec![]` macro: `let mut v = vec![..];`
 +
 +error: calls to `push` immediately after creation
 +  --> $DIR/vec_init_then_push.rs:94:5
 +   |
 +LL | /     let mut v2 = Vec::new();
 +LL | |     v2.push(0);
 +LL | |     v2.push(1);
 +LL | |     v2.push(0);
 +...  |
 +LL | |     v2.push(1);
 +LL | |     v2.push(0);
 +   | |_______________^ help: consider using the `vec![]` macro: `let mut v2 = vec![..];`
 +
++error: calls to `push` immediately after creation
++  --> $DIR/vec_init_then_push.rs:109:5
++   |
++LL | /     let mut v = Vec::new();
++LL | |     v.push((0i32, 0i32));
++   | |_________________________^ help: consider using the `vec![]` macro: `let v = vec![..];`
++
++error: aborting due to 8 previous errors
 +