diff --git a/.github/deploy.sh b/.github/deploy.sh index 34225a540290..5a59f94ec918 100644 --- a/.github/deploy.sh +++ b/.github/deploy.sh @@ -8,6 +8,7 @@ rm -rf out/master/ || exit 0 echo "Making the docs for master" mkdir out/master/ cp util/gh-pages/index.html out/master +cp util/gh-pages/script.js out/master cp util/gh-pages/lints.json out/master if [[ -n $TAG_NAME ]]; then diff --git a/CHANGELOG.md b/CHANGELOG.md index d25ad0ac6fa1..24204f86b5e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,9 +8,9 @@ document. [d0cf3481...master](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...master) -## Rust 1.61 (beta) +## Rust 1.61 -Current beta, released 2022-05-19 +Current stable, released 2022-05-19 [57b3c4b...d0cf3481](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...d0cf3481) @@ -111,7 +111,7 @@ Current beta, released 2022-05-19 ## Rust 1.60 -Current stable, released 2022-04-07 +Released 2022-04-07 [0eff589...57b3c4b](https://github.com/rust-lang/rust-clippy/compare/0eff589...57b3c4b) @@ -3290,6 +3290,8 @@ Released 2018-09-13 [`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 @@ -3297,6 +3299,7 @@ Released 2018-09-13 [`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 @@ -3332,10 +3335,12 @@ Released 2018-09-13 [`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match [`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 @@ -3350,8 +3355,11 @@ Released 2018-09-13 [`derivable_impls`]: https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq [`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord +[`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_script_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents +[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type [`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown @@ -3359,9 +3367,11 @@ Released 2018-09-13 [`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 @@ -3413,6 +3423,8 @@ Released 2018-09-13 [`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 @@ -3425,9 +3437,11 @@ Released 2018-09-13 [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send [`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 @@ -3456,8 +3470,11 @@ Released 2018-09-13 [`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 [`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters @@ -3531,6 +3548,7 @@ Released 2018-09-13 [`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 @@ -3549,6 +3567,7 @@ Released 2018-09-13 [`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 @@ -3592,6 +3611,7 @@ Released 2018-09-13 [`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_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 @@ -3605,19 +3625,25 @@ Released 2018-09-13 [`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 [`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 [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite @@ -3642,6 +3668,7 @@ Released 2018-09-13 [`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 [`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 @@ -3658,14 +3685,18 @@ Released 2018-09-13 [`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_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 @@ -3684,10 +3715,12 @@ Released 2018-09-13 [`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 @@ -3696,7 +3729,6 @@ Released 2018-09-13 [`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 -[`significant_drop_in_scrutinee`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_in_scrutinee [`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 @@ -3707,6 +3739,7 @@ Released 2018-09-13 [`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 @@ -3718,7 +3751,9 @@ Released 2018-09-13 [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting [`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 @@ -3753,6 +3788,7 @@ Released 2018-09-13 [`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 @@ -3782,6 +3818,7 @@ Released 2018-09-13 [`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_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 @@ -3822,5 +3859,6 @@ Released 2018-09-13 [`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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fc663de8f792..6ab2bd59137f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -67,9 +67,9 @@ and resolved paths. [`T-AST`] issues will generally need you to match against a predefined syntax structure. To figure out how this syntax structure is encoded in the AST, it is recommended to run -`rustc -Z ast-json` on an example of the structure and compare with the [nodes in the AST docs]. +`rustc -Z unpretty=ast-tree` on an example of the structure and compare with the [nodes in the AST docs]. Usually the lint will end up to be a nested series of matches and ifs, [like so][deep-nesting]. -But we can make it nest-less by using [if_chain] macro, [like this][nest-less]. +But we can make it nest-less by using [let chains], [like this][nest-less]. [`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good-first-issue`] first. Sometimes they are only somewhat involved code wise, but not difficult per-se. @@ -87,9 +87,9 @@ an AST expression). `match_def_path()` in Clippy's `utils` module can also be us [`E-medium`]: https://github.com/rust-lang/rust-clippy/labels/E-medium [`ty`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty [nodes in the AST docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/ -[deep-nesting]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/mem_forget.rs#L29-L43 -[if_chain]: https://docs.rs/if_chain/*/if_chain -[nest-less]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/bit_mask.rs#L124-L150 +[deep-nesting]: https://github.com/rust-lang/rust-clippy/blob/5e4f0922911536f80d9591180fa604229ac13939/clippy_lints/src/mem_forget.rs#L31-L45 +[let chains]: https://github.com/rust-lang/rust/pull/94927 +[nest-less]: https://github.com/rust-lang/rust-clippy/blob/5e4f0922911536f80d9591180fa604229ac13939/clippy_lints/src/bit_mask.rs#L133-L159 ## Writing code diff --git a/Cargo.toml b/Cargo.toml index dd6518d5241b..03e6bf03afef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ clippy_lints = { path = "clippy_lints" } semver = "1.0" rustc_tools_util = { path = "rustc_tools_util" } tempfile = { version = "3.2", optional = true } +termize = "0.1" [dev-dependencies] compiletest_rs = { version = "0.7.1", features = ["tmp"] } diff --git a/clippy_dev/src/lint.rs b/clippy_dev/src/lint.rs index 1bc1a39542db..9e463aa741c7 100644 --- a/clippy_dev/src/lint.rs +++ b/clippy_dev/src/lint.rs @@ -13,7 +13,7 @@ fn exit_if_err(status: io::Result) { } } -pub fn run(path: &str) { +pub fn run<'a>(path: &str, args: impl Iterator) { let is_file = match fs::metadata(path) { Ok(metadata) => metadata.is_file(), Err(e) => { @@ -30,6 +30,7 @@ pub fn run(path: &str) { .args(["-Z", "no-codegen"]) .args(["--edition", "2021"]) .arg(path) + .args(args) .status(), ); } else { @@ -42,6 +43,8 @@ pub fn run(path: &str) { .expect("failed to create tempdir"); let status = Command::new(cargo_clippy_path()) + .arg("clippy") + .args(args) .current_dir(path) .env("CARGO_TARGET_DIR", target.as_ref()) .status(); diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index ebf8f38d4906..8dd2b7f95346 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -76,7 +76,8 @@ fn main() { }, ("lint", Some(matches)) => { let path = matches.value_of("path").unwrap(); - lint::run(path); + let args = matches.values_of("args").into_iter().flatten(); + lint::run(path, args); }, ("rename_lint", Some(matches)) => { let old_name = matches.value_of("old_name").unwrap(); @@ -123,7 +124,7 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { * the lint count in README.md is correct\n \ * the changelog contains markdown link references at the bottom\n \ * all lint groups include the correct lints\n \ - * lint modules in `clippy_lints/*` are visible in `src/lifb.rs` via `pub mod`\n \ + * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \ * all lints are registered in the lint store", ) .arg(Arg::with_name("print-only").long("print-only").help( @@ -278,11 +279,44 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { Lint a package directory: cargo dev lint tests/ui-cargo/wildcard_dependencies/fail cargo dev lint ~/my-project + + Run rustfix: + cargo dev lint ~/my-project -- --fix + + Set lint levels: + cargo dev lint file.rs -- -W clippy::pedantic + cargo dev lint ~/my-project -- -- -W clippy::pedantic "}) .arg( Arg::with_name("path") .required(true) .help("The path to a file or package directory to lint"), + ) + .arg( + Arg::with_name("args") + .multiple(true) + .help("Pass extra arguments to cargo/clippy-driver"), + ), + ) + .subcommand( + SubCommand::with_name("rename_lint") + .about("Renames the given lint") + .arg( + Arg::with_name("old_name") + .index(1) + .required(true) + .help("The name of the lint to rename"), + ) + .arg( + Arg::with_name("new_name") + .index(2) + .required_unless("uplift") + .help("The new name of the lint"), + ) + .arg( + Arg::with_name("uplift") + .long("uplift") + .help("This lint will be uplifted into rustc"), ), ) .subcommand( diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 10f67d301f88..07d196387887 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -133,7 +133,7 @@ fn to_camel_case(name: &str) -> String { .collect() } -fn get_stabilisation_version() -> String { +fn get_stabilization_version() -> String { fn parse_manifest(contents: &str) -> Option { let version = contents .lines() @@ -199,7 +199,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { }, }; - let version = get_stabilisation_version(); + let version = get_stabilization_version(); let lint_name = lint.name; let category = lint.category; let name_camel = to_camel_case(lint.name); diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 1a6a4336da27..5024e63bfa73 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -17,7 +17,7 @@ const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev u const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html"; -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy, PartialEq, Eq)] pub enum UpdateMode { Check, Change, @@ -66,8 +66,13 @@ fn generate_lint_files( |res| { for lint in usable_lints .iter() - .map(|l| &l.name) - .chain(deprecated_lints.iter().map(|l| &l.name)) + .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(); @@ -372,7 +377,7 @@ fn exit_with_failure() { } /// Lint data parsed from the Clippy source code. -#[derive(Clone, PartialEq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug)] struct Lint { name: String, group: String, @@ -414,7 +419,7 @@ impl Lint { } } -#[derive(Clone, PartialEq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug)] struct DeprecatedLint { name: String, reason: String, diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index e109ee0009ee..da1b646f4777 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -87,9 +87,7 @@ impl ApproxConstant { let s = s.as_str(); if s.parse::().is_ok() { for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS { - if is_approx_const(constant, s, min_digits) - && msrv.as_ref().map_or(true, |msrv| meets_msrv(self.msrv.as_ref(), msrv)) - { + if is_approx_const(constant, s, min_digits) && msrv.map_or(true, |msrv| meets_msrv(self.msrv, msrv)) { span_lint_and_help( cx, APPROX_CONSTANT, diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index 12c1bddf79d5..4c2d3366483a 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -50,7 +50,7 @@ declare_clippy_lint! { /// ### 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 behaviour it should be + /// 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 diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 8b0e11cb802e..3de91f3d24a9 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -6,7 +6,7 @@ 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, MacArgs, MacArgsEq, MetaItemKind, NestedMetaItem}; +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, @@ -586,21 +586,10 @@ impl EarlyLintPass for EarlyAttributes { fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::Item) { for attr in &item.attrs { - let attr_item = if let AttrKind::Normal(ref attr, _) = attr.kind { - attr - } else { - return; - }; - - if attr.style == AttrStyle::Outer { - if let MacArgs::Eq(_, MacArgsEq::Ast(expr)) = &attr_item.args - && !matches!(expr.kind, rustc_ast::ExprKind::Lit(..)) { - return; - } - if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) { - return; - } - + 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_item = Span::new(attr.span.hi(), item.span.lo(), item.span.ctxt(), item.span.parent()); @@ -624,7 +613,7 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Option) { if_chain! { - if meets_msrv(msrv.as_ref(), &msrvs::TOOL_ATTRIBUTES); + 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(); diff --git a/clippy_lints/src/bit_mask.rs b/clippy_lints/src/bit_mask.rs index ca4af66cad16..dc7e400fdc28 100644 --- a/clippy_lints/src/bit_mask.rs +++ b/clippy_lints/src/bit_mask.rs @@ -1,7 +1,6 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::sugg::Sugg; -use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -130,23 +129,24 @@ impl<'tcx> LateLintPass<'tcx> for BitMask { } } } - if_chain! { - if let ExprKind::Binary(op, left, right) = &e.kind; - if BinOpKind::Eq == op.node; - if let ExprKind::Binary(op1, left1, right1) = &left.kind; - if BinOpKind::BitAnd == op1.node; - if let ExprKind::Lit(lit) = &right1.kind; - if let LitKind::Int(n, _) = lit.node; - if let ExprKind::Lit(lit1) = &right.kind; - if let LitKind::Int(0, _) = lit1.node; - if n.leading_zeros() == n.count_zeros(); - if n > u128::from(self.verbose_bit_mask_threshold); - then { - span_lint_and_then(cx, - VERBOSE_BIT_MASK, - e.span, - "bit mask could be simplified with a call to `trailing_zeros`", - |diag| { + + if let ExprKind::Binary(op, left, right) = &e.kind + && BinOpKind::Eq == op.node + && let ExprKind::Binary(op1, left1, right1) = &left.kind + && BinOpKind::BitAnd == op1.node + && let ExprKind::Lit(lit) = &right1.kind + && let LitKind::Int(n, _) = lit.node + && let ExprKind::Lit(lit1) = &right.kind + && let LitKind::Int(0, _) = lit1.node + && n.leading_zeros() == n.count_zeros() + && n > u128::from(self.verbose_bit_mask_threshold) + { + span_lint_and_then( + cx, + VERBOSE_BIT_MASK, + e.span, + "bit mask could be simplified with a call to `trailing_zeros`", + |diag| { let sugg = Sugg::hir(cx, left1, "...").maybe_par(); diag.span_suggestion( e.span, @@ -154,8 +154,8 @@ impl<'tcx> LateLintPass<'tcx> for BitMask { format!("{}.trailing_zeros() >= {}", sugg, n.count_ones()), Applicability::MaybeIncorrect, ); - }); - } + }, + ); } } } diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index f7449c8dc72e..0adb6327164e 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -137,7 +137,7 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { } for (n, expr) in self.terminals.iter().enumerate() { if eq_expr_value(self.cx, e, expr) { - #[allow(clippy::cast_possible_truncation)] + #[expect(clippy::cast_possible_truncation)] return Ok(Bool::Term(n as u8)); } @@ -149,7 +149,7 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { if eq_expr_value(self.cx, e_lhs, expr_lhs); if eq_expr_value(self.cx, e_rhs, expr_rhs); then { - #[allow(clippy::cast_possible_truncation)] + #[expect(clippy::cast_possible_truncation)] return Ok(Bool::Not(Box::new(Bool::Term(n as u8)))); } } @@ -157,7 +157,7 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { let n = self.terminals.len(); self.terminals.push(e); if n < 32 { - #[allow(clippy::cast_possible_truncation)] + #[expect(clippy::cast_possible_truncation)] Ok(Bool::Term(n as u8)) } else { Err("too many literals".to_owned()) diff --git a/clippy_lints/src/borrow_as_ptr.rs b/clippy_lints/src/borrow_as_ptr.rs index 9f8eb488c29b..0993adbae2e6 100644 --- a/clippy_lints/src/borrow_as_ptr.rs +++ b/clippy_lints/src/borrow_as_ptr.rs @@ -57,7 +57,7 @@ impl BorrowAsPtr { impl<'tcx> LateLintPass<'tcx> for BorrowAsPtr { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::BORROW_AS_PTR) { + if !meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) { return; } diff --git a/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/clippy_lints/src/casts/cast_abs_to_unsigned.rs index e9b0f1f672de..6bac6bf83f8e 100644 --- a/clippy_lints/src/casts/cast_abs_to_unsigned.rs +++ b/clippy_lints/src/casts/cast_abs_to_unsigned.rs @@ -16,10 +16,10 @@ pub(super) fn check( cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, - msrv: &Option, + msrv: Option, ) { if_chain! { - if meets_msrv(msrv.as_ref(), &msrvs::UNSIGNED_ABS); + if meets_msrv(msrv, msrvs::UNSIGNED_ABS); if cast_from.is_integral(); if cast_to.is_integral(); if cast_from.is_signed(); diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index 4a95bed1148d..938458e30cad 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -16,7 +16,7 @@ pub(super) fn check( cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, - msrv: &Option, + msrv: Option, ) { if !should_lint(cx, expr, cast_from, cast_to, msrv) { return; @@ -68,7 +68,7 @@ fn should_lint( expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, - msrv: &Option, + msrv: Option, ) -> bool { // Do not suggest using From in consts/statics until it is valid to do so (see #2267). if in_constant(cx, expr.hir_id) { @@ -93,9 +93,9 @@ fn should_lint( } else { 64 }; - from_nbits < to_nbits + !is_isize_or_usize(cast_from) && from_nbits < to_nbits }, - (false, true) if matches!(cast_from.kind(), ty::Bool) && meets_msrv(msrv.as_ref(), &msrvs::FROM_BOOL) => true, + (false, true) if matches!(cast_from.kind(), ty::Bool) && meets_msrv(msrv, msrvs::FROM_BOOL) => true, (_, _) => { matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64)) }, diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index 2238668abca7..027c660ce3b2 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -8,9 +8,9 @@ use rustc_semver::RustcVersion; use super::CAST_SLICE_DIFFERENT_SIZES; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Option) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Option) { // suggestion is invalid if `ptr::slice_from_raw_parts` does not exist - if !meets_msrv(msrv.as_ref(), &msrvs::PTR_SLICE_RAW_PARTS) { + if !meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS) { return; } @@ -121,7 +121,7 @@ fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Optio let to_slice_ty = get_raw_slice_ty_mut(cast_to)?; // If the expression that makes up the source of this cast is itself a cast, recursively - // call `expr_cast_chain_tys` and update the end type with the final tartet type. + // call `expr_cast_chain_tys` and update the end type with the final target type. // Otherwise, this cast is not immediately nested, just construct the info for this cast if let Some(prev_info) = expr_cast_chain_tys(cx, cast_expr) { Some(CastChainInfo { diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index b58252dcb942..daf3b7b4ce4f 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -306,7 +306,7 @@ declare_clippy_lint! { /// Checks for casts of `&T` to `&mut T` anywhere in the code. /// /// ### Why is this bad? - /// It’s basically guaranteed to be undefined behaviour. + /// It’s basically guaranteed to be undefined behavior. /// `UnsafeCell` is the only way to obtain aliasable data that is considered /// mutable. /// @@ -413,6 +413,7 @@ declare_clippy_lint! { } declare_clippy_lint! { + /// ### What it does /// Checks for `as` casts between raw pointers to slices with differently sized elements. /// /// ### Why is this bad? @@ -531,7 +532,7 @@ impl_lint_pass!(Casts => [ impl<'tcx> LateLintPass<'tcx> for Casts { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !in_external_macro(cx.sess(), expr.span) { - ptr_as_ptr::check(cx, expr, &self.msrv); + ptr_as_ptr::check(cx, expr, self.msrv); } if expr.span.from_expansion() { @@ -561,9 +562,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_possible_wrap::check(cx, expr, cast_from, cast_to); cast_precision_loss::check(cx, expr, cast_from, cast_to); cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to); - cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); + cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv); } - cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); + cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv); cast_enum_constructor::check(cx, expr, cast_expr, cast_from); } } @@ -571,8 +572,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_ref_to_mut::check(cx, expr); cast_ptr_alignment::check(cx, expr); char_lit_as_u8::check(cx, expr); - ptr_as_ptr::check(cx, expr, &self.msrv); - cast_slice_different_sizes::check(cx, expr, &self.msrv); + ptr_as_ptr::check(cx, expr, self.msrv); + cast_slice_different_sizes::check(cx, expr, self.msrv); } extract_msrv_attr!(LateContext); diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs index fb04f93fbcf9..46d45d09661a 100644 --- a/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/clippy_lints/src/casts/ptr_as_ptr.rs @@ -12,8 +12,8 @@ use rustc_semver::RustcVersion; use super::PTR_AS_PTR; -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option) { - if !meets_msrv(msrv.as_ref(), &msrvs::POINTER_CAST) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option) { + if !meets_msrv(msrv, msrvs::POINTER_CAST) { return; } diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 31cc3698592b..7eeaaa019214 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -30,7 +30,6 @@ declare_clippy_lint! { /// Could be written: /// /// ```rust - /// # use std::convert::TryFrom; /// # let foo = 1; /// # let _ = /// i32::try_from(foo).is_ok() @@ -57,7 +56,7 @@ impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); impl<'tcx> LateLintPass<'tcx> for CheckedConversions { fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::TRY_FROM) { + if !meets_msrv(self.msrv, msrvs::TRY_FROM) { return; } @@ -123,7 +122,7 @@ struct Conversion<'a> { } /// The kind of conversion that is checked -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] enum ConversionType { SignedToUnsigned, SignedToSigned, diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 2bf7f8689054..317c4bfb3226 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -48,7 +48,7 @@ impl CognitiveComplexity { impl_lint_pass!(CognitiveComplexity => [COGNITIVE_COMPLEXITY]); impl CognitiveComplexity { - #[allow(clippy::cast_possible_truncation)] + #[expect(clippy::cast_possible_truncation)] fn check<'tcx>( &mut self, cx: &LateContext<'tcx>, @@ -70,7 +70,7 @@ impl CognitiveComplexity { let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::Result) { returns } else { - #[allow(clippy::integer_division)] + #[expect(clippy::integer_division)] (returns / 2) }; diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index 826eb0ae6b13..ec55009f347d 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -109,7 +109,10 @@ fn check_arm<'tcx>( (Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b), }; // the binding must not be used in the if guard - if outer_guard.map_or(true, |(Guard::If(e) | Guard::IfLet(Let { init: e, .. }))| !is_local_used(cx, *e, binding_id)); + if outer_guard.map_or( + true, + |(Guard::If(e) | Guard::IfLet(Let { init: e, .. }))| !is_local_used(cx, *e, binding_id) + ); // ...or anywhere in the inner expression if match inner { IfLetOrMatch::IfLet(_, _, body, els) => { diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index a0e5d3026331..f99d793c201d 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_in_test_function; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{is_in_cfg_test, is_in_test_function}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -37,7 +37,7 @@ impl LateLintPass<'_> for DbgMacro { let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; if cx.tcx.is_diagnostic_item(sym::dbg_macro, macro_call.def_id) { // we make an exception for test code - if is_in_test_function(cx.tcx, expr.hir_id) { + if is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id) { return; } let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index f7e4bc24321c..243dfd3a4618 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -110,7 +110,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { } } - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn check_block(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) { // start from the `let mut _ = _::default();` and look at all the following // statements, see if they re-assign the fields of the binding diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index f3996e5b44d7..3d9f9ed41ce1 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -116,7 +116,6 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { } impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { - #[allow(clippy::too_many_lines)] fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { match &expr.kind { ExprKind::Call(func, args) => { diff --git a/clippy_lints/src/default_union_representation.rs b/clippy_lints/src/default_union_representation.rs index 9b5da0bd8a66..d559ad423df5 100644 --- a/clippy_lints/src/default_union_representation.rs +++ b/clippy_lints/src/default_union_representation.rs @@ -25,7 +25,7 @@ declare_clippy_lint! { /// /// fn main() { /// let _x: u32 = unsafe { - /// Foo { a: 0_i32 }.b // Undefined behaviour: `b` is allowed to be padding + /// Foo { a: 0_i32 }.b // Undefined behavior: `b` is allowed to be padding /// }; /// } /// ``` @@ -39,7 +39,7 @@ declare_clippy_lint! { /// /// fn main() { /// let _x: u32 = unsafe { - /// Foo { a: 0_i32 }.b // Now defined behaviour, this is just an i32 -> u32 transmute + /// Foo { a: 0_i32 }.b // Now defined behavior, this is just an i32 -> u32 transmute /// }; /// } /// ``` diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index bba27576c892..5d5ea0f49c8c 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -194,7 +194,6 @@ declare_deprecated_lint! { /// ### Deprecation reason /// The `avoid_breaking_exported_api` config option was added, which /// enables the `enum_variant_names` lint for public items. - /// ``` #[clippy::version = "1.54.0"] pub PUB_ENUM_VARIANT_NAMES, "set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items" diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index fe3911983421..ea4c0207bb01 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -168,7 +168,7 @@ struct RefPat { } impl<'tcx> LateLintPass<'tcx> for Dereferencing { - #[allow(clippy::too_many_lines)] + #[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() { @@ -580,7 +580,7 @@ fn find_adjustments<'tcx>( } } -#[allow(clippy::needless_pass_by_value)] +#[expect(clippy::needless_pass_by_value)] fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, data: StateData) { match state { State::DerefMethod { diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 545bc7d21033..fe99f4a8d55d 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,8 +1,9 @@ -use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::paths; use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::{is_lint_allowed, match_def_path}; use if_chain::if_chain; +use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor}; use rustc_hir::{ BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, TraitRef, UnsafeSource, Unsafety, @@ -101,8 +102,8 @@ declare_clippy_lint! { /// types. /// /// ### Why is this bad? - /// To avoid surprising behaviour, these traits should - /// agree and the behaviour of `Copy` cannot be overridden. In almost all + /// 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. @@ -156,11 +157,44 @@ declare_clippy_lint! { "deriving `serde::Deserialize` on a type that has methods using `unsafe`" } +declare_clippy_lint! { + /// ### 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 + /// ```rust + /// #[derive(PartialEq)] + /// struct Foo { + /// i_am_eq: i32, + /// i_am_eq_too: Vec, + /// } + /// ``` + /// Use instead: + /// ```rust + /// #[derive(PartialEq, Eq)] + /// struct Foo { + /// i_am_eq: i32, + /// i_am_eq_too: Vec, + /// } + /// ``` + #[clippy::version = "1.62.0"] + pub DERIVE_PARTIAL_EQ_WITHOUT_EQ, + style, + "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`" +} + declare_lint_pass!(Derive => [ EXPL_IMPL_CLONE_ON_COPY, DERIVE_HASH_XOR_EQ, DERIVE_ORD_XOR_PARTIAL_ORD, - UNSAFE_DERIVE_DESERIALIZE + UNSAFE_DERIVE_DESERIALIZE, + DERIVE_PARTIAL_EQ_WITHOUT_EQ ]); impl<'tcx> LateLintPass<'tcx> for Derive { @@ -171,14 +205,14 @@ impl<'tcx> LateLintPass<'tcx> for Derive { }) = item.kind { let ty = cx.tcx.type_of(item.def_id); - let is_automatically_derived = - cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived); + let is_automatically_derived = cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived); check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived); check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived); if is_automatically_derived { check_unsafe_derive_deserialize(cx, item, trait_ref, ty); + check_partial_eq_without_eq(cx, item.span, trait_ref, ty); } else { check_copy_clone(cx, item, trait_ref, ty); } @@ -419,3 +453,36 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { self.cx.tcx.hir() } } + +/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint. +fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) { + if_chain! { + if let ty::Adt(adt, substs) = ty.kind(); + if let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq); + if let Some(def_id) = trait_ref.trait_def_id(); + if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id); + if !implements_trait(cx, ty, eq_trait_def_id, substs); + then { + // If all of our fields implement `Eq`, we can implement `Eq` too + for variant in adt.variants() { + for field in &variant.fields { + let ty = field.ty(cx.tcx, substs); + + if !implements_trait(cx, ty, eq_trait_def_id, substs) { + return; + } + } + } + + span_lint_and_sugg( + cx, + DERIVE_PARTIAL_EQ_WITHOUT_EQ, + span.ctxt().outer_expn_data().call_site, + "you are deriving `PartialEq` and can implement `Eq`", + "consider deriving `Eq` as well", + "PartialEq, Eq".to_string(), + Applicability::MachineApplicable, + ) + } + } +} diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index b3fd8af4730d..aaec88f50c77 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -198,7 +198,7 @@ declare_clippy_lint! { "presence of `fn main() {` in code examples" } -#[allow(clippy::module_name_repetitions)] +#[expect(clippy::module_name_repetitions)] #[derive(Clone)] pub struct DocMarkdown { valid_idents: FxHashSet, @@ -373,7 +373,7 @@ fn lint_for_missing_headers<'tcx>( /// `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. -#[allow(clippy::cast_possible_truncation)] +#[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 @@ -428,7 +428,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs /// 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. - #[allow(clippy::unnecessary_wraps)] // we're following a type signature + #[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())) } diff --git a/clippy_lints/src/double_comparison.rs b/clippy_lints/src/double_comparison.rs index 176092e5b280..be95375789d5 100644 --- a/clippy_lints/src/double_comparison.rs +++ b/clippy_lints/src/double_comparison.rs @@ -40,7 +40,7 @@ declare_clippy_lint! { declare_lint_pass!(DoubleComparisons => [DOUBLE_COMPARISONS]); impl<'tcx> DoubleComparisons { - #[allow(clippy::similar_names)] + #[expect(clippy::similar_names)] fn check_binop(cx: &LateContext<'tcx>, op: BinOpKind, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>, span: Span) { let (lkind, llhs, lrhs, rkind, rlhs, rrhs) = match (&lhs.kind, &rhs.kind) { (ExprKind::Binary(lb, llhs, lrhs), ExprKind::Binary(rb, rlhs, rrhs)) => { diff --git a/clippy_lints/src/duplicate_mod.rs b/clippy_lints/src/duplicate_mod.rs new file mode 100644 index 000000000000..c6c7b959d4f4 --- /dev/null +++ b/clippy_lints/src/duplicate_mod.rs @@ -0,0 +1,102 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_ast::ast::{Crate, Inline, Item, ItemKind, ModKind}; +use rustc_errors::MultiSpan; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{FileName, Span}; +use std::collections::BTreeMap; +use std::path::PathBuf; + +declare_clippy_lint! { + /// ### 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 + /// ```rust,ignore + /// // lib.rs + /// mod a; + /// mod b; + /// ``` + /// ```rust,ignore + /// // a.rs + /// #[path = "./b.rs"] + /// mod b; + /// ``` + /// + /// Use instead: + /// + /// ```rust,ignore + /// // lib.rs + /// mod a; + /// mod b; + /// ``` + /// ```rust,ignore + /// // a.rs + /// use crate::b; + /// ``` + #[clippy::version = "1.62.0"] + pub DUPLICATE_MOD, + suspicious, + "file loaded as module multiple times" +} + +#[derive(PartialOrd, Ord, PartialEq, Eq)] +struct Modules { + local_path: PathBuf, + spans: Vec, +} + +#[derive(Default)] +pub struct DuplicateMod { + /// map from the canonicalized path to `Modules`, `BTreeMap` to make the + /// order deterministic for tests + modules: BTreeMap, +} + +impl_lint_pass!(DuplicateMod => [DUPLICATE_MOD]); + +impl EarlyLintPass for DuplicateMod { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if let ItemKind::Mod(_, ModKind::Loaded(_, Inline::No, mod_spans)) = &item.kind + && let FileName::Real(real) = cx.sess().source_map().span_to_filename(mod_spans.inner_span) + && let Some(local_path) = real.into_local_path() + && let Ok(absolute_path) = local_path.canonicalize() + { + let modules = self.modules.entry(absolute_path).or_insert(Modules { + local_path, + spans: Vec::new(), + }); + modules.spans.push(item.span_with_attributes()); + } + } + + fn check_crate_post(&mut self, cx: &EarlyContext<'_>, _: &Crate) { + for Modules { local_path, spans } in self.modules.values() { + if spans.len() < 2 { + continue; + } + + let mut multi_span = MultiSpan::from_spans(spans.clone()); + let (&first, duplicates) = spans.split_first().unwrap(); + + multi_span.push_span_label(first, "first loaded here"); + for &duplicate in duplicates { + multi_span.push_span_label(duplicate, "loaded again here"); + } + + span_lint_and_help( + cx, + DUPLICATE_MOD, + multi_span, + &format!("file is loaded as a module multiple times: `{}`", local_path.display()), + None, + "replace all but one `mod` item with `use` items", + ); + } + } +} diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index d3d3ed2c2357..c5a987842c3f 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -63,7 +63,7 @@ declare_clippy_lint! { declare_lint_pass!(HashMapPass => [MAP_ENTRY]); impl<'tcx> LateLintPass<'tcx> for HashMapPass { - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let (cond_expr, then_expr, else_expr) = match higher::If::hir(expr) { Some(higher::If { cond, then, r#else }) => (cond, then, r#else), @@ -319,7 +319,7 @@ struct Insertion<'tcx> { /// `or_insert_with`. /// * Determine if there's any sub-expression that can't be placed in a closure. /// * Determine if there's only a single insert statement. `or_insert` can be used in this case. -#[allow(clippy::struct_excessive_bools)] +#[expect(clippy::struct_excessive_bools)] struct InsertSearcher<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, /// The map expression used in the contains call. diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index e2a5430da08c..10be245b3629 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -8,7 +8,6 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, IntTy, UintTy}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use std::convert::TryFrom; declare_clippy_lint! { /// ### What it does @@ -37,7 +36,7 @@ declare_clippy_lint! { declare_lint_pass!(UnportableVariant => [ENUM_CLIKE_UNPORTABLE_VARIANT]); impl<'tcx> LateLintPass<'tcx> for UnportableVariant { - #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap, clippy::cast_sign_loss)] + #[expect(clippy::cast_possible_wrap)] fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if cx.tcx.data_layout.pointer_size.bits() != 64 { return; diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index 346d03ca5568..e029b8e85379 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -240,7 +240,7 @@ impl LateLintPass<'_> for EnumVariantNames { assert!(last.is_some()); } - #[allow(clippy::similar_names)] + #[expect(clippy::similar_names)] fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { let item_name = item.ident.name.as_str(); let item_camel = to_camel_case(item_name); diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 51c811b304ca..afb5d32f9533 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -72,7 +72,7 @@ declare_clippy_lint! { declare_lint_pass!(EqOp => [EQ_OP, OP_REF]); impl<'tcx> LateLintPass<'tcx> for EqOp { - #[allow(clippy::similar_names, clippy::too_many_lines)] + #[expect(clippy::similar_names, clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if_chain! { if let Some((macro_call, macro_name)) = first_node_macro_backtrace(cx, e).find_map(|macro_call| { @@ -138,7 +138,6 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { }, }; if let Some(trait_id) = trait_id { - #[allow(clippy::match_same_arms)] match (&left.kind, &right.kind) { // do not suggest to dereference literals (&ExprKind::Lit(..), _) | (_, &ExprKind::Lit(..)) => {}, diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 245a4fc12fd4..7a81fb37e841 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -4,8 +4,6 @@ use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; -use std::convert::TryInto; - declare_clippy_lint! { /// ### What it does /// Checks for excessive diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 088d9996516e..9f868df3ad06 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -32,7 +32,6 @@ declare_clippy_lint! { /// // Good /// struct Foo(i32); /// - /// use std::convert::TryFrom; /// impl TryFrom for Foo { /// type Error = (); /// fn try_from(s: String) -> Result { diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 79ce53f7a5f2..42503c26de1d 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -215,7 +215,7 @@ fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { // 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. -#[allow(clippy::cast_possible_truncation)] +#[expect(clippy::cast_possible_truncation)] fn get_integer_from_float_constant(value: &Constant) -> Option { match value { F32(num) if num.fract() == 0.0 => { diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index c2f52605151e..5d25c1d06341 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -55,7 +55,7 @@ impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); impl<'tcx> LateLintPass<'tcx> for FromOverInto { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::RE_REBALANCING_COHERENCE) { + if !meets_msrv(self.msrv, msrvs::RE_REBALANCING_COHERENCE) { return; } diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 9525c163ece1..b8d227855d97 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -57,7 +57,7 @@ impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]); impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::BOOL_THEN) { + if !meets_msrv(self.msrv, msrvs::BOOL_THEN) { return; } diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index feb1b1014b18..4f9680f60fe8 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -62,7 +62,7 @@ declare_clippy_lint! { declare_lint_pass!(ImplicitHasher => [IMPLICIT_HASHER]); impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { - #[allow(clippy::cast_possible_truncation, clippy::too_many_lines)] + #[expect(clippy::cast_possible_truncation, clippy::too_many_lines)] fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { use rustc_span::BytePos; diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index d650d6e9a858..647947d5d30d 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -164,7 +164,7 @@ fn lint_implicit_returns( }) .visit_block(block); if add_return { - #[allow(clippy::option_if_let_else)] + #[expect(clippy::option_if_let_else)] if let Some(span) = call_site_span { lint_return(cx, span); LintLocation::Parent @@ -196,7 +196,7 @@ fn lint_implicit_returns( _ => { - #[allow(clippy::option_if_let_else)] + #[expect(clippy::option_if_let_else)] if let Some(span) = call_site_span { lint_return(cx, span); LintLocation::Parent diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 8a84513b7792..9ce5b8e17a9a 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -14,7 +14,6 @@ use rustc_middle::ty; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{symbol::Ident, Span}; -use std::convert::TryInto; declare_clippy_lint! { /// ### What it does @@ -75,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice { if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some(); if let Some(IfLet {let_pat, if_then, ..}) = IfLet::hir(cx, expr); if !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id); - if meets_msrv(self.msrv.as_ref(), &msrvs::SLICE_PATTERNS); + if meets_msrv(self.msrv, msrvs::SLICE_PATTERNS); let found_slices = find_slice_values(cx, let_pat); if !found_slices.is_empty(); diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index 3716d36ad881..8db7b307ddb7 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -52,7 +52,7 @@ enum Side { } impl IntPlusOne { - #[allow(clippy::cast_sign_loss)] + #[expect(clippy::cast_sign_loss)] fn check_lit(lit: &Lit, target_value: i128) -> bool { if let LitKind::Int(value, ..) = lit.kind { return value == (target_value as u128); diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index e68718f9fdf9..48ceeb43ee76 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -46,6 +46,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ 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_types::DISALLOWED_TYPES), LintId::of(doc::MISSING_SAFETY_DOC), @@ -59,6 +60,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ 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(duration_subsec::DURATION_SUBSEC), LintId::of(entry::MAP_ENTRY), LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), @@ -69,8 +71,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(erasing_op::ERASING_OP), LintId::of(escape::BOXED_LOCAL), LintId::of(eta_reduction::REDUNDANT_CLOSURE), - LintId::of(eval_order_dependence::DIVERGING_SUB_EXPRESSION), - LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE), LintId::of(explicit_write::EXPLICIT_WRITE), LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), LintId::of(float_literal::EXCESSIVE_PRECISION), @@ -228,6 +228,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ 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(mut_key::MUTABLE_KEY_TYPE), LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK), LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), @@ -263,6 +264,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(ranges::MANUAL_RANGE_CONTAINS), LintId::of(ranges::RANGE_ZIP_WITH_LEN), LintId::of(ranges::REVERSED_EMPTY_RANGES), + LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT), LintId::of(redundant_clone::REDUNDANT_CLONE), LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL), LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index 6f3c433af31a..b15c979d0c70 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -12,7 +12,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(double_comparison::DOUBLE_COMPARISONS), LintId::of(double_parens::DOUBLE_PARENS), LintId::of(duration_subsec::DURATION_SUBSEC), - LintId::of(eval_order_dependence::DIVERGING_SUB_EXPRESSION), LintId::of(explicit_write::EXPLICIT_WRITE), LintId::of(format::USELESS_FORMAT), LintId::of(functions::TOO_MANY_ARGUMENTS), @@ -59,6 +58,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(misc::SHORT_CIRCUIT_STATEMENT), 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(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), LintId::of(needless_bool::BOOL_COMPARISON), LintId::of(needless_bool::NEEDLESS_BOOL), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index c888a5feda2a..5552ea8aa80a 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -113,6 +113,7 @@ store.register_lints(&[ 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, @@ -132,6 +133,7 @@ store.register_lints(&[ drop_forget_ref::FORGET_NON_DROP, drop_forget_ref::FORGET_REF, drop_forget_ref::UNDROPPED_MANUALLY_DROPS, + duplicate_mod::DUPLICATE_MOD, duration_subsec::DURATION_SUBSEC, else_if_without_else::ELSE_IF_WITHOUT_ELSE, empty_drop::EMPTY_DROP, @@ -149,8 +151,6 @@ store.register_lints(&[ escape::BOXED_LOCAL, eta_reduction::REDUNDANT_CLOSURE, eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS, - eval_order_dependence::DIVERGING_SUB_EXPRESSION, - eval_order_dependence::EVAL_ORDER_DEPENDENCE, excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS, excessive_bools::STRUCT_EXCESSIVE_BOOLS, exhaustive_items::EXHAUSTIVE_ENUMS, @@ -381,6 +381,8 @@ store.register_lints(&[ 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, modulo_arithmetic::MODULO_ARITHMETIC, @@ -446,6 +448,7 @@ store.register_lints(&[ ranges::RANGE_PLUS_ONE, ranges::RANGE_ZIP_WITH_LEN, ranges::REVERSED_EMPTY_RANGES, + rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT, redundant_clone::REDUNDANT_CLONE, redundant_closure_call::REDUNDANT_CLOSURE_CALL, redundant_else::REDUNDANT_ELSE, @@ -470,12 +473,12 @@ store.register_lints(&[ shadow::SHADOW_REUSE, shadow::SHADOW_SAME, shadow::SHADOW_UNRELATED, + significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE, 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, stable_sort_primitive::STABLE_SORT_PRIMITIVE, - significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE, strings::STRING_ADD, strings::STRING_ADD_ASSIGN, strings::STRING_FROM_UTF8_AS_BYTES, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index 34d1555049da..d43c7e03533c 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -29,6 +29,8 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(strings::STRING_LIT_AS_BYTES), LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY), + LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), + LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR), LintId::of(transmute::USELESS_TRANSMUTE), LintId::of(use_self::USE_SELF), diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index 63232fd41130..2ee2c6e3358c 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -84,8 +84,6 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(strings::STRING_ADD_ASSIGN), - LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), - LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(transmute::TRANSMUTE_PTR_TO_PTR), LintId::of(types::LINKEDLIST), LintId::of(types::OPTION_OPTION), diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index 77ec6c83ba4b..a6d3a06dc16e 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -46,6 +46,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve 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(modulo_arithmetic::MODULO_ARITHMETIC), diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index d183c0449cd5..62f26d821a0d 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -16,6 +16,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(comparison_chain::COMPARISON_CHAIN), LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(dereference::NEEDLESS_BORROW), + LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ), LintId::of(disallowed_methods::DISALLOWED_METHODS), LintId::of(disallowed_types::DISALLOWED_TYPES), LintId::of(doc::MISSING_SAFETY_DOC), diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index 7a881bfe3991..338a6a28bc97 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -14,7 +14,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), LintId::of(drop_forget_ref::DROP_NON_DROP), LintId::of(drop_forget_ref::FORGET_NON_DROP), - LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE), + LintId::of(duplicate_mod::DUPLICATE_MOD), LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), LintId::of(format_impl::PRINT_IN_FORMAT_IMPL), LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), @@ -26,6 +26,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(methods::SUSPICIOUS_MAP), LintId::of(mut_key::MUTABLE_KEY_TYPE), LintId::of(octal_escapes::OCTAL_ESCAPES), + LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT), LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), ]) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 09071a255c5e..4ac834f72405 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -8,6 +8,7 @@ #![feature(iter_intersperse)] #![feature(let_chains)] #![feature(let_else)] +#![feature(lint_reasons)] #![feature(once_cell)] #![feature(rustc_private)] #![feature(stmt_expr_attributes)] @@ -210,6 +211,7 @@ mod doc; mod double_comparison; mod double_parens; mod drop_forget_ref; +mod duplicate_mod; mod duration_subsec; mod else_if_without_else; mod empty_drop; @@ -223,7 +225,6 @@ mod equatable_if_let; mod erasing_op; mod escape; mod eta_reduction; -mod eval_order_dependence; mod excessive_bools; mod exhaustive_items; mod exit; @@ -299,6 +300,7 @@ 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 modulo_arithmetic; mod mut_key; @@ -345,6 +347,7 @@ mod ptr_offset_with_cast; mod pub_use; mod question_mark; mod ranges; +mod rc_clone_in_vec_init; mod redundant_clone; mod redundant_closure_call; mod redundant_else; @@ -417,7 +420,7 @@ 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::TryConf; +use crate::utils::conf::{format_error, TryConf}; /// Register all pre expansion lints /// @@ -462,7 +465,7 @@ pub fn read_conf(sess: &Session) -> Conf { sess.struct_err(&format!( "error reading Clippy's configuration file `{}`: {}", file_name.display(), - error + format_error(error) )) .emit(); } @@ -473,7 +476,7 @@ pub fn read_conf(sess: &Session) -> Conf { /// Register all lints and lint groups with the rustc plugin registry /// /// Used in `./src/driver.rs`. -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) { register_removed_non_tool_lints(store); @@ -583,8 +586,17 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); 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))); + 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))); @@ -669,7 +681,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(arithmetic::Arithmetic::default())); store.register_late_pass(|| Box::new(assign_ops::AssignOps)); store.register_late_pass(|| Box::new(let_if_seq::LetIfSeq)); - store.register_late_pass(|| Box::new(eval_order_dependence::EvalOrderDependence)); + 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)); @@ -892,6 +904,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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())); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 269d3c62eafc..9998712b8527 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -204,7 +204,6 @@ impl WarningType { } } -#[allow(clippy::module_name_repetitions)] #[derive(Copy, Clone)] pub struct LiteralDigitGrouping { lint_fraction_readability: bool, @@ -432,7 +431,7 @@ impl LiteralDigitGrouping { } } -#[allow(clippy::module_name_repetitions)] +#[expect(clippy::module_name_repetitions)] #[derive(Copy, Clone)] pub struct DecimalLiteralRepresentation { threshold: u64, diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index f029067d3671..75d771f992a8 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -620,7 +620,6 @@ declare_lint_pass!(Loops => [ ]); impl<'tcx> LateLintPass<'tcx> for Loops { - #[allow(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let for_loop = higher::ForLoop::hir(expr); if let Some(higher::ForLoop { diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 6ed141fa4a5a..09f9c05b4fce 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -19,7 +19,7 @@ use std::mem; /// Checks for looping over a range and then indexing a sequence with it. /// The iteratee must be a range literal. -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index 772d251b620a..4801a84eb92c 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -13,7 +13,7 @@ use rustc_span::symbol::{sym, Symbol}; use rustc_typeck::hir_ty_to_ty; use std::iter::Iterator; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] enum IncrementVisitorVarState { Initial, // Not examined yet IncrOnce, // Incremented exactly once, may be a loop counter diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 20a8294a0d1a..82760607ba29 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -239,7 +239,7 @@ fn uses_iter<'tcx>(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tc v.uses_iter } -#[allow(clippy::too_many_lines)] +#[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>, diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index ca1ccb93bf3f..da806918be06 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -35,7 +35,8 @@ struct PathAndSpan { span: Span, } -/// `MacroRefData` includes the name of the macro. +/// `MacroRefData` includes the name of the macro +/// and the path from `SourceMap::span_to_filename`. #[derive(Debug, Clone)] pub struct MacroRefData { name: String, @@ -48,7 +49,7 @@ impl MacroRefData { } #[derive(Default)] -#[allow(clippy::module_name_repetitions)] +#[expect(clippy::module_name_repetitions)] pub struct MacroUseImports { /// the actual import path used and the span of the attribute above it. imports: Vec<(String, Span)>, @@ -134,7 +135,6 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { self.push_unique_macro_pat_ty(cx, ty.span); } } - #[allow(clippy::too_many_lines)] fn check_crate_post(&mut self, cx: &LateContext<'_>) { let mut used = FxHashMap::default(); let mut check_dup = vec![]; diff --git a/clippy_lints/src/main_recursion.rs b/clippy_lints/src/main_recursion.rs index fad8fa467d4b..20333c150e3d 100644 --- a/clippy_lints/src/main_recursion.rs +++ b/clippy_lints/src/main_recursion.rs @@ -12,7 +12,7 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// Apart from special setups (which we could detect following attributes like #![no_std]), - /// recursing into main() seems like an unintuitive antipattern we should be able to detect. + /// recursing into main() seems like an unintuitive anti-pattern we should be able to detect. /// /// ### Example /// ```no_run diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs index ac3d9447b6bd..60bbcde4f1de 100644 --- a/clippy_lints/src/manual_bits.rs +++ b/clippy_lints/src/manual_bits.rs @@ -48,7 +48,7 @@ impl_lint_pass!(ManualBits => [MANUAL_BITS]); impl<'tcx> LateLintPass<'tcx> for ManualBits { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::MANUAL_BITS) { + if !meets_msrv(self.msrv, msrvs::MANUAL_BITS) { return; } diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 8475e367b09f..230ae029ed9d 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -46,7 +46,7 @@ declare_clippy_lint! { declare_lint_pass!(ManualMap => [MANUAL_MAP]); impl<'tcx> LateLintPass<'tcx> for ManualMap { - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let (scrutinee, then_pat, then_body, else_pat, else_body) = match IfLetOrMatch::parse(cx, expr) { Some(IfLetOrMatch::IfLet(scrutinee, pat, body, Some(r#else))) => (scrutinee, pat, body, None, r#else), diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 09164690700e..80845ace3f94 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; -use clippy_utils::{is_lint_allowed, meets_msrv, msrvs}; +use clippy_utils::{is_doc_hidden, is_lint_allowed, meets_msrv, msrvs}; use rustc_ast::ast::{self, VisibilityKind}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -61,7 +61,7 @@ declare_clippy_lint! { "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]" } -#[allow(clippy::module_name_repetitions)] +#[expect(clippy::module_name_repetitions)] pub struct ManualNonExhaustiveStruct { msrv: Option, } @@ -75,7 +75,7 @@ impl ManualNonExhaustiveStruct { impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]); -#[allow(clippy::module_name_repetitions)] +#[expect(clippy::module_name_repetitions)] pub struct ManualNonExhaustiveEnum { msrv: Option, constructed_enum_variants: FxHashSet<(DefId, DefId)>, @@ -97,7 +97,7 @@ impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]); impl EarlyLintPass for ManualNonExhaustiveStruct { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) { + if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) { return; } @@ -149,7 +149,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct { impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) { + if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) { return; } @@ -160,7 +160,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { let id = cx.tcx.hir().local_def_id(v.id); (matches!(v.data, hir::VariantData::Unit(_)) && v.ident.as_str().starts_with('_') - && cx.tcx.is_doc_hidden(id.to_def_id())) + && is_doc_hidden(cx.tcx.hir().attrs(v.id))) .then(|| (id, v.span)) }); if let Some((id, span)) = iter.next() diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index aacabf303a70..dfb3efc4e28b 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -68,7 +68,7 @@ enum StripKind { impl<'tcx> LateLintPass<'tcx> for ManualStrip { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::STR_STRIP_PREFIX) { + if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) { return; } diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index ceb66947d02c..a13d191375bf 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -144,7 +144,7 @@ impl MapClone { fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) { let mut applicability = Applicability::MachineApplicable; - let (message, sugg_method) = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) { + let (message, sugg_method) = if is_copy && meets_msrv(self.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") diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index 9b7344fb8b0b..a96a7fe55f3a 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -16,7 +16,7 @@ use std::collections::hash_map::Entry; use super::MATCH_SAME_ARMS; -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 { let mut h = SpanlessHash::new(cx); @@ -225,9 +225,9 @@ fn iter_matching_struct_fields<'a>( Iter(left.iter(), right.iter()) } -#[allow(clippy::similar_names)] +#[expect(clippy::similar_names)] impl<'a> NormalizedPat<'a> { - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> Self { match pat.kind { PatKind::Wild | PatKind::Binding(.., None) => Self::Wild, diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index 39fe54648fbc..a59711d4cace 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -1,15 +1,22 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{indent_of, snippet_block, snippet_with_applicability}; +use clippy_utils::macros::HirNode; +use clippy_utils::source::{indent_of, snippet, snippet_block, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::{get_parent_expr, is_refutable, peel_blocks}; use rustc_errors::Applicability; -use rustc_hir::{Arm, Expr, ExprKind, Local, Node, PatKind}; +use rustc_hir::{Arm, Expr, ExprKind, Node, PatKind}; use rustc_lint::LateContext; +use rustc_span::Span; use super::MATCH_SINGLE_BINDING; -#[allow(clippy::too_many_lines)] -pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) { +enum AssignmentExpr { + Assign { span: Span, match_span: Span }, + Local { span: Span, pat_span: Span }, +} + +#[expect(clippy::too_many_lines)] +pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'a>) { if expr.span.from_expansion() || arms.len() != 1 || is_refutable(cx, arms[0].pat) { return; } @@ -42,61 +49,59 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e let mut applicability = Applicability::MaybeIncorrect; match arms[0].pat.kind { PatKind::Binding(..) | PatKind::Tuple(_, _) | PatKind::Struct(..) => { - // If this match is in a local (`let`) stmt - let (target_span, sugg) = if let Some(parent_let_node) = opt_parent_let(cx, ex) { - ( - parent_let_node.span, + let (target_span, sugg) = match opt_parent_assign_span(cx, ex) { + Some(AssignmentExpr::Assign { span, match_span }) => { + let sugg = sugg_with_curlies( + cx, + (ex, expr), + (bind_names, matched_vars), + &*snippet_body, + &mut applicability, + Some(span), + ); + + span_lint_and_sugg( + cx, + MATCH_SINGLE_BINDING, + span.to(match_span), + "this assignment could be simplified", + "consider removing the `match` expression", + sugg, + applicability, + ); + + return; + }, + Some(AssignmentExpr::Local { span, pat_span }) => ( + span, format!( "let {} = {};\n{}let {} = {};", snippet_with_applicability(cx, bind_names, "..", &mut applicability), snippet_with_applicability(cx, matched_vars, "..", &mut applicability), " ".repeat(indent_of(cx, expr.span).unwrap_or(0)), - snippet_with_applicability(cx, parent_let_node.pat.span, "..", &mut applicability), + snippet_with_applicability(cx, pat_span, "..", &mut applicability), snippet_body ), - ) - } else { - // If we are in closure, we need curly braces around suggestion - let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0)); - let (mut cbrace_start, mut cbrace_end) = ("".to_string(), "".to_string()); - if let Some(parent_expr) = get_parent_expr(cx, expr) { - if let ExprKind::Closure(..) = parent_expr.kind { - cbrace_end = format!("\n{}}}", indent); - // Fix body indent due to the closure - indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); - cbrace_start = format!("{{\n{}", indent); - } - } - // If the parent is already an arm, and the body is another match statement, - // we need curly braces around suggestion - let parent_node_id = cx.tcx.hir().get_parent_node(expr.hir_id); - if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) { - if let ExprKind::Match(..) = arm.body.kind { - cbrace_end = format!("\n{}}}", indent); - // Fix body indent due to the match - indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); - cbrace_start = format!("{{\n{}", indent); - } - } - ( - expr.span, - format!( - "{}let {} = {};\n{}{}{}", - cbrace_start, - snippet_with_applicability(cx, bind_names, "..", &mut applicability), - snippet_with_applicability(cx, matched_vars, "..", &mut applicability), - indent, - snippet_body, - cbrace_end - ), - ) + ), + None => { + let sugg = sugg_with_curlies( + cx, + (ex, expr), + (bind_names, matched_vars), + &*snippet_body, + &mut applicability, + None, + ); + (expr.span, sugg) + }, }; + span_lint_and_sugg( cx, MATCH_SINGLE_BINDING, target_span, "this match could be written as a `let` statement", - "consider using `let` statement", + "consider using a `let` statement", sugg, applicability, ); @@ -110,6 +115,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e indent, snippet_body ); + span_lint_and_sugg( cx, MATCH_SINGLE_BINDING, @@ -135,15 +141,76 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e } } -/// Returns true if the `ex` match expression is in a local (`let`) statement -fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'a>> { +/// Returns true if the `ex` match expression is in a local (`let`) or assign expression +fn opt_parent_assign_span<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option { let map = &cx.tcx.hir(); - if_chain! { - if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id)); - if let Some(Node::Local(parent_let_expr)) = map.find(map.get_parent_node(parent_arm_expr.hir_id)); - then { - return Some(parent_let_expr); - } + + if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id)) { + return match map.find(map.get_parent_node(parent_arm_expr.hir_id)) { + Some(Node::Local(parent_let_expr)) => Some(AssignmentExpr::Local { + span: parent_let_expr.span, + pat_span: parent_let_expr.pat.span(), + }), + Some(Node::Expr(Expr { + kind: ExprKind::Assign(parent_assign_expr, match_expr, _), + .. + })) => Some(AssignmentExpr::Assign { + span: parent_assign_expr.span, + match_span: match_expr.span, + }), + _ => None, + }; } + None } + +fn sugg_with_curlies<'a>( + cx: &LateContext<'a>, + (ex, match_expr): (&Expr<'a>, &Expr<'a>), + (bind_names, matched_vars): (Span, Span), + snippet_body: &str, + applicability: &mut Applicability, + assignment: Option, +) -> String { + let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0)); + + let (mut cbrace_start, mut cbrace_end) = (String::new(), String::new()); + if let Some(parent_expr) = get_parent_expr(cx, match_expr) { + if let ExprKind::Closure(..) = parent_expr.kind { + cbrace_end = format!("\n{}}}", indent); + // Fix body indent due to the closure + indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); + cbrace_start = format!("{{\n{}", indent); + } + } + + // If the parent is already an arm, and the body is another match statement, + // we need curly braces around suggestion + let parent_node_id = cx.tcx.hir().get_parent_node(match_expr.hir_id); + if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) { + if let ExprKind::Match(..) = arm.body.kind { + cbrace_end = format!("\n{}}}", indent); + // Fix body indent due to the match + indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); + cbrace_start = format!("{{\n{}", indent); + } + } + + let assignment_str = assignment.map_or_else(String::new, |span| { + let mut s = snippet(cx, span, "..").to_string(); + s.push_str(" = "); + s + }); + + format!( + "{}let {} = {};\n{}{}{}{}", + cbrace_start, + snippet_with_applicability(cx, bind_names, "..", applicability), + snippet_with_applicability(cx, matched_vars, "..", applicability), + indent, + assignment_str, + snippet_body, + cbrace_end + ) +} diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index fc45ccee1852..6f8d766aef7c 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -10,7 +10,7 @@ use rustc_span::sym; use super::{MATCH_WILDCARD_FOR_SINGLE_VARIANTS, WILDCARD_ENUM_MATCH_ARM}; -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { let ty = cx.typeck_results().expr_ty(ex).peel_refs(); let adt_def = match ty.kind() { @@ -56,7 +56,6 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { recurse_or_patterns(arm.pat, |pat| { let path = match &peel_hir_pat_refs(pat).0.kind { PatKind::Path(path) => { - #[allow(clippy::match_same_arms)] let id = match cx.qpath_res(path, pat.hir_id) { Res::Def( DefKind::Const | DefKind::ConstParam | DefKind::AnonConst | DefKind::InlineConst, diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 401ecef460c3..3d8391bce2b2 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -658,7 +658,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } if !contains_cfg_arm(cx, expr, ex, arms) { if source == MatchSource::Normal { - if !(meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) + if !(meets_msrv(self.msrv, msrvs::MATCHES_MACRO) && match_like_matches::check_match(cx, expr, ex, arms)) { match_same_arms::check(cx, arms); @@ -685,7 +685,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { match_wild_err_arm::check(cx, ex, arms); wild_in_or_pats::check(cx, arms); } else { - if meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) { + if meets_msrv(self.msrv, msrvs::MATCHES_MACRO) { match_like_matches::check(cx, expr); } redundant_pattern_match::check(cx, expr); diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 37b67647efe9..1a8b9d15f370 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -340,7 +340,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn find_good_method_for_match<'a>( cx: &LateContext<'_>, arms: &[Arm<'_>], diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 054937e3e36b..41073d40f3d7 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -254,7 +254,7 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace { then { check_replace_option_with_none(cx, src, dest, expr.span); check_replace_with_uninit(cx, src, dest, expr.span); - if meets_msrv(self.msrv.as_ref(), &msrvs::MEM_TAKE) { + if meets_msrv(self.msrv, msrvs::MEM_TAKE) { check_replace_with_default(cx, src, dest, expr.span); } } diff --git a/clippy_lints/src/methods/cloned_instead_of_copied.rs b/clippy_lints/src/methods/cloned_instead_of_copied.rs index 6d30bb5a278b..e9aeab2d5b62 100644 --- a/clippy_lints/src/methods/cloned_instead_of_copied.rs +++ b/clippy_lints/src/methods/cloned_instead_of_copied.rs @@ -10,16 +10,16 @@ use rustc_span::{sym, Span}; use super::CLONED_INSTEAD_OF_COPIED; -pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: Option<&RustcVersion>) { +pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: Option) { let recv_ty = cx.typeck_results().expr_ty_adjusted(recv); let inner_ty = match recv_ty.kind() { // `Option` -> `T` ty::Adt(adt, subst) - if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && meets_msrv(msrv, &msrvs::OPTION_COPIED) => + if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && meets_msrv(msrv, msrvs::OPTION_COPIED) => { subst.type_at(0) }, - _ if is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, &msrvs::ITERATOR_COPIED) => { + _ if is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) => { match get_iterator_item_ty(cx, recv_ty) { // ::Item Some(ty) => ty, diff --git a/clippy_lints/src/methods/err_expect.rs b/clippy_lints/src/methods/err_expect.rs index be9d4ad94fb8..570a1b87358d 100644 --- a/clippy_lints/src/methods/err_expect.rs +++ b/clippy_lints/src/methods/err_expect.rs @@ -13,7 +13,7 @@ pub(super) fn check( cx: &LateContext<'_>, _expr: &rustc_hir::Expr<'_>, recv: &rustc_hir::Expr<'_>, - msrv: Option<&RustcVersion>, + msrv: Option, expect_span: Span, err_span: Span, ) { @@ -21,7 +21,7 @@ pub(super) fn check( if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); // Test the version to make sure the lint can be showed (expect_err has been // introduced in rust 1.17.0 : https://github.com/rust-lang/rust/pull/38982) - if meets_msrv(msrv, &msrvs::EXPECT_ERR); + if meets_msrv(msrv, msrvs::EXPECT_ERR); // Grabs the `Result` type let result_type = cx.typeck_results().expr_ty(recv); diff --git a/clippy_lints/src/methods/expect_used.rs b/clippy_lints/src/methods/expect_used.rs index 55be513c5bb1..fbc3348f1855 100644 --- a/clippy_lints/src/methods/expect_used.rs +++ b/clippy_lints/src/methods/expect_used.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_in_test_function; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; @@ -7,7 +8,7 @@ use rustc_span::sym; use super::EXPECT_USED; /// lint use of `expect()` for `Option`s and `Result`s -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_expect_in_tests: bool) { let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) { @@ -18,6 +19,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr None }; + if allow_expect_in_tests && is_in_test_function(cx.tcx, expr.hir_id) { + return; + } + if let Some((lint, kind, none_value)) = mess { span_lint_and_help( cx, diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs index f0d69a1f42e7..38ec4d8e3ab8 100644 --- a/clippy_lints/src/methods/filter_map_next.rs +++ b/clippy_lints/src/methods/filter_map_next.rs @@ -14,10 +14,10 @@ pub(super) fn check<'tcx>( expr: &'tcx hir::Expr<'_>, recv: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, - msrv: Option<&RustcVersion>, + msrv: Option, ) { if is_trait_method(cx, expr, sym::Iterator) { - if !meets_msrv(msrv, &msrvs::ITERATOR_FIND_MAP) { + if !meets_msrv(msrv, msrvs::ITERATOR_FIND_MAP) { return; } diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs index ad333df2f2d5..aa176dcc8b4a 100644 --- a/clippy_lints/src/methods/is_digit_ascii_radix.rs +++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs @@ -15,9 +15,9 @@ pub(super) fn check<'tcx>( expr: &'tcx Expr<'_>, self_arg: &'tcx Expr<'_>, radix: &'tcx Expr<'_>, - msrv: Option<&RustcVersion>, + msrv: Option, ) { - if !meets_msrv(msrv, &msrvs::IS_ASCII_DIGIT) { + if !meets_msrv(msrv, msrvs::IS_ASCII_DIGIT) { return; } diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index 9ec84e76519a..4a8e7ce4ddbb 100644 --- a/clippy_lints/src/methods/map_unwrap_or.rs +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -19,13 +19,13 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'_>, map_arg: &'tcx hir::Expr<'_>, unwrap_arg: &'tcx hir::Expr<'_>, - msrv: Option<&RustcVersion>, + msrv: Option, ) -> bool { // lint if the caller of `map()` is an `Option` 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); - if is_result && !meets_msrv(msrv, &msrvs::RESULT_MAP_OR_ELSE) { + if is_result && !meets_msrv(msrv, msrvs::RESULT_MAP_OR_ELSE) { return false; } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f3be71f6b8bb..35fc452ed7cf 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1563,7 +1563,7 @@ declare_clippy_lint! { #[clippy::version = "1.39.0"] pub MANUAL_SATURATING_ARITHMETIC, style, - "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`" + "`.checked_add/sub(x).unwrap_or(MAX/MIN)`" } declare_clippy_lint! { @@ -1776,8 +1776,6 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// use std::iter::FromIterator; - /// /// let five_fives = std::iter::repeat(5).take(5); /// /// let v = Vec::from_iter(five_fives); @@ -2200,14 +2198,23 @@ declare_clippy_lint! { pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, + allow_expect_in_tests: bool, + allow_unwrap_in_tests: bool, } impl Methods { #[must_use] - pub fn new(avoid_breaking_exported_api: bool, msrv: Option) -> Self { + pub fn new( + avoid_breaking_exported_api: bool, + msrv: Option, + 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, } } } @@ -2306,7 +2313,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { return; } - check_methods(cx, expr, self.msrv.as_ref()); + self.check_methods(cx, expr); match expr.kind { hir::ExprKind::Call(func, args) => { @@ -2322,7 +2329,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { single_char_add_str::check(cx, expr, args); into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, args); single_char_pattern::check(cx, expr, method_call.ident.name, args); - unnecessary_to_owned::check(cx, expr, method_call.ident.name, args, self.msrv.as_ref()); + unnecessary_to_owned::check(cx, expr, method_call.ident.name, args, self.msrv); }, hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => { let mut info = BinaryExprInfo { @@ -2505,196 +2512,201 @@ impl<'tcx> LateLintPass<'tcx> for Methods { extract_msrv_attr!(LateContext); } -#[allow(clippy::too_many_lines)] -fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) { - 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, msrv), - ("collect", []) => match method_call(recv) { - Some((name @ ("cloned" | "copied"), [recv2], _)) => { - iter_cloned_collect::check(cx, name, expr, recv2); +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); + } + }, + _ => {}, + }, + (name @ "count", args @ []) => match method_call(recv) { + Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args), + 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), + _ => {}, + }, + ("drain", [arg]) => { + iter_with_drain::check(cx, expr, recv, span, 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, 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); }, - Some(("map", [m_recv, m_arg], _)) => { - map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv); + ("find_map", [arg]) => { + unnecessary_filter_map::check(cx, expr, arg, name); }, - Some(("take", [take_self_arg, take_arg], _)) => { - if meets_msrv(msrv, &msrvs::STR_REPEAT) { - manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); + ("flat_map", [arg]) => { + flat_map_identity::check(cx, expr, arg, span); + flat_map_option::check(cx, expr, arg, span); + }, + (name @ "flatten", args @ []) => 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, recv2, name, args), + _ => {}, + }, + ("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); } }, - _ => {}, - }, - (name @ "count", args @ []) => match method_call(recv) { - Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args), - Some((name2 @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => { - iter_count::check(cx, expr, recv2, name2); + ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"), + ("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), + ("join", [join_arg]) => { + if let Some(("collect", _, span)) = method_call(recv) { + unnecessary_join::check(cx, expr, recv, join_arg, span); + } }, - Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg), - _ => {}, - }, - ("drain", [arg]) => { - iter_with_drain::check(cx, expr, recv, span, 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, msrv, span, err_span), - _ => expect_used::check(cx, expr, recv), - }, - ("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); - }, - (name @ "flatten", args @ []) => 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, recv2, name, args), - _ => {}, - }, - ("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_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"), - ("is_file", []) => filetype_is_file::check(cx, expr, recv), - ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, msrv), - ("is_none", []) => check_is_some_is_none(cx, expr, recv, false), - ("is_some", []) => check_is_some_is_none(cx, expr, recv, true), - ("join", [join_arg]) => { - if let Some(("collect", _, span)) = method_call(recv) { - unnecessary_join::check(cx, expr, recv, join_arg, span); - } - }, - ("last", args @ []) | ("skip", args @ [_]) => { - if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) { - if let ("cloned", []) = (name2, args2) { - iter_overeager_cloned::check(cx, expr, recv2, name, args); + ("last", args @ []) | ("skip", args @ [_]) => { + if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) { + if let ("cloned", []) = (name2, args2) { + iter_overeager_cloned::check(cx, expr, recv2, name, args); + } } - } - }, - (name @ ("map" | "map_err"), [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, msrv), - ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, 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), - _ => {}, + }, + (name @ ("map" | "map_err"), [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), - (name @ "next", args @ []) => { - if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) { - match (name2, args2) { - ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv2, name, args), - ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg), - ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, 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), - _ => {}, + map_identity::check(cx, expr, recv, m_arg, name, span); + }, + ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map), + (name @ "next", args @ []) => { + if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) { + match (name2, args2) { + ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv2, name, args), + ("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", args @ [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, recv2, name, args), - 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"), - ("or_else", [arg]) => { - if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) { - unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); - } - }, - ("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, 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", args @ [_arg]) => { - if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) { - if let ("cloned", []) = (name2, args2) { - iter_overeager_cloned::check(cx, expr, recv2, name, args); + }, + ("nth", args @ [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, recv2, name, args), + 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"), + ("or_else", [arg]) => { + if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) { + unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); } - } - }, - ("take", []) => needless_option_take::check(cx, expr, recv), - ("to_os_string" | "to_owned" | "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); + }, + ("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", args @ [_arg]) => { + if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) { + if let ("cloned", []) = (name2, args2) { + iter_overeager_cloned::check(cx, expr, recv2, name, args); + } + } + }, + ("take", []) => needless_option_take::check(cx, expr, recv), + ("to_os_string" | "to_owned" | "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, 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(("or", [recv, or_arg], or_span)) => { - or_then_unwrap::check(cx, expr, recv, or_arg, or_span); + Some(("map", [m_recv, m_arg], span)) => { + option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span); }, _ => {}, - } - unwrap_used::check(cx, expr, recv); - }, - ("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); + ("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"); + }, }, _ => {}, - }, - ("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, msrv) => {}, - _ => { - unwrap_or_else_default::check(cx, expr, recv, u_arg); - unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"); - }, - }, - _ => {}, + } } } } @@ -2821,7 +2833,7 @@ const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ ShouldImplTraitCase::new("std::ops::Sub", "sub", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), ]; -#[derive(Clone, Copy, PartialEq, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] enum SelfKind { Value, Ref, diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index ba2d2914315f..b50a173d8359 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -19,9 +19,9 @@ pub(super) fn check<'tcx>( as_ref_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>, is_mut: bool, - msrv: Option<&RustcVersion>, + msrv: Option, ) { - if !meets_msrv(msrv, &msrvs::OPTION_AS_DEREF) { + if !meets_msrv(msrv, msrvs::OPTION_AS_DEREF) { return; } diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 52891eeed069..90651a6ba045 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -24,7 +24,7 @@ pub(super) fn check( self_arg: &Expr<'_>, pat_arg: &Expr<'_>, count: u128, - msrv: Option<&RustcVersion>, + msrv: Option, ) { if count < 2 || !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() { return; @@ -34,7 +34,7 @@ pub(super) fn check( IterUsageKind::Nth(n) => count > n + 1, IterUsageKind::NextTuple => count > 2, }; - let manual = count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE); + let manual = count == 2 && meets_msrv(msrv, msrvs::STR_SPLIT_ONCE); match parse_iter_usage(cx, expr.span.ctxt(), cx.tcx.hir().parent_iter(expr.hir_id)) { Some(usage) if needless(usage.kind) => lint_needless(cx, method_name, expr, self_arg, pat_arg), @@ -271,7 +271,7 @@ enum IterUsageKind { NextTuple, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] enum UnwrapKind { Unwrap, QuestionMark, diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 02b882e8b55e..97c4feb3122a 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -26,7 +26,7 @@ pub fn check<'tcx>( expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>], - msrv: Option<&RustcVersion>, + msrv: Option, ) { if_chain! { if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); @@ -65,13 +65,12 @@ fn check_addr_of_expr( 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::>(); - if let Some(target_ty) = match adjustments[..] - { + if let // For matching uses of `Cow::from` [ Adjustment { kind: Adjust::Deref(None), - .. + target: referent_ty, }, Adjustment { kind: Adjust::Borrow(_), @@ -82,7 +81,7 @@ fn check_addr_of_expr( | [ Adjustment { kind: Adjust::Deref(None), - .. + target: referent_ty, }, Adjustment { kind: Adjust::Borrow(_), @@ -97,7 +96,7 @@ fn check_addr_of_expr( | [ Adjustment { kind: Adjust::Deref(None), - .. + target: referent_ty, }, Adjustment { kind: Adjust::Deref(Some(OverloadedDeref { .. })), @@ -107,17 +106,24 @@ fn check_addr_of_expr( kind: Adjust::Borrow(_), target: target_ty, }, - ] => Some(target_ty), - _ => None, - }; + ] = adjustments[..]; let receiver_ty = cx.typeck_results().expr_ty(receiver); - // Only flag cases where the receiver is copyable or the method is `Cow::into_owned`. This - // restriction is to ensure there is not overlap between `redundant_clone` and this lint. - if is_copy(cx, receiver_ty) || is_cow_into_owned(cx, method_name, method_def_id); + 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 { - let (target_ty, n_target_refs) = peel_mid_ty_refs(*target_ty); - let (receiver_ty, n_receiver_refs) = peel_mid_ty_refs(receiver_ty); if receiver_ty == target_ty && n_target_refs >= n_receiver_refs { span_lint_and_sugg( cx, @@ -192,7 +198,7 @@ fn check_into_iter_call_arg( expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>, - msrv: Option<&RustcVersion>, + msrv: Option, ) -> bool { if_chain! { if let Some(parent) = get_parent_expr(cx, expr); @@ -207,7 +213,11 @@ fn check_into_iter_call_arg( 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" }; + 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. diff --git a/clippy_lints/src/methods/unwrap_used.rs b/clippy_lints/src/methods/unwrap_used.rs index 44676d78c607..5c761014927c 100644 --- a/clippy_lints/src/methods/unwrap_used.rs +++ b/clippy_lints/src/methods/unwrap_used.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_in_test_function; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; @@ -7,7 +8,7 @@ use rustc_span::sym; use super::UNWRAP_USED; /// lint use of `unwrap()` for `Option`s and `Result`s -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_unwrap_in_tests: bool) { let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) { @@ -18,6 +19,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr None }; + if allow_unwrap_in_tests && is_in_test_function(cx.tcx, expr.hir_id) { + return; + } + if let Some((lint, kind, none_value)) = mess { span_lint_and_help( cx, diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index ac82dd306a52..7fdc28c5a062 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::source::{snippet, snippet_opt}; -use clippy_utils::ty::implements_trait; +use clippy_utils::ty::{implements_trait, is_copy}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -20,8 +20,8 @@ use rustc_span::symbol::sym; use clippy_utils::consts::{constant, Constant}; use clippy_utils::sugg::Sugg; use clippy_utils::{ - get_item_name, get_parent_expr, in_constant, is_diag_trait_item, is_integer_const, iter_input_pats, - last_path_segment, match_any_def_paths, path_def_id, paths, unsext, SpanlessEq, + get_item_name, get_parent_expr, in_constant, is_integer_const, iter_input_pats, last_path_segment, + match_any_def_paths, path_def_id, paths, unsext, SpanlessEq, }; declare_clippy_lint! { @@ -548,7 +548,7 @@ fn is_array(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _)) } -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) { #[derive(Default)] struct EqImpl { @@ -569,33 +569,34 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: }) } - let (arg_ty, snip) = match expr.kind { - ExprKind::MethodCall(.., args, _) if args.len() == 1 => { - if_chain!( - if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if is_diag_trait_item(cx, expr_def_id, sym::ToString) - || is_diag_trait_item(cx, expr_def_id, sym::ToOwned); - then { - (cx.typeck_results().expr_ty(&args[0]), snippet(cx, args[0].span, "..")) - } else { - return; - } - ) + let typeck = cx.typeck_results(); + let (arg, arg_span) = match expr.kind { + ExprKind::MethodCall(.., [arg], _) + if typeck + .type_dependent_def_id(expr.hir_id) + .and_then(|id| cx.tcx.trait_of_item(id)) + .map_or(false, |id| { + matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ToString | sym::ToOwned)) + }) => + { + (arg, arg.span) }, - ExprKind::Call(path, [arg]) => { + ExprKind::Call(path, [arg]) if path_def_id(cx, path) .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM])) - .is_some() - { - (cx.typeck_results().expr_ty(arg), snippet(cx, arg.span, "..")) - } else { - return; - } + .map_or(false, |idx| match idx { + 0 => true, + 1 => !is_copy(cx, typeck.expr_ty(expr)), + _ => false, + }) => + { + (arg, arg.span) }, _ => return, }; - let other_ty = cx.typeck_results().expr_ty(other); + let arg_ty = typeck.expr_ty(arg); + let other_ty = typeck.expr_ty(other); let without_deref = symmetric_partial_eq(cx, arg_ty, other_ty).unwrap_or_default(); let with_deref = arg_ty @@ -627,13 +628,14 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: return; } + let arg_snip = snippet(cx, arg_span, ".."); let expr_snip; let eq_impl; if with_deref.is_implemented() { - expr_snip = format!("*{}", snip); + expr_snip = format!("*{}", arg_snip); eq_impl = with_deref; } else { - expr_snip = snip.to_string(); + expr_snip = arg_snip.to_string(); eq_impl = without_deref; }; diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 06209bfe7b08..16d65966c100 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -93,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { span: Span, hir_id: HirId, ) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::CONST_IF_MATCH) { + if !meets_msrv(self.msrv, msrvs::CONST_IF_MATCH) { return; } @@ -146,7 +146,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { let mir = cx.tcx.optimized_mir(def_id); - if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv.as_ref()) { + if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv) { if cx.tcx.is_const_fn_raw(def_id.to_def_id()) { cx.tcx.sess.span_err(span, err.as_ref()); } diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index a20377f320b2..b99052e66ba5 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -7,7 +7,8 @@ use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint; -use rustc_ast::ast; +use if_chain::if_chain; +use rustc_ast::ast::{self, MetaItem, MetaItemKind}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::{self, DefIdTree}; @@ -57,6 +58,20 @@ impl MissingDoc { *self.doc_hidden_stack.last().expect("empty doc_hidden_stack") } + fn has_include(meta: Option) -> bool { + if_chain! { + if let Some(meta) = meta; + if let MetaItemKind::List(list) = meta.kind; + if let Some(meta) = list.get(0); + if let Some(name) = meta.ident(); + then { + name.name == sym::include + } else { + false + } + } + } + fn check_missing_docs_attrs( &self, cx: &LateContext<'_>, @@ -80,7 +95,9 @@ impl MissingDoc { return; } - let has_doc = attrs.iter().any(|a| a.doc_str().is_some()); + let has_doc = attrs + .iter() + .any(|a| a.doc_str().is_some() || Self::has_include(a.meta())); if !has_doc { span_lint( cx, diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/mixed_read_write_in_expression.rs similarity index 98% rename from clippy_lints/src/eval_order_dependence.rs rename to clippy_lints/src/mixed_read_write_in_expression.rs index 65599a0587d4..405fc23e8de0 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -40,8 +40,8 @@ declare_clippy_lint! { /// let a = tmp + x; /// ``` #[clippy::version = "pre 1.29.0"] - pub EVAL_ORDER_DEPENDENCE, - suspicious, + pub MIXED_READ_WRITE_IN_EXPRESSION, + restriction, "whether a variable read occurs before a write depends on sub-expression evaluation order" } @@ -73,7 +73,7 @@ declare_clippy_lint! { "whether an expression contains a diverging sub expression" } -declare_lint_pass!(EvalOrderDependence => [EVAL_ORDER_DEPENDENCE, DIVERGING_SUB_EXPRESSION]); +declare_lint_pass!(EvalOrderDependence => [MIXED_READ_WRITE_IN_EXPRESSION, DIVERGING_SUB_EXPRESSION]); impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { @@ -303,7 +303,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> { if !is_in_assignment_position(self.cx, expr) { span_lint_and_note( self.cx, - EVAL_ORDER_DEPENDENCE, + MIXED_READ_WRITE_IN_EXPRESSION, expr.span, &format!("unsequenced read of `{}`", self.cx.tcx.hir().name(self.var)), Some(self.write_expr.span), diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index cd1bc2023702..8dba60f3a585 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -16,7 +16,7 @@ declare_clippy_lint! { /// ### 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 behaviour + /// Therefore mutating something in a `debug_assert!` macro results in different behavior /// between a release and debug build. /// /// ### Example @@ -92,10 +92,6 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> { self.found = true; return; }, - ExprKind::If(..) => { - self.found = true; - return; - }, ExprKind::Path(_) => { if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) { if adj diff --git a/clippy_lints/src/needless_bitwise_bool.rs b/clippy_lints/src/needless_bitwise_bool.rs index 95395e2e136d..623d22bc9bdf 100644 --- a/clippy_lints/src/needless_bitwise_bool.rs +++ b/clippy_lints/src/needless_bitwise_bool.rs @@ -53,7 +53,7 @@ fn is_bitwise_operation(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { false } -fn suggesstion_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { +fn suggestion_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { if let ExprKind::Binary(ref op, left, right) = expr.kind { if let (Some(l_snippet), Some(r_snippet)) = (snippet_opt(cx, left.span), snippet_opt(cx, right.span)) { let op_snippet = match op.node { @@ -75,7 +75,7 @@ impl LateLintPass<'_> for NeedlessBitwiseBool { expr.span, "use of bitwise operator instead of lazy operator between booleans", |diag| { - if let Some(sugg) = suggesstion_snippet(cx, expr) { + if let Some(sugg) = suggestion_snippet(cx, expr) { diag.span_suggestion(expr.span, "try", sugg, Applicability::MachineApplicable); } }, diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 4034079a90c0..38960103d5e0 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -70,7 +70,7 @@ macro_rules! need { } impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn check_fn( &mut self, cx: &LateContext<'tcx>, diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index 6ba9ba0753d4..707f3b2181ac 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -34,7 +34,6 @@ declare_clippy_lint! { declare_lint_pass!(NegMultiply => [NEG_MULTIPLY]); -#[allow(clippy::match_same_arms)] impl<'tcx> LateLintPass<'tcx> for NegMultiply { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if let ExprKind::Binary(ref op, left, right) = e.kind { diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 2bdccb425071..093ec389335d 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -58,7 +58,6 @@ pub struct NewWithoutDefault { impl_lint_pass!(NewWithoutDefault => [NEW_WITHOUT_DEFAULT]); impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { - #[allow(clippy::too_many_lines)] fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { if let hir::ItemKind::Impl(hir::Impl { of_trait: None, diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index e3bc40c4b491..7f6b535c7b16 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -191,7 +191,7 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> { } } - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn check_ident(&mut self, ident: Ident) { let interned_name = ident.name.as_str(); if interned_name.chars().any(char::is_uppercase) { diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 9af3059a37f9..e3ded716341f 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -52,7 +52,7 @@ declare_clippy_lint! { /// 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 behaviour** introduced with unsafe code. + /// led to an **undefined behavior** introduced with unsafe code. /// /// ### Example /// @@ -124,7 +124,7 @@ impl<'tcx> PassByRefOrValue { // Cap the calculated bit width at 32-bits to reduce // portability problems between 32 and 64-bit targets let bit_width = cmp::min(bit_width, 32); - #[allow(clippy::integer_division)] + #[expect(clippy::integer_division)] let byte_width = bit_width / 8; // Use a limit of 2 times the register byte width byte_width * 2 diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index be319ee110d2..a4d265111f9a 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -163,7 +163,6 @@ enum Level { Lower, } -#[allow(rustc::usage_of_ty_tykind)] fn find_first_mismatch<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> Option<(Span, Mutability, Level)> { let mut result = None; pat.walk(|p| { diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index c35eeeac67a3..86460c1b27e3 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -514,7 +514,7 @@ fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Optio } } -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: &[PtrArg<'tcx>]) -> Vec { struct V<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index be7610f365c5..a47dc26f6034 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -193,7 +193,7 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { check_range_zip_with_len(cx, path, args, expr.span); }, ExprKind::Binary(ref op, l, r) => { - if meets_msrv(self.msrv.as_ref(), &msrvs::RANGE_CONTAINS) { + if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) { check_possible_range_contains(cx, op.node, l, r, expr); } }, @@ -207,7 +207,13 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { extract_msrv_attr!(LateContext); } -fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, expr: &Expr<'_>) { +fn check_possible_range_contains( + cx: &LateContext<'_>, + op: BinOpKind, + left: &Expr<'_>, + right: &Expr<'_>, + expr: &Expr<'_>, +) { if in_constant(cx, expr.hir_id) { return; } @@ -219,21 +225,19 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' _ => return, }; // value, name, order (higher/lower), inclusiveness - if let (Some((lval, lid, name_span, lval_span, lord, linc)), Some((rval, rid, _, rval_span, rord, rinc))) = - (check_range_bounds(cx, l), check_range_bounds(cx, r)) - { + 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 lid != rid || lord == rord { + if l.id != r.id || l.ord == r.ord { return; } - let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(l), &lval, &rval); - if combine_and && ord == Some(rord) { + 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 rord == Ordering::Less { - (lval_span, rval_span, linc, rinc) + 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 { - (rval_span, lval_span, rinc, linc) + (r.val_span, l.val_span, r.inc, l.inc) }; // we only lint inclusive lower bounds if !l_inc { @@ -245,7 +249,7 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' ("Range", "..") }; let mut applicability = Applicability::MachineApplicable; - let name = snippet_with_applicability(cx, name_span, "_", &mut applicability); + 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 { "" }; @@ -258,13 +262,13 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' format!("({}{}{}{}).contains(&{})", lo, space, range_op, hi, name), applicability, ); - } else if !combine_and && ord == Some(lord) { + } 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 lord == Ordering::Less { - (lval_span, rval_span, linc, rinc) + 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 { - (rval_span, lval_span, rinc, linc) + (r.val_span, l.val_span, r.inc, l.inc) }; if l_inc { return; @@ -275,7 +279,7 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' ("RangeInclusive", "..=") }; let mut applicability = Applicability::MachineApplicable; - let name = snippet_with_applicability(cx, name_span, "_", &mut applicability); + 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 { "" }; @@ -292,7 +296,20 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' } } -fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant, HirId, Span, Span, Ordering, bool)> { +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> { if let ExprKind::Binary(ref op, l, r) = ex.kind { let (inclusive, ordering) = match op.node { BinOpKind::Gt => (false, Ordering::Greater), @@ -303,11 +320,27 @@ fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant, }; if let Some(id) = path_to_local(l) { if let Some((c, _)) = constant(cx, cx.typeck_results(), r) { - return Some((c, id, l.span, r.span, ordering, inclusive)); + 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((c, id, r.span, l.span, ordering.reverse(), inclusive)); + return Some(RangeBounds { + val: c, + expr: l, + id, + name_span: r.span, + val_span: l.span, + ord: ordering.reverse(), + inc: inclusive, + }); } } } @@ -330,7 +363,7 @@ fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: // `.iter()` and `.len()` called on same `Path` if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_args[0].kind; if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind; - if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments); + if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments); then { span_lint(cx, RANGE_ZIP_WITH_LEN, diff --git a/clippy_lints/src/rc_clone_in_vec_init.rs b/clippy_lints/src/rc_clone_in_vec_init.rs new file mode 100644 index 000000000000..110f58f3734d --- /dev/null +++ b/clippy_lints/src/rc_clone_in_vec_init.rs @@ -0,0 +1,141 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::higher::VecArgs; +use clippy_utils::last_path_segment; +use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::source::{indent_of, snippet}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, QPath, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{sym, Span, Symbol}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for `Arc::new` or `Rc::new` in `vec![elem; len]` + /// + /// ### Why is this bad? + /// This will create `elem` once and clone it `len` times - doing so with `Arc` or `Rc` + /// is a bit misleading, as it will create references to the same pointer, rather + /// than different instances. + /// + /// ### Example + /// ```rust + /// 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: + /// ```rust + /// + /// // 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]; + /// ``` + #[clippy::version = "1.62.0"] + pub RC_CLONE_IN_VEC_INIT, + suspicious, + "initializing `Arc` or `Rc` in `vec![elem; len]`" +} +declare_lint_pass!(RcCloneInVecInit => [RC_CLONE_IN_VEC_INIT]); + +impl LateLintPass<'_> for RcCloneInVecInit { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return; }; + let Some(VecArgs::Repeat(elem, len)) = VecArgs::hir(cx, expr) else { return; }; + let Some(symbol) = new_reference_call(cx, elem) else { return; }; + + emit_lint(cx, symbol, macro_call.span, elem, len); + } +} + +fn elem_snippet(cx: &LateContext<'_>, elem: &Expr<'_>, symbol_name: &str) -> String { + let elem_snippet = snippet(cx, elem.span, "..").to_string(); + if elem_snippet.contains('\n') { + // This string must be found in `elem_snippet`, otherwise we won't be constructing + // the snippet in the first place. + let reference_creation = format!("{symbol_name}::new"); + let (code_until_reference_creation, _right) = elem_snippet.split_once(&reference_creation).unwrap(); + + return format!("{code_until_reference_creation}{reference_creation}(..)"); + } + + elem_snippet +} + +fn loop_init_suggestion(elem: &str, len: &str, indent: &str) -> String { + format!( + r#"{{ +{indent} let mut v = Vec::with_capacity({len}); +{indent} (0..{len}).for_each(|_| v.push({elem})); +{indent} v +{indent}}}"# + ) +} + +fn extract_suggestion(elem: &str, len: &str, indent: &str) -> String { + format!( + "{{ +{indent} let data = {elem}; +{indent} vec![data; {len}] +{indent}}}" + ) +} + +fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr<'_>, len: &Expr<'_>) { + let symbol_name = symbol.as_str(); + + span_lint_and_then( + cx, + RC_CLONE_IN_VEC_INIT, + lint_span, + &format!("calling `{symbol_name}::new` in `vec![elem; len]`"), + |diag| { + let len_snippet = snippet(cx, len.span, ".."); + let elem_snippet = elem_snippet(cx, elem, symbol_name); + let indentation = " ".repeat(indent_of(cx, lint_span).unwrap_or(0)); + let loop_init_suggestion = loop_init_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation); + let extract_suggestion = extract_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation); + + diag.note(format!("each element will point to the same `{symbol_name}` instance")); + diag.span_suggestion( + lint_span, + format!("consider initializing each `{symbol_name}` element individually"), + loop_init_suggestion, + Applicability::Unspecified, + ); + diag.span_suggestion( + lint_span, + format!( + "or if this is intentional, consider extracting the `{symbol_name}` initialization to a variable" + ), + extract_suggestion, + Applicability::Unspecified, + ); + }, + ); +} + +/// Checks whether the given `expr` is a call to `Arc::new` or `Rc::new` +fn new_reference_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + if_chain! { + if let ExprKind::Call(func, _args) = expr.kind; + if let ExprKind::Path(ref func_path @ QPath::TypeRelative(ty, _)) = func.kind; + if let TyKind::Path(ref ty_path) = ty.kind; + if let Some(def_id) = cx.qpath_res(ty_path, ty.hir_id).opt_def_id(); + if last_path_segment(func_path).ident.name == sym::new; + + then { + return cx.tcx.get_diagnostic_name(def_id).filter(|symbol| symbol == &sym::Arc || symbol == &sym::Rc); + } + } + + None +} diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 37aac8b2a497..0004b8afdd37 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -19,7 +19,6 @@ use rustc_mir_dataflow::{Analysis, AnalysisDomain, CallReturnPlaces, GenKill, Ge use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{BytePos, Span}; use rustc_span::sym; -use std::convert::TryFrom; use std::ops::ControlFlow; macro_rules! unwrap_or_continue { @@ -71,7 +70,7 @@ declare_clippy_lint! { declare_lint_pass!(RedundantClone => [REDUNDANT_CLONE]); impl<'tcx> LateLintPass<'tcx> for RedundantClone { - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn check_fn( &mut self, cx: &LateContext<'tcx>, @@ -633,7 +632,7 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> { } /// Collect possible borrowed for every `&mut` local. -/// For exampel, `_1 = &mut _2` generate _1: {_2,...} +/// For example, `_1 = &mut _2` generate _1: {_2,...} /// Known Problems: not sure all borrowed are tracked struct PossibleOriginVisitor<'a, 'tcx> { possible_origin: TransitiveRelation, diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs index 40a62fd6d201..40b03068f6c7 100644 --- a/clippy_lints/src/redundant_field_names.rs +++ b/clippy_lints/src/redundant_field_names.rs @@ -51,7 +51,7 @@ impl_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]); impl EarlyLintPass for RedundantFieldNames { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::FIELD_INIT_SHORTHAND) { + if !meets_msrv(self.msrv, msrvs::FIELD_INIT_SHORTHAND) { return; } diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index ea5064217abe..2d26c49252fa 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -99,7 +99,7 @@ impl RedundantStaticLifetimes { impl EarlyLintPass for RedundantStaticLifetimes { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::STATIC_IN_CONST) { + if !meets_msrv(self.msrv, msrvs::STATIC_IN_CONST) { return; } diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 811a7bb9c153..f789cec6d6ac 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -54,13 +54,12 @@ impl EarlyLintPass for DerefAddrOf { then { let mut applicability = Applicability::MachineApplicable; let sugg = if e.span.from_expansion() { - #[allow(clippy::option_if_let_else)] if let Some(macro_source) = snippet_opt(cx, e.span) { // Remove leading whitespace from the given span // e.g: ` $visitor` turns into `$visitor` let trim_leading_whitespaces = |span| { snippet_opt(cx, span).and_then(|snip| { - #[allow(clippy::cast_possible_truncation)] + #[expect(clippy::cast_possible_truncation)] snip.find(|c: char| !c.is_whitespace()).map(|pos| { span.lo() + BytePos(pos as u32) }) @@ -68,7 +67,7 @@ impl EarlyLintPass for DerefAddrOf { }; let mut generate_snippet = |pattern: &str| { - #[allow(clippy::cast_possible_truncation)] + #[expect(clippy::cast_possible_truncation)] macro_source.rfind(pattern).map(|pattern_pos| { let rpos = pattern_pos + pattern.len(); let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32)); diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index a92097e1d24c..67129299e2f9 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -7,7 +7,6 @@ use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{BytePos, Span}; -use std::convert::TryFrom; declare_clippy_lint! { /// ### What it does @@ -79,7 +78,6 @@ impl<'tcx> LateLintPass<'tcx> for Regex { } } -#[allow(clippy::cast_possible_truncation)] // truncation very unlikely here #[must_use] fn str_span(base: Span, c: regex_syntax::ast::Span, offset: u8) -> Span { let offset = u32::from(offset); diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs index bfc03116fe2d..ba03ef937211 100644 --- a/clippy_lints/src/renamed_lints.rs +++ b/clippy_lints/src/renamed_lints.rs @@ -9,6 +9,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"), ("clippy::disallowed_method", "clippy::disallowed_methods"), ("clippy::disallowed_type", "clippy::disallowed_types"), + ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), ("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"), ("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"), ("clippy::identity_conversion", "clippy::useless_conversion"), diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs index c5c174cc8f61..9158cbcc04e1 100644 --- a/clippy_lints/src/same_name_method.rs +++ b/clippy_lints/src/same_name_method.rs @@ -54,11 +54,11 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { if matches!(cx.tcx.def_kind(id.def_id), DefKind::Impl) && let item = cx.tcx.hir().item(id) && let ItemKind::Impl(Impl { - items, - of_trait, - self_ty, - .. - }) = &item.kind + items, + of_trait, + self_ty, + .. + }) = &item.kind && let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind { if !map.contains_key(res) { diff --git a/clippy_lints/src/tabs_in_doc_comments.rs b/clippy_lints/src/tabs_in_doc_comments.rs index 15543b6a2627..e223aea297fc 100644 --- a/clippy_lints/src/tabs_in_doc_comments.rs +++ b/clippy_lints/src/tabs_in_doc_comments.rs @@ -4,7 +4,6 @@ use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{BytePos, Span}; -use std::convert::TryFrom; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 911da3997ae4..6c60fb4b8e02 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -36,7 +36,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.38.0"] pub TYPE_REPETITION_IN_BOUNDS, - pedantic, + nursery, "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`" } @@ -66,7 +66,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.47.0"] pub TRAIT_DUPLICATION_IN_BOUNDS, - pedantic, + nursery, "Check if the same trait bounds are specified twice during a function declaration" } diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 342f23f030cd..d2a040beb0cf 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -27,7 +27,7 @@ declare_clippy_lint! { /// architecture. /// /// ### Why is this bad? - /// It's basically guaranteed to be undefined behaviour. + /// It's basically guaranteed to be undefined behavior. /// /// ### Known problems /// When accessing C, users might want to store pointer @@ -40,7 +40,7 @@ declare_clippy_lint! { #[clippy::version = "pre 1.29.0"] pub WRONG_TRANSMUTE, correctness, - "transmutes that are confusing at best, undefined behaviour at worst and always useless" + "transmutes that are confusing at best, undefined behavior at worst and always useless" } // FIXME: Move this to `complexity` again, after #5343 is fixed diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index 10d2ae2eb1db..a1312fcda0b7 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -5,6 +5,7 @@ use rustc_errors::Applicability; use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind}; use rustc_lint::LateContext; use rustc_span::symbol::sym; +use rustc_typeck::hir_ty_to_ty; use super::{utils, REDUNDANT_ALLOCATION}; @@ -54,8 +55,10 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ }; let inner_span = match qpath_generic_tys(inner_qpath).next() { Some(ty) => { - // Box> is smaller than Box because of wide pointers - if matches!(ty.kind, TyKind::TraitObject(..)) { + // Reallocation of a fat pointer causes it to become thin. `hir_ty_to_ty` is safe to use + // here because `mod.rs` guarantees this lint is only run on types outside of bodies and + // is not run on locals. + if !hir_ty_to_ty(cx.tcx, ty).is_sized(cx.tcx.at(ty.span), cx.param_env) { return false; } ty.span diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 465d8a914fb2..5a8677f90be4 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -1,17 +1,18 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_lint_allowed; use clippy_utils::source::walk_span_to_context; +use clippy_utils::{get_parent_node, is_lint_allowed}; use rustc_data_structures::sync::Lrc; -use rustc_hir::{Block, BlockCheckMode, UnsafeSource}; +use rustc_hir as hir; +use rustc_hir::{Block, BlockCheckMode, ItemKind, Node, UnsafeSource}; use rustc_lexer::{tokenize, TokenKind}; 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::{BytePos, Pos, SyntaxContext}; +use rustc_span::{BytePos, Pos, Span, SyntaxContext}; declare_clippy_lint! { /// ### What it does - /// Checks for `unsafe` blocks without a `// SAFETY: ` comment + /// Checks for `unsafe` blocks and impls without a `// SAFETY: ` comment /// explaining why the unsafe operations performed inside /// the block are safe. /// @@ -34,7 +35,7 @@ declare_clippy_lint! { /// ``` /// /// ### Why is this bad? - /// Undocumented unsafe blocks can make it difficult to + /// Undocumented unsafe blocks and impls can make it difficult to /// read and maintain code, as well as uncover unsoundness /// and bugs. /// @@ -66,7 +67,7 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) && !in_external_macro(cx.tcx.sess, block.span) && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id) - && !is_unsafe_from_proc_macro(cx, block) + && !is_unsafe_from_proc_macro(cx, block.span) && !block_has_safety_comment(cx, block) { let source_map = cx.tcx.sess.source_map(); @@ -86,11 +87,37 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { ); } } + + fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { + if let hir::ItemKind::Impl(imple) = item.kind + && imple.unsafety == hir::Unsafety::Unsafe + && !in_external_macro(cx.tcx.sess, item.span) + && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id()) + && !is_unsafe_from_proc_macro(cx, item.span) + && !item_has_safety_comment(cx, item) + { + let source_map = cx.tcx.sess.source_map(); + let span = if source_map.is_multiline(item.span) { + source_map.span_until_char(item.span, '\n') + } else { + item.span + }; + + span_lint_and_help( + cx, + UNDOCUMENTED_UNSAFE_BLOCKS, + span, + "unsafe impl missing a safety comment", + None, + "consider adding a safety comment on the preceding line", + ); + } + } } -fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, block: &Block<'_>) -> bool { +fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, span: Span) -> bool { let source_map = cx.sess().source_map(); - let file_pos = source_map.lookup_byte_offset(block.span.lo()); + let file_pos = source_map.lookup_byte_offset(span.lo()); file_pos .sf .src @@ -100,7 +127,7 @@ fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, block: &Block<'_>) -> bool { } /// Checks if the lines immediately preceding the block contain a safety comment. -fn block_has_safety_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool { +fn block_has_safety_comment(cx: &LateContext<'_>, block: &hir::Block<'_>) -> bool { // This intentionally ignores text before the start of a function so something like: // ``` // // SAFETY: reason @@ -109,13 +136,115 @@ fn block_has_safety_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool { // won't work. This is to avoid dealing with where such a comment should be place relative to // attributes and doc comments. + span_from_macro_expansion_has_safety_comment(cx, block.span) || span_in_body_has_safety_comment(cx, block.span) +} + +/// Checks if the lines immediately preceding the item contain a safety comment. +#[allow(clippy::collapsible_match)] +fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool { + if span_from_macro_expansion_has_safety_comment(cx, item.span) { + return true; + } + + if item.span.ctxt() == SyntaxContext::root() { + if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) { + let comment_start = match parent_node { + Node::Crate(parent_mod) => { + comment_start_before_impl_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item) + }, + Node::Item(parent_item) => { + if let ItemKind::Mod(parent_mod) = &parent_item.kind { + comment_start_before_impl_in_mod(cx, parent_mod, parent_item.span, item) + } else { + // Doesn't support impls in this position. Pretend a comment was found. + return true; + } + }, + Node::Stmt(stmt) => { + if let Some(stmt_parent) = get_parent_node(cx.tcx, stmt.hir_id) { + match stmt_parent { + Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo), + _ => { + // Doesn't support impls in this position. Pretend a comment was found. + return true; + }, + } + } else { + // Problem getting the parent node. Pretend a comment was found. + return true; + } + }, + _ => { + // Doesn't support impls in this position. Pretend a comment was found. + return true; + }, + }; + + let source_map = cx.sess().source_map(); + if let Some(comment_start) = comment_start + && let Ok(unsafe_line) = source_map.lookup_line(item.span.lo()) + && let Ok(comment_start_line) = source_map.lookup_line(comment_start) + && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) + && let Some(src) = unsafe_line.sf.src.as_deref() + { + comment_start_line.line < unsafe_line.line && text_has_safety_comment( + src, + &unsafe_line.sf.lines[comment_start_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) + } else { + // Problem getting source text. Pretend a comment was found. + true + } + } else { + // No parent node. Pretend a comment was found. + true + } + } else { + false + } +} + +fn comment_start_before_impl_in_mod( + cx: &LateContext<'_>, + parent_mod: &hir::Mod<'_>, + parent_mod_span: Span, + imple: &hir::Item<'_>, +) -> Option { + parent_mod.item_ids.iter().enumerate().find_map(|(idx, item_id)| { + if *item_id == imple.item_id() { + if idx == 0 { + // mod A { /* comment */ unsafe impl T {} ... } + // ^------------------------------------------^ returns the start of this span + // ^---------------------^ finally checks comments in this range + if let Some(sp) = walk_span_to_context(parent_mod_span, SyntaxContext::root()) { + return Some(sp.lo()); + } + } else { + // some_item /* comment */ unsafe impl T {} + // ^-------^ returns the end of this span + // ^---------------^ finally checks comments in this range + let prev_item = cx.tcx.hir().item(parent_mod.item_ids[idx - 1]); + if let Some(sp) = walk_span_to_context(prev_item.span, SyntaxContext::root()) { + return Some(sp.hi()); + } + } + } + None + }) +} + +fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { let source_map = cx.sess().source_map(); - let ctxt = block.span.ctxt(); - if ctxt != SyntaxContext::root() { - // From a macro expansion. Get the text from the start of the macro declaration to start of the unsafe block. + let ctxt = span.ctxt(); + if ctxt == SyntaxContext::root() { + false + } else { + // From a macro expansion. Get the text from the start of the macro declaration to start of the + // unsafe block. // macro_rules! foo { () => { stuff }; (x) => { unsafe { stuff } }; } // ^--------------------------------------------^ - if let Ok(unsafe_line) = source_map.lookup_line(block.span.lo()) + if let Ok(unsafe_line) = source_map.lookup_line(span.lo()) && let Ok(macro_line) = source_map.lookup_line(ctxt.outer_expn_data().def_site.lo()) && Lrc::ptr_eq(&unsafe_line.sf, ¯o_line.sf) && let Some(src) = unsafe_line.sf.src.as_deref() @@ -129,24 +258,35 @@ fn block_has_safety_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool { // Problem getting source text. Pretend a comment was found. true } - } else if let Ok(unsafe_line) = source_map.lookup_line(block.span.lo()) + } +} + +fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { + let source_map = cx.sess().source_map(); + let ctxt = span.ctxt(); + if ctxt == SyntaxContext::root() && let Some(body) = cx.enclosing_body - && let Some(body_span) = walk_span_to_context(cx.tcx.hir().body(body).value.span, SyntaxContext::root()) - && let Ok(body_line) = source_map.lookup_line(body_span.lo()) - && Lrc::ptr_eq(&unsafe_line.sf, &body_line.sf) - && let Some(src) = unsafe_line.sf.src.as_deref() { - // Get the text from the start of function body to the unsafe block. - // fn foo() { some_stuff; unsafe { stuff }; other_stuff; } - // ^-------------^ - body_line.line < unsafe_line.line && text_has_safety_comment( - src, - &unsafe_line.sf.lines[body_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos.to_usize(), - ) + if let Ok(unsafe_line) = source_map.lookup_line(span.lo()) + && let Some(body_span) = walk_span_to_context(cx.tcx.hir().body(body).value.span, SyntaxContext::root()) + && let Ok(body_line) = source_map.lookup_line(body_span.lo()) + && Lrc::ptr_eq(&unsafe_line.sf, &body_line.sf) + && let Some(src) = unsafe_line.sf.src.as_deref() + { + // Get the text from the start of function body to the unsafe block. + // fn foo() { some_stuff; unsafe { stuff }; other_stuff; } + // ^-------------^ + body_line.line < unsafe_line.line && text_has_safety_comment( + src, + &unsafe_line.sf.lines[body_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) + } else { + // Problem getting source text. Pretend a comment was found. + true + } } else { - // Problem getting source text. Pretend a comment was found. - true + false } } diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 6d909c34690d..9f4c5555f11b 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -98,7 +98,7 @@ fn handle_uninit_vec_pair<'tcx>( // Check T of Vec if !is_uninit_value_valid_for_ty(cx, substs.type_at(0)) { // FIXME: #7698, false positive of the internal lints - #[allow(clippy::collapsible_span_lint_calls)] + #[expect(clippy::collapsible_span_lint_calls)] span_lint_and_then( cx, UNINIT_VEC, diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index f3f1f53aac56..d86002c926ef 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -3,6 +3,7 @@ use clippy_utils::source::snippet_with_macro_callsite; use clippy_utils::visitors::for_each_value_source; use core::ops::ControlFlow; use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; @@ -71,14 +72,18 @@ fn needs_inferred_result_ty(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { .. }, _, - ) => cx.qpath_res(path, *hir_id).opt_def_id(), - ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(e.hir_id), + ) => match cx.qpath_res(path, *hir_id) { + Res::Def(DefKind::AssocFn | DefKind::Fn, id) => id, + _ => return false, + }, + ExprKind::MethodCall(..) => match cx.typeck_results().type_dependent_def_id(e.hir_id) { + Some(id) => id, + None => return false, + }, _ => return false, }; - if let Some(id) = id - && let sig = cx.tcx.fn_sig(id).skip_binder() - && let ty::Param(output_ty) = *sig.output().kind() - { + let sig = cx.tcx.fn_sig(id).skip_binder(); + if let ty::Param(output_ty) = *sig.output().kind() { sig.inputs().iter().all(|&ty| !ty_contains_param(ty, output_ty.index)) } else { false diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index ae431aac83b8..04e2f301bfd8 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -61,13 +61,13 @@ impl_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]); impl EarlyLintPass for UnnestedOrPatterns { fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) { - if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) { + if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &a.pat); } } fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) { + if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { if let ast::ExprKind::Let(pat, _, _) = &e.kind { lint_unnested_or_patterns(cx, pat); } @@ -75,13 +75,13 @@ impl EarlyLintPass for UnnestedOrPatterns { } fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) { - if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) { + if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &p.pat); } } fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) { - if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) { + if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &l.pat); } } diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index bfd17a687499..52585e59566c 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -130,7 +130,7 @@ fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { snippet_opt(cx, span.with_hi(ty.span.hi())).map_or((ty.span, Applicability::MaybeIncorrect), |fn_source| { position_before_rarrow(&fn_source).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { ( - #[allow(clippy::cast_possible_truncation)] + #[expect(clippy::cast_possible_truncation)] ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), Applicability::MachineApplicable, ) diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 138f8bccb3f5..66f7748e9e08 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -198,7 +198,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { if_chain! { if !hir_ty.span.from_expansion(); - if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS); + if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, in_body, @@ -225,7 +225,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if !expr.span.from_expansion(); - if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS); + if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last(); if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id); then {} else { return; } @@ -256,7 +256,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) { if_chain! { if !pat.span.from_expansion(); - if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS); + if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last(); if let PatKind::Path(QPath::Resolved(_, path)) = pat.kind; if !matches!(path.res, Res::SelfTy { .. } | Res::Def(DefKind::TyParam, _)); diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index abd8a3623703..4a3b5383c892 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -41,7 +41,7 @@ pub struct UselessConversion { impl_lint_pass!(UselessConversion => [USELESS_CONVERSION]); -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] impl<'tcx> LateLintPass<'tcx> for UselessConversion { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if e.span.from_expansion() { diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 74b0168a1794..cd4d16fe95f7 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -6,7 +6,8 @@ use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor}; use serde::Deserialize; use std::error::Error; use std::path::{Path, PathBuf}; -use std::{env, fmt, fs, io}; +use std::str::FromStr; +use std::{cmp, env, fmt, fs, io, iter}; /// Holds information used by `MISSING_ENFORCED_IMPORT_RENAMES` lint. #[derive(Clone, Debug, Deserialize)] @@ -43,18 +44,33 @@ pub enum DisallowedType { #[derive(Default)] pub struct TryConf { pub conf: Conf, - pub errors: Vec, + pub errors: Vec>, } impl TryConf { - fn from_error(error: impl Error) -> Self { + fn from_error(error: impl Error + 'static) -> Self { Self { conf: Conf::default(), - errors: vec![error.to_string()], + errors: vec![Box::new(error)], } } } +#[derive(Debug)] +struct ConfError(String); + +impl fmt::Display for ConfError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ::fmt(&self.0, f) + } +} + +impl Error for ConfError {} + +fn conf_error(s: String) -> Box { + Box::new(ConfError(s)) +} + macro_rules! define_Conf { ($( $(#[doc = $doc:literal])+ @@ -103,11 +119,11 @@ macro_rules! define_Conf { while let Some(name) = map.next_key::<&str>()? { match Field::deserialize(name.into_deserializer())? { $(Field::$name => { - $(errors.push(format!("deprecated field `{}`. {}", name, $dep));)? + $(errors.push(conf_error(format!("deprecated field `{}`. {}", name, $dep)));)? match map.next_value() { - Err(e) => errors.push(e.to_string()), + Err(e) => errors.push(conf_error(e.to_string())), Ok(value) => match $name { - Some(_) => errors.push(format!("duplicate field `{}`", name)), + Some(_) => errors.push(conf_error(format!("duplicate field `{}`", name))), None => $name = Some(value), } } @@ -316,6 +332,14 @@ define_Conf! { /// /// 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), } /// Search for the configuration file. @@ -375,3 +399,102 @@ pub fn read(path: &Path) -> TryConf { }; toml::from_str(&content).unwrap_or_else(TryConf::from_error) } + +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) -> String { + let s = error.to_string(); + + if_chain! { + if error.downcast::().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::>(); + 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::>(); + Some((prefix, fields, suffix)) + } else { + None + } + } +} + +fn calculate_dimensions(fields: &[&str]) -> (usize, Vec) { + let columns = env::var("CLIPPY_TERMINAL_WIDTH") + .ok() + .and_then(|s| ::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::>(); + + (rows, column_widths) +} diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 79e7410c3a83..ba1ff65479d6 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -12,7 +12,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; -#[allow(clippy::module_name_repetitions)] +#[expect(clippy::module_name_repetitions)] #[derive(Copy, Clone)] pub struct UselessVec { pub too_large_for_stack: u64, @@ -83,7 +83,7 @@ impl UselessVec { let snippet = match *vec_args { higher::VecArgs::Repeat(elem, len) => { if let Some((Constant::Int(len_constant), _)) = constant(cx, cx.typeck_results(), len) { - #[allow(clippy::cast_possible_truncation)] + #[expect(clippy::cast_possible_truncation)] if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack { return; } @@ -110,7 +110,6 @@ impl UselessVec { }, higher::VecArgs::Vec(args) => { if let Some(last) = args.iter().last() { - #[allow(clippy::cast_possible_truncation)] if args.len() as u64 * size_of(cx, last) > self.too_large_for_stack { return; } diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs index fbf2b3e081b8..35db45e2b0c9 100644 --- a/clippy_lints/src/vec_init_then_push.rs +++ b/clippy_lints/src/vec_init_then_push.rs @@ -1,19 +1,30 @@ 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::{path_to_local, path_to_local_id}; -use if_chain::if_chain; +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::{BindingAnnotation, Block, Expr, ExprKind, HirId, Local, PatKind, Stmt, StmtKind}; +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; +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. @@ -43,26 +54,88 @@ pub struct VecInitThenPush { struct VecPushSearcher { local_id: HirId, init: VecInitKind, - lhs_is_local: bool, - lhs_span: Span, + lhs_is_let: bool, + let_ty_span: Option, + name: Symbol, err_span: Span, - found: u64, + found: u128, + last_push_expr: HirId, } impl VecPushSearcher { fn display_err(&self, cx: &LateContext<'_>) { - match self.init { + let required_pushes_before_extension = match self.init { _ if self.found == 0 => return, - VecInitKind::WithLiteralCapacity(x) if x > self.found => return, + VecInitKind::WithConstCapacity(x) if x > self.found => return, + VecInitKind::WithConstCapacity(x) => x, VecInitKind::WithExprCapacity(_) => return, - _ => (), + _ => 3, }; - let mut s = if self.lhs_is_local { + 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, parent) { + 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() }; - s.push_str(&snippet(cx, self.lhs_span, "..")); + 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( @@ -83,60 +156,63 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush { } fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { - if_chain! { - if !in_external_macro(cx.sess(), local.span); - if let Some(init) = local.init; - if let PatKind::Binding(BindingAnnotation::Mutable, id, _, None) = local.pat.kind; - if let Some(init_kind) = get_vec_init_kind(cx, init); - then { - self.searcher = Some(VecPushSearcher { - local_id: id, - init: init_kind, - lhs_is_local: true, - lhs_span: local.ty.map_or(local.pat.span, |t| local.pat.span.to(t.span)), - err_span: local.span, - found: 0, - }); - } + if let Some(init_expr) = local.init + && let PatKind::Binding(BindingAnnotation::Mutable, 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_chain! { - if self.searcher.is_none(); - if !in_external_macro(cx.sess(), expr.span); - if let ExprKind::Assign(left, right, _) = expr.kind; - if let Some(id) = path_to_local(left); - if let Some(init_kind) = get_vec_init_kind(cx, right); - then { - self.searcher = Some(VecPushSearcher { - local_id: id, - init: init_kind, - lhs_is_local: false, - lhs_span: left.span, - err_span: expr.span, - found: 0, - }); - } + 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_chain! { - if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind; - if let ExprKind::MethodCall(path, [self_arg, _], _) = expr.kind; - if path_to_local_id(self_arg, searcher.local_id); - if path.ident.name.as_str() == "push"; - then { - self.searcher = Some(VecPushSearcher { - found: searcher.found + 1, - err_span: searcher.err_span.to(stmt.span), - .. searcher - }); - } else { - searcher.display_err(cx); - } + 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); } } } diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 54b93a20a057..d2493c055a51 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -342,8 +342,6 @@ impl EarlyLintPass for Write { 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; - // FIXME: remove this `#[allow(...)]` once the issue #5822 gets fixed - #[allow(clippy::option_if_let_else)] let suggestion = if let Some(e) = expr { snippet_with_applicability(cx, e.span, "v", &mut applicability) } else { @@ -528,7 +526,6 @@ impl Write { /// ```rust,ignore /// (Some("string to write: {}"), Some(buf)) /// ``` - #[allow(clippy::too_many_lines)] fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) -> (Option, Option) { let mut parser = parser::Parser::new(&cx.sess().parse_sess, tts, false, None); let expr = if is_write { diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 7919800483f5..b379f8c06c60 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -242,7 +242,7 @@ pub fn eq_item(l: &Item, r: &Item, mut eq_kind: impl FnMut(&K, &K) -> b eq_id(l.ident, r.ident) && over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind) } -#[allow(clippy::too_many_lines)] // Just a big match statement +#[expect(clippy::too_many_lines)] // Just a big match statement pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { use ItemKind::*; match (l, r) { diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 904b1a05ccc3..49318849d580 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -1,12 +1,11 @@ use rustc_ast::ast; +use rustc_ast::attr; use rustc_errors::Applicability; use rustc_session::Session; -use rustc_ast::attr; use rustc_span::sym; use std::str::FromStr; /// Deprecation status of attributes known by Clippy. -#[allow(dead_code)] pub enum DeprecationStatus { /// Attribute is deprecated Deprecated, @@ -158,4 +157,3 @@ pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool { .filter_map(ast::Attribute::meta_item_list) .any(|l| attr::list_contains_name(&l, sym::hidden)) } - diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index a80c7ee49295..9f162a117b2d 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -13,7 +13,6 @@ use rustc_middle::ty::{self, EarlyBinder, FloatTy, ScalarInt, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::Symbol; use std::cmp::Ordering::{self, Equal}; -use std::convert::TryInto; use std::hash::{Hash, Hasher}; use std::iter; @@ -130,12 +129,10 @@ impl Constant { match (left, right) { (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)), (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)), - (&Self::Int(l), &Self::Int(r)) => { - if let ty::Int(int_ty) = *cmp_type.kind() { - Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty))) - } else { - Some(l.cmp(&r)) - } + (&Self::Int(l), &Self::Int(r)) => match *cmp_type.kind() { + ty::Int(int_ty) => Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty))), + ty::Uint(_) => Some(l.cmp(&r)), + _ => bug!("Not an int type"), }, (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r), (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r), @@ -352,7 +349,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } } - #[allow(clippy::cast_possible_wrap)] + #[expect(clippy::cast_possible_wrap)] fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option { use self::Constant::{Bool, Int}; match *o { diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index b142397f71b9..04afe5ac3733 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -77,7 +77,7 @@ pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into( cx: &'a T, lint: &'static Lint, - span: Span, + span: impl Into, msg: &str, help_span: Option, help: &str, @@ -155,13 +155,7 @@ where }); } -pub fn span_lint_hir( - cx: &LateContext<'_>, - lint: &'static Lint, - hir_id: HirId, - sp: Span, - msg: &str, -) { +pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) { cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |diag| { let mut diag = diag.build(msg); docs_link(&mut diag, lint); @@ -278,14 +272,9 @@ pub fn span_lint_and_sugg_for_edges( let sugg_lines_count = sugg.lines().count(); if sugg_lines_count > MAX_SUGGESTION_HIGHLIGHT_LINES { let sm = cx.sess().source_map(); - if let (Ok(line_upper), Ok(line_bottom)) = - (sm.lookup_line(sp.lo()), sm.lookup_line(sp.hi())) - { + if let (Ok(line_upper), Ok(line_bottom)) = (sm.lookup_line(sp.lo()), sm.lookup_line(sp.hi())) { let split_idx = MAX_SUGGESTION_HIGHLIGHT_LINES / 2; - let span_upper = sm.span_until_char( - sp.with_hi(line_upper.sf.lines[line_upper.line + split_idx]), - '\n', - ); + let span_upper = sm.span_until_char(sp.with_hi(line_upper.sf.lines[line_upper.line + split_idx]), '\n'); let span_bottom = sp.with_lo(line_bottom.sf.lines[line_bottom.line - split_idx]); let sugg_lines_vec = sugg.lines().collect::>(); diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index a6ef6d79fc02..1a784b6cdda4 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -96,7 +96,7 @@ fn fn_eagerness<'tcx>( } } -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion { struct V<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 2095fc966c5d..1e0fc789af24 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -2,10 +2,11 @@ #![deny(clippy::missing_docs_in_private_items)] +use crate::consts::{constant_simple, Constant}; use crate::ty::is_type_diagnostic_item; use crate::{is_expn_of, match_def_path, paths}; use if_chain::if_chain; -use rustc_ast::ast::{self, LitKind}; +use rustc_ast::ast; use rustc_hir as hir; use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath}; use rustc_lint::LateContext; @@ -431,7 +432,7 @@ pub enum VecInitKind { /// `Vec::default()` or `Default::default()` Default, /// `Vec::with_capacity(123)` - WithLiteralCapacity(u64), + WithConstCapacity(u128), /// `Vec::with_capacity(slice.len())` WithExprCapacity(HirId), } @@ -449,15 +450,11 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - return Some(VecInitKind::Default); } else if name.ident.name.as_str() == "with_capacity" { let arg = args.get(0)?; - if_chain! { - if let ExprKind::Lit(lit) = &arg.kind; - if let LitKind::Int(num, _) = lit.node; - then { - return Some(VecInitKind::WithLiteralCapacity(num.try_into().ok()?)); - } - } - return Some(VecInitKind::WithExprCapacity(arg.hir_id)); - } + return match constant_simple(cx, cx.typeck_results(), arg) { + Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)), + _ => Some(VecInitKind::WithExprCapacity(arg.hir_id)), + }; + }; }, ExprKind::Path(QPath::Resolved(_, path)) if match_def_path(cx, path.res.opt_def_id()?, &paths::DEFAULT_TRAIT_METHOD) diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index aa21f15ee5d9..c440793b90e0 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -64,7 +64,6 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { } } - #[allow(dead_code)] pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { self.inter_expr().eq_block(left, right) } @@ -194,7 +193,7 @@ impl HirEqInterExpr<'_, '_, '_> { res } - #[allow(clippy::similar_names)] + #[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; @@ -361,7 +360,7 @@ impl HirEqInterExpr<'_, '_, '_> { } } - #[allow(clippy::similar_names)] + #[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)) => { @@ -407,7 +406,6 @@ impl HirEqInterExpr<'_, '_, '_> { left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r)) } - #[allow(clippy::similar_names)] 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), @@ -562,7 +560,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { std::mem::discriminant(&b.rules).hash(&mut self.s); } - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] pub fn hash_expr(&mut self, e: &Expr<'_>) { let simple_const = self .maybe_typeck_results diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 6db7f247a992..adb37cc9d751 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2,6 +2,7 @@ #![feature(control_flow_enum)] #![feature(let_else)] #![feature(let_chains)] +#![feature(lint_reasons)] #![feature(once_cell)] #![feature(rustc_private)] #![recursion_limit = "512"] @@ -35,7 +36,6 @@ extern crate rustc_typeck; #[macro_use] pub mod sym_helper; -#[allow(clippy::module_name_repetitions)] pub mod ast_utils; pub mod attrs; pub mod comparisons; @@ -67,6 +67,7 @@ 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; @@ -77,9 +78,9 @@ 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, 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, + 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_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::place::PlaceBase; @@ -116,8 +117,8 @@ pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Opt None } -pub fn meets_msrv(msrv: Option<&RustcVersion>, lint_msrv: &RustcVersion) -> bool { - msrv.map_or(true, |msrv| msrv.meets(*lint_msrv)) +pub fn meets_msrv(msrv: Option, lint_msrv: RustcVersion) -> bool { + msrv.map_or(true, |msrv| msrv.meets(lint_msrv)) } #[macro_export] @@ -1554,14 +1555,14 @@ pub fn int_bits(tcx: TyCtxt<'_>, ity: rustc_ty::IntTy) -> u64 { Integer::from_int_ty(&tcx, ity).size().bits() } -#[allow(clippy::cast_possible_wrap)] +#[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 } -#[allow(clippy::cast_sign_loss)] +#[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); @@ -2123,6 +2124,27 @@ pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { }) } +/// 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. /// diff --git a/clippy_utils/src/numeric_literal.rs b/clippy_utils/src/numeric_literal.rs index b92d42e83232..3fb5415ce029 100644 --- a/clippy_utils/src/numeric_literal.rs +++ b/clippy_utils/src/numeric_literal.rs @@ -1,7 +1,7 @@ use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind}; use std::iter; -#[derive(Debug, PartialEq, Copy, Clone)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum Radix { Binary, Octal, diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 60971fb716db..9b9cbff2d146 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -43,9 +43,9 @@ pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"]; pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"]; pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"]; pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; @@ -63,7 +63,7 @@ pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"]; pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"]; pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"]; pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; #[cfg(feature = "internal")] pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; @@ -117,17 +117,17 @@ pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"]; pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"]; /// Preferably use the diagnostic item `sym::Result` where possible pub const RESULT: [&str; 3] = ["core", "result", "Result"]; @@ -169,9 +169,9 @@ pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"]; pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const TOKIO_IO_ASYNCREADEXT: [&str; 5] = ["tokio", "io", "util", "async_read_ext", "AsyncReadExt"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_write_ext", "AsyncWriteExt"]; pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"]; pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"]; diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 66d373a1bf81..a6d7042fabc2 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -18,7 +18,7 @@ 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 { +pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option) -> McfResult { let def_id = body.source.def_id(); let mut current = def_id; loop { @@ -268,7 +268,7 @@ fn check_terminator<'a, 'tcx>( tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>, - msrv: Option<&RustcVersion>, + msrv: Option, ) -> McfResult { let span = terminator.source_info.span; match &terminator.kind { @@ -352,7 +352,7 @@ fn check_terminator<'a, 'tcx>( } } -fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> bool { +fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option) -> 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 { @@ -361,7 +361,7 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> b // 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()) + RustcVersion::parse(since.as_str()) .expect("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted"), ) } else { diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index c69a3d8d2a15..04ef2f57447c 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -137,7 +137,7 @@ pub fn position_before_rarrow(s: &str) -> Option { } /// Reindent a multiline string with possibility of ignoring the first line. -#[allow(clippy::needless_pass_by_value)] +#[expect(clippy::needless_pass_by_value)] pub fn reindent_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option) -> Cow<'_, str> { let s_space = reindent_multiline_inner(&s, ignore_first, indent, ' '); let s_tab = reindent_multiline_inner(&s_space, ignore_first, indent, '\t'); diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 18915553e61c..1de73a80ad20 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -17,7 +17,6 @@ use rustc_middle::ty; use rustc_span::source_map::{BytePos, CharPos, Pos, Span, SyntaxContext}; use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use std::borrow::Cow; -use std::convert::TryInto; use std::fmt::{Display, Write as _}; use std::iter; use std::ops::{Add, Neg, Not, Sub}; @@ -50,7 +49,7 @@ impl Display for Sugg<'_> { } } -#[allow(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method +#[expect(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method impl<'a> Sugg<'a> { /// Prepare a suggestion from an expression. pub fn hir_opt(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { @@ -318,7 +317,6 @@ impl<'a> Sugg<'a> { /// Convenience method to create the `..` or `...` /// suggestion. - #[allow(dead_code)] pub fn range(self, end: &Self, limit: ast::RangeLimits) -> Sugg<'static> { match limit { ast::RangeLimits::HalfOpen => make_assoc(AssocOp::DotDot, &self, end), @@ -886,7 +884,6 @@ impl<'tcx> DerefDelegate<'_, 'tcx> { impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} - #[allow(clippy::too_many_lines)] fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) { if let PlaceBase::Local(id) = cmt.place.base { let map = self.cx.tcx.hir(); diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index b09eb8c6cd10..07d3d2807634 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -603,7 +603,7 @@ impl core::ops::Add for EnumValue { } /// Attempts to read the given constant as though it were an an enum value. -#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] +#[expect(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] pub fn read_explicit_enum_value(tcx: TyCtxt<'_>, id: DefId) -> Option { if let Ok(ConstValue::Scalar(Scalar::Int(value))) = tcx.const_eval_poly(id) { match tcx.type_of(id).kind() { diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 4236e3aae2fb..abba9b005582 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -44,7 +44,6 @@ struct MutVarsDelegate { } impl<'tcx> MutVarsDelegate { - #[allow(clippy::similar_names)] fn update(&mut self, cat: &PlaceWithHirId<'tcx>) { match cat.place.base { PlaceBase::Local(id) => { diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index c00bc2bd213f..b6c8f1d516e5 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -1,4 +1,4 @@ -use crate::path_to_local_id; +use crate::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -436,3 +436,61 @@ pub fn for_each_value_source<'tcx, B>( _ => 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, +) -> ControlFlow { + struct V<'cx, 'tcx, F, B> { + cx: &'cx LateContext<'tcx>, + local_id: HirId, + expr_id: HirId, + found: bool, + res: ControlFlow, + f: F, + } + impl<'cx, 'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow, 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(()) + } +} diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 307cf2f3a904..e8f0c338fd58 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -28,7 +28,7 @@ because that's clearly a non-descriptive name. - [Debugging](#debugging) - [PR Checklist](#pr-checklist) - [Adding configuration to a lint](#adding-configuration-to-a-lint) - - [Cheatsheet](#cheatsheet) + - [Cheat Sheet](#cheat-sheet) ## Setup @@ -432,7 +432,7 @@ The project's MSRV can then be matched against the feature MSRV in the LintPass using the `meets_msrv` utility function. ``` rust -if !meets_msrv(self.msrv.as_ref(), &msrvs::STR_STRIP_PREFIX) { +if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) { return; } ``` @@ -649,14 +649,14 @@ in the following steps: with the configuration value and a rust file that should be linted by Clippy. The test can otherwise be written as usual. -## Cheatsheet +## Cheat Sheet Here are some pointers to things you are likely going to need for every lint: * [Clippy utils][utils] - Various helper functions. Maybe the function you need is already in here ([`is_type_diagnostic_item`], [`implements_trait`], [`snippet`], etc) * [Clippy diagnostics][diagnostics] -* [The `if_chain` macro][if_chain] +* [Let chains][let-chains] * [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro] * [`Span`][span] * [`Applicability`][applicability] @@ -684,7 +684,7 @@ don't hesitate to ask on [Zulip] or in the issue/PR. [`is_type_diagnostic_item`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.is_type_diagnostic_item.html [`implements_trait`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.implements_trait.html [`snippet`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/source/fn.snippet.html -[if_chain]: https://docs.rs/if_chain/*/if_chain/ +[let-chains]: https://github.com/rust-lang/rust/pull/94927 [from_expansion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion [in_external_macro]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html [span]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md index 828bf4cbef94..131ac3c3611e 100644 --- a/doc/common_tools_writing_lints.md +++ b/doc/common_tools_writing_lints.md @@ -62,16 +62,14 @@ Starting with an `expr`, you can check whether it is calling a specific method ` ```rust impl<'tcx> LateLintPass<'tcx> for MyStructLint { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if_chain! { - // Check our expr is calling a method - if let hir::ExprKind::MethodCall(path, _, [_self_arg, ..]) = &expr.kind; + // Check our expr is calling a method + if let hir::ExprKind::MethodCall(path, _, [_self_arg, ..]) = &expr.kind // Check the name of this method is `some_method` - if path.ident.name == sym!(some_method); + && path.ident.name == sym!(some_method) // Optionally, check the type of the self argument. // - See "Checking for a specific type" - then { + { // ... - } } } } @@ -165,18 +163,16 @@ use clippy_utils::{is_type_diagnostic_item, return_ty}; impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { - if_chain! { - // Check if item is a method/function - if let ImplItemKind::Fn(ref signature, _) = impl_item.kind; + // Check if item is a method/function + if let ImplItemKind::Fn(ref signature, _) = impl_item.kind // Check the method is named `some_method` - if impl_item.ident.name == sym!(some_method); + && impl_item.ident.name == sym!(some_method) // We can also check it has a parameter `self` - if signature.decl.implicit_self.has_implicit_self(); + && signature.decl.implicit_self.has_implicit_self() // We can go further and even check if its return type is `String` - if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type)); - then { - // ... - } + && is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type)) + { + // ... } } } diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index c694037021a5..e63f65ce2f75 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -6,16 +6,15 @@ readme = "README.md" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-clippy" categories = ["development-tools"] -edition = "2018" +edition = "2021" publish = false [dependencies] +cargo_metadata = "0.14" clap = "2.33" flate2 = "1.0" -fs_extra = "1.2" rayon = "1.5.1" serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" tar = "0.4" toml = "0.5" ureq = "2.2" diff --git a/lintcheck/lintcheck_crates.toml b/lintcheck/lintcheck_crates.toml index dfee28f1a871..4fbae8614ca3 100644 --- a/lintcheck/lintcheck_crates.toml +++ b/lintcheck/lintcheck_crates.toml @@ -24,7 +24,7 @@ unicode-xid = {name = "unicode-xid", versions = ['0.2.1']} 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", version = ['1.0.5']} +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 diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs new file mode 100644 index 000000000000..de32b4843601 --- /dev/null +++ b/lintcheck/src/config.rs @@ -0,0 +1,140 @@ +use clap::{App, Arg, ArgMatches}; +use std::env; +use std::path::PathBuf; + +fn get_clap_config<'a>() -> ArgMatches<'a> { + App::new("lintcheck") + .about("run clippy on a set of crates and check output") + .arg( + Arg::with_name("only") + .takes_value(true) + .value_name("CRATE") + .long("only") + .help("Only process a single crate of the list"), + ) + .arg( + Arg::with_name("crates-toml") + .takes_value(true) + .value_name("CRATES-SOURCES-TOML-PATH") + .long("crates-toml") + .help("Set the path for a crates.toml where lintcheck should read the sources from"), + ) + .arg( + Arg::with_name("threads") + .takes_value(true) + .value_name("N") + .short("j") + .long("jobs") + .help("Number of threads to use, 0 automatic choice"), + ) + .arg( + Arg::with_name("fix") + .long("--fix") + .help("Runs cargo clippy --fix and checks if all suggestions apply"), + ) + .arg( + Arg::with_name("filter") + .long("--filter") + .takes_value(true) + .multiple(true) + .value_name("clippy_lint_name") + .help("Apply a filter to only collect specified lints, this also overrides `allow` attributes"), + ) + .arg( + Arg::with_name("markdown") + .long("--markdown") + .help("Change the reports table to use markdown links"), + ) + .get_matches() +} + +#[derive(Debug)] +pub(crate) struct LintcheckConfig { + /// max number of jobs to spawn (default 1) + pub max_jobs: usize, + /// we read the sources to check from here + pub sources_toml_path: PathBuf, + /// we save the clippy lint results here + pub lintcheck_results_path: PathBuf, + /// Check only a specified package + pub only: Option, + /// whether to just run --fix and not collect all the warnings + pub fix: bool, + /// A list of lints that this lintcheck run should focus on + pub lint_filter: Vec, + /// Indicate if the output should support markdown syntax + pub markdown: bool, +} + +impl LintcheckConfig { + pub fn new() -> Self { + let clap_config = get_clap_config(); + + // first, check if we got anything passed via the LINTCHECK_TOML env var, + // if not, ask clap if we got any value for --crates-toml + // if not, use the default "lintcheck/lintcheck_crates.toml" + let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| { + clap_config + .value_of("crates-toml") + .clone() + .unwrap_or("lintcheck/lintcheck_crates.toml") + .to_string() + }); + + let markdown = clap_config.is_present("markdown"); + let sources_toml_path = PathBuf::from(sources_toml); + + // for the path where we save the lint results, get the filename without extension (so for + // wasd.toml, use "wasd"...) + let filename: PathBuf = sources_toml_path.file_stem().unwrap().into(); + let lintcheck_results_path = PathBuf::from(format!( + "lintcheck-logs/{}_logs.{}", + filename.display(), + if markdown { "md" } else { "txt" } + )); + + // look at the --threads arg, if 0 is passed, ask rayon rayon how many threads it would spawn and + // use half of that for the physical core count + // by default use a single thread + let max_jobs = match clap_config.value_of("threads") { + Some(threads) => { + let threads: usize = threads + .parse() + .unwrap_or_else(|_| panic!("Failed to parse '{}' to a digit", threads)); + if threads == 0 { + // automatic choice + // Rayon seems to return thread count so half that for core count + (rayon::current_num_threads() / 2) as usize + } else { + threads + } + }, + // no -j passed, use a single thread + None => 1, + }; + + let lint_filter: Vec = clap_config + .values_of("filter") + .map(|iter| { + iter.map(|lint_name| { + let mut filter = lint_name.replace('_', "-"); + if !filter.starts_with("clippy::") { + filter.insert_str(0, "clippy::"); + } + filter + }) + .collect() + }) + .unwrap_or_default(); + + LintcheckConfig { + max_jobs, + sources_toml_path, + lintcheck_results_path, + only: clap_config.value_of("only").map(String::from), + fix: clap_config.is_present("fix"), + lint_filter, + markdown, + } + } +} diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 816efbdaedf3..dff9d27db0a6 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -7,23 +7,25 @@ #![allow(clippy::collapsible_else_if)] -use std::ffi::OsStr; +mod config; + +use config::LintcheckConfig; + +use std::collections::HashMap; +use std::env; use std::fmt::Write as _; +use std::fs::write; +use std::io::ErrorKind; +use std::path::{Path, PathBuf}; use std::process::Command; use std::sync::atomic::{AtomicUsize, Ordering}; -use std::{collections::HashMap, io::ErrorKind}; -use std::{ - env, - fs::write, - path::{Path, PathBuf}, - thread, - time::Duration, -}; - -use clap::{App, Arg, ArgMatches}; +use std::thread; +use std::time::Duration; + +use cargo_metadata::diagnostic::DiagnosticLevel; +use cargo_metadata::Message; use rayon::prelude::*; use serde::{Deserialize, Serialize}; -use serde_json::Value; use walkdir::{DirEntry, WalkDir}; #[cfg(not(windows))] @@ -93,37 +95,67 @@ struct Crate { #[derive(Debug)] struct ClippyWarning { crate_name: String, - crate_version: String, file: String, - line: String, - column: String, - linttype: String, + line: usize, + column: usize, + lint_type: String, message: String, is_ice: bool, } #[allow(unused)] impl ClippyWarning { + fn new(cargo_message: Message, krate: &Crate) -> Option { + let diag = match cargo_message { + Message::CompilerMessage(message) => message.message, + _ => return None, + }; + + let lint_type = diag.code?.code; + if !(lint_type.contains("clippy") || diag.message.contains("clippy")) + || diag.message.contains("could not read cargo metadata") + { + return None; + } + + let span = diag.spans.into_iter().find(|span| span.is_primary)?; + + let file = match Path::new(&span.file_name).strip_prefix(env!("CARGO_HOME")) { + Ok(stripped) => format!("$CARGO_HOME/{}", stripped.display()), + Err(_) => format!( + "target/lintcheck/sources/{}-{}/{}", + krate.name, krate.version, span.file_name + ), + }; + + Some(Self { + crate_name: krate.name.clone(), + file, + line: span.line_start, + column: span.column_start, + lint_type, + message: diag.message, + is_ice: diag.level == DiagnosticLevel::Ice, + }) + } + fn to_output(&self, markdown: bool) -> String { - let file = format!("{}-{}/{}", &self.crate_name, &self.crate_version, &self.file); - let file_with_pos = format!("{}:{}:{}", &file, &self.line, &self.column); + let file_with_pos = format!("{}:{}:{}", &self.file, &self.line, &self.column); if markdown { - let lint = format!("`{}`", self.linttype); + let lint = format!("`{}`", self.lint_type); + + let mut file = self.file.clone(); + if !file.starts_with('$') { + file.insert_str(0, "../"); + } let mut output = String::from("| "); - let _ = write!( - output, - "[`{}`](../target/lintcheck/sources/{}#L{})", - file_with_pos, file, self.line - ); + let _ = write!(output, "[`{}`]({}#L{})", file_with_pos, file, self.line); let _ = write!(output, r#" | {:<50} | "{}" |"#, lint, self.message); output.push('\n'); output } else { - format!( - "target/lintcheck/sources/{} {} \"{}\"\n", - file_with_pos, self.linttype, self.message - ) + format!("{} {} \"{}\"\n", file_with_pos, self.lint_type, self.message) } } } @@ -278,18 +310,17 @@ impl Crate { &self, cargo_clippy_path: &Path, target_dir_index: &AtomicUsize, - thread_limit: usize, total_crates_to_lint: usize, - fix: bool, + config: &LintcheckConfig, lint_filter: &Vec, ) -> Vec { // advance the atomic index by one let index = target_dir_index.fetch_add(1, Ordering::SeqCst); // "loop" the index within 0..thread_limit - let thread_index = index % thread_limit; + let thread_index = index % config.max_jobs; let perc = (index * 100) / total_crates_to_lint; - if thread_limit == 1 { + if config.max_jobs == 1 { println!( "{}/{} {}% Linting {} {}", index, total_crates_to_lint, perc, &self.name, &self.version @@ -305,7 +336,7 @@ impl Crate { let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir"); - let mut args = if fix { + let mut args = if config.fix { vec!["--fix", "--"] } else { vec!["--", "--message-format=json", "--"] @@ -356,7 +387,7 @@ impl Crate { ); } - if fix { + if config.fix { if let Some(stderr) = stderr .lines() .find(|line| line.contains("failed to automatically apply fixes suggested by rustc to crate")) @@ -371,127 +402,15 @@ impl Crate { return Vec::new(); } - let output_lines = stdout.lines(); - let warnings: Vec = output_lines - .into_iter() - // get all clippy warnings and ICEs - .filter(|line| filter_clippy_warnings(&line)) - .map(|json_msg| parse_json_message(json_msg, &self)) + // get all clippy warnings and ICEs + let warnings: Vec = Message::parse_stream(stdout.as_bytes()) + .filter_map(|msg| ClippyWarning::new(msg.unwrap(), &self)) .collect(); warnings } } -#[derive(Debug)] -struct LintcheckConfig { - /// max number of jobs to spawn (default 1) - max_jobs: usize, - /// we read the sources to check from here - sources_toml_path: PathBuf, - /// we save the clippy lint results here - lintcheck_results_path: PathBuf, - /// whether to just run --fix and not collect all the warnings - fix: bool, - /// A list of lints that this lintcheck run should focus on - lint_filter: Vec, - /// Indicate if the output should support markdown syntax - markdown: bool, -} - -impl LintcheckConfig { - fn from_clap(clap_config: &ArgMatches) -> Self { - // first, check if we got anything passed via the LINTCHECK_TOML env var, - // if not, ask clap if we got any value for --crates-toml - // if not, use the default "lintcheck/lintcheck_crates.toml" - let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| { - clap_config - .value_of("crates-toml") - .clone() - .unwrap_or("lintcheck/lintcheck_crates.toml") - .to_string() - }); - - let markdown = clap_config.is_present("markdown"); - let sources_toml_path = PathBuf::from(sources_toml); - - // for the path where we save the lint results, get the filename without extension (so for - // wasd.toml, use "wasd"...) - let filename: PathBuf = sources_toml_path.file_stem().unwrap().into(); - let lintcheck_results_path = PathBuf::from(format!( - "lintcheck-logs/{}_logs.{}", - filename.display(), - if markdown { "md" } else { "txt" } - )); - - // look at the --threads arg, if 0 is passed, ask rayon rayon how many threads it would spawn and - // use half of that for the physical core count - // by default use a single thread - let max_jobs = match clap_config.value_of("threads") { - Some(threads) => { - let threads: usize = threads - .parse() - .unwrap_or_else(|_| panic!("Failed to parse '{}' to a digit", threads)); - if threads == 0 { - // automatic choice - // Rayon seems to return thread count so half that for core count - (rayon::current_num_threads() / 2) as usize - } else { - threads - } - }, - // no -j passed, use a single thread - None => 1, - }; - let fix: bool = clap_config.is_present("fix"); - let lint_filter: Vec = clap_config - .values_of("filter") - .map(|iter| { - iter.map(|lint_name| { - let mut filter = lint_name.replace('_', "-"); - if !filter.starts_with("clippy::") { - filter.insert_str(0, "clippy::"); - } - filter - }) - .collect() - }) - .unwrap_or_default(); - - LintcheckConfig { - max_jobs, - sources_toml_path, - lintcheck_results_path, - fix, - lint_filter, - markdown, - } - } -} - -/// takes a single json-formatted clippy warnings and returns true (we are interested in that line) -/// or false (we aren't) -fn filter_clippy_warnings(line: &str) -> bool { - // we want to collect ICEs because clippy might have crashed. - // these are summarized later - if line.contains("internal compiler error: ") { - return true; - } - // in general, we want all clippy warnings - // however due to some kind of bug, sometimes there are absolute paths - // to libcore files inside the message - // or we end up with cargo-metadata output (https://github.com/rust-lang/rust-clippy/issues/6508) - - // filter out these message to avoid unnecessary noise in the logs - if line.contains("clippy::") - && !(line.contains("could not read cargo metadata") - || (line.contains(".rustup") && line.contains("toolchains"))) - { - return true; - } - false -} - /// Builds clippy inside the repo to make sure we have a clippy executable we can use. fn build_clippy() { let status = Command::new("cargo") @@ -527,10 +446,8 @@ fn read_crates(toml_path: &Path) -> Vec { path: PathBuf::from(path), options: tk.options.clone(), }); - } - - // if we have multiple versions, save each one - if let Some(ref versions) = tk.versions { + } else if let Some(ref versions) = tk.versions { + // if we have multiple versions, save each one versions.iter().for_each(|ver| { crate_sources.push(CrateSource::CratesIo { name: tk.name.clone(), @@ -538,16 +455,18 @@ fn read_crates(toml_path: &Path) -> Vec { options: tk.options.clone(), }); }) - } - // otherwise, we should have a git source - if tk.git_url.is_some() && tk.git_hash.is_some() { + } else if tk.git_url.is_some() && tk.git_hash.is_some() { + // otherwise, we should have a git source crate_sources.push(CrateSource::Git { name: tk.name.clone(), url: tk.git_url.clone().unwrap(), commit: tk.git_hash.clone().unwrap(), options: tk.options.clone(), }); + } else { + panic!("Invalid crate source: {tk:?}"); } + // if we have a version as well as a git data OR only one git data, something is funky if tk.versions.is_some() && (tk.git_url.is_some() || tk.git_hash.is_some()) || tk.git_hash.is_some() != tk.git_url.is_some() @@ -568,57 +487,13 @@ fn read_crates(toml_path: &Path) -> Vec { crate_sources } -/// Parse the json output of clippy and return a `ClippyWarning` -fn parse_json_message(json_message: &str, krate: &Crate) -> ClippyWarning { - let jmsg: Value = serde_json::from_str(&json_message).unwrap_or_else(|e| panic!("Failed to parse json:\n{:?}", e)); - - let file: String = jmsg["message"]["spans"][0]["file_name"] - .to_string() - .trim_matches('"') - .into(); - - let file = if file.contains(".cargo") { - // if we deal with macros, a filename may show the origin of a macro which can be inside a dep from - // the registry. - // don't show the full path in that case. - - // /home/matthias/.cargo/registry/src/github.aaakk.us.kg-1ecc6299db9ec823/syn-1.0.63/src/custom_keyword.rs - let path = PathBuf::from(file); - let mut piter = path.iter(); - // consume all elements until we find ".cargo", so that "/home/matthias" is skipped - let _: Option<&OsStr> = piter.find(|x| x == &std::ffi::OsString::from(".cargo")); - // collect the remaining segments - let file = piter.collect::(); - format!("{}", file.display()) - } else { - file - }; - - ClippyWarning { - crate_name: krate.name.to_string(), - crate_version: krate.version.to_string(), - file, - line: jmsg["message"]["spans"][0]["line_start"] - .to_string() - .trim_matches('"') - .into(), - column: jmsg["message"]["spans"][0]["text"][0]["highlight_start"] - .to_string() - .trim_matches('"') - .into(), - linttype: jmsg["message"]["code"]["code"].to_string().trim_matches('"').into(), - message: jmsg["message"]["message"].to_string().trim_matches('"').into(), - is_ice: json_message.contains("internal compiler error: "), - } -} - /// Generate a short list of occurring lints-types and their count fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String, usize>) { // count lint type occurrences let mut counter: HashMap<&String, usize> = HashMap::new(); clippy_warnings .iter() - .for_each(|wrn| *counter.entry(&wrn.linttype).or_insert(0) += 1); + .for_each(|wrn| *counter.entry(&wrn.lint_type).or_insert(0) += 1); // collect into a tupled list for sorting let mut stats: Vec<(&&String, &usize)> = counter.iter().map(|(lint, count)| (lint, count)).collect(); @@ -667,22 +542,14 @@ fn lintcheck_needs_rerun(lintcheck_logs_path: &Path) -> bool { logs_modified < clippy_modified } -/// lintchecks `main()` function -/// -/// # Panics -/// -/// This function panics if the clippy binaries don't exist -/// or if lintcheck is executed from the wrong directory (aka none-repo-root) -pub fn main() { +fn main() { // assert that we launch lintcheck from the repo root (via cargo lintcheck) if std::fs::metadata("lintcheck/Cargo.toml").is_err() { eprintln!("lintcheck needs to be run from clippys repo root!\nUse `cargo lintcheck` alternatively."); std::process::exit(3); } - let clap_config = &get_clap_config(); - - let config = LintcheckConfig::from_clap(clap_config); + let config = LintcheckConfig::new(); println!("Compiling clippy..."); build_clippy(); @@ -736,76 +603,46 @@ pub fn main() { }) .collect(); - let clippy_warnings: Vec = if let Some(only_one_crate) = clap_config.value_of("only") { - // if we don't have the specified crate in the .toml, throw an error - if !crates.iter().any(|krate| { - let name = match krate { - CrateSource::CratesIo { name, .. } | CrateSource::Git { name, .. } | CrateSource::Path { name, .. } => { - name - }, - }; - name == only_one_crate - }) { - eprintln!( - "ERROR: could not find crate '{}' in lintcheck/lintcheck_crates.toml", - only_one_crate - ); - std::process::exit(1); - } + let crates: Vec = crates + .into_iter() + .filter(|krate| { + if let Some(only_one_crate) = &config.only { + let name = match krate { + CrateSource::CratesIo { name, .. } + | CrateSource::Git { name, .. } + | CrateSource::Path { name, .. } => name, + }; - // only check a single crate that was passed via cmdline - crates - .into_iter() - .map(|krate| krate.download_and_extract()) - .filter(|krate| krate.name == only_one_crate) - .flat_map(|krate| { - krate.run_clippy_lints(&cargo_clippy_path, &AtomicUsize::new(0), 1, 1, config.fix, &lint_filter) - }) - .collect() - } else { - if config.max_jobs > 1 { - // run parallel with rayon - - // Ask rayon for thread count. Assume that half of that is the number of physical cores - // Use one target dir for each core so that we can run N clippys in parallel. - // We need to use different target dirs because cargo would lock them for a single build otherwise, - // killing the parallelism. However this also means that deps will only be reused half/a - // quarter of the time which might result in a longer wall clock runtime - - // This helps when we check many small crates with dep-trees that don't have a lot of branches in - // order to achieve some kind of parallelism - - // by default, use a single thread - let num_cpus = config.max_jobs; - let num_crates = crates.len(); - - // check all crates (default) - crates - .into_par_iter() - .map(|krate| krate.download_and_extract()) - .flat_map(|krate| { - krate.run_clippy_lints( - &cargo_clippy_path, - &counter, - num_cpus, - num_crates, - config.fix, - &lint_filter, - ) - }) - .collect() - } else { - // run sequential - let num_crates = crates.len(); - crates - .into_iter() - .map(|krate| krate.download_and_extract()) - .flat_map(|krate| { - krate.run_clippy_lints(&cargo_clippy_path, &counter, 1, num_crates, config.fix, &lint_filter) - }) - .collect() - } - }; + name == only_one_crate + } else { + true + } + }) + .map(|krate| krate.download_and_extract()) + .collect(); + + if crates.is_empty() { + eprintln!( + "ERROR: could not find crate '{}' in lintcheck/lintcheck_crates.toml", + config.only.unwrap(), + ); + std::process::exit(1); + } + + // run parallel with rayon + + // This helps when we check many small crates with dep-trees that don't have a lot of branches in + // order to achieve some kind of parallelism + + rayon::ThreadPoolBuilder::new() + .num_threads(config.max_jobs) + .build_global() + .unwrap(); + + let clippy_warnings: Vec = crates + .par_iter() + .flat_map(|krate| krate.run_clippy_lints(&cargo_clippy_path, &counter, crates.len(), &config, &lint_filter)) + .collect(); // if we are in --fix mode, don't change the log files, terminate here if config.fix { @@ -837,7 +674,7 @@ pub fn main() { text.push_str("| file | lint | message |\n"); text.push_str("| --- | --- | --- |\n"); } - write!(text, "{}", all_msgs.join("")); + write!(text, "{}", all_msgs.join("")).unwrap(); text.push_str("\n\n### ICEs:\n"); for (cratename, msg) in ices.iter() { let _ = write!(text, "{}: '{}'", cratename, msg); @@ -949,75 +786,10 @@ fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) { }); } -fn get_clap_config<'a>() -> ArgMatches<'a> { - App::new("lintcheck") - .about("run clippy on a set of crates and check output") - .arg( - Arg::with_name("only") - .takes_value(true) - .value_name("CRATE") - .long("only") - .help("only process a single crate of the list"), - ) - .arg( - Arg::with_name("crates-toml") - .takes_value(true) - .value_name("CRATES-SOURCES-TOML-PATH") - .long("crates-toml") - .help("set the path for a crates.toml where lintcheck should read the sources from"), - ) - .arg( - Arg::with_name("threads") - .takes_value(true) - .value_name("N") - .short("j") - .long("jobs") - .help("number of threads to use, 0 automatic choice"), - ) - .arg( - Arg::with_name("fix") - .long("--fix") - .help("runs cargo clippy --fix and checks if all suggestions apply"), - ) - .arg( - Arg::with_name("filter") - .long("--filter") - .takes_value(true) - .multiple(true) - .value_name("clippy_lint_name") - .help("apply a filter to only collect specified lints, this also overrides `allow` attributes"), - ) - .arg( - Arg::with_name("markdown") - .long("--markdown") - .help("change the reports table to use markdown links"), - ) - .get_matches() -} - /// Returns the path to the Clippy project directory -/// -/// # Panics -/// -/// Panics if the current directory could not be retrieved, there was an error reading any of the -/// Cargo.toml files or ancestor directory is the clippy root directory #[must_use] -pub fn clippy_project_root() -> PathBuf { - let current_dir = std::env::current_dir().unwrap(); - for path in current_dir.ancestors() { - let result = std::fs::read_to_string(path.join("Cargo.toml")); - if let Err(err) = &result { - if err.kind() == std::io::ErrorKind::NotFound { - continue; - } - } - - let content = result.unwrap(); - if content.contains("[package]\nname = \"clippy\"") { - return path.to_path_buf(); - } - } - panic!("error: Can't determine root of project. Please run inside a Clippy working dir."); +fn clippy_project_root() -> &'static Path { + Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap() } #[test] diff --git a/rust-toolchain b/rust-toolchain index 03acb51306d7..997e7ba9382b 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-05-05" +channel = "nightly-2022-05-19" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/main.rs b/src/main.rs index 240e233420f0..9ee4a40cbf24 100644 --- a/src/main.rs +++ b/src/main.rs @@ -123,8 +123,12 @@ impl ClippyCmd { .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); diff --git a/tests/dogfood.rs b/tests/dogfood.rs index eb97d1933d5d..9da80518ce91 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -21,7 +21,7 @@ fn dogfood_clippy() { // "" is the root package for package in &["", "clippy_dev", "clippy_lints", "clippy_utils", "rustc_tools_util"] { - run_clippy_for_package(package); + run_clippy_for_package(package, &[]); } } @@ -38,7 +38,7 @@ fn run_metadata_collection_lint() { // Run collection as is std::env::set_var("ENABLE_METADATA_COLLECTION", "1"); - run_clippy_for_package("clippy_lints"); + run_clippy_for_package("clippy_lints", &["-A", "unfulfilled_lint_expectations"]); // Check if cargo caching got in the way if let Ok(file) = File::open(metadata_output_path) { @@ -61,10 +61,10 @@ fn run_metadata_collection_lint() { .unwrap(); // Running the collection again - run_clippy_for_package("clippy_lints"); + run_clippy_for_package("clippy_lints", &["-A", "unfulfilled_lint_expectations"]); } -fn run_clippy_for_package(project: &str) { +fn run_clippy_for_package(project: &str, args: &[&str]) { let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let mut command = Command::new(&*test_utils::CARGO_CLIPPY_PATH); @@ -76,6 +76,7 @@ fn run_clippy_for_package(project: &str) { .arg("--all-targets") .arg("--all-features") .arg("--") + .args(args) .args(&["-D", "clippy::all"]) .args(&["-D", "clippy::pedantic"]) .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir diff --git a/tests/ui-cargo/duplicate_mod/fail/Cargo.toml b/tests/ui-cargo/duplicate_mod/fail/Cargo.toml new file mode 100644 index 000000000000..bf3c817de1c4 --- /dev/null +++ b/tests/ui-cargo/duplicate_mod/fail/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "duplicate_mod" +edition = "2021" +publish = false +version = "0.1.0" diff --git a/tests/ui-cargo/duplicate_mod/fail/src/a.rs b/tests/ui-cargo/duplicate_mod/fail/src/a.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/ui-cargo/duplicate_mod/fail/src/a.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/duplicate_mod/fail/src/b.rs b/tests/ui-cargo/duplicate_mod/fail/src/b.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/ui-cargo/duplicate_mod/fail/src/b.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/duplicate_mod/fail/src/c.rs b/tests/ui-cargo/duplicate_mod/fail/src/c.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/ui-cargo/duplicate_mod/fail/src/c.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/duplicate_mod/fail/src/from_other_module.rs b/tests/ui-cargo/duplicate_mod/fail/src/from_other_module.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/ui-cargo/duplicate_mod/fail/src/from_other_module.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/duplicate_mod/fail/src/main.rs b/tests/ui-cargo/duplicate_mod/fail/src/main.rs new file mode 100644 index 000000000000..79b343da2470 --- /dev/null +++ b/tests/ui-cargo/duplicate_mod/fail/src/main.rs @@ -0,0 +1,16 @@ +mod a; + +mod b; +#[path = "b.rs"] +mod b2; + +mod c; +#[path = "c.rs"] +mod c2; +#[path = "c.rs"] +mod c3; + +mod from_other_module; +mod other_module; + +fn main() {} diff --git a/tests/ui-cargo/duplicate_mod/fail/src/main.stderr b/tests/ui-cargo/duplicate_mod/fail/src/main.stderr new file mode 100644 index 000000000000..00d7739c8a2e --- /dev/null +++ b/tests/ui-cargo/duplicate_mod/fail/src/main.stderr @@ -0,0 +1,42 @@ +error: file is loaded as a module multiple times: `$DIR/b.rs` + --> $DIR/main.rs:3:1 + | +LL | mod b; + | ^^^^^^ first loaded here +LL | / #[path = "b.rs"] +LL | | mod b2; + | |_______^ loaded again here + | + = note: `-D clippy::duplicate-mod` implied by `-D warnings` + = help: replace all but one `mod` item with `use` items + +error: file is loaded as a module multiple times: `$DIR/c.rs` + --> $DIR/main.rs:7:1 + | +LL | mod c; + | ^^^^^^ first loaded here +LL | / #[path = "c.rs"] +LL | | mod c2; + | |_______^ loaded again here +LL | / #[path = "c.rs"] +LL | | mod c3; + | |_______^ loaded again here + | + = help: replace all but one `mod` item with `use` items + +error: file is loaded as a module multiple times: `$DIR/from_other_module.rs` + --> $DIR/main.rs:13:1 + | +LL | mod from_other_module; + | ^^^^^^^^^^^^^^^^^^^^^^ first loaded here + | + ::: $DIR/other_module/mod.rs:1:1 + | +LL | / #[path = "../from_other_module.rs"] +LL | | mod m; + | |______^ loaded again here + | + = help: replace all but one `mod` item with `use` items + +error: aborting due to 3 previous errors + diff --git a/tests/ui-cargo/duplicate_mod/fail/src/other_module/mod.rs b/tests/ui-cargo/duplicate_mod/fail/src/other_module/mod.rs new file mode 100644 index 000000000000..36ce7286aded --- /dev/null +++ b/tests/ui-cargo/duplicate_mod/fail/src/other_module/mod.rs @@ -0,0 +1,2 @@ +#[path = "../from_other_module.rs"] +mod m; diff --git a/tests/ui-toml/expect_used/clippy.toml b/tests/ui-toml/expect_used/clippy.toml new file mode 100644 index 000000000000..6933b8164195 --- /dev/null +++ b/tests/ui-toml/expect_used/clippy.toml @@ -0,0 +1 @@ +allow-expect-in-tests = true diff --git a/tests/ui-toml/expect_used/expect_used.rs b/tests/ui-toml/expect_used/expect_used.rs new file mode 100644 index 000000000000..22dcd3ae9d69 --- /dev/null +++ b/tests/ui-toml/expect_used/expect_used.rs @@ -0,0 +1,29 @@ +// compile-flags: --test +#![warn(clippy::expect_used)] + +fn expect_option() { + let opt = Some(0); + let _ = opt.expect(""); +} + +fn expect_result() { + let res: Result = Ok(0); + let _ = res.expect(""); +} + +fn main() { + expect_option(); + expect_result(); +} + +#[test] +fn test_expect_option() { + let opt = Some(0); + let _ = opt.expect(""); +} + +#[test] +fn test_expect_result() { + let res: Result = Ok(0); + let _ = res.expect(""); +} diff --git a/tests/ui-toml/expect_used/expect_used.stderr b/tests/ui-toml/expect_used/expect_used.stderr new file mode 100644 index 000000000000..9cb2199ed21c --- /dev/null +++ b/tests/ui-toml/expect_used/expect_used.stderr @@ -0,0 +1,19 @@ +error: used `expect()` on `an Option` value + --> $DIR/expect_used.rs:6:13 + | +LL | let _ = opt.expect(""); + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::expect-used` implied by `-D warnings` + = help: if this value is an `None`, it will panic + +error: used `expect()` on `a Result` value + --> $DIR/expect_used.rs:11:13 + | +LL | let _ = res.expect(""); + | ^^^^^^^^^^^^^^ + | + = help: if this value is an `Err`, it will panic + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 8701809b4daa..92838aa57dfd 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,43 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `await-holding-invalid-types`, `max-include-file-size`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of + allow-expect-in-tests + allow-unwrap-in-tests + allowed-scripts + 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-types + doc-valid-idents + enable-raw-pointer-heuristic-for-send + enforced-import-renames + enum-variant-name-threshold + enum-variant-size-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 diff --git a/tests/ui-toml/unwrap_used/clippy.toml b/tests/ui-toml/unwrap_used/clippy.toml new file mode 100644 index 000000000000..154626ef4e81 --- /dev/null +++ b/tests/ui-toml/unwrap_used/clippy.toml @@ -0,0 +1 @@ +allow-unwrap-in-tests = true diff --git a/tests/ui-toml/unwrap_used/unwrap_used.rs b/tests/ui-toml/unwrap_used/unwrap_used.rs new file mode 100644 index 000000000000..74d0d7c2650d --- /dev/null +++ b/tests/ui-toml/unwrap_used/unwrap_used.rs @@ -0,0 +1,73 @@ +// compile-flags: --test + +#![allow(unused_mut, clippy::from_iter_instead_of_collect)] +#![warn(clippy::unwrap_used)] +#![deny(clippy::get_unwrap)] + +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::collections::VecDeque; + +struct GetFalsePositive { + arr: [u32; 3], +} + +impl GetFalsePositive { + fn get(&self, pos: usize) -> Option<&u32> { + self.arr.get(pos) + } + fn get_mut(&mut self, pos: usize) -> Option<&mut u32> { + self.arr.get_mut(pos) + } +} + +fn main() { + let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); + let mut some_slice = &mut [0, 1, 2, 3]; + let mut some_vec = vec![0, 1, 2, 3]; + let mut some_vecdeque: VecDeque<_> = some_vec.iter().cloned().collect(); + let mut some_hashmap: HashMap = HashMap::from_iter(vec![(1, 'a'), (2, 'b')]); + let mut some_btreemap: BTreeMap = BTreeMap::from_iter(vec![(1, 'a'), (2, 'b')]); + let mut false_positive = GetFalsePositive { arr: [0, 1, 2] }; + + { + // Test `get().unwrap()` + let _ = boxed_slice.get(1).unwrap(); + let _ = some_slice.get(0).unwrap(); + let _ = some_vec.get(0).unwrap(); + let _ = some_vecdeque.get(0).unwrap(); + let _ = some_hashmap.get(&1).unwrap(); + let _ = some_btreemap.get(&1).unwrap(); + #[allow(clippy::unwrap_used)] + let _ = false_positive.get(0).unwrap(); + // Test with deref + let _: u8 = *boxed_slice.get(1).unwrap(); + } + + { + // Test `get_mut().unwrap()` + *boxed_slice.get_mut(0).unwrap() = 1; + *some_slice.get_mut(0).unwrap() = 1; + *some_vec.get_mut(0).unwrap() = 1; + *some_vecdeque.get_mut(0).unwrap() = 1; + // Check false positives + #[allow(clippy::unwrap_used)] + { + *some_hashmap.get_mut(&1).unwrap() = 'b'; + *some_btreemap.get_mut(&1).unwrap() = 'b'; + *false_positive.get_mut(0).unwrap() = 1; + } + } + + { + // Test `get().unwrap().foo()` and `get_mut().unwrap().bar()` + let _ = some_vec.get(0..1).unwrap().to_vec(); + let _ = some_vec.get_mut(0..1).unwrap().to_vec(); + } +} + +#[test] +fn test() { + let boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); + let _ = boxed_slice.get(1).unwrap(); +} diff --git a/tests/ui-toml/unwrap_used/unwrap_used.stderr b/tests/ui-toml/unwrap_used/unwrap_used.stderr new file mode 100644 index 000000000000..6bcfa0a8b564 --- /dev/null +++ b/tests/ui-toml/unwrap_used/unwrap_used.stderr @@ -0,0 +1,197 @@ +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:35:17 + | +LL | let _ = boxed_slice.get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]` + | +note: the lint level is defined here + --> $DIR/unwrap_used.rs:5:9 + | +LL | #![deny(clippy::get_unwrap)] + | ^^^^^^^^^^^^^^^^^^ + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:35:17 + | +LL | let _ = boxed_slice.get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unwrap-used` implied by `-D warnings` + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:36:17 + | +LL | let _ = some_slice.get(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:36:17 + | +LL | let _ = some_slice.get(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:37:17 + | +LL | let _ = some_vec.get(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:37:17 + | +LL | let _ = some_vec.get(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:38:17 + | +LL | let _ = some_vecdeque.get(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:38:17 + | +LL | let _ = some_vecdeque.get(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:39:17 + | +LL | let _ = some_hashmap.get(&1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:39:17 + | +LL | let _ = some_hashmap.get(&1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:40:17 + | +LL | let _ = some_btreemap.get(&1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:40:17 + | +LL | let _ = some_btreemap.get(&1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:44:21 + | +LL | let _: u8 = *boxed_slice.get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:44:22 + | +LL | let _: u8 = *boxed_slice.get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:49:9 + | +LL | *boxed_slice.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:49:10 + | +LL | *boxed_slice.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:50:9 + | +LL | *some_slice.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:50:10 + | +LL | *some_slice.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:51:9 + | +LL | *some_vec.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:51:10 + | +LL | *some_vec.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:52:9 + | +LL | *some_vecdeque.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:52:10 + | +LL | *some_vecdeque.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:64:17 + | +LL | let _ = some_vec.get(0..1).unwrap().to_vec(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:64:17 + | +LL | let _ = some_vec.get(0..1).unwrap().to_vec(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:65:17 + | +LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:65:17 + | +LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:72:13 + | +LL | let _ = boxed_slice.get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]` + +error: aborting due to 27 previous errors + diff --git a/tests/ui/absurd-extreme-comparisons.rs b/tests/ui/absurd-extreme-comparisons.rs index d205b383d1ff..f682b280c1b8 100644 --- a/tests/ui/absurd-extreme-comparisons.rs +++ b/tests/ui/absurd-extreme-comparisons.rs @@ -37,7 +37,7 @@ fn main() { use std::cmp::{Ordering, PartialEq, PartialOrd}; -#[derive(PartialEq, PartialOrd)] +#[derive(PartialEq, Eq, PartialOrd)] pub struct U(u64); impl PartialEq for U { diff --git a/tests/ui/assign_ops2.rs b/tests/ui/assign_ops2.rs index 4703a8c77778..f6d3a8fa3f0d 100644 --- a/tests/ui/assign_ops2.rs +++ b/tests/ui/assign_ops2.rs @@ -24,7 +24,7 @@ fn main() { use std::ops::{Mul, MulAssign}; -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct Wrap(i64); impl Mul for Wrap { diff --git a/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs b/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs index 26c88489b03c..a2ef0fe827c0 100644 --- a/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs +++ b/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs @@ -5,7 +5,6 @@ extern crate proc_macro; use proc_macro::{token_stream, Delimiter, Group, Ident, Span, TokenStream, TokenTree}; -use std::iter::FromIterator; fn read_ident(iter: &mut token_stream::IntoIter) -> Ident { match iter.next() { diff --git a/tests/ui/blacklisted_name.rs b/tests/ui/blacklisted_name.rs index 57d7139fef56..27df732a0880 100644 --- a/tests/ui/blacklisted_name.rs +++ b/tests/ui/blacklisted_name.rs @@ -46,10 +46,10 @@ fn issue_1647_ref_mut() { mod tests { fn issue_7305() { - // `blackisted_name` lint should not be triggered inside of the test code. + // `blacklisted_name` lint should not be triggered inside of the test code. let foo = 0; - // Check that even in nested functions warning is still not triggere. + // Check that even in nested functions warning is still not triggered. fn nested() { let foo = 0; } diff --git a/tests/ui/branches_sharing_code/shared_at_top.rs b/tests/ui/branches_sharing_code/shared_at_top.rs index 51a46481399b..bdeb0a39558d 100644 --- a/tests/ui/branches_sharing_code/shared_at_top.rs +++ b/tests/ui/branches_sharing_code/shared_at_top.rs @@ -1,4 +1,4 @@ -#![allow(dead_code, clippy::eval_order_dependence)] +#![allow(dead_code, clippy::mixed_read_write_in_expression)] #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] // This tests the branches_sharing_code lint at the start of blocks diff --git a/tests/ui/branches_sharing_code/valid_if_blocks.rs b/tests/ui/branches_sharing_code/valid_if_blocks.rs index 0c70e3748ec1..a26141be2373 100644 --- a/tests/ui/branches_sharing_code/valid_if_blocks.rs +++ b/tests/ui/branches_sharing_code/valid_if_blocks.rs @@ -1,4 +1,4 @@ -#![allow(dead_code, clippy::eval_order_dependence)] +#![allow(dead_code, clippy::mixed_read_write_in_expression)] #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] // This tests valid if blocks that shouldn't trigger the lint diff --git a/tests/ui/cast_size_32bit.stderr b/tests/ui/cast_size_32bit.stderr index 140676a5ffcf..7125f741c150 100644 --- a/tests/ui/cast_size_32bit.stderr +++ b/tests/ui/cast_size_32bit.stderr @@ -14,26 +14,12 @@ LL | x0 as f64; | = note: `-D clippy::cast-precision-loss` implied by `-D warnings` -error: casting `isize` to `f64` may become silently lossy if you later change the type - --> $DIR/cast_size_32bit.rs:15:5 - | -LL | x0 as f64; - | ^^^^^^^^^ help: try: `f64::from(x0)` - | - = note: `-D clippy::cast-lossless` implied by `-D warnings` - error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) --> $DIR/cast_size_32bit.rs:16:5 | LL | x1 as f64; | ^^^^^^^^^ -error: casting `usize` to `f64` may become silently lossy if you later change the type - --> $DIR/cast_size_32bit.rs:16:5 - | -LL | x1 as f64; - | ^^^^^^^^^ help: try: `f64::from(x1)` - error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) --> $DIR/cast_size_32bit.rs:17:5 | diff --git a/tests/ui/checked_conversions.fixed b/tests/ui/checked_conversions.fixed index 12290db3dcf5..0983d393b560 100644 --- a/tests/ui/checked_conversions.fixed +++ b/tests/ui/checked_conversions.fixed @@ -7,8 +7,6 @@ )] #![warn(clippy::checked_conversions)] -use std::convert::TryFrom; - // Positive tests // Signed to unsigned diff --git a/tests/ui/checked_conversions.rs b/tests/ui/checked_conversions.rs index 895a301e8212..7d26ace47fdf 100644 --- a/tests/ui/checked_conversions.rs +++ b/tests/ui/checked_conversions.rs @@ -7,8 +7,6 @@ )] #![warn(clippy::checked_conversions)] -use std::convert::TryFrom; - // Positive tests // Signed to unsigned diff --git a/tests/ui/checked_conversions.stderr b/tests/ui/checked_conversions.stderr index 18518def0acb..2e518040561c 100644 --- a/tests/ui/checked_conversions.stderr +++ b/tests/ui/checked_conversions.stderr @@ -1,5 +1,5 @@ error: checked cast can be simplified - --> $DIR/checked_conversions.rs:17:13 + --> $DIR/checked_conversions.rs:15:13 | LL | let _ = value <= (u32::max_value() as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` @@ -7,91 +7,91 @@ LL | let _ = value <= (u32::max_value() as i64) && value >= 0; = note: `-D clippy::checked-conversions` implied by `-D warnings` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:18:13 + --> $DIR/checked_conversions.rs:16:13 | LL | let _ = value <= (u32::MAX as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:22:13 + --> $DIR/checked_conversions.rs:20:13 | LL | let _ = value <= i64::from(u16::max_value()) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:23:13 + --> $DIR/checked_conversions.rs:21:13 | LL | let _ = value <= i64::from(u16::MAX) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:27:13 + --> $DIR/checked_conversions.rs:25:13 | LL | let _ = value <= (u8::max_value() as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:28:13 + --> $DIR/checked_conversions.rs:26:13 | LL | let _ = value <= (u8::MAX as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:34:13 + --> $DIR/checked_conversions.rs:32:13 | LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:35:13 + --> $DIR/checked_conversions.rs:33:13 | LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:39:13 + --> $DIR/checked_conversions.rs:37:13 | LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:40:13 + --> $DIR/checked_conversions.rs:38:13 | LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:46:13 + --> $DIR/checked_conversions.rs:44:13 | LL | let _ = value <= i32::max_value() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:47:13 + --> $DIR/checked_conversions.rs:45:13 | LL | let _ = value <= i32::MAX as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:51:13 + --> $DIR/checked_conversions.rs:49:13 | LL | let _ = value <= isize::max_value() as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:52:13 + --> $DIR/checked_conversions.rs:50:13 | LL | let _ = value <= isize::MAX as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:56:13 + --> $DIR/checked_conversions.rs:54:13 | LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:57:13 + --> $DIR/checked_conversions.rs:55:13 | LL | let _ = value <= u16::MAX as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` diff --git a/tests/ui/cmp_owned/asymmetric_partial_eq.fixed b/tests/ui/cmp_owned/asymmetric_partial_eq.fixed index 3305ac9bf8b6..abd059c23080 100644 --- a/tests/ui/cmp_owned/asymmetric_partial_eq.fixed +++ b/tests/ui/cmp_owned/asymmetric_partial_eq.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![allow(unused, clippy::redundant_clone)] // See #5700 +#![allow(unused, clippy::redundant_clone, clippy::derive_partial_eq_without_eq)] // See #5700 // Define the types in each module to avoid trait impls leaking between modules. macro_rules! impl_types { diff --git a/tests/ui/cmp_owned/asymmetric_partial_eq.rs b/tests/ui/cmp_owned/asymmetric_partial_eq.rs index 88bc2f51dd66..020ef5f840bd 100644 --- a/tests/ui/cmp_owned/asymmetric_partial_eq.rs +++ b/tests/ui/cmp_owned/asymmetric_partial_eq.rs @@ -1,5 +1,5 @@ // run-rustfix -#![allow(unused, clippy::redundant_clone)] // See #5700 +#![allow(unused, clippy::redundant_clone, clippy::derive_partial_eq_without_eq)] // See #5700 // Define the types in each module to avoid trait impls leaking between modules. macro_rules! impl_types { diff --git a/tests/ui/cmp_owned/with_suggestion.fixed b/tests/ui/cmp_owned/with_suggestion.fixed index 05fb96339e33..b28c4378e33c 100644 --- a/tests/ui/cmp_owned/with_suggestion.fixed +++ b/tests/ui/cmp_owned/with_suggestion.fixed @@ -45,7 +45,7 @@ impl ToOwned for Foo { } } -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] struct Bar; impl PartialEq for Bar { @@ -61,7 +61,7 @@ impl std::borrow::Borrow for Bar { } } -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] struct Baz; impl ToOwned for Baz { diff --git a/tests/ui/cmp_owned/with_suggestion.rs b/tests/ui/cmp_owned/with_suggestion.rs index 0a02825ed82f..c1089010fe10 100644 --- a/tests/ui/cmp_owned/with_suggestion.rs +++ b/tests/ui/cmp_owned/with_suggestion.rs @@ -45,7 +45,7 @@ impl ToOwned for Foo { } } -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] struct Bar; impl PartialEq for Bar { @@ -61,7 +61,7 @@ impl std::borrow::Borrow for Bar { } } -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] struct Baz; impl ToOwned for Baz { diff --git a/tests/ui/cmp_owned/without_suggestion.rs b/tests/ui/cmp_owned/without_suggestion.rs index f44a3901fb48..d8a202cb6a1c 100644 --- a/tests/ui/cmp_owned/without_suggestion.rs +++ b/tests/ui/cmp_owned/without_suggestion.rs @@ -9,6 +9,10 @@ fn main() { let x = &&Baz; let y = &Baz; y.to_owned() == **x; + + let x = 0u32; + let y = U32Wrapper(x); + let _ = U32Wrapper::from(x) == y; } struct Foo; @@ -26,7 +30,7 @@ impl ToOwned for Foo { } } -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] struct Baz; impl ToOwned for Baz { @@ -36,7 +40,7 @@ impl ToOwned for Baz { } } -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] struct Bar; impl PartialEq for Bar { @@ -51,3 +55,21 @@ impl std::borrow::Borrow for Bar { &FOO } } + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +struct U32Wrapper(u32); +impl From for U32Wrapper { + fn from(x: u32) -> Self { + Self(x) + } +} +impl PartialEq for U32Wrapper { + fn eq(&self, other: &u32) -> bool { + self.0 == *other + } +} +impl PartialEq for u32 { + fn eq(&self, other: &U32Wrapper) -> bool { + *self == other.0 + } +} diff --git a/tests/ui/cmp_owned/without_suggestion.stderr b/tests/ui/cmp_owned/without_suggestion.stderr index 2ea3d8fac0d1..d2dd14d8edbb 100644 --- a/tests/ui/cmp_owned/without_suggestion.stderr +++ b/tests/ui/cmp_owned/without_suggestion.stderr @@ -13,7 +13,7 @@ LL | y.to_owned() == **x; | ^^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating error: this creates an owned instance just for comparison - --> $DIR/without_suggestion.rs:18:9 + --> $DIR/without_suggestion.rs:22:9 | LL | self.to_owned() == *other | ^^^^^^^^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating diff --git a/tests/ui/crashes/auxiliary/proc_macro_crash.rs b/tests/ui/crashes/auxiliary/proc_macro_crash.rs index ed8e7a708a5e..5ff2af7cd825 100644 --- a/tests/ui/crashes/auxiliary/proc_macro_crash.rs +++ b/tests/ui/crashes/auxiliary/proc_macro_crash.rs @@ -12,7 +12,6 @@ extern crate proc_macro; use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree}; -use std::iter::FromIterator; #[proc_macro] pub fn macro_test(input_stream: TokenStream) -> TokenStream { diff --git a/tests/ui/crashes/ice-6254.rs b/tests/ui/crashes/ice-6254.rs index c19eca43884a..a2a60a169153 100644 --- a/tests/ui/crashes/ice-6254.rs +++ b/tests/ui/crashes/ice-6254.rs @@ -2,6 +2,7 @@ // panicked at 'assertion failed: rows.iter().all(|r| r.len() == v.len())', // compiler/rustc_mir_build/src/thir/pattern/_match.rs:2030:5 +#[allow(clippy::derive_partial_eq_without_eq)] #[derive(PartialEq)] struct Foo(i32); const FOO_REF_REF: &&Foo = &&Foo(42); diff --git a/tests/ui/crashes/ice-6254.stderr b/tests/ui/crashes/ice-6254.stderr index 95ebf23d8181..f37ab2e9b0c7 100644 --- a/tests/ui/crashes/ice-6254.stderr +++ b/tests/ui/crashes/ice-6254.stderr @@ -1,5 +1,5 @@ error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/ice-6254.rs:12:9 + --> $DIR/ice-6254.rs:13:9 | LL | FOO_REF_REF => {}, | ^^^^^^^^^^^ diff --git a/tests/ui/crashes/ice-8821.rs b/tests/ui/crashes/ice-8821.rs new file mode 100644 index 000000000000..fb87b79aeed8 --- /dev/null +++ b/tests/ui/crashes/ice-8821.rs @@ -0,0 +1,8 @@ +#![warn(clippy::let_unit_value)] + +fn f() {} +static FN: fn() = f; + +fn main() { + let _: () = FN(); +} diff --git a/tests/ui/crashes/ice-8821.stderr b/tests/ui/crashes/ice-8821.stderr new file mode 100644 index 000000000000..486096e0a06d --- /dev/null +++ b/tests/ui/crashes/ice-8821.stderr @@ -0,0 +1,10 @@ +error: this let-binding has unit value + --> $DIR/ice-8821.rs:7:5 + | +LL | let _: () = FN(); + | ^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `FN();` + | + = note: `-D clippy::let-unit-value` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/dbg_macro.rs b/tests/ui/dbg_macro.rs index baf01174b676..25294e8c766f 100644 --- a/tests/ui/dbg_macro.rs +++ b/tests/ui/dbg_macro.rs @@ -46,3 +46,15 @@ mod issue7274 { pub fn issue8481() { dbg!(1); } + +#[cfg(test)] +fn foo2() { + dbg!(1); +} + +#[cfg(test)] +mod mod1 { + fn func() { + dbg!(1); + } +} diff --git a/tests/ui/derive_hash_xor_eq.rs b/tests/ui/derive_hash_xor_eq.rs index 10abe22d53e5..813ddc566464 100644 --- a/tests/ui/derive_hash_xor_eq.rs +++ b/tests/ui/derive_hash_xor_eq.rs @@ -1,3 +1,5 @@ +#![allow(clippy::derive_partial_eq_without_eq)] + #[derive(PartialEq, Hash)] struct Foo; diff --git a/tests/ui/derive_hash_xor_eq.stderr b/tests/ui/derive_hash_xor_eq.stderr index b383072ca4db..e5184bd1407c 100644 --- a/tests/ui/derive_hash_xor_eq.stderr +++ b/tests/ui/derive_hash_xor_eq.stderr @@ -1,12 +1,12 @@ error: you are deriving `Hash` but have implemented `PartialEq` explicitly - --> $DIR/derive_hash_xor_eq.rs:10:10 + --> $DIR/derive_hash_xor_eq.rs:12:10 | LL | #[derive(Hash)] | ^^^^ | = note: `#[deny(clippy::derive_hash_xor_eq)]` on by default note: `PartialEq` implemented here - --> $DIR/derive_hash_xor_eq.rs:13:1 + --> $DIR/derive_hash_xor_eq.rs:15:1 | LL | / impl PartialEq for Bar { LL | | fn eq(&self, _: &Bar) -> bool { @@ -17,13 +17,13 @@ LL | | } = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) error: you are deriving `Hash` but have implemented `PartialEq` explicitly - --> $DIR/derive_hash_xor_eq.rs:19:10 + --> $DIR/derive_hash_xor_eq.rs:21:10 | LL | #[derive(Hash)] | ^^^^ | note: `PartialEq` implemented here - --> $DIR/derive_hash_xor_eq.rs:22:1 + --> $DIR/derive_hash_xor_eq.rs:24:1 | LL | / impl PartialEq for Baz { LL | | fn eq(&self, _: &Baz) -> bool { @@ -34,7 +34,7 @@ LL | | } = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) error: you are implementing `Hash` explicitly but have derived `PartialEq` - --> $DIR/derive_hash_xor_eq.rs:31:1 + --> $DIR/derive_hash_xor_eq.rs:33:1 | LL | / impl std::hash::Hash for Bah { LL | | fn hash(&self, _: &mut H) {} @@ -42,14 +42,14 @@ LL | | } | |_^ | note: `PartialEq` implemented here - --> $DIR/derive_hash_xor_eq.rs:28:10 + --> $DIR/derive_hash_xor_eq.rs:30:10 | LL | #[derive(PartialEq)] | ^^^^^^^^^ = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) error: you are implementing `Hash` explicitly but have derived `PartialEq` - --> $DIR/derive_hash_xor_eq.rs:49:5 + --> $DIR/derive_hash_xor_eq.rs:51:5 | LL | / impl Hash for Foo3 { LL | | fn hash(&self, _: &mut H) {} @@ -57,7 +57,7 @@ LL | | } | |_____^ | note: `PartialEq` implemented here - --> $DIR/derive_hash_xor_eq.rs:46:14 + --> $DIR/derive_hash_xor_eq.rs:48:14 | LL | #[derive(PartialEq)] | ^^^^^^^^^ diff --git a/tests/ui/derive_partial_eq_without_eq.fixed b/tests/ui/derive_partial_eq_without_eq.fixed new file mode 100644 index 000000000000..7d4d1b3b6490 --- /dev/null +++ b/tests/ui/derive_partial_eq_without_eq.fixed @@ -0,0 +1,98 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::derive_partial_eq_without_eq)] + +// Don't warn on structs that aren't PartialEq +struct NotPartialEq { + foo: u32, + bar: String, +} + +// Eq can be derived but is missing +#[derive(Debug, PartialEq, Eq)] +struct MissingEq { + foo: u32, + bar: String, +} + +// Eq is derived +#[derive(PartialEq, Eq)] +struct NotMissingEq { + foo: u32, + bar: String, +} + +// Eq is manually implemented +#[derive(PartialEq)] +struct ManualEqImpl { + foo: u32, + bar: String, +} + +impl Eq for ManualEqImpl {} + +// Cannot be Eq because f32 isn't Eq +#[derive(PartialEq)] +struct CannotBeEq { + foo: u32, + bar: f32, +} + +// Don't warn if PartialEq is manually implemented +struct ManualPartialEqImpl { + foo: u32, + bar: String, +} + +impl PartialEq for ManualPartialEqImpl { + fn eq(&self, other: &Self) -> bool { + self.foo == other.foo && self.bar == other.bar + } +} + +// Generic fields should be properly checked for Eq-ness +#[derive(PartialEq)] +struct GenericNotEq { + foo: T, + bar: U, +} + +#[derive(PartialEq, Eq)] +struct GenericEq { + foo: T, + bar: U, +} + +#[derive(PartialEq, Eq)] +struct TupleStruct(u32); + +#[derive(PartialEq, Eq)] +struct GenericTupleStruct(T); + +#[derive(PartialEq)] +struct TupleStructNotEq(f32); + +#[derive(PartialEq, Eq)] +enum Enum { + Foo(u32), + Bar { a: String, b: () }, +} + +#[derive(PartialEq, Eq)] +enum GenericEnum { + Foo(T), + Bar { a: U, b: V }, +} + +#[derive(PartialEq)] +enum EnumNotEq { + Foo(u32), + Bar { a: String, b: f32 }, +} + +// Ensure that rustfix works properly when `PartialEq` has other derives on either side +#[derive(Debug, PartialEq, Eq, Clone)] +struct RustFixWithOtherDerives; + +fn main() {} diff --git a/tests/ui/derive_partial_eq_without_eq.rs b/tests/ui/derive_partial_eq_without_eq.rs new file mode 100644 index 000000000000..ab4e1df1ca40 --- /dev/null +++ b/tests/ui/derive_partial_eq_without_eq.rs @@ -0,0 +1,98 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::derive_partial_eq_without_eq)] + +// Don't warn on structs that aren't PartialEq +struct NotPartialEq { + foo: u32, + bar: String, +} + +// Eq can be derived but is missing +#[derive(Debug, PartialEq)] +struct MissingEq { + foo: u32, + bar: String, +} + +// Eq is derived +#[derive(PartialEq, Eq)] +struct NotMissingEq { + foo: u32, + bar: String, +} + +// Eq is manually implemented +#[derive(PartialEq)] +struct ManualEqImpl { + foo: u32, + bar: String, +} + +impl Eq for ManualEqImpl {} + +// Cannot be Eq because f32 isn't Eq +#[derive(PartialEq)] +struct CannotBeEq { + foo: u32, + bar: f32, +} + +// Don't warn if PartialEq is manually implemented +struct ManualPartialEqImpl { + foo: u32, + bar: String, +} + +impl PartialEq for ManualPartialEqImpl { + fn eq(&self, other: &Self) -> bool { + self.foo == other.foo && self.bar == other.bar + } +} + +// Generic fields should be properly checked for Eq-ness +#[derive(PartialEq)] +struct GenericNotEq { + foo: T, + bar: U, +} + +#[derive(PartialEq)] +struct GenericEq { + foo: T, + bar: U, +} + +#[derive(PartialEq)] +struct TupleStruct(u32); + +#[derive(PartialEq)] +struct GenericTupleStruct(T); + +#[derive(PartialEq)] +struct TupleStructNotEq(f32); + +#[derive(PartialEq)] +enum Enum { + Foo(u32), + Bar { a: String, b: () }, +} + +#[derive(PartialEq)] +enum GenericEnum { + Foo(T), + Bar { a: U, b: V }, +} + +#[derive(PartialEq)] +enum EnumNotEq { + Foo(u32), + Bar { a: String, b: f32 }, +} + +// Ensure that rustfix works properly when `PartialEq` has other derives on either side +#[derive(Debug, PartialEq, Clone)] +struct RustFixWithOtherDerives; + +fn main() {} diff --git a/tests/ui/derive_partial_eq_without_eq.stderr b/tests/ui/derive_partial_eq_without_eq.stderr new file mode 100644 index 000000000000..bf55165890a5 --- /dev/null +++ b/tests/ui/derive_partial_eq_without_eq.stderr @@ -0,0 +1,46 @@ +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:13:17 + | +LL | #[derive(Debug, PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + | + = note: `-D clippy::derive-partial-eq-without-eq` implied by `-D warnings` + +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:61:10 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:67:10 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:70:10 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:76:10 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:82:10 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:95:17 + | +LL | #[derive(Debug, PartialEq, Clone)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: aborting due to 7 previous errors + diff --git a/tests/ui/equatable_if_let.fixed b/tests/ui/equatable_if_let.fixed index 88918d9671e4..47bf25e409bd 100644 --- a/tests/ui/equatable_if_let.fixed +++ b/tests/ui/equatable_if_let.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_variables, dead_code)] +#![allow(unused_variables, dead_code, clippy::derive_partial_eq_without_eq)] #![warn(clippy::equatable_if_let)] use std::cmp::Ordering; diff --git a/tests/ui/equatable_if_let.rs b/tests/ui/equatable_if_let.rs index 9a7ab75ef450..d498bca2455b 100644 --- a/tests/ui/equatable_if_let.rs +++ b/tests/ui/equatable_if_let.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_variables, dead_code)] +#![allow(unused_variables, dead_code, clippy::derive_partial_eq_without_eq)] #![warn(clippy::equatable_if_let)] use std::cmp::Ordering; diff --git a/tests/ui/expect_tool_lint_rfc_2383.rs b/tests/ui/expect_tool_lint_rfc_2383.rs index 28b37f96e911..985835ffa653 100644 --- a/tests/ui/expect_tool_lint_rfc_2383.rs +++ b/tests/ui/expect_tool_lint_rfc_2383.rs @@ -22,9 +22,9 @@ mod rustc_ok { #[expect(illegal_floating_point_literal_pattern)] match x { - 5.0 => {} - 6.0 => {} - _ => {} + 5.0 => {}, + 6.0 => {}, + _ => {}, } } } @@ -38,9 +38,9 @@ mod rustc_warn { #[expect(illegal_floating_point_literal_pattern)] match x { - 5 => {} - 6 => {} - _ => {} + 5 => {}, + 6 => {}, + _ => {}, } } } diff --git a/tests/ui/from_iter_instead_of_collect.fixed b/tests/ui/from_iter_instead_of_collect.fixed index 12db43b53436..403c3b3e4438 100644 --- a/tests/ui/from_iter_instead_of_collect.fixed +++ b/tests/ui/from_iter_instead_of_collect.fixed @@ -4,7 +4,6 @@ #![allow(unused_imports)] use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque}; -use std::iter::FromIterator; struct Foo(Vec); diff --git a/tests/ui/from_iter_instead_of_collect.rs b/tests/ui/from_iter_instead_of_collect.rs index f5ec190e0cdc..fefc7b01a65b 100644 --- a/tests/ui/from_iter_instead_of_collect.rs +++ b/tests/ui/from_iter_instead_of_collect.rs @@ -4,7 +4,6 @@ #![allow(unused_imports)] use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque}; -use std::iter::FromIterator; struct Foo(Vec); diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr index 8f08ac8c3ff4..8aa3c3c01f81 100644 --- a/tests/ui/from_iter_instead_of_collect.stderr +++ b/tests/ui/from_iter_instead_of_collect.stderr @@ -1,5 +1,5 @@ error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:19:9 + --> $DIR/from_iter_instead_of_collect.rs:18:9 | LL | >::from_iter(iter.into_iter().copied()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.into_iter().copied().collect::()` @@ -7,85 +7,85 @@ LL | >::from_iter(iter.into_iter().copied()) = note: `-D clippy::from-iter-instead-of-collect` implied by `-D warnings` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:25:13 + --> $DIR/from_iter_instead_of_collect.rs:24:13 | LL | let _ = Vec::from_iter(iter_expr); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter_expr.collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:27:13 + --> $DIR/from_iter_instead_of_collect.rs:26:13 | LL | let _ = HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `vec![5, 5, 5, 5].iter().enumerate().collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:32:19 + --> $DIR/from_iter_instead_of_collect.rs:31:19 | LL | assert_eq!(a, Vec::from_iter(0..3)); | ^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:33:19 + --> $DIR/from_iter_instead_of_collect.rs:32:19 | LL | assert_eq!(a, Vec::::from_iter(0..3)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:35:17 + --> $DIR/from_iter_instead_of_collect.rs:34:17 | LL | let mut b = VecDeque::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:38:17 + --> $DIR/from_iter_instead_of_collect.rs:37:17 | LL | let mut b = VecDeque::::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:43:21 + --> $DIR/from_iter_instead_of_collect.rs:42:21 | LL | let mut b = collections::VecDeque::::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:48:14 + --> $DIR/from_iter_instead_of_collect.rs:47:14 | LL | let bm = BTreeMap::from_iter(values.iter().cloned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `values.iter().cloned().collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:49:19 + --> $DIR/from_iter_instead_of_collect.rs:48:19 | LL | let mut bar = BTreeMap::from_iter(bm.range(0..2)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `bm.range(0..2).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:52:19 + --> $DIR/from_iter_instead_of_collect.rs:51:19 | LL | let mut bts = BTreeSet::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:56:17 + --> $DIR/from_iter_instead_of_collect.rs:55:17 | LL | let _ = collections::BTreeSet::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:57:17 + --> $DIR/from_iter_instead_of_collect.rs:56:17 | LL | let _ = collections::BTreeSet::::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:60:15 + --> $DIR/from_iter_instead_of_collect.rs:59:15 | LL | for _i in Vec::from_iter([1, 2, 3].iter()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `[1, 2, 3].iter().collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:61:15 + --> $DIR/from_iter_instead_of_collect.rs:60:15 | LL | for _i in Vec::<&i32>::from_iter([1, 2, 3].iter()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `[1, 2, 3].iter().collect::>()` diff --git a/tests/ui/get_unwrap.fixed b/tests/ui/get_unwrap.fixed index c3a36dcabd1a..8f165d675890 100644 --- a/tests/ui/get_unwrap.fixed +++ b/tests/ui/get_unwrap.fixed @@ -7,7 +7,6 @@ use std::collections::BTreeMap; use std::collections::HashMap; use std::collections::VecDeque; -use std::iter::FromIterator; struct GetFalsePositive { arr: [u32; 3], diff --git a/tests/ui/get_unwrap.rs b/tests/ui/get_unwrap.rs index d77a202aa39c..786749daa746 100644 --- a/tests/ui/get_unwrap.rs +++ b/tests/ui/get_unwrap.rs @@ -7,7 +7,6 @@ use std::collections::BTreeMap; use std::collections::HashMap; use std::collections::VecDeque; -use std::iter::FromIterator; struct GetFalsePositive { arr: [u32; 3], diff --git a/tests/ui/get_unwrap.stderr b/tests/ui/get_unwrap.stderr index cb5f44fbd59e..ea8fec527351 100644 --- a/tests/ui/get_unwrap.stderr +++ b/tests/ui/get_unwrap.stderr @@ -1,5 +1,5 @@ error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:36:17 + --> $DIR/get_unwrap.rs:35:17 | LL | let _ = boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]` @@ -11,7 +11,7 @@ LL | #![deny(clippy::get_unwrap)] | ^^^^^^^^^^^^^^^^^^ error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:36:17 + --> $DIR/get_unwrap.rs:35:17 | LL | let _ = boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,13 +20,13 @@ LL | let _ = boxed_slice.get(1).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:37:17 + --> $DIR/get_unwrap.rs:36:17 | LL | let _ = some_slice.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:37:17 + --> $DIR/get_unwrap.rs:36:17 | LL | let _ = some_slice.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -34,13 +34,13 @@ LL | let _ = some_slice.get(0).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:38:17 + --> $DIR/get_unwrap.rs:37:17 | LL | let _ = some_vec.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:38:17 + --> $DIR/get_unwrap.rs:37:17 | LL | let _ = some_vec.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,13 +48,13 @@ LL | let _ = some_vec.get(0).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:39:17 + --> $DIR/get_unwrap.rs:38:17 | LL | let _ = some_vecdeque.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:39:17 + --> $DIR/get_unwrap.rs:38:17 | LL | let _ = some_vecdeque.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,13 +62,13 @@ LL | let _ = some_vecdeque.get(0).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:40:17 + --> $DIR/get_unwrap.rs:39:17 | LL | let _ = some_hashmap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:40:17 + --> $DIR/get_unwrap.rs:39:17 | LL | let _ = some_hashmap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -76,13 +76,13 @@ LL | let _ = some_hashmap.get(&1).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:41:17 + --> $DIR/get_unwrap.rs:40:17 | LL | let _ = some_btreemap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:41:17 + --> $DIR/get_unwrap.rs:40:17 | LL | let _ = some_btreemap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -90,13 +90,13 @@ LL | let _ = some_btreemap.get(&1).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:45:21 + --> $DIR/get_unwrap.rs:44:21 | LL | let _: u8 = *boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:45:22 + --> $DIR/get_unwrap.rs:44:22 | LL | let _: u8 = *boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -104,13 +104,13 @@ LL | let _: u8 = *boxed_slice.get(1).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:50:9 + --> $DIR/get_unwrap.rs:49:9 | LL | *boxed_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:50:10 + --> $DIR/get_unwrap.rs:49:10 | LL | *boxed_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -118,13 +118,13 @@ LL | *boxed_slice.get_mut(0).unwrap() = 1; = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:51:9 + --> $DIR/get_unwrap.rs:50:9 | LL | *some_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:51:10 + --> $DIR/get_unwrap.rs:50:10 | LL | *some_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,13 +132,13 @@ LL | *some_slice.get_mut(0).unwrap() = 1; = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:52:9 + --> $DIR/get_unwrap.rs:51:9 | LL | *some_vec.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:52:10 + --> $DIR/get_unwrap.rs:51:10 | LL | *some_vec.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -146,13 +146,13 @@ LL | *some_vec.get_mut(0).unwrap() = 1; = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:53:9 + --> $DIR/get_unwrap.rs:52:9 | LL | *some_vecdeque.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:53:10 + --> $DIR/get_unwrap.rs:52:10 | LL | *some_vecdeque.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -160,13 +160,13 @@ LL | *some_vecdeque.get_mut(0).unwrap() = 1; = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:65:17 + --> $DIR/get_unwrap.rs:64:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:65:17 + --> $DIR/get_unwrap.rs:64:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -174,13 +174,13 @@ LL | let _ = some_vec.get(0..1).unwrap().to_vec(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:66:17 + --> $DIR/get_unwrap.rs:65:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:66:17 + --> $DIR/get_unwrap.rs:65:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/infinite_iter.rs b/tests/ui/infinite_iter.rs index 1fe688977659..a1e5fad0c621 100644 --- a/tests/ui/infinite_iter.rs +++ b/tests/ui/infinite_iter.rs @@ -51,7 +51,6 @@ fn main() { mod finite_collect { use std::collections::HashSet; - use std::iter::FromIterator; struct C; impl FromIterator for C { diff --git a/tests/ui/infinite_iter.stderr b/tests/ui/infinite_iter.stderr index 5f5e7ac9f253..ba277e36339a 100644 --- a/tests/ui/infinite_iter.stderr +++ b/tests/ui/infinite_iter.stderr @@ -98,7 +98,7 @@ LL | (0..).all(|x| x == 24); // maybe infinite iter | ^^^^^^^^^^^^^^^^^^^^^^ error: infinite iteration detected - --> $DIR/infinite_iter.rs:64:31 + --> $DIR/infinite_iter.rs:63:31 | LL | let _: HashSet = (0..).collect(); // Infinite iter | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/manual_str_repeat.fixed b/tests/ui/manual_str_repeat.fixed index dc140257f321..0704ba2f933e 100644 --- a/tests/ui/manual_str_repeat.fixed +++ b/tests/ui/manual_str_repeat.fixed @@ -4,7 +4,7 @@ #![warn(clippy::manual_str_repeat)] use std::borrow::Cow; -use std::iter::{repeat, FromIterator}; +use std::iter::repeat; fn main() { let _: String = "test".repeat(10); diff --git a/tests/ui/manual_str_repeat.rs b/tests/ui/manual_str_repeat.rs index 0d69c989b2ed..f522be439aa0 100644 --- a/tests/ui/manual_str_repeat.rs +++ b/tests/ui/manual_str_repeat.rs @@ -4,7 +4,7 @@ #![warn(clippy::manual_str_repeat)] use std::borrow::Cow; -use std::iter::{repeat, FromIterator}; +use std::iter::repeat; fn main() { let _: String = std::iter::repeat("test").take(10).collect(); diff --git a/tests/ui/map_err.rs b/tests/ui/map_err.rs index 00e037843f8c..bb35ab1a14ef 100644 --- a/tests/ui/map_err.rs +++ b/tests/ui/map_err.rs @@ -1,6 +1,5 @@ #![warn(clippy::map_err_ignore)] #![allow(clippy::unnecessary_wraps)] -use std::convert::TryFrom; use std::error::Error; use std::fmt; diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr index 37e87e64de28..c035840521e4 100644 --- a/tests/ui/map_err.stderr +++ b/tests/ui/map_err.stderr @@ -1,5 +1,5 @@ error: `map_err(|_|...` wildcard pattern discards the original error - --> $DIR/map_err.rs:23:32 + --> $DIR/map_err.rs:22:32 | LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); | ^^^ diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index b8dc8179f7d7..de46e6cff55b 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -111,3 +111,16 @@ fn main() { let x = 1; println!("Not an array index start"); } + +#[allow(dead_code)] +fn issue_8723() { + let (mut val, idx) = ("a b", 1); + + let (pre, suf) = val.split_at(idx); + val = { + println!("{}", pre); + suf + }; + + let _ = val; +} diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index fe63dcd63f2b..eea64fcb292b 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -126,3 +126,17 @@ fn main() { _ => println!("Not an array index start"), } } + +#[allow(dead_code)] +fn issue_8723() { + let (mut val, idx) = ("a b", 1); + + val = match val.split_at(idx) { + (pre, suf) => { + println!("{}", pre); + suf + }, + }; + + let _ = val; +} diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index d939291f53c4..5d4e7314b213 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -9,7 +9,7 @@ LL | | } | |_____^ | = note: `-D clippy::match-single-binding` implied by `-D warnings` -help: consider using `let` statement +help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); LL + { @@ -25,7 +25,7 @@ LL | | (x, y, z) => println!("{} {} {}", x, y, z), LL | | } | |_____^ | -help: consider using `let` statement +help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); LL + println!("{} {} {}", x, y, z); @@ -88,7 +88,7 @@ LL | | Point { x, y } => println!("Coords: ({}, {})", x, y), LL | | } | |_____^ | -help: consider using `let` statement +help: consider using a `let` statement | LL ~ let Point { x, y } = p; LL + println!("Coords: ({}, {})", x, y); @@ -102,7 +102,7 @@ LL | | Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1), LL | | } | |_____^ | -help: consider using `let` statement +help: consider using a `let` statement | LL ~ let Point { x: x1, y: y1 } = p; LL + println!("Coords: ({}, {})", x1, y1); @@ -116,7 +116,7 @@ LL | | ref r => println!("Got a reference to {}", r), LL | | } | |_____^ | -help: consider using `let` statement +help: consider using a `let` statement | LL ~ let ref r = x; LL + println!("Got a reference to {}", r); @@ -130,7 +130,7 @@ LL | | ref mut mr => println!("Got a mutable reference to {}", mr), LL | | } | |_____^ | -help: consider using `let` statement +help: consider using a `let` statement | LL ~ let ref mut mr = x; LL + println!("Got a mutable reference to {}", mr); @@ -144,7 +144,7 @@ LL | | Point { x, y } => x * y, LL | | }; | |______^ | -help: consider using `let` statement +help: consider using a `let` statement | LL ~ let Point { x, y } = coords(); LL + let product = x * y; @@ -159,7 +159,7 @@ LL | | unwrapped => unwrapped, LL | | }) | |_________^ | -help: consider using `let` statement +help: consider using a `let` statement | LL ~ .map(|i| { LL + let unwrapped = i.unwrap(); @@ -176,5 +176,25 @@ LL | | _ => println!("Not an array index start"), LL | | } | |_____^ help: consider using the match body instead: `println!("Not an array index start");` -error: aborting due to 12 previous errors +error: this assignment could be simplified + --> $DIR/match_single_binding.rs:134:5 + | +LL | / val = match val.split_at(idx) { +LL | | (pre, suf) => { +LL | | println!("{}", pre); +LL | | suf +LL | | }, +LL | | }; + | |_____^ + | +help: consider removing the `match` expression + | +LL ~ let (pre, suf) = val.split_at(idx); +LL + val = { +LL + println!("{}", pre); +LL + suf +LL ~ }; + | + +error: aborting due to 13 previous errors diff --git a/tests/ui/match_single_binding2.stderr b/tests/ui/match_single_binding2.stderr index d34933194660..22bf7d8be4a2 100644 --- a/tests/ui/match_single_binding2.stderr +++ b/tests/ui/match_single_binding2.stderr @@ -8,7 +8,7 @@ LL | | }, | |_____________^ | = note: `-D clippy::match-single-binding` implied by `-D warnings` -help: consider using `let` statement +help: consider using a `let` statement | LL ~ Some((iter, _item)) => { LL + let (min, max) = iter.size_hint(); @@ -24,7 +24,7 @@ LL | | (a, b) => println!("a {:?} and b {:?}", a, b), LL | | } | |_____________^ | -help: consider using `let` statement +help: consider using a `let` statement | LL ~ let (a, b) = get_tup(); LL + println!("a {:?} and b {:?}", a, b); diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index 977ce54327b3..9805097084d3 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -26,7 +26,6 @@ use std::collections::BTreeMap; use std::collections::HashMap; use std::collections::HashSet; use std::collections::VecDeque; -use std::iter::FromIterator; use std::ops::Mul; use std::rc::{self, Rc}; use std::sync::{self, Arc}; diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index b63672dd6fdb..6be38b24fbda 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -1,5 +1,5 @@ error: methods called `new` usually return `Self` - --> $DIR/methods.rs:104:5 + --> $DIR/methods.rs:103:5 | LL | / fn new() -> i32 { LL | | 0 @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::new-ret-no-self` implied by `-D warnings` error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead - --> $DIR/methods.rs:125:13 + --> $DIR/methods.rs:124:13 | LL | let _ = v.iter().filter(|&x| { | _____________^ diff --git a/tests/ui/eval_order_dependence.rs b/tests/ui/mixed_read_write_in_expression.rs similarity index 97% rename from tests/ui/eval_order_dependence.rs rename to tests/ui/mixed_read_write_in_expression.rs index aad78319d482..7640057ab6e3 100644 --- a/tests/ui/eval_order_dependence.rs +++ b/tests/ui/mixed_read_write_in_expression.rs @@ -1,4 +1,4 @@ -#[warn(clippy::eval_order_dependence)] +#[warn(clippy::mixed_read_write_in_expression)] #[allow( unused_assignments, unused_variables, diff --git a/tests/ui/eval_order_dependence.stderr b/tests/ui/mixed_read_write_in_expression.stderr similarity index 63% rename from tests/ui/eval_order_dependence.stderr rename to tests/ui/mixed_read_write_in_expression.stderr index 7c6265a08790..2e951cdbcbfd 100644 --- a/tests/ui/eval_order_dependence.stderr +++ b/tests/ui/mixed_read_write_in_expression.stderr @@ -1,48 +1,48 @@ error: unsequenced read of `x` - --> $DIR/eval_order_dependence.rs:14:9 + --> $DIR/mixed_read_write_in_expression.rs:14:9 | LL | } + x; | ^ | - = note: `-D clippy::eval-order-dependence` implied by `-D warnings` + = note: `-D clippy::mixed-read-write-in-expression` implied by `-D warnings` note: whether read occurs before this write depends on evaluation order - --> $DIR/eval_order_dependence.rs:12:9 + --> $DIR/mixed_read_write_in_expression.rs:12:9 | LL | x = 1; | ^^^^^ error: unsequenced read of `x` - --> $DIR/eval_order_dependence.rs:17:5 + --> $DIR/mixed_read_write_in_expression.rs:17:5 | LL | x += { | ^ | note: whether read occurs before this write depends on evaluation order - --> $DIR/eval_order_dependence.rs:18:9 + --> $DIR/mixed_read_write_in_expression.rs:18:9 | LL | x = 20; | ^^^^^^ error: unsequenced read of `x` - --> $DIR/eval_order_dependence.rs:30:12 + --> $DIR/mixed_read_write_in_expression.rs:30:12 | LL | a: x, | ^ | note: whether read occurs before this write depends on evaluation order - --> $DIR/eval_order_dependence.rs:32:13 + --> $DIR/mixed_read_write_in_expression.rs:32:13 | LL | x = 6; | ^^^^^ error: unsequenced read of `x` - --> $DIR/eval_order_dependence.rs:39:9 + --> $DIR/mixed_read_write_in_expression.rs:39:9 | LL | x += { | ^ | note: whether read occurs before this write depends on evaluation order - --> $DIR/eval_order_dependence.rs:40:13 + --> $DIR/mixed_read_write_in_expression.rs:40:13 | LL | x = 20; | ^^^^^^ diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed index 47c974e614b9..f49771997117 100644 --- a/tests/ui/range_contains.fixed +++ b/tests/ui/range_contains.fixed @@ -6,7 +6,7 @@ #[allow(clippy::short_circuit_statement)] #[allow(clippy::unnecessary_operation)] fn main() { - let x = 9_u32; + let x = 9_i32; // order shouldn't matter (8..12).contains(&x); @@ -43,6 +43,12 @@ fn main() { let y = 3.; (0. ..1.).contains(&y); !(0. ..=1.).contains(&y); + + // handle negatives #8721 + (-10..=10).contains(&x); + x >= 10 && x <= -10; + (-3. ..=3.).contains(&y); + y >= 3. && y <= -3.; } // Fix #6373 diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs index 835deced5e4c..9e2180b0c994 100644 --- a/tests/ui/range_contains.rs +++ b/tests/ui/range_contains.rs @@ -6,7 +6,7 @@ #[allow(clippy::short_circuit_statement)] #[allow(clippy::unnecessary_operation)] fn main() { - let x = 9_u32; + let x = 9_i32; // order shouldn't matter x >= 8 && x < 12; @@ -43,6 +43,12 @@ fn main() { let y = 3.; y >= 0. && y < 1.; y < 0. || y > 1.; + + // handle negatives #8721 + x >= -10 && x <= 10; + x >= 10 && x <= -10; + y >= -3. && y <= 3.; + y >= 3. && y <= -3.; } // Fix #6373 diff --git a/tests/ui/range_contains.stderr b/tests/ui/range_contains.stderr index bc79f1bca846..1817ee1715d1 100644 --- a/tests/ui/range_contains.stderr +++ b/tests/ui/range_contains.stderr @@ -84,5 +84,17 @@ error: manual `!RangeInclusive::contains` implementation LL | y < 0. || y > 1.; | ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)` -error: aborting due to 14 previous errors +error: manual `RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:48:5 + | +LL | x >= -10 && x <= 10; + | ^^^^^^^^^^^^^^^^^^^ help: use: `(-10..=10).contains(&x)` + +error: manual `RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:50:5 + | +LL | y >= -3. && y <= 3.; + | ^^^^^^^^^^^^^^^^^^^ help: use: `(-3. ..=3.).contains(&y)` + +error: aborting due to 16 previous errors diff --git a/tests/ui/rc_clone_in_vec_init/arc.rs b/tests/ui/rc_clone_in_vec_init/arc.rs new file mode 100644 index 000000000000..384060e6eae5 --- /dev/null +++ b/tests/ui/rc_clone_in_vec_init/arc.rs @@ -0,0 +1,68 @@ +#![warn(clippy::rc_clone_in_vec_init)] +use std::sync::{Arc, Mutex}; + +fn main() {} + +fn should_warn_simple_case() { + let v = vec![Arc::new("x".to_string()); 2]; +} + +fn should_warn_simple_case_with_big_indentation() { + if true { + let k = 1; + dbg!(k); + if true { + let v = vec![Arc::new("x".to_string()); 2]; + } + } +} + +fn should_warn_complex_case() { + let v = vec![ + std::sync::Arc::new(Mutex::new({ + let x = 1; + dbg!(x); + x + })); + 2 + ]; + + let v1 = vec![ + Arc::new(Mutex::new({ + let x = 1; + dbg!(x); + x + })); + 2 + ]; +} + +fn should_not_warn_custom_arc() { + #[derive(Clone)] + struct Arc; + + impl Arc { + fn new() -> Self { + Arc + } + } + + let v = vec![Arc::new(); 2]; +} + +fn should_not_warn_vec_from_elem_but_not_arc() { + let v = vec![String::new(); 2]; + let v1 = vec![1; 2]; + let v2 = vec![ + Box::new(std::sync::Arc::new({ + let y = 3; + dbg!(y); + y + })); + 2 + ]; +} + +fn should_not_warn_vec_macro_but_not_from_elem() { + let v = vec![Arc::new("x".to_string())]; +} diff --git a/tests/ui/rc_clone_in_vec_init/arc.stderr b/tests/ui/rc_clone_in_vec_init/arc.stderr new file mode 100644 index 000000000000..ce84186c8e30 --- /dev/null +++ b/tests/ui/rc_clone_in_vec_init/arc.stderr @@ -0,0 +1,109 @@ +error: calling `Arc::new` in `vec![elem; len]` + --> $DIR/arc.rs:7:13 + | +LL | let v = vec![Arc::new("x".to_string()); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::rc-clone-in-vec-init` implied by `-D warnings` + = note: each element will point to the same `Arc` instance +help: consider initializing each `Arc` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Arc::new("x".to_string()))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Arc` initialization to a variable + | +LL ~ let v = { +LL + let data = Arc::new("x".to_string()); +LL + vec![data; 2] +LL ~ }; + | + +error: calling `Arc::new` in `vec![elem; len]` + --> $DIR/arc.rs:15:21 + | +LL | let v = vec![Arc::new("x".to_string()); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each element will point to the same `Arc` instance +help: consider initializing each `Arc` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Arc::new("x".to_string()))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Arc` initialization to a variable + | +LL ~ let v = { +LL + let data = Arc::new("x".to_string()); +LL + vec![data; 2] +LL ~ }; + | + +error: calling `Arc::new` in `vec![elem; len]` + --> $DIR/arc.rs:21:13 + | +LL | let v = vec![ + | _____________^ +LL | | std::sync::Arc::new(Mutex::new({ +LL | | let x = 1; +LL | | dbg!(x); +... | +LL | | 2 +LL | | ]; + | |_____^ + | + = note: each element will point to the same `Arc` instance +help: consider initializing each `Arc` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(std::sync::Arc::new(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Arc` initialization to a variable + | +LL ~ let v = { +LL + let data = std::sync::Arc::new(..); +LL + vec![data; 2] +LL ~ }; + | + +error: calling `Arc::new` in `vec![elem; len]` + --> $DIR/arc.rs:30:14 + | +LL | let v1 = vec![ + | ______________^ +LL | | Arc::new(Mutex::new({ +LL | | let x = 1; +LL | | dbg!(x); +... | +LL | | 2 +LL | | ]; + | |_____^ + | + = note: each element will point to the same `Arc` instance +help: consider initializing each `Arc` element individually + | +LL ~ let v1 = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Arc::new(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Arc` initialization to a variable + | +LL ~ let v1 = { +LL + let data = Arc::new(..); +LL + vec![data; 2] +LL ~ }; + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/rc_clone_in_vec_init/rc.rs b/tests/ui/rc_clone_in_vec_init/rc.rs new file mode 100644 index 000000000000..0394457fe170 --- /dev/null +++ b/tests/ui/rc_clone_in_vec_init/rc.rs @@ -0,0 +1,69 @@ +#![warn(clippy::rc_clone_in_vec_init)] +use std::rc::Rc; +use std::sync::Mutex; + +fn main() {} + +fn should_warn_simple_case() { + let v = vec![Rc::new("x".to_string()); 2]; +} + +fn should_warn_simple_case_with_big_indentation() { + if true { + let k = 1; + dbg!(k); + if true { + let v = vec![Rc::new("x".to_string()); 2]; + } + } +} + +fn should_warn_complex_case() { + let v = vec![ + std::rc::Rc::new(Mutex::new({ + let x = 1; + dbg!(x); + x + })); + 2 + ]; + + let v1 = vec![ + Rc::new(Mutex::new({ + let x = 1; + dbg!(x); + x + })); + 2 + ]; +} + +fn should_not_warn_custom_arc() { + #[derive(Clone)] + struct Rc; + + impl Rc { + fn new() -> Self { + Rc + } + } + + let v = vec![Rc::new(); 2]; +} + +fn should_not_warn_vec_from_elem_but_not_rc() { + let v = vec![String::new(); 2]; + let v1 = vec![1; 2]; + let v2 = vec![ + Box::new(std::rc::Rc::new({ + let y = 3; + dbg!(y); + y + })); + 2 + ]; +} + +fn should_not_warn_vec_macro_but_not_from_elem() { + let v = vec![Rc::new("x".to_string())]; +} diff --git a/tests/ui/rc_clone_in_vec_init/rc.stderr b/tests/ui/rc_clone_in_vec_init/rc.stderr new file mode 100644 index 000000000000..0f5cc0cf98fe --- /dev/null +++ b/tests/ui/rc_clone_in_vec_init/rc.stderr @@ -0,0 +1,109 @@ +error: calling `Rc::new` in `vec![elem; len]` + --> $DIR/rc.rs:8:13 + | +LL | let v = vec![Rc::new("x".to_string()); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::rc-clone-in-vec-init` implied by `-D warnings` + = note: each element will point to the same `Rc` instance +help: consider initializing each `Rc` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Rc::new("x".to_string()))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Rc` initialization to a variable + | +LL ~ let v = { +LL + let data = Rc::new("x".to_string()); +LL + vec![data; 2] +LL ~ }; + | + +error: calling `Rc::new` in `vec![elem; len]` + --> $DIR/rc.rs:16:21 + | +LL | let v = vec![Rc::new("x".to_string()); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each element will point to the same `Rc` instance +help: consider initializing each `Rc` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Rc::new("x".to_string()))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Rc` initialization to a variable + | +LL ~ let v = { +LL + let data = Rc::new("x".to_string()); +LL + vec![data; 2] +LL ~ }; + | + +error: calling `Rc::new` in `vec![elem; len]` + --> $DIR/rc.rs:22:13 + | +LL | let v = vec![ + | _____________^ +LL | | std::rc::Rc::new(Mutex::new({ +LL | | let x = 1; +LL | | dbg!(x); +... | +LL | | 2 +LL | | ]; + | |_____^ + | + = note: each element will point to the same `Rc` instance +help: consider initializing each `Rc` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(std::rc::Rc::new(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Rc` initialization to a variable + | +LL ~ let v = { +LL + let data = std::rc::Rc::new(..); +LL + vec![data; 2] +LL ~ }; + | + +error: calling `Rc::new` in `vec![elem; len]` + --> $DIR/rc.rs:31:14 + | +LL | let v1 = vec![ + | ______________^ +LL | | Rc::new(Mutex::new({ +LL | | let x = 1; +LL | | dbg!(x); +... | +LL | | 2 +LL | | ]; + | |_____^ + | + = note: each element will point to the same `Rc` instance +help: consider initializing each `Rc` element individually + | +LL ~ let v1 = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Rc::new(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Rc` initialization to a variable + | +LL ~ let v1 = { +LL + let data = Rc::new(..); +LL + vec![data; 2] +LL ~ }; + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/recursive_format_impl.stderr b/tests/ui/recursive_format_impl.stderr index 6171696ed69d..1a717ac92d8b 100644 --- a/tests/ui/recursive_format_impl.stderr +++ b/tests/ui/recursive_format_impl.stderr @@ -6,15 +6,6 @@ LL | write!(f, "{}", self.to_string()) | = note: `-D clippy::recursive-format-impl` implied by `-D warnings` -error: unnecessary use of `to_string` - --> $DIR/recursive_format_impl.rs:61:50 - | -LL | Self::E(string) => write!(f, "E {}", string.to_string()), - | ^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::unnecessary-to-owned` implied by `-D warnings` - = note: this error originates in the macro `$crate::format_args` (in Nightly builds, run with -Z macro-backtrace for more info) - error: using `self` as `Display` in `impl Display` will cause infinite recursion --> $DIR/recursive_format_impl.rs:73:9 | @@ -87,5 +78,5 @@ LL | write!(f, "{}", &&**&&*self) | = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 11 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/redundant_allocation.rs b/tests/ui/redundant_allocation.rs index 80f94e5f3cbb..cf7d8c6e349a 100644 --- a/tests/ui/redundant_allocation.rs +++ b/tests/ui/redundant_allocation.rs @@ -97,4 +97,39 @@ mod box_dyn { pub fn test_rc_box(_: Rc>>) {} } +// https://github.com/rust-lang/rust-clippy/issues/8604 +mod box_fat_ptr { + use std::boxed::Box; + use std::path::Path; + use std::rc::Rc; + use std::sync::Arc; + + pub struct DynSized { + foo: [usize], + } + + struct S { + a: Box>, + b: Rc>, + c: Arc>, + + e: Box>, + f: Box>, + g: Box>, + } + + pub fn test_box_str(_: Box>) {} + pub fn test_rc_str(_: Rc>) {} + pub fn test_arc_str(_: Arc>) {} + + pub fn test_box_slice(_: Box>) {} + pub fn test_box_path(_: Box>) {} + pub fn test_box_custom(_: Box>) {} + + pub fn test_rc_box_str(_: Rc>>) {} + pub fn test_rc_box_slice(_: Rc>>) {} + pub fn test_rc_box_path(_: Rc>>) {} + pub fn test_rc_box_custom(_: Rc>>) {} +} + fn main() {} diff --git a/tests/ui/redundant_allocation.stderr b/tests/ui/redundant_allocation.stderr index c3b10e5f5e67..fab1b069fcbc 100644 --- a/tests/ui/redundant_allocation.stderr +++ b/tests/ui/redundant_allocation.stderr @@ -143,5 +143,41 @@ LL | pub fn test_rc_box(_: Rc>>) {} = note: `Box>` is already on the heap, `Rc>>` makes an extra allocation = help: consider using just `Rc>` or `Box>` -error: aborting due to 16 previous errors +error: usage of `Rc>>` + --> $DIR/redundant_allocation.rs:129:31 + | +LL | pub fn test_rc_box_str(_: Rc>>) {} + | ^^^^^^^^^^^^^^^^^ + | + = note: `Box>` is already on the heap, `Rc>>` makes an extra allocation + = help: consider using just `Rc>` or `Box>` + +error: usage of `Rc>>` + --> $DIR/redundant_allocation.rs:130:33 + | +LL | pub fn test_rc_box_slice(_: Rc>>) {} + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `Box>` is already on the heap, `Rc>>` makes an extra allocation + = help: consider using just `Rc>` or `Box>` + +error: usage of `Rc>>` + --> $DIR/redundant_allocation.rs:131:32 + | +LL | pub fn test_rc_box_path(_: Rc>>) {} + | ^^^^^^^^^^^^^^^^^^ + | + = note: `Box>` is already on the heap, `Rc>>` makes an extra allocation + = help: consider using just `Rc>` or `Box>` + +error: usage of `Rc>>` + --> $DIR/redundant_allocation.rs:132:34 + | +LL | pub fn test_rc_box_custom(_: Rc>>) {} + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `Box>` is already on the heap, `Rc>>` makes an extra allocation + = help: consider using just `Rc>` or `Box>` + +error: aborting due to 20 previous errors diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index 9c4079ad6d30..53288be9404c 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -10,6 +10,7 @@ #![allow(clippy::cognitive_complexity)] #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] +#![allow(clippy::mixed_read_write_in_expression)] #![allow(clippy::for_loops_over_fallibles)] #![allow(clippy::useless_conversion)] #![allow(clippy::match_result_ok)] @@ -39,6 +40,7 @@ #![warn(clippy::cognitive_complexity)] #![warn(clippy::disallowed_methods)] #![warn(clippy::disallowed_types)] +#![warn(clippy::mixed_read_write_in_expression)] #![warn(clippy::for_loops_over_fallibles)] #![warn(clippy::for_loops_over_fallibles)] #![warn(clippy::useless_conversion)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index e83e66b7fbd4..539f34f847ac 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -10,6 +10,7 @@ #![allow(clippy::cognitive_complexity)] #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] +#![allow(clippy::mixed_read_write_in_expression)] #![allow(clippy::for_loops_over_fallibles)] #![allow(clippy::useless_conversion)] #![allow(clippy::match_result_ok)] @@ -39,6 +40,7 @@ #![warn(clippy::cyclomatic_complexity)] #![warn(clippy::disallowed_method)] #![warn(clippy::disallowed_type)] +#![warn(clippy::eval_order_dependence)] #![warn(clippy::for_loop_over_option)] #![warn(clippy::for_loop_over_result)] #![warn(clippy::identity_conversion)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index f811b10d0171..8ea46b580a8c 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:35:9 + --> $DIR/rename.rs:36:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` @@ -7,202 +7,208 @@ LL | #![warn(clippy::block_in_if_condition_expr)] = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:36:9 + --> $DIR/rename.rs:37:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> $DIR/rename.rs:37:9 + --> $DIR/rename.rs:38:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> $DIR/rename.rs:38:9 + --> $DIR/rename.rs:39:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> $DIR/rename.rs:39:9 + --> $DIR/rename.rs:40:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> $DIR/rename.rs:40:9 + --> $DIR/rename.rs:41:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> $DIR/rename.rs:41:9 + --> $DIR/rename.rs:42:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` +error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` + --> $DIR/rename.rs:43:9 + | +LL | #![warn(clippy::eval_order_dependence)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` + error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles` - --> $DIR/rename.rs:42:9 + --> $DIR/rename.rs:44:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles` - --> $DIR/rename.rs:43:9 + --> $DIR/rename.rs:45:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> $DIR/rename.rs:44:9 + --> $DIR/rename.rs:46:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> $DIR/rename.rs:45:9 + --> $DIR/rename.rs:47:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> $DIR/rename.rs:46:9 + --> $DIR/rename.rs:48:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> $DIR/rename.rs:47:9 + --> $DIR/rename.rs:49:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:48:9 + --> $DIR/rename.rs:50:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:49:9 + --> $DIR/rename.rs:51:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:50:9 + --> $DIR/rename.rs:52:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:51:9 + --> $DIR/rename.rs:53:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> $DIR/rename.rs:52:9 + --> $DIR/rename.rs:54:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:53:9 + --> $DIR/rename.rs:55:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:54:9 + --> $DIR/rename.rs:56:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:55:9 + --> $DIR/rename.rs:57:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> $DIR/rename.rs:56:9 + --> $DIR/rename.rs:58:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> $DIR/rename.rs:57:9 + --> $DIR/rename.rs:59:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> $DIR/rename.rs:58:9 + --> $DIR/rename.rs:60:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> $DIR/rename.rs:59:9 + --> $DIR/rename.rs:61:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:60:9 + --> $DIR/rename.rs:62:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:61:9 + --> $DIR/rename.rs:63:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> $DIR/rename.rs:62:9 + --> $DIR/rename.rs:64:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:63:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:66:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:65:9 + --> $DIR/rename.rs:67:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:68:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:67:9 + --> $DIR/rename.rs:69:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:68:9 + --> $DIR/rename.rs:70:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` -error: aborting due to 34 previous errors +error: aborting due to 35 previous errors diff --git a/tests/ui/self_assignment.rs b/tests/ui/self_assignment.rs index a7cbb9cd78b1..ef6476229102 100644 --- a/tests/ui/self_assignment.rs +++ b/tests/ui/self_assignment.rs @@ -39,7 +39,7 @@ pub fn negatives_not_equal(mut a: usize, b: &mut usize, mut s: S) { t.0 = t.1; } -#[allow(clippy::eval_order_dependence)] +#[allow(clippy::mixed_read_write_in_expression)] pub fn negatives_side_effects() { let mut v = vec![1, 2, 3, 4, 5]; let mut i = 0; diff --git a/tests/ui/significant_drop_in_scrutinee.rs b/tests/ui/significant_drop_in_scrutinee.rs index c4a3301e7226..f83a6dd0eb28 100644 --- a/tests/ui/significant_drop_in_scrutinee.rs +++ b/tests/ui/significant_drop_in_scrutinee.rs @@ -29,8 +29,8 @@ fn should_not_trigger_lint_with_mutex_guard_outside_match() { match is_foo { true => { mutex.lock().unwrap().bar(); - } - false => {} + }, + false => {}, }; } @@ -43,8 +43,8 @@ fn should_not_trigger_lint_with_mutex_guard_when_taking_ownership_in_match() { Ok(guard) => { guard.foo(); mutex.lock().unwrap().bar(); - } - _ => {} + }, + _ => {}, }; } @@ -57,8 +57,8 @@ fn should_trigger_lint_with_mutex_guard_in_match_scrutinee() { match mutex.lock().unwrap().foo() { true => { mutex.lock().unwrap().bar(); - } - false => {} + }, + false => {}, }; } @@ -68,10 +68,10 @@ fn should_not_trigger_lint_for_insignificant_drop() { match 1u64.to_string().is_empty() { true => { println!("It was empty") - } + }, false => { println!("It was not empty") - } + }, } } @@ -131,12 +131,12 @@ fn should_trigger_lint_with_wrapped_mutex() { 1 => { println!("Got 1. Is it still 1?"); println!("{}", s.lock_m().get_the_value()); - } + }, 2 => { println!("Got 2. Is it still 2?"); println!("{}", s.lock_m().get_the_value()); - } - _ => {} + }, + _ => {}, } println!("All done!"); } @@ -152,12 +152,12 @@ fn should_trigger_lint_with_double_wrapped_mutex() { 1 => { println!("Got 1. Is it still 1?"); println!("{}", s.lock_m().get_the_value()); - } + }, 2 => { println!("Got 2. Is it still 2?"); println!("{}", s.lock_m().get_the_value()); - } - _ => {} + }, + _ => {}, } println!("All done!"); } @@ -201,10 +201,10 @@ fn should_trigger_lint_for_vec() { let current_count = counter.i.load(Ordering::Relaxed); println!("Current count {}", current_count); assert_eq!(current_count, 0); - } - 1 => {} - 3 => {} - _ => {} + }, + 1 => {}, + 3 => {}, + _ => {}, }; } @@ -224,8 +224,8 @@ fn should_trigger_lint_for_tuple_in_scrutinee() { println!("started"); mutex1.lock().unwrap().s.len(); println!("done"); - } - (_, _) => {} + }, + (_, _) => {}, }; match (true, mutex1.lock().unwrap().s.len(), true) { @@ -233,8 +233,8 @@ fn should_trigger_lint_for_tuple_in_scrutinee() { println!("started"); mutex1.lock().unwrap().s.len(); println!("done"); - } - (_, _, _) => {} + }, + (_, _, _) => {}, }; let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() }); @@ -244,8 +244,8 @@ fn should_trigger_lint_for_tuple_in_scrutinee() { mutex1.lock().unwrap().s.len(); mutex2.lock().unwrap().s.len(); println!("done"); - } - (_, _, _) => {} + }, + (_, _, _) => {}, }; let mutex3 = Mutex::new(StateWithField { s: "three".to_owned() }); @@ -255,19 +255,18 @@ fn should_trigger_lint_for_tuple_in_scrutinee() { mutex1.lock().unwrap().s.len(); mutex2.lock().unwrap().s.len(); println!("done"); - } - _ => {} + }, + _ => {}, }; - match (true, mutex3.lock().unwrap().s.as_str()) { (_, "three") => { println!("started"); mutex1.lock().unwrap().s.len(); mutex2.lock().unwrap().s.len(); println!("done"); - } - (_, _) => {} + }, + (_, _) => {}, }; } } @@ -282,15 +281,15 @@ fn should_trigger_lint_for_accessing_field_in_mutex_in_one_side_of_binary_op() { match mutex.lock().unwrap().s.len() > 1 { true => { mutex.lock().unwrap().s.len(); - } - false => {} + }, + false => {}, }; match 1 < mutex.lock().unwrap().s.len() { true => { mutex.lock().unwrap().s.len(); - } - false => {} + }, + false => {}, }; } @@ -300,20 +299,30 @@ fn should_trigger_lint_for_accessing_field_in_mutex_in_one_side_of_binary_op() { // drop problem, the lint recommends moving the entire binary operation. fn should_trigger_lint_for_accessing_fields_in_mutex_in_both_sides_of_binary_op() { let mutex1 = Mutex::new(StateWithField { s: "state".to_owned() }); - let mutex2 = Mutex::new(StateWithField { s: "statewithfield".to_owned() }); + let mutex2 = Mutex::new(StateWithField { + s: "statewithfield".to_owned(), + }); match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() { true => { - println!("{} < {}", mutex1.lock().unwrap().s.len(), mutex2.lock().unwrap().s.len()); - } - false => {} + println!( + "{} < {}", + mutex1.lock().unwrap().s.len(), + mutex2.lock().unwrap().s.len() + ); + }, + false => {}, }; match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() { true => { - println!("{} >= {}", mutex1.lock().unwrap().s.len(), mutex2.lock().unwrap().s.len()); - } - false => {} + println!( + "{} >= {}", + mutex1.lock().unwrap().s.len(), + mutex2.lock().unwrap().s.len() + ); + }, + false => {}, }; } @@ -328,8 +337,8 @@ fn should_not_trigger_lint_for_closure_in_scrutinee() { match get_mutex_guard() > 1 { true => { mutex1.lock().unwrap().s.len(); - } - false => {} + }, + false => {}, }; } @@ -343,8 +352,8 @@ fn should_trigger_lint_for_return_from_closure_in_scrutinee() { match get_mutex_guard().s.len() > 1 { true => { mutex1.lock().unwrap().s.len(); - } - false => {} + }, + false => {}, }; } @@ -357,13 +366,20 @@ fn should_trigger_lint_for_return_from_match_in_scrutinee() { // Should trigger lint because the nested match within the scrutinee returns a temporary with a // significant drop is but not used directly in any match arms, so it has a potentially // surprising lifetime. - match match i { 100 => mutex1.lock().unwrap(), _ => mutex2.lock().unwrap() }.s.len() > 1 { + match match i { + 100 => mutex1.lock().unwrap(), + _ => mutex2.lock().unwrap(), + } + .s + .len() + > 1 + { true => { mutex1.lock().unwrap().s.len(); - } + }, false => { println!("nothing to do here"); - } + }, }; } @@ -376,11 +392,19 @@ fn should_trigger_lint_for_return_from_if_in_scrutinee() { // Should trigger lint because the nested if-expression within the scrutinee returns a temporary // with a significant drop is but not used directly in any match arms, so it has a potentially // surprising lifetime. - match if i > 1 { mutex1.lock().unwrap() } else { mutex2.lock().unwrap() }.s.len() > 1 { + match if i > 1 { + mutex1.lock().unwrap() + } else { + mutex2.lock().unwrap() + } + .s + .len() + > 1 + { true => { mutex1.lock().unwrap().s.len(); - } - false => {} + }, + false => {}, }; } @@ -392,11 +416,15 @@ fn should_not_trigger_lint_for_if_in_scrutinee() { // Should not trigger the lint because the temporary with a significant drop *is* dropped within // the body of the if-expression nested within the match scrutinee, and therefore does not have // a potentially surprising lifetime. - match if i > 1 { mutex.lock().unwrap().s.len() > 1 } else { false } { + match if i > 1 { + mutex.lock().unwrap().s.len() > 1 + } else { + false + } { true => { mutex.lock().unwrap().s.len(); - } - false => {} + }, + false => {}, }; } @@ -430,7 +458,9 @@ struct StateStringWithBoxedMutexGuard { impl StateStringWithBoxedMutexGuard { fn new() -> StateStringWithBoxedMutexGuard { - StateStringWithBoxedMutexGuard { s: Mutex::new("A String".to_owned()) } + StateStringWithBoxedMutexGuard { + s: Mutex::new("A String".to_owned()), + } } fn lock(&self) -> Box> { Box::new(self.s.lock().unwrap()) @@ -450,7 +480,6 @@ fn should_trigger_lint_for_boxed_mutex_guard_holding_string() { }; } - struct StateWithIntField { i: u64, } @@ -467,36 +496,36 @@ fn should_trigger_lint_in_assign_expr() { match mutex.lock().unwrap().i = i { _ => { println!("{}", mutex.lock().unwrap().i); - } + }, }; match i = mutex.lock().unwrap().i { _ => { println!("{}", mutex.lock().unwrap().i); - } + }, }; match mutex.lock().unwrap().i += 1 { _ => { println!("{}", mutex.lock().unwrap().i); - } + }, }; match i += mutex.lock().unwrap().i { _ => { println!("{}", mutex.lock().unwrap().i); - } + }, }; } #[derive(Debug)] enum RecursiveEnum { - Foo(Option>) + Foo(Option>), } #[derive(Debug)] enum GenericRecursiveEnum { - Foo(T, Option>>) + Foo(T, Option>>), } fn should_not_cause_stack_overflow() { @@ -506,20 +535,20 @@ fn should_not_cause_stack_overflow() { match f { RecursiveEnum::Foo(Some(f)) => { println!("{:?}", f) - } + }, RecursiveEnum::Foo(f) => { println!("{:?}", f) - } + }, } let f = GenericRecursiveEnum::Foo(1u64, Some(Box::new(GenericRecursiveEnum::Foo(2u64, None)))); match f { GenericRecursiveEnum::Foo(i, Some(f)) => { println!("{} {:?}", i, f) - } + }, GenericRecursiveEnum::Foo(i, f) => { println!("{} {:?}", i, f) - } + }, } } diff --git a/tests/ui/significant_drop_in_scrutinee.stderr b/tests/ui/significant_drop_in_scrutinee.stderr index c442e93f539a..af1605649850 100644 --- a/tests/ui/significant_drop_in_scrutinee.stderr +++ b/tests/ui/significant_drop_in_scrutinee.stderr @@ -102,13 +102,13 @@ LL | match mutex3.lock().unwrap().s.as_str() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:263:22 + --> $DIR/significant_drop_in_scrutinee.rs:262:22 | LL | match (true, mutex3.lock().unwrap().s.as_str()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:282:11 + --> $DIR/significant_drop_in_scrutinee.rs:281:11 | LL | match mutex.lock().unwrap().s.len() > 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -120,7 +120,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:289:11 + --> $DIR/significant_drop_in_scrutinee.rs:288:11 | LL | match 1 < mutex.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,7 +132,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:305:11 + --> $DIR/significant_drop_in_scrutinee.rs:306:11 | LL | match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -144,7 +144,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:312:11 + --> $DIR/significant_drop_in_scrutinee.rs:317:11 | LL | match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -156,7 +156,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:343:11 + --> $DIR/significant_drop_in_scrutinee.rs:352:11 | LL | match get_mutex_guard().s.len() > 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -168,31 +168,53 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:360:11 - | -LL | match match i { 100 => mutex1.lock().unwrap(), _ => mutex2.lock().unwrap() }.s.len() > 1 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + --> $DIR/significant_drop_in_scrutinee.rs:369:11 + | +LL | match match i { + | ___________^ +LL | | 100 => mutex1.lock().unwrap(), +LL | | _ => mutex2.lock().unwrap(), +LL | | } +LL | | .s +LL | | .len() +LL | | > 1 + | |___________^ | help: try moving the temporary above the match | -LL ~ let value = match i { 100 => mutex1.lock().unwrap(), _ => mutex2.lock().unwrap() }.s.len() > 1; -LL ~ match value { - | +LL ~ let value = match i { +LL + 100 => mutex1.lock().unwrap(), +LL + _ => mutex2.lock().unwrap(), +LL + } +LL + .s +LL + .len() + ... error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:379:11 - | -LL | match if i > 1 { mutex1.lock().unwrap() } else { mutex2.lock().unwrap() }.s.len() > 1 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + --> $DIR/significant_drop_in_scrutinee.rs:395:11 + | +LL | match if i > 1 { + | ___________^ +LL | | mutex1.lock().unwrap() +LL | | } else { +LL | | mutex2.lock().unwrap() +... | +LL | | .len() +LL | | > 1 + | |___________^ | help: try moving the temporary above the match | -LL ~ let value = if i > 1 { mutex1.lock().unwrap() } else { mutex2.lock().unwrap() }.s.len() > 1; -LL ~ match value { - | +LL ~ let value = if i > 1 { +LL + mutex1.lock().unwrap() +LL + } else { +LL + mutex2.lock().unwrap() +LL + } +LL + .s + ... error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:421:11 + --> $DIR/significant_drop_in_scrutinee.rs:449:11 | LL | match s.lock().deref().deref() { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -204,13 +226,13 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:447:11 + --> $DIR/significant_drop_in_scrutinee.rs:477:11 | LL | match s.lock().deref().deref() { | ^^^^^^^^^^^^^^^^^^^^^^^^ error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:467:11 + --> $DIR/significant_drop_in_scrutinee.rs:496:11 | LL | match mutex.lock().unwrap().i = i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -222,7 +244,7 @@ LL ~ match () { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:473:11 + --> $DIR/significant_drop_in_scrutinee.rs:502:11 | LL | match i = mutex.lock().unwrap().i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -234,7 +256,7 @@ LL ~ match () { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:479:11 + --> $DIR/significant_drop_in_scrutinee.rs:508:11 | LL | match mutex.lock().unwrap().i += 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -246,7 +268,7 @@ LL ~ match () { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:485:11 + --> $DIR/significant_drop_in_scrutinee.rs:514:11 | LL | match i += mutex.lock().unwrap().i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/temporary_assignment.rs b/tests/ui/temporary_assignment.rs index b4a931043b00..ac4c1bc65979 100644 --- a/tests/ui/temporary_assignment.rs +++ b/tests/ui/temporary_assignment.rs @@ -1,5 +1,4 @@ #![warn(clippy::temporary_assignment)] -#![allow(const_item_mutation)] use std::ops::{Deref, DerefMut}; diff --git a/tests/ui/temporary_assignment.stderr b/tests/ui/temporary_assignment.stderr index 4cc32c79f05c..7d79901a28d1 100644 --- a/tests/ui/temporary_assignment.stderr +++ b/tests/ui/temporary_assignment.stderr @@ -1,5 +1,5 @@ error: assignment to temporary - --> $DIR/temporary_assignment.rs:48:5 + --> $DIR/temporary_assignment.rs:47:5 | LL | Struct { field: 0 }.field = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | Struct { field: 0 }.field = 1; = note: `-D clippy::temporary-assignment` implied by `-D warnings` error: assignment to temporary - --> $DIR/temporary_assignment.rs:49:5 + --> $DIR/temporary_assignment.rs:48:5 | LL | / MultiStruct { LL | | structure: Struct { field: 0 }, @@ -17,13 +17,13 @@ LL | | .field = 1; | |______________^ error: assignment to temporary - --> $DIR/temporary_assignment.rs:54:5 + --> $DIR/temporary_assignment.rs:53:5 | LL | ArrayStruct { array: [0] }.array[0] = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: assignment to temporary - --> $DIR/temporary_assignment.rs:55:5 + --> $DIR/temporary_assignment.rs:54:5 | LL | (0, 0).0 = 1; | ^^^^^^^^^^^^ diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs index 7be15b0b2dd3..33b6a82f9d2c 100644 --- a/tests/ui/undocumented_unsafe_blocks.rs +++ b/tests/ui/undocumented_unsafe_blocks.rs @@ -1,7 +1,7 @@ // aux-build:proc_macro_unsafe.rs #![warn(clippy::undocumented_unsafe_blocks)] -#![allow(clippy::let_unit_value)] +#![allow(clippy::let_unit_value, clippy::missing_safety_doc)] extern crate proc_macro_unsafe; @@ -334,4 +334,155 @@ pub fn print_binary_tree() { println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); } +mod unsafe_impl_smoke_test { + unsafe trait A {} + + // error: no safety comment + unsafe impl A for () {} + + // Safety: ok + unsafe impl A for (i32) {} + + mod sub_mod { + // error: + unsafe impl B for (u32) {} + unsafe trait B {} + } + + #[rustfmt::skip] + mod sub_mod2 { + // + // SAFETY: ok + // + + unsafe impl B for (u32) {} + unsafe trait B {} + } +} + +mod unsafe_impl_from_macro { + unsafe trait T {} + + // error + macro_rules! no_safety_comment { + ($t:ty) => { + unsafe impl T for $t {} + }; + } + + // ok + no_safety_comment!(()); + + // ok + macro_rules! with_safety_comment { + ($t:ty) => { + // SAFETY: + unsafe impl T for $t {} + }; + } + + // ok + with_safety_comment!((i32)); +} + +mod unsafe_impl_macro_and_not_macro { + unsafe trait T {} + + // error + macro_rules! no_safety_comment { + ($t:ty) => { + unsafe impl T for $t {} + }; + } + + // ok + no_safety_comment!(()); + + // error + unsafe impl T for (i32) {} + + // ok + no_safety_comment!(u32); + + // error + unsafe impl T for (bool) {} +} + +#[rustfmt::skip] +mod unsafe_impl_valid_comment { + unsafe trait SaFety {} + // SaFety: + unsafe impl SaFety for () {} + + unsafe trait MultiLineComment {} + // The following impl is safe + // ... + // Safety: reason + unsafe impl MultiLineComment for () {} + + unsafe trait NoAscii {} + // 安全 SAFETY: 以下のコードは安全です + unsafe impl NoAscii for () {} + + unsafe trait InlineAndPrecedingComment {} + // SAFETY: + /* comment */ unsafe impl InlineAndPrecedingComment for () {} + + unsafe trait BuriedSafety {} + // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor + // incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation + // ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + // reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint + // occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est + // laborum. Safety: + // Tellus elementum sagittis vitae et leo duis ut diam quam. Sit amet nulla facilisi + // morbi tempus iaculis urna. Amet luctus venenatis lectus magna. At quis risus sed vulputate odio + // ut. Luctus venenatis lectus magna fringilla urna. Tortor id aliquet lectus proin nibh nisl + // condimentum id venenatis. Vulputate dignissim suspendisse in est ante in nibh mauris cursus. + unsafe impl BuriedSafety for () {} + + unsafe trait MultiLineBlockComment {} + /* This is a description + * Safety: */ + unsafe impl MultiLineBlockComment for () {} +} + +#[rustfmt::skip] +mod unsafe_impl_invalid_comment { + unsafe trait NoComment {} + + unsafe impl NoComment for () {} + + unsafe trait InlineComment {} + + /* SAFETY: */ unsafe impl InlineComment for () {} + + unsafe trait TrailingComment {} + + unsafe impl TrailingComment for () {} // SAFETY: + + unsafe trait Interference {} + // SAFETY: + const BIG_NUMBER: i32 = 1000000; + unsafe impl Interference for () {} +} + +unsafe trait ImplInFn {} + +fn impl_in_fn() { + // error + unsafe impl ImplInFn for () {} + + // SAFETY: ok + unsafe impl ImplInFn for (i32) {} +} + +unsafe trait CrateRoot {} + +// error +unsafe impl CrateRoot for () {} + +// SAFETY: ok +unsafe impl CrateRoot for (i32) {} + fn main() {} diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui/undocumented_unsafe_blocks.stderr index 87d445bd7b86..b79949e9d06d 100644 --- a/tests/ui/undocumented_unsafe_blocks.stderr +++ b/tests/ui/undocumented_unsafe_blocks.stderr @@ -147,5 +147,121 @@ LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); | = help: consider adding a safety comment on the preceding line -error: aborting due to 18 previous errors +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:341:5 + | +LL | unsafe impl A for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:348:9 + | +LL | unsafe impl B for (u32) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:369:13 + | +LL | unsafe impl T for $t {} + | ^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | no_safety_comment!(()); + | ---------------------- in this macro invocation + | + = help: consider adding a safety comment on the preceding line + = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:394:13 + | +LL | unsafe impl T for $t {} + | ^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | no_safety_comment!(()); + | ---------------------- in this macro invocation + | + = help: consider adding a safety comment on the preceding line + = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:402:5 + | +LL | unsafe impl T for (i32) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:394:13 + | +LL | unsafe impl T for $t {} + | ^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | no_safety_comment!(u32); + | ----------------------- in this macro invocation + | + = help: consider adding a safety comment on the preceding line + = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:408:5 + | +LL | unsafe impl T for (bool) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:454:5 + | +LL | unsafe impl NoComment for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:458:19 + | +LL | /* SAFETY: */ unsafe impl InlineComment for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:462:5 + | +LL | unsafe impl TrailingComment for () {} // SAFETY: + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:467:5 + | +LL | unsafe impl Interference for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:474:5 + | +LL | unsafe impl ImplInFn for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:483:1 + | +LL | unsafe impl CrateRoot for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: aborting due to 31 previous errors diff --git a/tests/ui/unit_cmp.rs b/tests/ui/unit_cmp.rs index 8d3a4eed82e3..3d271104361b 100644 --- a/tests/ui/unit_cmp.rs +++ b/tests/ui/unit_cmp.rs @@ -1,5 +1,9 @@ #![warn(clippy::unit_cmp)] -#![allow(clippy::no_effect, clippy::unnecessary_operation)] +#![allow( + clippy::no_effect, + clippy::unnecessary_operation, + clippy::derive_partial_eq_without_eq +)] #[derive(PartialEq)] pub struct ContainsUnit(()); // should be fine diff --git a/tests/ui/unit_cmp.stderr b/tests/ui/unit_cmp.stderr index 824506a4257e..41cf19ae685e 100644 --- a/tests/ui/unit_cmp.stderr +++ b/tests/ui/unit_cmp.stderr @@ -1,5 +1,5 @@ error: ==-comparison of unit values detected. This will always be true - --> $DIR/unit_cmp.rs:12:8 + --> $DIR/unit_cmp.rs:16:8 | LL | if { | ________^ @@ -12,7 +12,7 @@ LL | | } {} = note: `-D clippy::unit-cmp` implied by `-D warnings` error: >-comparison of unit values detected. This will always be false - --> $DIR/unit_cmp.rs:18:8 + --> $DIR/unit_cmp.rs:22:8 | LL | if { | ________^ @@ -23,7 +23,7 @@ LL | | } {} | |_____^ error: `assert_eq` of unit values detected. This will always succeed - --> $DIR/unit_cmp.rs:24:5 + --> $DIR/unit_cmp.rs:28:5 | LL | / assert_eq!( LL | | { @@ -35,7 +35,7 @@ LL | | ); | |_____^ error: `debug_assert_eq` of unit values detected. This will always succeed - --> $DIR/unit_cmp.rs:32:5 + --> $DIR/unit_cmp.rs:36:5 | LL | / debug_assert_eq!( LL | | { @@ -47,7 +47,7 @@ LL | | ); | |_____^ error: `assert_ne` of unit values detected. This will always fail - --> $DIR/unit_cmp.rs:41:5 + --> $DIR/unit_cmp.rs:45:5 | LL | / assert_ne!( LL | | { @@ -59,7 +59,7 @@ LL | | ); | |_____^ error: `debug_assert_ne` of unit values detected. This will always fail - --> $DIR/unit_cmp.rs:49:5 + --> $DIR/unit_cmp.rs:53:5 | LL | / debug_assert_ne!( LL | | { diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index 7455e22d49b2..f4f76cd3dd49 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -78,10 +78,10 @@ fn main() { require_slice(array.as_ref()); require_slice(array_ref.as_ref()); require_slice(slice); - require_slice(x_ref); + require_slice(&x_ref.to_owned()); // No longer flagged because of #8759. require_x(&Cow::::Owned(x.clone())); - require_x(x_ref); + require_x(&x_ref.to_owned()); // No longer flagged because of #8759. require_deref_c_str(c_str); require_deref_os_str(os_str); @@ -152,6 +152,7 @@ fn main() { 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) {} @@ -272,3 +273,59 @@ mod issue_8507 { 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()); + } +} diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index bbcd00ad2204..fe09a489ab0a 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -78,10 +78,10 @@ fn main() { require_slice(&array.to_owned()); require_slice(&array_ref.to_owned()); require_slice(&slice.to_owned()); - require_slice(&x_ref.to_owned()); + require_slice(&x_ref.to_owned()); // No longer flagged because of #8759. require_x(&Cow::::Owned(x.clone()).into_owned()); - require_x(&x_ref.to_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()); @@ -152,6 +152,7 @@ fn main() { 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) {} @@ -272,3 +273,59 @@ mod issue_8507 { 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()); + } +} diff --git a/tests/ui/unnecessary_to_owned.stderr b/tests/ui/unnecessary_to_owned.stderr index f9713559e4f7..af7e7b41fb00 100644 --- a/tests/ui/unnecessary_to_owned.stderr +++ b/tests/ui/unnecessary_to_owned.stderr @@ -47,6 +47,18 @@ note: this value is dropped without further use 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 | @@ -151,24 +163,12 @@ error: unnecessary use of `to_owned` LL | require_slice(&slice.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `slice` -error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:81:19 - | -LL | require_slice(&x_ref.to_owned()); - | ^^^^^^^^^^^^^^^^^ help: use: `x_ref` - error: unnecessary use of `into_owned` --> $DIR/unnecessary_to_owned.rs:83:42 | LL | require_x(&Cow::::Owned(x.clone()).into_owned()); | ^^^^^^^^^^^^^ help: remove this -error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:84:15 - | -LL | require_x(&x_ref.to_owned()); - | ^^^^^^^^^^^^^^^^^ help: use: `x_ref` - error: unnecessary use of `to_owned` --> $DIR/unnecessary_to_owned.rs:86:25 | @@ -476,7 +476,7 @@ LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owne | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:197:14 + --> $DIR/unnecessary_to_owned.rs:198:14 | LL | for t in file_types.to_vec() { | ^^^^^^^^^^^^^^^^^^^ @@ -492,22 +492,22 @@ LL + let path = match get_file_path(t) { | error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:220:14 + --> $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:225:14 + --> $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:272:24 + --> $DIR/unnecessary_to_owned.rs:273:24 | LL | Box::new(build(y.to_string())) | ^^^^^^^^^^^^^ help: use: `y` -error: aborting due to 79 previous errors +error: aborting due to 78 previous errors diff --git a/tests/ui/useless_conversion_try.rs b/tests/ui/useless_conversion_try.rs index 3787ea991445..39f54c27bee1 100644 --- a/tests/ui/useless_conversion_try.rs +++ b/tests/ui/useless_conversion_try.rs @@ -1,7 +1,5 @@ #![deny(clippy::useless_conversion)] -use std::convert::{TryFrom, TryInto}; - fn test_generic(val: T) -> T { let _ = T::try_from(val).unwrap(); val.try_into().unwrap() diff --git a/tests/ui/useless_conversion_try.stderr b/tests/ui/useless_conversion_try.stderr index 2e0d9129bfb3..b691c13f7dbb 100644 --- a/tests/ui/useless_conversion_try.stderr +++ b/tests/ui/useless_conversion_try.stderr @@ -1,5 +1,5 @@ error: useless conversion to the same type: `T` - --> $DIR/useless_conversion_try.rs:6:13 + --> $DIR/useless_conversion_try.rs:4:13 | LL | let _ = T::try_from(val).unwrap(); | ^^^^^^^^^^^^^^^^ @@ -12,7 +12,7 @@ LL | #![deny(clippy::useless_conversion)] = help: consider removing `T::try_from()` error: useless conversion to the same type: `T` - --> $DIR/useless_conversion_try.rs:7:5 + --> $DIR/useless_conversion_try.rs:5:5 | LL | val.try_into().unwrap() | ^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ LL | val.try_into().unwrap() = help: consider removing `.try_into()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion_try.rs:29:21 + --> $DIR/useless_conversion_try.rs:27:21 | LL | let _: String = "foo".to_string().try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | let _: String = "foo".to_string().try_into().unwrap(); = help: consider removing `.try_into()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion_try.rs:30:21 + --> $DIR/useless_conversion_try.rs:28:21 | LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); = help: consider removing `TryFrom::try_from()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion_try.rs:31:13 + --> $DIR/useless_conversion_try.rs:29:13 | LL | let _ = String::try_from("foo".to_string()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | let _ = String::try_from("foo".to_string()).unwrap(); = help: consider removing `String::try_from()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion_try.rs:32:13 + --> $DIR/useless_conversion_try.rs:30:13 | LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -52,7 +52,7 @@ LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); = help: consider removing `String::try_from()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion_try.rs:33:21 + --> $DIR/useless_conversion_try.rs:31:21 | LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); = help: consider removing `.try_into()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion_try.rs:34:21 + --> $DIR/useless_conversion_try.rs:32:21 | LL | let _: String = "".to_owned().try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -68,7 +68,7 @@ LL | let _: String = "".to_owned().try_into().unwrap(); = help: consider removing `.try_into()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion_try.rs:35:27 + --> $DIR/useless_conversion_try.rs:33:27 | LL | let _: String = match String::from("_").try_into() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/vec_init_then_push.rs b/tests/ui/vec_init_then_push.rs index 5099aad83bcb..531745424a7d 100644 --- a/tests/ui/vec_init_then_push.rs +++ b/tests/ui/vec_init_then_push.rs @@ -29,6 +29,12 @@ fn main() { // 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 { @@ -44,3 +50,57 @@ pub fn no_lint() -> Vec { } } } + +fn _from_iter(items: impl Iterator) -> Vec { + let mut v = Vec::new(); + v.push(0); + v.push(1); + v.extend(items); + v +} + +fn _cond_push(x: bool) -> Vec { + let mut v = Vec::new(); + v.push(0); + if x { + v.push(1); + } + v.push(2); + v +} + +fn _push_then_edit(x: u32) -> Vec { + 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 { + 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 +} diff --git a/tests/ui/vec_init_then_push.stderr b/tests/ui/vec_init_then_push.stderr index 9ec3e10e6247..50b029fc3372 100644 --- a/tests/ui/vec_init_then_push.stderr +++ b/tests/ui/vec_init_then_push.stderr @@ -3,7 +3,7 @@ error: calls to `push` immediately after creation | LL | / let mut def_err: Vec = Default::default(); LL | | def_err.push(0); - | |____________________^ help: consider using the `vec![]` macro: `let mut def_err: Vec = vec![..];` + | |____________________^ help: consider using the `vec![]` macro: `let def_err: Vec = vec![..];` | = note: `-D clippy::vec-init-then-push` implied by `-D warnings` @@ -30,5 +30,37 @@ LL | / new_err = Vec::new(); LL | | new_err.push(0); | |____________________^ help: consider using the `vec![]` macro: `new_err = vec![..];` -error: aborting due to 4 previous errors +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: aborting due to 7 previous errors diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index 97c974003c62..2076d1299783 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -94,6 +94,29 @@ @media (min-width: 992px) { .search-control { margin-top: 0; + float: right; + } + } + + @media (min-width: 405px) { + #upper-filters { + display: flex; + } + } + + @media (max-width: 430px) { + /* Turn the version filter list to the left */ + #version-filter-selector { + right: 0; + left: auto; + } + } + + @media (max-width: 412px) { + #upper-filters, + .panel-body .search-control { + padding-right: 8px; + padding-left: 8px; } } @@ -244,7 +267,7 @@ cursor: pointer; } - .theme-choice>li:hover { + .theme-choice > li:hover { background: var(--theme-hover); } @@ -273,23 +296,44 @@ border: 1px solid var(--theme-popup-border); } - #filter-label, #filter-clear { + #version-filter-selector .checkbox { + display: flex; + } + + #version-filter { + min-width: available; + } + + #version-filter li label { + padding-right: 0; + width: 35%; + } + + .version-filter-input { + height: 60%; + width: 30%; + text-align: center; + border: none; + border-bottom: 1px solid #000000; + } + + #filter-label, .filter-clear { background: var(--searchbar-bg); color: var(--searchbar-fg); border-color: var(--theme-popup-border); filter: brightness(95%); } - #filter-label:hover, #filter-clear:hover { + #filter-label:hover, .filter-clear:hover { filter: brightness(90%); } - #filter-input { + .filter-input { background: var(--searchbar-bg); color: var(--searchbar-fg); border-color: var(--theme-popup-border); } - #filter-input::-webkit-input-placeholder, - #filter-input::-moz-placeholder { + .filter-input::-webkit-input-placeholder, + .filter-input::-moz-placeholder { color: var(--searchbar-fg); opacity: 30%; } @@ -338,7 +382,7 @@

Clippy Lints

-
+
+
+
+ + +
+
+
-
+
- - + + - @@ -406,12 +484,15 @@

Clippy Lints

-
+

{{lint.id}} + + 📋 +
@@ -462,295 +543,6 @@

- + diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js new file mode 100644 index 000000000000..bf4ce79b2cbc --- /dev/null +++ b/util/gh-pages/script.js @@ -0,0 +1,371 @@ +(function () { + var md = window.markdownit({ + html: true, + linkify: true, + typographer: true, + highlight: function (str, lang) { + if (lang && hljs.getLanguage(lang)) { + try { + return '
' +
+                        hljs.highlight(lang, str, true).value +
+                        '
'; + } catch (__) {} + } + + return '
' + md.utils.escapeHtml(str) + '
'; + } + }); + + function scrollToLint(lintId) { + var target = document.getElementById(lintId); + if (!target) { + return; + } + target.scrollIntoView(); + } + + function scrollToLintByURL($scope) { + var removeListener = $scope.$on('ngRepeatFinished', function(ngRepeatFinishedEvent) { + scrollToLint(window.location.hash.slice(1)); + removeListener(); + }); + } + + function selectGroup($scope, selectedGroup) { + var groups = $scope.groups; + for (var group in groups) { + if (groups.hasOwnProperty(group)) { + if (group === selectedGroup) { + groups[group] = true; + } else { + groups[group] = false; + } + } + } + } + + angular.module("clippy", []) + .filter('markdown', function ($sce) { + return function (text) { + return $sce.trustAsHtml( + md.render(text || '') + // Oh deer, what a hack :O + .replace(' minorVersion) { return false; } + break; + default: + return true + } + } + } + + return true; + } + + $scope.byGroups = function (lint) { + return $scope.groups[lint.group]; + }; + + $scope.bySearch = function (lint, index, array) { + let searchStr = $scope.search; + // It can be `null` I haven't missed this value + if (searchStr == null || searchStr.length < 3) { + return true; + } + searchStr = searchStr.toLowerCase(); + + // Search by id + if (lint.id.indexOf(searchStr.replace("-", "_")) !== -1) { + return true; + } + + // Search the description + // The use of `for`-loops instead of `foreach` enables us to return early + let terms = searchStr.split(" "); + let docsLowerCase = lint.docs.toLowerCase(); + for (index = 0; index < terms.length; index++) { + // This is more likely and will therefor be checked first + if (docsLowerCase.indexOf(terms[index]) !== -1) { + continue; + } + + if (lint.id.indexOf(terms[index]) !== -1) { + continue; + } + + return false; + } + + return true; + } + + $scope.copyToClipboard = function (lint) { + const clipboard = document.getElementById("clipboard-" + lint.id); + if (clipboard) { + let resetClipboardTimeout = null; + let resetClipboardIcon = clipboard.innerHTML; + + function resetClipboard() { + resetClipboardTimeout = null; + clipboard.innerHTML = resetClipboardIcon; + } + + navigator.clipboard.writeText("clippy::" + lint.id); + + clipboard.innerHTML = "✓"; + if (resetClipboardTimeout !== null) { + clearTimeout(resetClipboardTimeout); + } + resetClipboardTimeout = setTimeout(resetClipboard, 1000); + } + } + + // Get data + $scope.open = {}; + $scope.loading = true; + // This will be used to jump into the source code of the version that this documentation is for. + $scope.docVersion = window.location.pathname.split('/')[2] || "master"; + + if (window.location.hash.length > 1) { + $scope.search = window.location.hash.slice(1); + $scope.open[window.location.hash.slice(1)] = true; + scrollToLintByURL($scope); + } + + $http.get('./lints.json') + .success(function (data) { + $scope.data = data; + $scope.loading = false; + + var selectedGroup = getQueryVariable("sel"); + if (selectedGroup) { + selectGroup($scope, selectedGroup.toLowerCase()); + } + + scrollToLintByURL($scope); + + setTimeout(function () { + var el = document.getElementById('filter-input'); + if (el) { el.focus() } + }, 0); + }) + .error(function (data) { + $scope.error = data; + $scope.loading = false; + }); + + window.addEventListener('hashchange', function () { + // trigger re-render + $timeout(function () { + $scope.levels = LEVEL_FILTERS_DEFAULT; + $scope.search = window.location.hash.slice(1); + $scope.open[window.location.hash.slice(1)] = true; + + scrollToLintByURL($scope); + }); + return true; + }, false); + }); +})(); + +function getQueryVariable(variable) { + var query = window.location.search.substring(1); + var vars = query.split('&'); + for (var i = 0; i < vars.length; i++) { + var pair = vars[i].split('='); + if (decodeURIComponent(pair[0]) == variable) { + return decodeURIComponent(pair[1]); + } + } +} + +function setTheme(theme, store) { + let enableHighlight = false; + let enableNight = false; + let enableAyu = false; + + if (theme == "ayu") { + enableAyu = true; + } else if (theme == "coal" || theme == "navy") { + enableNight = true; + } else if (theme == "rust") { + enableHighlight = true; + } else { + enableHighlight = true; + // this makes sure that an unknown theme request gets set to a known one + theme = "light"; + } + document.getElementsByTagName("body")[0].className = theme; + + document.getElementById("styleHighlight").disabled = !enableHighlight; + document.getElementById("styleNight").disabled = !enableNight; + document.getElementById("styleAyu").disabled = !enableAyu; + + if (store) { + try { + localStorage.setItem('clippy-lint-list-theme', theme); + } catch (e) { } + } +} + +// loading the theme after the initial load +setTheme(localStorage.getItem('clippy-lint-list-theme'), false);