summaryrefslogtreecommitdiffstats
path: root/src/tools/clippy/clippy_lints/src
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/clippy/clippy_lints/src')
-rw-r--r--src/tools/clippy/clippy_lints/src/absolute_paths.rs100
-rw-r--r--src/tools/clippy/clippy_lints/src/allow_attributes.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/assertions_on_constants.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/box_default.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/mod.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs64
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/copies.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/copy_iterator.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/dbg_macro.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/declared_lints.rs23
-rw-r--r--src/tools/clippy/clippy_lints/src/default.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/dereference.rs1070
-rw-r--r--src/tools/clippy/clippy_lints/src/derivable_impls.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/derive.rs38
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_methods.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_names.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/doc.rs33
-rw-r--r--src/tools/clippy/clippy_lints/src/drop_forget_ref.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/empty_drop.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/empty_enum.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/empty_structs_with_brackets.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/endian_bytes.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/entry.rs33
-rw-r--r--src/tools/clippy/clippy_lints/src/enum_clike.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/error_impl_error.rs87
-rw-r--r--src/tools/clippy/clippy_lints/src/escape.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/eta_reduction.rs337
-rw-r--r--src/tools/clippy/clippy_lints/src/excessive_nesting.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/explicit_write.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/fn_null_check.rs102
-rw-r--r--src/tools/clippy/clippy_lints/src/format.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/format_args.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/format_impl.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/four_forward_slashes.rs99
-rw-r--r--src/tools/clippy/clippy_lints/src/from_over_into.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/must_use.rs17
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/result.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/future_not_send.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/if_let_mutex.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs52
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_return.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/incorrect_impls.rs213
-rw-r--r--src/tools/clippy/clippy_lints/src/index_refutable_slice.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/indexing_slicing.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/inherent_impl.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/inherent_to_string.rs35
-rw-r--r--src/tools/clippy/clippy_lints/src/init_numbered_fields.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/instant_subtraction.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/items_after_test_module.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/large_const_arrays.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/large_enum_variant.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/large_futures.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/large_include_file.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/large_stack_frames.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/len_zero.rs50
-rw-r--r--src/tools/clippy/clippy_lints/src/let_if_seq.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/let_underscore.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs41
-rw-r--r--src/tools/clippy/clippy_lints/src/lifetimes.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/literal_representation.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/manual_find.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/missing_spin_loop.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/never_loop.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/utils.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/macro_use.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_bits.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_clamp.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_float_methods.rs175
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_let_else.rs49
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_range_patterns.rs31
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_strip.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/map_unit_fn.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/match_result_ok.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/manual_filter.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/manual_map.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/manual_utils.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_on_vec_items.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/mod.rs39
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs196
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs152
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/single_match.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/try_err.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/bytecount.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/drain_collect.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/err_expect.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/expect_used.rs44
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/filter_map.rs324
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs53
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/filter_next.rs51
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/format_collect.rs33
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/get_first.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_skip_next.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_skip_zero.rs34
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_clone.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_collect_result_unit.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_flatten.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_identity.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs351
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/needless_collect.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/ok_expect.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/open_options.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs124
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs74
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs52
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/stable_sort_primitive.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/str_splitn.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs58
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs62
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs24
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs54
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs83
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unwrap_or_else_default.rs66
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unwrap_used.rs53
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/useless_asref.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/utils.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/min_ident_chars.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_assert_message.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_inline.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/module_style.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/mut_key.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/mut_reference.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_bool.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_else.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_for_each.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_if.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_late_init.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_parens_on_range_literals.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs441
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs24
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_question_mark.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/new_without_default.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/no_effect.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/non_copy_const.rs75
-rw-r--r--src/tools/clippy/clippy_lints/src/non_expressive_names.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs36
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/bit_mask.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs207
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/eq_op.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/mod.rs43
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/op_ref.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/option_env_unwrap.rs39
-rw-r--r--src/tools/clippy/clippy_lints/src/option_if_let_else.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/panic_unimplemented.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/partialeq_to_none.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr.rs41
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/question_mark.rs72
-rw-r--r--src/tools/clippy/clippy_lints/src/question_mark_used.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/ranges.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/raw_strings.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_async_block.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_closure_call.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_locals.rs126
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_slicing.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/reference.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/regex.rs66
-rw-r--r--src/tools/clippy/clippy_lints/src/renamed_lints.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/returns.rs90
-rw-r--r--src/tools/clippy/clippy_lints/src/self_named_constructors.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/semicolon_block.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/shadow.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs65
-rw-r--r--src/tools/clippy/clippy_lints/src/single_call_fn.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/single_component_path_imports.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/size_of_ref.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs133
-rw-r--r--src/tools/clippy/clippy_lints/src/std_instead_of_core.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/strings.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs49
-rw-r--r--src/tools/clippy/clippy_lints/src/swap.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/temporary_assignment.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/to_digit_is_some.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/unsound_collection_transmute.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/utils.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs324
-rw-r--r--src/tools/clippy/clippy_lints/src/types/borrowed_box.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/types/box_collection.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/types/linked_list.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/types/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/types/option_option.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/types/rc_buffer.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/types/rc_mutex.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/types/vec_box.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/uninit_vec.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unnamed_address.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_async.rs108
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_io_amount.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_unit.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/unwrap.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/use_self.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/useless_conversion.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/author.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/conf.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/if_chain_style.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/vec.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/vec_init_then_push.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/visibility.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/wildcard_imports.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/write.rs22
-rw-r--r--src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs4
343 files changed, 5545 insertions, 2656 deletions
diff --git a/src/tools/clippy/clippy_lints/src/absolute_paths.rs b/src/tools/clippy/clippy_lints/src/absolute_paths.rs
new file mode 100644
index 000000000..04417c4c4
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/absolute_paths.rs
@@ -0,0 +1,100 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::source::snippet_opt;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
+use rustc_hir::{HirId, ItemKind, Node, Path};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::symbol::kw;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of items through absolute paths, like `std::env::current_dir`.
+ ///
+ /// ### Why is this bad?
+ /// Many codebases have their own style when it comes to importing, but one that is seldom used
+ /// is using absolute paths *everywhere*. This is generally considered unidiomatic, and you
+ /// should add a `use` statement.
+ ///
+ /// The default maximum segments (2) is pretty strict, you may want to increase this in
+ /// `clippy.toml`.
+ ///
+ /// Note: One exception to this is code from macro expansion - this does not lint such cases, as
+ /// using absolute paths is the proper way of referencing items in one.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let x = std::f64::consts::PI;
+ /// ```
+ /// Use any of the below instead, or anything else:
+ /// ```rust
+ /// use std::f64;
+ /// use std::f64::consts;
+ /// use std::f64::consts::PI;
+ /// let x = f64::consts::PI;
+ /// let x = consts::PI;
+ /// let x = PI;
+ /// use std::f64::consts as f64_consts;
+ /// let x = f64_consts::PI;
+ /// ```
+ #[clippy::version = "1.73.0"]
+ pub ABSOLUTE_PATHS,
+ restriction,
+ "checks for usage of an item without a `use` statement"
+}
+impl_lint_pass!(AbsolutePaths => [ABSOLUTE_PATHS]);
+
+pub struct AbsolutePaths {
+ pub absolute_paths_max_segments: u64,
+ pub absolute_paths_allowed_crates: FxHashSet<String>,
+}
+
+impl LateLintPass<'_> for AbsolutePaths {
+ // We should only lint `QPath::Resolved`s, but since `Path` is only used in `Resolved` and `UsePath`
+ // we don't need to use a visitor or anything as we can just check if the `Node` for `hir_id` isn't
+ // a `Use`
+ #[expect(clippy::cast_possible_truncation)]
+ fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, hir_id: HirId) {
+ let Self {
+ absolute_paths_max_segments,
+ absolute_paths_allowed_crates,
+ } = self;
+
+ if !path.span.from_expansion()
+ && let Some(node) = cx.tcx.hir().find(hir_id)
+ && !matches!(node, Node::Item(item) if matches!(item.kind, ItemKind::Use(_, _)))
+ && let [first, rest @ ..] = path.segments
+ // Handle `::std`
+ && let (segment, len) = if first.ident.name == kw::PathRoot {
+ // Indexing is fine as `PathRoot` must be followed by another segment. `len() - 1`
+ // is fine here for the same reason
+ (&rest[0], path.segments.len() - 1)
+ } else {
+ (first, path.segments.len())
+ }
+ && len > *absolute_paths_max_segments as usize
+ && let Some(segment_snippet) = snippet_opt(cx, segment.ident.span)
+ && segment_snippet == segment.ident.as_str()
+ {
+ let is_abs_external =
+ matches!(segment.res, Res::Def(DefKind::Mod, DefId { index, .. }) if index == CRATE_DEF_INDEX);
+ let is_abs_crate = segment.ident.name == kw::Crate;
+
+ if is_abs_external && absolute_paths_allowed_crates.contains(segment.ident.name.as_str())
+ || is_abs_crate && absolute_paths_allowed_crates.contains("crate")
+ {
+ return;
+ }
+
+ if is_abs_external || is_abs_crate {
+ span_lint(
+ cx,
+ ABSOLUTE_PATHS,
+ path.span,
+ "consider bringing this path into scope with the `use` keyword",
+ );
+ }
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/allow_attributes.rs b/src/tools/clippy/clippy_lints/src/allow_attributes.rs
index eb2118471..e1ef514ed 100644
--- a/src/tools/clippy/clippy_lints/src/allow_attributes.rs
+++ b/src/tools/clippy/clippy_lints/src/allow_attributes.rs
@@ -1,5 +1,6 @@
use ast::{AttrStyle, Attribute};
-use clippy_utils::{diagnostics::span_lint_and_sugg, is_from_proc_macro};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_from_proc_macro;
use rustc_ast as ast;
use rustc_errors::Applicability;
use rustc_lint::{LateContext, LateLintPass, LintContext};
diff --git a/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs b/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs
index 98ee8a9a8..35a04b5e4 100644
--- a/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs
+++ b/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs
@@ -1,9 +1,8 @@
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::last_path_segment;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
+use clippy_utils::{is_from_proc_macro, last_path_segment};
use rustc_hir::{Expr, ExprKind};
-use rustc_lint::LateContext;
-use rustc_lint::LateLintPass;
+use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_middle::ty::print::with_forced_trimmed_paths;
use rustc_middle::ty::GenericArgKind;
@@ -39,10 +38,11 @@ declare_clippy_lint! {
}
declare_lint_pass!(ArcWithNonSendSync => [ARC_WITH_NON_SEND_SYNC]);
-impl LateLintPass<'_> for ArcWithNonSendSync {
- fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
- let ty = cx.typeck_results().expr_ty(expr);
- if is_type_diagnostic_item(cx, ty, sym::Arc)
+impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+ if !expr.span.from_expansion()
+ && let ty = cx.typeck_results().expr_ty(expr)
+ && is_type_diagnostic_item(cx, ty, sym::Arc)
&& let ExprKind::Call(func, [arg]) = expr.kind
&& let ExprKind::Path(func_path) = func.kind
&& last_path_segment(&func_path).ident.name == sym::new
@@ -55,6 +55,7 @@ impl LateLintPass<'_> for ArcWithNonSendSync {
&& let Some(sync) = cx.tcx.lang_items().sync_trait()
&& let [is_send, is_sync] = [send, sync].map(|id| implements_trait(cx, arg_ty, id, &[]))
&& !(is_send && is_sync)
+ && !is_from_proc_macro(cx, expr)
{
span_lint_and_then(
cx,
diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
index a8dc0cb3b..b90914e93 100644
--- a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
+++ b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
@@ -31,14 +31,20 @@ declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]);
impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants {
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
- let Some(macro_call) = root_macro_call_first_node(cx, e) else { return };
+ let Some(macro_call) = root_macro_call_first_node(cx, e) else {
+ return;
+ };
let is_debug = match cx.tcx.get_diagnostic_name(macro_call.def_id) {
Some(sym::debug_assert_macro) => true,
Some(sym::assert_macro) => false,
_ => return,
};
- let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else { return };
- let Some(Constant::Bool(val)) = constant(cx, cx.typeck_results(), condition) else { return };
+ let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else {
+ return;
+ };
+ let Some(Constant::Bool(val)) = constant(cx, cx.typeck_results(), condition) else {
+ return;
+ };
if val {
span_lint_and_help(
cx,
diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs
index f6d6c23bb..2980c9d6d 100644
--- a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs
+++ b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs
@@ -47,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
&& let result_type_with_refs = cx.typeck_results().expr_ty(recv)
&& let result_type = result_type_with_refs.peel_refs()
&& is_type_diagnostic_item(cx, result_type, sym::Result)
- && let ty::Adt(_, substs) = result_type.kind()
+ && let ty::Adt(_, args) = result_type.kind()
{
if !is_copy(cx, result_type) {
if result_type_with_refs != result_type {
@@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
let semicolon = if is_expr_final_block_expr(cx.tcx, e) {";"} else {""};
let mut app = Applicability::MachineApplicable;
match method_segment.ident.as_str() {
- "is_ok" if type_suitable_to_unwrap(cx, substs.type_at(1)) => {
+ "is_ok" if type_suitable_to_unwrap(cx, args.type_at(1)) => {
span_lint_and_sugg(
cx,
ASSERTIONS_ON_RESULT_STATES,
@@ -75,7 +75,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
app,
);
}
- "is_err" if type_suitable_to_unwrap(cx, substs.type_at(0)) => {
+ "is_err" if type_suitable_to_unwrap(cx, args.type_at(0)) => {
span_lint_and_sugg(
cx,
ASSERTIONS_ON_RESULT_STATES,
diff --git a/src/tools/clippy/clippy_lints/src/attrs.rs b/src/tools/clippy/clippy_lints/src/attrs.rs
index 2ba78f995..2a5be2756 100644
--- a/src/tools/clippy/clippy_lints/src/attrs.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs.rs
@@ -1,12 +1,10 @@
//! checks for attributes
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::is_from_proc_macro;
use clippy_utils::macros::{is_panic, macro_backtrace};
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
-use clippy_utils::{
- diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then},
- is_from_proc_macro,
-};
use if_chain::if_chain;
use rustc_ast::{AttrKind, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem};
use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs b/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs
index 9c0532474..1593d7b0f 100644
--- a/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs
+++ b/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs
@@ -1,9 +1,8 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
-use clippy_utils::get_parent_expr;
-use clippy_utils::higher;
use clippy_utils::source::snippet_block_with_applicability;
use clippy_utils::ty::implements_trait;
use clippy_utils::visitors::{for_each_expr, Descend};
+use clippy_utils::{get_parent_expr, higher};
use core::ops::ControlFlow;
use if_chain::if_chain;
use rustc_errors::Applicability;
@@ -85,8 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions {
);
}
} else {
- let span =
- block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
+ let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
if span.from_expansion() || expr.span.from_expansion() {
return;
}
diff --git a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
index e8775b081..450359771 100644
--- a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
+++ b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
@@ -61,7 +61,7 @@ fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -
)
})
.map_or(false, |assoc_item| {
- let proj = Ty::new_projection(cx.tcx,assoc_item.def_id, cx.tcx.mk_substs_trait(ty, []));
+ let proj = Ty::new_projection(cx.tcx, assoc_item.def_id, cx.tcx.mk_args_trait(ty, []));
let nty = cx.tcx.normalize_erasing_regions(cx.param_env, proj);
nty.is_bool()
@@ -70,14 +70,18 @@ fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -
impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
+ let Some(macro_call) = root_macro_call_first_node(cx, expr) else {
+ return;
+ };
let macro_name = cx.tcx.item_name(macro_call.def_id);
let eq_macro = match macro_name.as_str() {
"assert_eq" | "debug_assert_eq" => true,
"assert_ne" | "debug_assert_ne" => false,
_ => return,
};
- let Some ((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return };
+ let Some((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else {
+ return;
+ };
let a_span = a.span.source_callsite();
let b_span = b.span.source_callsite();
@@ -126,7 +130,9 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison {
let mut suggestions = vec![(name_span, non_eq_mac.to_string()), (lit_span, String::new())];
if bool_value ^ eq_macro {
- let Some(sugg) = Sugg::hir_opt(cx, non_lit_expr) else { return };
+ let Some(sugg) = Sugg::hir_opt(cx, non_lit_expr) else {
+ return;
+ };
suggestions.push((non_lit_expr.span, (!sugg).to_string()));
}
diff --git a/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs b/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs
index bdb3a0116..1828dd651 100644
--- a/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs
+++ b/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs
@@ -4,7 +4,9 @@ use rustc_hir::{Block, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use clippy_utils::{diagnostics::span_lint_and_then, in_constant, is_else_clause, is_integer_literal, sugg::Sugg};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::{in_constant, is_else_clause, is_integer_literal};
use rustc_errors::Applicability;
declare_clippy_lint! {
diff --git a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
index 814108ed8..b3dbbb08f 100644
--- a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
@@ -1,9 +1,8 @@
use crate::reference::DEREF_ADDROF;
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::is_from_proc_macro;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::implements_trait;
-use clippy_utils::{get_parent_expr, is_lint_allowed};
+use clippy_utils::{get_parent_expr, is_from_proc_macro, is_lint_allowed};
use rustc_errors::Applicability;
use rustc_hir::{ExprKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
diff --git a/src/tools/clippy/clippy_lints/src/box_default.rs b/src/tools/clippy/clippy_lints/src/box_default.rs
index e42c3fe24..fa9c525fc 100644
--- a/src/tools/clippy/clippy_lints/src/box_default.rs
+++ b/src/tools/clippy/clippy_lints/src/box_default.rs
@@ -1,12 +1,10 @@
-use clippy_utils::{
- diagnostics::span_lint_and_sugg, get_parent_node, is_default_equivalent, macros::macro_backtrace, match_path,
- path_def_id, paths, ty::expr_sig,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::macros::macro_backtrace;
+use clippy_utils::ty::expr_sig;
+use clippy_utils::{get_parent_node, is_default_equivalent, match_path, path_def_id, paths};
use rustc_errors::Applicability;
-use rustc_hir::{
- intravisit::{walk_ty, Visitor},
- Block, Expr, ExprKind, Local, Node, QPath, TyKind,
-};
+use rustc_hir::intravisit::{walk_ty, Visitor};
+use rustc_hir::{Block, Expr, ExprKind, Local, Node, QPath, TyKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::print::with_forced_trimmed_paths;
diff --git a/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs b/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs
index 1633ffd58..1e56ed5f4 100644
--- a/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs
@@ -3,10 +3,8 @@ use clippy_utils::source::snippet_opt;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
-use rustc_middle::{
- mir::Mutability,
- ty::{self, Ty, TypeAndMut},
-};
+use rustc_middle::mir::Mutability;
+use rustc_middle::ty::{self, Ty, TypeAndMut};
use super::AS_PTR_CAST_MUT;
@@ -17,7 +15,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
&& let ExprKind::MethodCall(method_name, receiver, [], _) = cast_expr.peel_blocks().kind
&& method_name.ident.name == rustc_span::sym::as_ptr
&& let Some(as_ptr_did) = cx.typeck_results().type_dependent_def_id(cast_expr.peel_blocks().hir_id)
- && let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did).subst_identity()
+ && let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did).instantiate_identity()
&& let Some(first_param_ty) = as_ptr_sig.skip_binder().inputs().iter().next()
&& let ty::Ref(_, _, Mutability::Not) = first_param_ty.kind()
&& let Some(recv) = snippet_opt(cx, receiver.span)
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
index 6c8ee296c..5bf467efa 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
@@ -66,7 +66,7 @@ fn is_used_as_unaligned(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
if matches!(name.ident.as_str(), "read_unaligned" | "write_unaligned")
&& let Some(def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id)
&& let Some(def_id) = cx.tcx.impl_of_method(def_id)
- && cx.tcx.type_of(def_id).subst_identity().is_unsafe_ptr()
+ && cx.tcx.type_of(def_id).instantiate_identity().is_unsafe_ptr()
{
true
} else {
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs b/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs
index 27cc5a1c3..4d9cc4cac 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs
@@ -1,10 +1,12 @@
+use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::{diagnostics::span_lint_and_then, source};
+use clippy_utils::source;
use if_chain::if_chain;
use rustc_ast::Mutability;
use rustc_hir::{Expr, ExprKind, Node};
use rustc_lint::LateContext;
-use rustc_middle::ty::{self, layout::LayoutOf, Ty, TypeAndMut};
+use rustc_middle::ty::layout::LayoutOf;
+use rustc_middle::ty::{self, Ty, TypeAndMut};
use super::CAST_SLICE_DIFFERENT_SIZES;
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
index 1233c632a..5e0123842 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
@@ -4,7 +4,8 @@ use clippy_utils::source::snippet_with_context;
use clippy_utils::{match_def_path, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
-use rustc_hir::{def_id::DefId, Expr, ExprKind};
+use rustc_hir::def_id::DefId;
+use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs
index 0ac6ef649..d34de305f 100644
--- a/src/tools/clippy/clippy_lints/src/casts/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs
@@ -181,6 +181,14 @@ declare_clippy_lint! {
/// ### Why is this bad?
/// It's just unnecessary.
///
+ /// ### Known problems
+ /// When the expression on the left is a function call, the lint considers the return type to be
+ /// a type alias if it's aliased through a `use` statement
+ /// (like `use std::io::Result as IoResult`). It will not lint such cases.
+ ///
+ /// This check is also rather primitive. It will only work on primitive types without any
+ /// intermediate references, raw pointers and trait objects may or may not work.
+ ///
/// ### Example
/// ```rust
/// let _ = 2i32 as i32;
diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs
index 15ffb00da..181dbcf6e 100644
--- a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs
@@ -1,9 +1,7 @@
-use std::borrow::Cow;
-
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
-use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Mutability, TyKind};
use rustc_lint::LateContext;
@@ -16,33 +14,41 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) {
return;
}
- if_chain! {
- if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind;
- let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr));
- if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, .. }) = cast_from.kind();
- if let ty::RawPtr(TypeAndMut { ty: to_pointee_ty, mutbl: to_mutbl }) = cast_to.kind();
- if matches!((from_mutbl, to_mutbl),
- (Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut));
+ if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind
+ && let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr))
+ && let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, .. }) = cast_from.kind()
+ && let ty::RawPtr(TypeAndMut { ty: to_pointee_ty, mutbl: to_mutbl }) = cast_to.kind()
+ && matches!((from_mutbl, to_mutbl),
+ (Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut))
// The `U` in `pointer::cast` have to be `Sized`
// as explained here: https://github.com/rust-lang/rust/issues/60602.
- if to_pointee_ty.is_sized(cx.tcx, cx.param_env);
- then {
- let mut applicability = Applicability::MachineApplicable;
- let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut applicability);
- let turbofish = match &cast_to_hir_ty.kind {
- TyKind::Infer => Cow::Borrowed(""),
- TyKind::Ptr(mut_ty) if matches!(mut_ty.ty.kind, TyKind::Infer) => Cow::Borrowed(""),
- _ => Cow::Owned(format!("::<{to_pointee_ty}>")),
- };
- span_lint_and_sugg(
- cx,
- PTR_AS_PTR,
- expr.span,
- "`as` casting between raw pointers without changing its mutability",
- "try `pointer::cast`, a safer alternative",
- format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_par()),
- applicability,
- );
- }
+ && to_pointee_ty.is_sized(cx.tcx, cx.param_env)
+ {
+ let mut app = Applicability::MachineApplicable;
+ let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app);
+ let turbofish = match &cast_to_hir_ty.kind {
+ TyKind::Infer => String::new(),
+ TyKind::Ptr(mut_ty) => {
+ if matches!(mut_ty.ty.kind, TyKind::Infer) {
+ String::new()
+ } else {
+ format!(
+ "::<{}>",
+ snippet_with_applicability(cx, mut_ty.ty.span, "/* type */", &mut app)
+ )
+ }
+ },
+ _ => return,
+ };
+
+ span_lint_and_sugg(
+ cx,
+ PTR_AS_PTR,
+ expr.span,
+ "`as` casting between raw pointers without changing its mutability",
+ "try `pointer::cast`, a safer alternative",
+ format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_par()),
+ app,
+ );
}
}
diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs
index f0c1df014..ce1ab1091 100644
--- a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs
@@ -1,6 +1,6 @@
-use clippy_utils::msrvs::POINTER_CAST_CONSTNESS;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{Msrv, POINTER_CAST_CONSTNESS};
use clippy_utils::sugg::Sugg;
-use clippy_utils::{diagnostics::span_lint_and_sugg, msrvs::Msrv};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, Mutability};
diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
index 71cf2aea0..86057bb74 100644
--- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
@@ -56,7 +56,7 @@ pub(super) fn check<'tcx>(
&format!("casting raw pointers to the same type and constness is unnecessary (`{cast_from}` -> `{cast_to}`)"),
"try",
cast_str.clone(),
- Applicability::MachineApplicable,
+ Applicability::MaybeIncorrect,
);
}
}
@@ -85,11 +85,6 @@ pub(super) fn check<'tcx>(
}
}
- // skip cast of fn call that returns type alias
- if let ExprKind::Cast(inner, ..) = expr.kind && is_cast_from_ty_alias(cx, inner, cast_from) {
- return false;
- }
-
// skip cast to non-primitive type
if_chain! {
if let ExprKind::Cast(_, cast_to) = expr.kind;
@@ -101,6 +96,11 @@ pub(super) fn check<'tcx>(
}
}
+ // skip cast of fn call that returns type alias
+ if let ExprKind::Cast(inner, ..) = expr.kind && is_cast_from_ty_alias(cx, inner, cast_from) {
+ return false;
+ }
+
if let Some(lit) = get_numeric_literal(cast_expr) {
let literal_str = &cast_str;
@@ -254,14 +254,12 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx
// function's declaration snippet is exactly equal to the `Ty`. That way, we can
// see whether it's a type alias.
//
- // Will this work for more complex types? Probably not!
+ // FIXME: This won't work if the type is given an alias through `use`, should we
+ // consider this a type alias as well?
if !snippet
.split("->")
- .skip(0)
- .map(|s| {
- s.trim() == cast_from.to_string()
- || s.split("where").any(|ty| ty.trim() == cast_from.to_string())
- })
+ .skip(1)
+ .map(|s| snippet_eq_ty(s, cast_from) || s.split("where").any(|ty| snippet_eq_ty(ty, cast_from)))
.any(|a| a)
{
return ControlFlow::Break(());
@@ -288,3 +286,7 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx
})
.is_some()
}
+
+fn snippet_eq_ty(snippet: &str, ty: Ty<'_>) -> bool {
+ snippet.trim() == ty.to_string() || snippet.trim().contains(&format!("::{ty}"))
+}
diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs
index 1c321f46e..e3a09636e 100644
--- a/src/tools/clippy/clippy_lints/src/copies.rs
+++ b/src/tools/clippy/clippy_lints/src/copies.rs
@@ -10,8 +10,7 @@ use core::iter;
use core::ops::ControlFlow;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefIdSet;
-use rustc_hir::intravisit;
-use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind};
+use rustc_hir::{intravisit, BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::query::Key;
use rustc_session::{declare_tool_lint, impl_lint_pass};
diff --git a/src/tools/clippy/clippy_lints/src/copy_iterator.rs b/src/tools/clippy/clippy_lints/src/copy_iterator.rs
index 0fc115232..5d04ad011 100644
--- a/src/tools/clippy/clippy_lints/src/copy_iterator.rs
+++ b/src/tools/clippy/clippy_lints/src/copy_iterator.rs
@@ -43,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for CopyIterator {
of_trait: Some(ref trait_ref),
..
}) = item.kind;
- let ty = cx.tcx.type_of(item.owner_id).subst_identity();
+ let ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
if is_copy(cx, ty);
if let Some(trait_id) = trait_ref.trait_def_id();
if cx.tcx.is_diagnostic_item(sym::Iterator, trait_id);
diff --git a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs
index 7436e9ce8..726674d88 100644
--- a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs
+++ b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs
@@ -5,7 +5,8 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::{symbol::sym, Span};
+use rustc_span::symbol::sym;
+use rustc_span::Span;
declare_clippy_lint! {
/// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/dbg_macro.rs b/src/tools/clippy/clippy_lints/src/dbg_macro.rs
index ea17e7a60..49452136d 100644
--- a/src/tools/clippy/clippy_lints/src/dbg_macro.rs
+++ b/src/tools/clippy/clippy_lints/src/dbg_macro.rs
@@ -71,7 +71,9 @@ impl DbgMacro {
impl LateLintPass<'_> for DbgMacro {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
- let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
+ 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) {
// allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml
if self.allow_dbg_in_tests
diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs
index 9d9ee6ba3..db114abfc 100644
--- a/src/tools/clippy/clippy_lints/src/declared_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs
@@ -37,6 +37,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::utils::internal_lints::produce_ice::PRODUCE_ICE_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH_INFO,
+ crate::absolute_paths::ABSOLUTE_PATHS_INFO,
crate::allow_attributes::ALLOW_ATTRIBUTES_INFO,
crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO,
crate::approx_const::APPROX_CONSTANT_INFO,
@@ -155,6 +156,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::enum_variants::MODULE_INCEPTION_INFO,
crate::enum_variants::MODULE_NAME_REPETITIONS_INFO,
crate::equatable_if_let::EQUATABLE_IF_LET_INFO,
+ crate::error_impl_error::ERROR_IMPL_ERROR_INFO,
crate::escape::BOXED_LOCAL_INFO,
crate::eta_reduction::REDUNDANT_CLOSURE_INFO,
crate::eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS_INFO,
@@ -171,7 +173,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::float_literal::LOSSY_FLOAT_LITERAL_INFO,
crate::floating_point_arithmetic::IMPRECISE_FLOPS_INFO,
crate::floating_point_arithmetic::SUBOPTIMAL_FLOPS_INFO,
- crate::fn_null_check::FN_NULL_CHECK_INFO,
crate::format::USELESS_FORMAT_INFO,
crate::format_args::FORMAT_IN_FORMAT_ARGS_INFO,
crate::format_args::TO_STRING_IN_FORMAT_ARGS_INFO,
@@ -184,6 +185,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING_INFO,
crate::formatting::SUSPICIOUS_ELSE_FORMATTING_INFO,
crate::formatting::SUSPICIOUS_UNARY_OP_FORMATTING_INFO,
+ crate::four_forward_slashes::FOUR_FORWARD_SLASHES_INFO,
crate::from_over_into::FROM_OVER_INTO_INFO,
crate::from_raw_with_void_ptr::FROM_RAW_WITH_VOID_PTR_INFO,
crate::from_str_radix_10::FROM_STR_RADIX_10_INFO,
@@ -201,12 +203,14 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::if_let_mutex::IF_LET_MUTEX_INFO,
crate::if_not_else::IF_NOT_ELSE_INFO,
crate::if_then_some_else_none::IF_THEN_SOME_ELSE_NONE_INFO,
+ crate::ignored_unit_patterns::IGNORED_UNIT_PATTERNS_INFO,
crate::implicit_hasher::IMPLICIT_HASHER_INFO,
crate::implicit_return::IMPLICIT_RETURN_INFO,
crate::implicit_saturating_add::IMPLICIT_SATURATING_ADD_INFO,
crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO,
crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO,
crate::incorrect_impls::INCORRECT_CLONE_IMPL_ON_COPY_TYPE_INFO,
+ crate::incorrect_impls::INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE_INFO,
crate::index_refutable_slice::INDEX_REFUTABLE_SLICE_INFO,
crate::indexing_slicing::INDEXING_SLICING_INFO,
crate::indexing_slicing::OUT_OF_BOUNDS_INDEXING_INFO,
@@ -273,6 +277,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::manual_async_fn::MANUAL_ASYNC_FN_INFO,
crate::manual_bits::MANUAL_BITS_INFO,
crate::manual_clamp::MANUAL_CLAMP_INFO,
+ crate::manual_float_methods::MANUAL_IS_FINITE_INFO,
+ crate::manual_float_methods::MANUAL_IS_INFINITE_INFO,
crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO,
crate::manual_let_else::MANUAL_LET_ELSE_INFO,
crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO,
@@ -303,6 +309,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS_INFO,
crate::matches::MATCH_WILD_ERR_ARM_INFO,
crate::matches::NEEDLESS_MATCH_INFO,
+ crate::matches::REDUNDANT_GUARDS_INFO,
crate::matches::REDUNDANT_PATTERN_MATCHING_INFO,
crate::matches::REST_PAT_IN_FULLY_BOUND_STRUCTS_INFO,
crate::matches::SIGNIFICANT_DROP_IN_SCRUTINEE_INFO,
@@ -331,11 +338,13 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::methods::EXPECT_USED_INFO,
crate::methods::EXTEND_WITH_DRAIN_INFO,
crate::methods::FILETYPE_IS_FILE_INFO,
+ crate::methods::FILTER_MAP_BOOL_THEN_INFO,
crate::methods::FILTER_MAP_IDENTITY_INFO,
crate::methods::FILTER_MAP_NEXT_INFO,
crate::methods::FILTER_NEXT_INFO,
crate::methods::FLAT_MAP_IDENTITY_INFO,
crate::methods::FLAT_MAP_OPTION_INFO,
+ crate::methods::FORMAT_COLLECT_INFO,
crate::methods::FROM_ITER_INSTEAD_OF_COLLECT_INFO,
crate::methods::GET_FIRST_INFO,
crate::methods::GET_LAST_WITH_LEN_INFO,
@@ -356,6 +365,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::methods::ITER_ON_SINGLE_ITEMS_INFO,
crate::methods::ITER_OVEREAGER_CLONED_INFO,
crate::methods::ITER_SKIP_NEXT_INFO,
+ crate::methods::ITER_SKIP_ZERO_INFO,
crate::methods::ITER_WITH_DRAIN_INFO,
crate::methods::MANUAL_FILTER_MAP_INFO,
crate::methods::MANUAL_FIND_MAP_INFO,
@@ -389,6 +399,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::methods::OR_THEN_UNWRAP_INFO,
crate::methods::PATH_BUF_PUSH_OVERWRITE_INFO,
crate::methods::RANGE_ZIP_WITH_LEN_INFO,
+ crate::methods::READONLY_WRITE_LOCK_INFO,
+ crate::methods::READ_LINE_WITHOUT_TRIM_INFO,
crate::methods::REPEAT_ONCE_INFO,
crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO,
crate::methods::SEARCH_IS_SOME_INFO,
@@ -400,10 +412,12 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::methods::SKIP_WHILE_NEXT_INFO,
crate::methods::STABLE_SORT_PRIMITIVE_INFO,
crate::methods::STRING_EXTEND_CHARS_INFO,
+ crate::methods::STRING_LIT_CHARS_ANY_INFO,
crate::methods::SUSPICIOUS_COMMAND_ARG_SPACE_INFO,
crate::methods::SUSPICIOUS_MAP_INFO,
crate::methods::SUSPICIOUS_SPLITN_INFO,
crate::methods::SUSPICIOUS_TO_OWNED_INFO,
+ crate::methods::TYPE_ID_ON_BOX_INFO,
crate::methods::UNINIT_ASSUMED_INIT_INFO,
crate::methods::UNIT_HASH_INFO,
crate::methods::UNNECESSARY_FILTER_MAP_INFO,
@@ -414,7 +428,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::methods::UNNECESSARY_LITERAL_UNWRAP_INFO,
crate::methods::UNNECESSARY_SORT_BY_INFO,
crate::methods::UNNECESSARY_TO_OWNED_INFO,
- crate::methods::UNWRAP_OR_ELSE_DEFAULT_INFO,
+ crate::methods::UNWRAP_OR_DEFAULT_INFO,
crate::methods::UNWRAP_USED_INFO,
crate::methods::USELESS_ASREF_INFO,
crate::methods::VEC_RESIZE_TO_ZERO_INFO,
@@ -469,6 +483,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::needless_if::NEEDLESS_IF_INFO,
crate::needless_late_init::NEEDLESS_LATE_INIT_INFO,
crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO,
+ crate::needless_pass_by_ref_mut::NEEDLESS_PASS_BY_REF_MUT_INFO,
crate::needless_pass_by_value::NEEDLESS_PASS_BY_VALUE_INFO,
crate::needless_question_mark::NEEDLESS_QUESTION_MARK_INFO,
crate::needless_update::NEEDLESS_UPDATE_INFO,
@@ -503,6 +518,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::operators::FLOAT_CMP_CONST_INFO,
crate::operators::FLOAT_EQUALITY_WITHOUT_ABS_INFO,
crate::operators::IDENTITY_OP_INFO,
+ crate::operators::IMPOSSIBLE_COMPARISONS_INFO,
crate::operators::INEFFECTIVE_BIT_MASK_INFO,
crate::operators::INTEGER_DIVISION_INFO,
crate::operators::MISREFACTORED_ASSIGN_OP_INFO,
@@ -511,6 +527,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::operators::NEEDLESS_BITWISE_BOOL_INFO,
crate::operators::OP_REF_INFO,
crate::operators::PTR_EQ_INFO,
+ crate::operators::REDUNDANT_COMPARISONS_INFO,
crate::operators::SELF_ASSIGNMENT_INFO,
crate::operators::VERBOSE_BIT_MASK_INFO,
crate::option_env_unwrap::OPTION_ENV_UNWRAP_INFO,
@@ -550,6 +567,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::redundant_closure_call::REDUNDANT_CLOSURE_CALL_INFO,
crate::redundant_else::REDUNDANT_ELSE_INFO,
crate::redundant_field_names::REDUNDANT_FIELD_NAMES_INFO,
+ crate::redundant_locals::REDUNDANT_LOCALS_INFO,
crate::redundant_pub_crate::REDUNDANT_PUB_CRATE_INFO,
crate::redundant_slicing::DEREF_BY_SLICING_INFO,
crate::redundant_slicing::REDUNDANT_SLICING_INFO,
@@ -563,6 +581,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::return_self_not_must_use::RETURN_SELF_NOT_MUST_USE_INFO,
crate::returns::LET_AND_RETURN_INFO,
crate::returns::NEEDLESS_RETURN_INFO,
+ crate::returns::NEEDLESS_RETURN_WITH_QUESTION_MARK_INFO,
crate::same_name_method::SAME_NAME_METHOD_INFO,
crate::self_named_constructors::SELF_NAMED_CONSTRUCTORS_INFO,
crate::semicolon_block::SEMICOLON_INSIDE_BLOCK_INFO,
diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs
index 80c22742b..763ad0264 100644
--- a/src/tools/clippy/clippy_lints/src/default.rs
+++ b/src/tools/clippy/clippy_lints/src/default.rs
@@ -150,7 +150,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
.fields
.iter()
.all(|field| {
- is_copy(cx, cx.tcx.type_of(field.did).subst_identity())
+ is_copy(cx, cx.tcx.type_of(field.did).instantiate_identity())
});
if !has_drop(cx, binding_type) || all_fields_are_copy;
then {
@@ -219,11 +219,11 @@ impl<'tcx> LateLintPass<'tcx> for Default {
// give correct suggestion if generics are involved (see #6944)
let binding_type = if_chain! {
- if let ty::Adt(adt_def, substs) = binding_type.kind();
- if !substs.is_empty();
+ if let ty::Adt(adt_def, args) = binding_type.kind();
+ if !args.is_empty();
then {
let adt_def_ty_name = cx.tcx.item_name(adt_def.did());
- let generic_args = substs.iter().collect::<Vec<_>>();
+ let generic_args = args.iter().collect::<Vec<_>>();
let tys_str = generic_args
.iter()
.map(ToString::to_string)
diff --git a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs
index ca9514ccc..a294c6937 100644
--- a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs
+++ b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs
@@ -1,5 +1,7 @@
-use clippy_utils::{diagnostics::span_lint_and_sugg, is_ty_alias, match_def_path, paths};
-use hir::{def::Res, ExprKind};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::{is_ty_alias, match_def_path, paths};
+use hir::def::Res;
+use hir::ExprKind;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
diff --git a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs
index f296b80d2..572990aab 100644
--- a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs
+++ b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs
@@ -1,7 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::last_path_segment;
use clippy_utils::source::snippet_with_context;
-use clippy_utils::{match_def_path, paths};
+use clippy_utils::{last_path_segment, match_def_path, paths};
use rustc_errors::Applicability;
use rustc_hir::{def, Expr, ExprKind, GenericArg, QPath, TyKind};
use rustc_lint::{LateContext, LateLintPass};
diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
index e53a9877b..d09428dbc 100644
--- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
+++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
@@ -4,15 +4,11 @@ use clippy_utils::{get_parent_node, numeric_literal};
use if_chain::if_chain;
use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
use rustc_errors::Applicability;
-use rustc_hir::{
- intravisit::{walk_expr, walk_stmt, Visitor},
- Body, Expr, ExprKind, HirId, ItemKind, Lit, Node, Stmt, StmtKind,
-};
+use rustc_hir::intravisit::{walk_expr, walk_stmt, Visitor};
+use rustc_hir::{Body, Expr, ExprKind, HirId, ItemKind, Lit, Node, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::{
- lint::in_external_macro,
- ty::{self, FloatTy, IntTy, PolyFnSig, Ty},
-};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::{self, FloatTy, IntTy, PolyFnSig, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use std::iter;
@@ -141,7 +137,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
ExprKind::MethodCall(_, receiver, args, _) => {
if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) {
- let fn_sig = self.cx.tcx.fn_sig(def_id).subst_identity().skip_binder();
+ let fn_sig = self.cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder();
for (expr, bound) in iter::zip(std::iter::once(*receiver).chain(args.iter()), fn_sig.inputs()) {
self.ty_bounds.push((*bound).into());
self.visit_expr(expr);
@@ -167,7 +163,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
.iter()
.find_map(|f_def| {
if f_def.ident(self.cx.tcx) == field.ident
- { Some(self.cx.tcx.type_of(f_def.did).subst_identity()) }
+ { Some(self.cx.tcx.type_of(f_def.did).instantiate_identity()) }
else { None }
});
self.ty_bounds.push(bound.into());
@@ -213,9 +209,9 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
fn fn_sig_opt<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<PolyFnSig<'tcx>> {
let node_ty = cx.typeck_results().node_type_opt(hir_id)?;
- // We can't use `Ty::fn_sig` because it automatically performs substs, this may result in FNs.
+ // We can't use `Ty::fn_sig` because it automatically performs args, this may result in FNs.
match node_ty.kind() {
- ty::FnDef(def_id, _) => Some(cx.tcx.fn_sig(*def_id).subst_identity()),
+ ty::FnDef(def_id, _) => Some(cx.tcx.fn_sig(*def_id).instantiate_identity()),
ty::FnPtr(fn_sig) => Some(*fn_sig),
_ => None,
}
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index 12f2f37e3..58c278550 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -3,22 +3,23 @@ use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exact
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::sugg::has_enclosing_paren;
-use clippy_utils::ty::{adt_and_variant_of_res, expr_sig, is_copy, peel_mid_ty_refs, ty_sig};
+use clippy_utils::ty::{is_copy, peel_mid_ty_refs};
use clippy_utils::{
- fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, path_to_local, walk_to_expr_usage,
+ expr_use_ctxt, get_parent_expr, get_parent_node, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode,
};
+use hir::def::DefKind;
+use hir::MatchSource;
use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::graph::iterate::{CycleDetector, TriColorDepthFirstSearch};
use rustc_errors::Applicability;
+use rustc_hir::def::Res;
+use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{walk_ty, Visitor};
use rustc_hir::{
- self as hir,
- def_id::{DefId, LocalDefId},
- BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, GenericArg, HirId, ImplItem,
- ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem,
- TraitItemKind, TyKind, UnOp,
+ self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, Mutability, Node, Pat, PatKind,
+ Path, QPath, TyKind, UnOp,
};
use rustc_index::bit_set::BitSet;
use rustc_infer::infer::TyCtxtInferExt;
@@ -26,13 +27,15 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::mir::{Rvalue, StatementKind};
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
use rustc_middle::ty::{
- self, Binder, BoundVariableKind, ClauseKind, EarlyBinder, FnSig, GenericArgKind, List, ParamEnv, ParamTy,
- ProjectionPredicate, Ty, TyCtxt, TypeVisitableExt, TypeckResults,
+ self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, List, ParamEnv, ParamTy, ProjectionPredicate, Ty,
+ TyCtxt, TypeVisitableExt, TypeckResults,
};
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::{symbol::sym, Span, Symbol};
+use rustc_span::symbol::sym;
+use rustc_span::{Span, Symbol};
use rustc_trait_selection::infer::InferCtxtExt as _;
-use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
+use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
+use rustc_trait_selection::traits::{Obligation, ObligationCause};
use std::collections::VecDeque;
declare_clippy_lint! {
@@ -77,6 +80,11 @@ declare_clippy_lint! {
/// Suggests that the receiver of the expression borrows
/// the expression.
///
+ /// ### Known problems
+ /// The lint cannot tell when the implementation of a trait
+ /// for `&T` and `T` do different things. Removing a borrow
+ /// in such a case can change the semantics of the code.
+ ///
/// ### Example
/// ```rust
/// fn fun(_a: &i32) {}
@@ -157,7 +165,7 @@ impl_lint_pass!(Dereferencing<'_> => [
#[derive(Default)]
pub struct Dereferencing<'tcx> {
- state: Option<(State, StateData)>,
+ state: Option<(State, StateData<'tcx>)>,
// While parsing a `deref` method call in ufcs form, the path to the function is itself an
// expression. This is to store the id of that expression so it can be skipped when
@@ -197,29 +205,28 @@ impl<'tcx> Dereferencing<'tcx> {
}
#[derive(Debug)]
-struct StateData {
+struct StateData<'tcx> {
/// Span of the top level expression
span: Span,
hir_id: HirId,
- position: Position,
+ adjusted_ty: Ty<'tcx>,
}
-#[derive(Debug)]
struct DerefedBorrow {
count: usize,
msg: &'static str,
- snip_expr: Option<HirId>,
+ stability: TyCoercionStability,
+ for_field_access: Option<Symbol>,
}
-#[derive(Debug)]
enum State {
// Any number of deref method calls.
DerefMethod {
// The number of calls in a sequence which changed the referenced type
ty_changed_count: usize,
- is_final_ufcs: bool,
+ is_ufcs: bool,
/// The required mutability
- target_mut: Mutability,
+ mutbl: Mutability,
},
DerefedBorrow(DerefedBorrow),
ExplicitDeref {
@@ -238,7 +245,7 @@ enum State {
// A reference operation considered by this lint pass
enum RefOp {
- Method(Mutability),
+ Method { mutbl: Mutability, is_ufcs: bool },
Deref,
AddrOf(Mutability),
}
@@ -288,48 +295,115 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
match (self.state.take(), kind) {
(None, kind) => {
let expr_ty = typeck.expr_ty(expr);
- let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, &self.msrv);
- match kind {
- RefOp::Deref => {
+ let use_cx = expr_use_ctxt(cx, expr);
+ let adjusted_ty = match &use_cx {
+ Some(use_cx) => match use_cx.adjustments {
+ [.., a] => a.target,
+ _ => expr_ty,
+ },
+ _ => typeck.expr_ty_adjusted(expr),
+ };
+
+ match (use_cx, kind) {
+ (Some(use_cx), RefOp::Deref) => {
let sub_ty = typeck.expr_ty(sub_expr);
- if let Position::FieldAccess {
- name,
- of_union: false,
- } = position
- && !ty_contains_field(sub_ty, name)
+ if let ExprUseNode::FieldAccess(name) = use_cx.node
+ && adjusted_ty.ty_adt_def().map_or(true, |adt| !adt.is_union())
+ && !ty_contains_field(sub_ty, name.name)
{
self.state = Some((
- State::ExplicitDerefField { name },
- StateData { span: expr.span, hir_id: expr.hir_id, position },
+ State::ExplicitDerefField { name: name.name },
+ StateData {
+ span: expr.span,
+ hir_id: expr.hir_id,
+ adjusted_ty,
+ },
));
- } else if position.is_deref_stable() && sub_ty.is_ref() {
+ } else if sub_ty.is_ref()
+ // Linting method receivers would require verifying that name lookup
+ // would resolve the same way. This is complicated by trait methods.
+ && !use_cx.node.is_recv()
+ && let Some(ty) = use_cx.node.defined_ty(cx)
+ && TyCoercionStability::for_defined_ty(cx, ty, use_cx.node.is_return()).is_deref_stable()
+ {
self.state = Some((
State::ExplicitDeref { mutability: None },
- StateData { span: expr.span, hir_id: expr.hir_id, position },
+ StateData {
+ span: expr.span,
+ hir_id: expr.hir_id,
+ adjusted_ty,
+ },
));
}
},
- RefOp::Method(target_mut)
+ (_, RefOp::Method { mutbl, is_ufcs })
if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
- && position.lint_explicit_deref() =>
+ // Allow explicit deref in method chains. e.g. `foo.deref().bar()`
+ && (is_ufcs || !in_postfix_position(cx, expr)) =>
{
let ty_changed_count = usize::from(!deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)));
self.state = Some((
State::DerefMethod {
ty_changed_count,
- is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
- target_mut,
+ is_ufcs,
+ mutbl,
},
StateData {
span: expr.span,
hir_id: expr.hir_id,
- position,
+ adjusted_ty,
},
));
},
- RefOp::AddrOf(mutability) => {
+ (Some(use_cx), RefOp::AddrOf(mutability)) => {
+ let defined_ty = use_cx.node.defined_ty(cx);
+
+ // Check needless_borrow for generic arguments.
+ if !use_cx.is_ty_unified
+ && let Some(DefinedTy::Mir(ty)) = defined_ty
+ && let ty::Param(ty) = *ty.value.skip_binder().kind()
+ && let Some((hir_id, fn_id, i)) = match use_cx.node {
+ ExprUseNode::MethodArg(_, _, 0) => None,
+ ExprUseNode::MethodArg(hir_id, None, i) => {
+ typeck.type_dependent_def_id(hir_id).map(|id| (hir_id, id, i))
+ },
+ ExprUseNode::FnArg(&Expr { kind: ExprKind::Path(ref p), hir_id, .. }, i)
+ if !path_has_args(p) => match typeck.qpath_res(p, hir_id) {
+ Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) => {
+ Some((hir_id, id, i))
+ },
+ _ => None,
+ },
+ _ => None,
+ } && let count = needless_borrow_generic_arg_count(
+ cx,
+ &mut self.possible_borrowers,
+ fn_id,
+ typeck.node_args(hir_id),
+ i,
+ ty,
+ expr,
+ &self.msrv,
+ ) && count != 0
+ {
+ self.state = Some((
+ State::DerefedBorrow(DerefedBorrow {
+ count: count - 1,
+ msg: "the borrowed expression implements the required traits",
+ stability: TyCoercionStability::None,
+ for_field_access: None,
+ }),
+ StateData {
+ span: expr.span,
+ hir_id: expr.hir_id,
+ adjusted_ty: use_cx.adjustments.last().map_or(expr_ty, |a| a.target),
+ },
+ ));
+ return;
+ }
+
// Find the number of times the borrow is auto-derefed.
- let mut iter = adjustments.iter();
+ let mut iter = use_cx.adjustments.iter();
let mut deref_count = 0usize;
let next_adjust = loop {
match iter.next() {
@@ -346,6 +420,58 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
};
};
+ let stability = defined_ty.map_or(TyCoercionStability::None, |ty| {
+ TyCoercionStability::for_defined_ty(cx, ty, use_cx.node.is_return())
+ });
+ let can_auto_borrow = match use_cx.node {
+ ExprUseNode::Callee => true,
+ ExprUseNode::FieldAccess(_) => adjusted_ty.ty_adt_def().map_or(true, |adt| !adt.is_union()),
+ ExprUseNode::MethodArg(hir_id, _, 0) if !use_cx.moved_before_use => {
+ // Check for calls to trait methods where the trait is implemented
+ // on a reference.
+ // Two cases need to be handled:
+ // * `self` methods on `&T` will never have auto-borrow
+ // * `&self` methods on `&T` can have auto-borrow, but `&self` methods on `T` will take
+ // priority.
+ if let Some(fn_id) = typeck.type_dependent_def_id(hir_id)
+ && let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
+ && let arg_ty
+ = cx.tcx.erase_regions(use_cx.adjustments.last().map_or(expr_ty, |a| a.target))
+ && let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
+ && let args = cx
+ .typeck_results()
+ .node_args_opt(hir_id).map(|args| &args[1..]).unwrap_or_default()
+ && let impl_ty = if cx.tcx.fn_sig(fn_id)
+ .instantiate_identity()
+ .skip_binder()
+ .inputs()[0].is_ref()
+ {
+ // Trait methods taking `&self`
+ sub_ty
+ } else {
+ // Trait methods taking `self`
+ arg_ty
+ } && impl_ty.is_ref()
+ && cx.tcx.infer_ctxt().build()
+ .type_implements_trait(
+ trait_id,
+ [impl_ty.into()].into_iter().chain(args.iter().copied()),
+ cx.param_env,
+ )
+ .must_apply_modulo_regions()
+ {
+ false
+ } else {
+ true
+ }
+ },
+ _ => false,
+ };
+
+ let deref_msg =
+ "this expression creates a reference which is immediately dereferenced by the compiler";
+ let borrow_msg = "this expression borrows a value the compiler would automatically borrow";
+
// Determine the required number of references before any can be removed. In all cases the
// reference made by the current expression will be removed. After that there are four cases to
// handle.
@@ -368,26 +494,18 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
// };
// }
// ```
- let deref_msg =
- "this expression creates a reference which is immediately dereferenced by the compiler";
- let borrow_msg = "this expression borrows a value the compiler would automatically borrow";
- let impl_msg = "the borrowed expression implements the required traits";
-
- let (required_refs, msg, snip_expr) = if position.can_auto_borrow() {
- (1, if deref_count == 1 { borrow_msg } else { deref_msg }, None)
- } else if let Position::ImplArg(hir_id) = position {
- (0, impl_msg, Some(hir_id))
- } else if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) =
- next_adjust.map(|a| &a.kind)
+ let (required_refs, msg) = if can_auto_borrow {
+ (1, if deref_count == 1 { borrow_msg } else { deref_msg })
+ } else if let Some(&Adjustment {
+ kind: Adjust::Borrow(AutoBorrow::Ref(_, mutability)),
+ ..
+ }) = next_adjust
+ && matches!(mutability, AutoBorrowMutability::Mut { .. })
+ && !stability.is_reborrow_stable()
{
- if matches!(mutability, AutoBorrowMutability::Mut { .. }) && !position.is_reborrow_stable()
- {
- (3, deref_msg, None)
- } else {
- (2, deref_msg, None)
- }
+ (3, deref_msg)
} else {
- (2, deref_msg, None)
+ (2, deref_msg)
};
if deref_count >= required_refs {
@@ -397,15 +515,19 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
// can't be removed without breaking the code. See earlier comment.
count: deref_count - required_refs,
msg,
- snip_expr,
+ stability,
+ for_field_access: match use_cx.node {
+ ExprUseNode::FieldAccess(name) => Some(name.name),
+ _ => None,
+ },
}),
StateData {
span: expr.span,
hir_id: expr.hir_id,
- position,
+ adjusted_ty: use_cx.adjustments.last().map_or(expr_ty, |a| a.target),
},
));
- } else if position.is_deref_stable()
+ } else if stability.is_deref_stable()
// Auto-deref doesn't combine with other adjustments
&& next_adjust.map_or(true, |a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
&& iter.all(|a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
@@ -415,24 +537,24 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
StateData {
span: expr.span,
hir_id: expr.hir_id,
- position,
+ adjusted_ty: use_cx.adjustments.last().map_or(expr_ty, |a| a.target),
},
));
}
},
- RefOp::Method(..) => (),
+ (None, _) | (_, RefOp::Method { .. }) => (),
}
},
(
Some((
State::DerefMethod {
- target_mut,
+ mutbl,
ty_changed_count,
..
},
data,
)),
- RefOp::Method(_),
+ RefOp::Method { is_ufcs, .. },
) => {
self.state = Some((
State::DerefMethod {
@@ -441,8 +563,8 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
} else {
ty_changed_count + 1
},
- is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
- target_mut,
+ is_ufcs,
+ mutbl,
},
data,
));
@@ -457,33 +579,44 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
));
},
(Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(mutability)) => {
- let position = data.position;
+ let adjusted_ty = data.adjusted_ty;
+ let stability = state.stability;
report(cx, expr, State::DerefedBorrow(state), data);
- if position.is_deref_stable() {
+ if stability.is_deref_stable() {
self.state = Some((
State::Borrow { mutability },
StateData {
span: expr.span,
hir_id: expr.hir_id,
- position,
+ adjusted_ty,
},
));
}
},
(Some((State::DerefedBorrow(state), data)), RefOp::Deref) => {
- let position = data.position;
+ let adjusted_ty = data.adjusted_ty;
+ let stability = state.stability;
+ let for_field_access = state.for_field_access;
report(cx, expr, State::DerefedBorrow(state), data);
- if let Position::FieldAccess{name, ..} = position
+ if let Some(name) = for_field_access
&& !ty_contains_field(typeck.expr_ty(sub_expr), name)
{
self.state = Some((
State::ExplicitDerefField { name },
- StateData { span: expr.span, hir_id: expr.hir_id, position },
+ StateData {
+ span: expr.span,
+ hir_id: expr.hir_id,
+ adjusted_ty,
+ },
));
- } else if position.is_deref_stable() {
+ } else if stability.is_deref_stable() {
self.state = Some((
State::ExplicitDeref { mutability: None },
- StateData { span: expr.span, hir_id: expr.hir_id, position },
+ StateData {
+ span: expr.span,
+ hir_id: expr.hir_id,
+ adjusted_ty,
+ },
));
}
},
@@ -589,7 +722,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
pat.spans,
"this pattern creates a reference to a reference",
|diag| {
- diag.multipart_suggestion("try this", replacements, app);
+ diag.multipart_suggestion("try", replacements, app);
},
);
}
@@ -605,8 +738,8 @@ fn try_parse_ref_op<'tcx>(
typeck: &'tcx TypeckResults<'_>,
expr: &'tcx Expr<'_>,
) -> Option<(RefOp, &'tcx Expr<'tcx>)> {
- let (def_id, arg) = match expr.kind {
- ExprKind::MethodCall(_, arg, [], _) => (typeck.type_dependent_def_id(expr.hir_id)?, arg),
+ let (is_ufcs, def_id, arg) = match expr.kind {
+ ExprKind::MethodCall(_, arg, [], _) => (false, typeck.type_dependent_def_id(expr.hir_id)?, arg),
ExprKind::Call(
Expr {
kind: ExprKind::Path(path),
@@ -614,7 +747,7 @@ fn try_parse_ref_op<'tcx>(
..
},
[arg],
- ) => (typeck.qpath_res(path, *hir_id).opt_def_id()?, arg),
+ ) => (true, typeck.qpath_res(path, *hir_id).opt_def_id()?, arg),
ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_unsafe_ptr() => {
return Some((RefOp::Deref, sub_expr));
},
@@ -622,9 +755,21 @@ fn try_parse_ref_op<'tcx>(
_ => return None,
};
if tcx.is_diagnostic_item(sym::deref_method, def_id) {
- Some((RefOp::Method(Mutability::Not), arg))
+ Some((
+ RefOp::Method {
+ mutbl: Mutability::Not,
+ is_ufcs,
+ },
+ arg,
+ ))
} else if tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()? {
- Some((RefOp::Method(Mutability::Mut), arg))
+ Some((
+ RefOp::Method {
+ mutbl: Mutability::Mut,
+ is_ufcs,
+ },
+ arg,
+ ))
} else {
None
}
@@ -643,420 +788,165 @@ fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
}
}
-/// The position of an expression relative to it's parent.
-#[derive(Clone, Copy, Debug)]
-enum Position {
- MethodReceiver,
- /// The method is defined on a reference type. e.g. `impl Foo for &T`
- MethodReceiverRefImpl,
- Callee,
- ImplArg(HirId),
- FieldAccess {
- name: Symbol,
- of_union: bool,
- }, // union fields cannot be auto borrowed
- Postfix,
- Deref,
- /// Any other location which will trigger auto-deref to a specific time.
- /// Contains the precedence of the parent expression and whether the target type is sized.
- DerefStable(i8, bool),
- /// Any other location which will trigger auto-reborrowing.
- /// Contains the precedence of the parent expression.
- ReborrowStable(i8),
- /// Contains the precedence of the parent expression.
- Other(i8),
-}
-impl Position {
- fn is_deref_stable(self) -> bool {
- matches!(self, Self::DerefStable(..))
+fn path_has_args(p: &QPath<'_>) -> bool {
+ match *p {
+ QPath::Resolved(_, Path { segments: [.., s], .. }) | QPath::TypeRelative(_, s) => s.args.is_some(),
+ _ => false,
}
+}
- fn is_reborrow_stable(self) -> bool {
- matches!(self, Self::DerefStable(..) | Self::ReborrowStable(_))
+fn in_postfix_position<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
+ if let Some(parent) = get_parent_expr(cx, e)
+ && parent.span.ctxt() == e.span.ctxt()
+ {
+ match parent.kind {
+ ExprKind::Call(child, _) | ExprKind::MethodCall(_, child, _, _) | ExprKind::Index(child, _, _)
+ if child.hir_id == e.hir_id => true,
+ ExprKind::Match(.., MatchSource::TryDesugar(_) | MatchSource::AwaitDesugar)
+ | ExprKind::Field(_, _) => true,
+ _ => false,
+ }
+ } else {
+ false
}
+}
- fn can_auto_borrow(self) -> bool {
- matches!(
- self,
- Self::MethodReceiver | Self::FieldAccess { of_union: false, .. } | Self::Callee
- )
+#[derive(Clone, Copy)]
+enum TyCoercionStability {
+ Deref,
+ Reborrow,
+ None,
+}
+impl TyCoercionStability {
+ fn is_deref_stable(self) -> bool {
+ matches!(self, Self::Deref)
}
- fn lint_explicit_deref(self) -> bool {
- matches!(self, Self::Other(_) | Self::DerefStable(..) | Self::ReborrowStable(_))
+ fn is_reborrow_stable(self) -> bool {
+ matches!(self, Self::Deref | Self::Reborrow)
}
- fn precedence(self) -> i8 {
- match self {
- Self::MethodReceiver
- | Self::MethodReceiverRefImpl
- | Self::Callee
- | Self::FieldAccess { .. }
- | Self::Postfix => PREC_POSTFIX,
- Self::ImplArg(_) | Self::Deref => PREC_PREFIX,
- Self::DerefStable(p, _) | Self::ReborrowStable(p) | Self::Other(p) => p,
+ fn for_defined_ty<'tcx>(cx: &LateContext<'tcx>, ty: DefinedTy<'tcx>, for_return: bool) -> Self {
+ match ty {
+ DefinedTy::Hir(ty) => Self::for_hir_ty(ty),
+ DefinedTy::Mir(ty) => Self::for_mir_ty(
+ cx.tcx,
+ ty.param_env,
+ cx.tcx.erase_late_bound_regions(ty.value),
+ for_return,
+ ),
}
}
-}
-
-/// Walks up the parent expressions attempting to determine both how stable the auto-deref result
-/// is, and which adjustments will be applied to it. Note this will not consider auto-borrow
-/// locations as those follow different rules.
-#[expect(clippy::too_many_lines)]
-fn walk_parents<'tcx>(
- cx: &LateContext<'tcx>,
- possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
- e: &'tcx Expr<'_>,
- msrv: &Msrv,
-) -> (Position, &'tcx [Adjustment<'tcx>]) {
- let mut adjustments = [].as_slice();
- let mut precedence = 0i8;
- let ctxt = e.span.ctxt();
- let position = walk_to_expr_usage(cx, e, &mut |parent, child_id| {
- // LocalTableInContext returns the wrong lifetime, so go use `expr_adjustments` instead.
- if adjustments.is_empty() && let Node::Expr(e) = cx.tcx.hir().get(child_id) {
- adjustments = cx.typeck_results().expr_adjustments(e);
- }
- match parent {
- Node::Local(Local { ty: Some(ty), span, .. }) if span.ctxt() == ctxt => {
- Some(binding_ty_auto_deref_stability(cx, ty, precedence, List::empty()))
- },
- Node::Item(&Item {
- kind: ItemKind::Static(..) | ItemKind::Const(..),
- owner_id,
- span,
- ..
- })
- | Node::TraitItem(&TraitItem {
- kind: TraitItemKind::Const(..),
- owner_id,
- span,
- ..
- })
- | Node::ImplItem(&ImplItem {
- kind: ImplItemKind::Const(..),
- owner_id,
- span,
- ..
- }) if span.ctxt() == ctxt => {
- let ty = cx.tcx.type_of(owner_id.def_id).subst_identity();
- Some(ty_auto_deref_stability(cx.tcx, cx.param_env, ty, precedence).position_for_result(cx))
- },
- Node::Item(&Item {
- kind: ItemKind::Fn(..),
- owner_id,
- span,
- ..
- })
- | Node::TraitItem(&TraitItem {
- kind: TraitItemKind::Fn(..),
- owner_id,
- span,
- ..
- })
- | Node::ImplItem(&ImplItem {
- kind: ImplItemKind::Fn(..),
- owner_id,
- span,
- ..
- }) if span.ctxt() == ctxt => {
- let output = cx
- .tcx
- .erase_late_bound_regions(cx.tcx.fn_sig(owner_id).subst_identity().output());
- Some(ty_auto_deref_stability(cx.tcx, cx.param_env, output, precedence).position_for_result(cx))
- },
-
- Node::ExprField(field) if field.span.ctxt() == ctxt => match get_parent_expr_for_hir(cx, field.hir_id) {
- Some(Expr {
- hir_id,
- kind: ExprKind::Struct(path, ..),
- ..
- }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
- .and_then(|(adt, variant)| {
- variant
- .fields
- .iter()
- .find(|f| f.name == field.ident.name)
- .map(|f| (adt, f))
- })
- .map(|(adt, field_def)| {
- ty_auto_deref_stability(
- cx.tcx,
- // Use the param_env of the target type.
- cx.tcx.param_env(adt.did()),
- cx.tcx.type_of(field_def.did).subst_identity(),
- precedence,
- )
- .position_for_arg()
- }),
- _ => None,
- },
+ // Checks the stability of type coercions when assigned to a binding with the given explicit type.
+ //
+ // e.g.
+ // let x = Box::new(Box::new(0u32));
+ // let y1: &Box<_> = x.deref();
+ // let y2: &Box<_> = &x;
+ //
+ // Here `y1` and `y2` would resolve to different types, so the type `&Box<_>` is not stable when
+ // switching to auto-dereferencing.
+ fn for_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Self {
+ let TyKind::Ref(_, ty) = &ty.kind else {
+ return Self::None;
+ };
+ let mut ty = ty;
- Node::Expr(parent) if parent.span.ctxt() == ctxt => match parent.kind {
- ExprKind::Ret(_) => {
- let owner_id = cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap());
- Some(
- if let Node::Expr(
- closure_expr @ Expr {
- kind: ExprKind::Closure(closure),
- ..
- },
- ) = cx.tcx.hir().get_by_def_id(owner_id)
- {
- closure_result_position(cx, closure, cx.typeck_results().expr_ty(closure_expr), precedence)
- } else {
- let output = cx
- .tcx
- .erase_late_bound_regions(cx.tcx.fn_sig(owner_id).subst_identity().output());
- ty_auto_deref_stability(cx.tcx, cx.param_env, output, precedence).position_for_result(cx)
- },
- )
- },
- ExprKind::Closure(closure) => Some(closure_result_position(
- cx,
- closure,
- cx.typeck_results().expr_ty(parent),
- precedence,
- )),
- ExprKind::Call(func, _) if func.hir_id == child_id => {
- (child_id == e.hir_id).then_some(Position::Callee)
+ loop {
+ break match ty.ty.kind {
+ TyKind::Ref(_, ref ref_ty) => {
+ ty = ref_ty;
+ continue;
},
- ExprKind::Call(func, args) => args
- .iter()
- .position(|arg| arg.hir_id == child_id)
- .zip(expr_sig(cx, func))
- .and_then(|(i, sig)| {
- sig.input_with_hir(i).map(|(hir_ty, ty)| {
- match hir_ty {
- // Type inference for closures can depend on how they're called. Only go by the explicit
- // types here.
- Some(hir_ty) => {
- binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars())
- },
- None => {
- // `e.hir_id == child_id` for https://github.com/rust-lang/rust-clippy/issues/9739
- // `!call_is_qualified(func)` for https://github.com/rust-lang/rust-clippy/issues/9782
- if e.hir_id == child_id
- && !call_is_qualified(func)
- && let ty::Param(param_ty) = ty.skip_binder().kind()
- {
- needless_borrow_impl_arg_position(
- cx,
- possible_borrowers,
- parent,
- i,
- *param_ty,
- e,
- precedence,
- msrv,
- )
- } else {
- ty_auto_deref_stability(
- cx.tcx,
- // Use the param_env of the target function.
- sig.predicates_id().map_or(ParamEnv::empty(), |id| cx.tcx.param_env(id)),
- cx.tcx.erase_late_bound_regions(ty),
- precedence
- ).position_for_arg()
- }
- },
- }
+ TyKind::Path(
+ QPath::TypeRelative(_, path)
+ | QPath::Resolved(
+ _,
+ Path {
+ segments: [.., path], ..
+ },
+ ),
+ ) => {
+ if let Some(args) = path.args
+ && args.args.iter().any(|arg| match arg {
+ hir::GenericArg::Infer(_) => true,
+ hir::GenericArg::Type(ty) => ty_contains_infer(ty),
+ _ => false,
})
- }),
- ExprKind::MethodCall(method, receiver, args, _) => {
- let fn_id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
- if receiver.hir_id == child_id {
- // Check for calls to trait methods where the trait is implemented on a reference.
- // Two cases need to be handled:
- // * `self` methods on `&T` will never have auto-borrow
- // * `&self` methods on `&T` can have auto-borrow, but `&self` methods on `T` will take
- // priority.
- if e.hir_id != child_id {
- return Some(Position::ReborrowStable(precedence))
- } else if let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
- && let arg_ty = cx.tcx.erase_regions(cx.typeck_results().expr_ty_adjusted(e))
- && let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
- && let subs = cx
- .typeck_results()
- .node_substs_opt(parent.hir_id).map(|subs| &subs[1..]).unwrap_or_default()
- && let impl_ty = if cx.tcx.fn_sig(fn_id)
- .subst_identity()
- .skip_binder()
- .inputs()[0].is_ref()
- {
- // Trait methods taking `&self`
- sub_ty
- } else {
- // Trait methods taking `self`
- arg_ty
- } && impl_ty.is_ref()
- && let infcx = cx.tcx.infer_ctxt().build()
- && infcx
- .type_implements_trait(
- trait_id,
- [impl_ty.into()].into_iter().chain(subs.iter().copied()),
- cx.param_env,
- )
- .must_apply_modulo_regions()
- {
- return Some(Position::MethodReceiverRefImpl)
- }
- return Some(Position::MethodReceiver);
+ {
+ Self::Reborrow
+ } else {
+ Self::Deref
}
- args.iter().position(|arg| arg.hir_id == child_id).map(|i| {
- let ty = cx.tcx.fn_sig(fn_id).subst_identity().input(i + 1);
- // `e.hir_id == child_id` for https://github.com/rust-lang/rust-clippy/issues/9739
- // `method.args.is_none()` for https://github.com/rust-lang/rust-clippy/issues/9782
- if e.hir_id == child_id
- && method.args.is_none()
- && let ty::Param(param_ty) = ty.skip_binder().kind()
- {
- needless_borrow_impl_arg_position(
- cx,
- possible_borrowers,
- parent,
- i + 1,
- *param_ty,
- e,
- precedence,
- msrv,
- )
- } else {
- ty_auto_deref_stability(
- cx.tcx,
- // Use the param_env of the target function.
- cx.tcx.param_env(fn_id),
- cx.tcx.erase_late_bound_regions(ty),
- precedence,
- )
- .position_for_arg()
- }
- })
- },
- ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess {
- name: name.name,
- of_union: is_union(cx.typeck_results(), child),
- }),
- ExprKind::Unary(UnOp::Deref, child) if child.hir_id == e.hir_id => Some(Position::Deref),
- ExprKind::Match(child, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
- | ExprKind::Index(child, _)
- if child.hir_id == e.hir_id =>
- {
- Some(Position::Postfix)
},
- _ if child_id == e.hir_id => {
- precedence = parent.precedence().order();
- None
- },
- _ => None,
- },
- _ => None,
+ TyKind::Slice(_)
+ | TyKind::Array(..)
+ | TyKind::Ptr(_)
+ | TyKind::BareFn(_)
+ | TyKind::Never
+ | TyKind::Tup(_)
+ | TyKind::Path(_) => Self::Deref,
+ TyKind::OpaqueDef(..)
+ | TyKind::Infer
+ | TyKind::Typeof(..)
+ | TyKind::TraitObject(..)
+ | TyKind::Err(_) => Self::Reborrow,
+ };
}
- })
- .unwrap_or(Position::Other(precedence));
- (position, adjustments)
-}
-
-fn is_union<'tcx>(typeck: &'tcx TypeckResults<'_>, path_expr: &'tcx Expr<'_>) -> bool {
- typeck
- .expr_ty_adjusted(path_expr)
- .ty_adt_def()
- .map_or(false, rustc_middle::ty::AdtDef::is_union)
-}
-
-fn closure_result_position<'tcx>(
- cx: &LateContext<'tcx>,
- closure: &'tcx Closure<'_>,
- ty: Ty<'tcx>,
- precedence: i8,
-) -> Position {
- match closure.fn_decl.output {
- FnRetTy::Return(hir_ty) => {
- if let Some(sig) = ty_sig(cx, ty)
- && let Some(output) = sig.output()
- {
- binding_ty_auto_deref_stability(cx, hir_ty, precedence, output.bound_vars())
- } else {
- Position::Other(precedence)
- }
- },
- FnRetTy::DefaultReturn(_) => Position::Other(precedence),
}
-}
-// Checks the stability of auto-deref when assigned to a binding with the given explicit type.
-//
-// e.g.
-// let x = Box::new(Box::new(0u32));
-// let y1: &Box<_> = x.deref();
-// let y2: &Box<_> = &x;
-//
-// Here `y1` and `y2` would resolve to different types, so the type `&Box<_>` is not stable when
-// switching to auto-dereferencing.
-fn binding_ty_auto_deref_stability<'tcx>(
- cx: &LateContext<'tcx>,
- ty: &'tcx hir::Ty<'_>,
- precedence: i8,
- binder_args: &'tcx List<BoundVariableKind>,
-) -> Position {
- let TyKind::Ref(_, ty) = &ty.kind else {
- return Position::Other(precedence);
- };
- let mut ty = ty;
+ fn for_mir_ty<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>, for_return: bool) -> Self {
+ let ty::Ref(_, mut ty, _) = *ty.kind() else {
+ return Self::None;
+ };
- loop {
- break match ty.ty.kind {
- TyKind::Ref(_, ref ref_ty) => {
- ty = ref_ty;
- continue;
- },
- TyKind::Path(
- QPath::TypeRelative(_, path)
- | QPath::Resolved(
- _,
- Path {
- segments: [.., path], ..
- },
- ),
- ) => {
- if let Some(args) = path.args
- && args.args.iter().any(|arg| match arg {
- GenericArg::Infer(_) => true,
- GenericArg::Type(ty) => ty_contains_infer(ty),
- _ => false,
- })
+ ty = tcx.try_normalize_erasing_regions(param_env, ty).unwrap_or(ty);
+ loop {
+ break match *ty.kind() {
+ ty::Ref(_, ref_ty, _) => {
+ ty = ref_ty;
+ continue;
+ },
+ ty::Param(_) if for_return => Self::Deref,
+ ty::Alias(ty::Weak | ty::Inherent, _) => unreachable!("should have been normalized away above"),
+ ty::Alias(ty::Projection, _) if !for_return && ty.has_non_region_param() => Self::Reborrow,
+ ty::Infer(_)
+ | ty::Error(_)
+ | ty::Bound(..)
+ | ty::Alias(ty::Opaque, ..)
+ | ty::Placeholder(_)
+ | ty::Dynamic(..)
+ | ty::Param(_) => Self::Reborrow,
+ ty::Adt(_, args)
+ if ty.has_placeholders()
+ || ty.has_opaque_types()
+ || (!for_return && args.has_non_region_param()) =>
{
- Position::ReborrowStable(precedence)
- } else {
- Position::DerefStable(
- precedence,
- cx.tcx
- .erase_late_bound_regions(Binder::bind_with_vars(
- cx.typeck_results().node_type(ty.ty.hir_id),
- binder_args,
- ))
- .is_sized(cx.tcx, cx.param_env.without_caller_bounds()),
- )
- }
- },
- TyKind::Slice(_) => Position::DerefStable(precedence, false),
- TyKind::Array(..) | TyKind::Ptr(_) | TyKind::BareFn(_) => Position::DerefStable(precedence, true),
- TyKind::Never
- | TyKind::Tup(_)
- | TyKind::Path(_) => Position::DerefStable(
- precedence,
- cx.tcx
- .erase_late_bound_regions(Binder::bind_with_vars(
- cx.typeck_results().node_type(ty.ty.hir_id),
- binder_args,
- ))
- .is_sized(cx.tcx, cx.param_env.without_caller_bounds()),
- ),
- TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(..) | TyKind::TraitObject(..) | TyKind::Err(_) => {
- Position::ReborrowStable(precedence)
- },
- };
+ Self::Reborrow
+ },
+ ty::Bool
+ | ty::Char
+ | ty::Int(_)
+ | ty::Uint(_)
+ | ty::Array(..)
+ | ty::Float(_)
+ | ty::RawPtr(..)
+ | ty::FnPtr(_)
+ | ty::Str
+ | ty::Slice(..)
+ | ty::Adt(..)
+ | ty::Foreign(_)
+ | ty::FnDef(..)
+ | ty::Generator(..)
+ | ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
+ | ty::Closure(..)
+ | ty::Never
+ | ty::Tuple(_)
+ | ty::Alias(ty::Projection, _) => Self::Deref,
+ };
+ }
}
}
@@ -1078,10 +968,10 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
}
}
- fn visit_generic_arg(&mut self, arg: &GenericArg<'_>) {
- if self.0 || matches!(arg, GenericArg::Infer(_)) {
+ fn visit_generic_arg(&mut self, arg: &hir::GenericArg<'_>) {
+ if self.0 || matches!(arg, hir::GenericArg::Infer(_)) {
self.0 = true;
- } else if let GenericArg::Type(ty) = arg {
+ } else if let hir::GenericArg::Type(ty) = arg {
self.visit_ty(ty);
}
}
@@ -1091,49 +981,29 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
v.0
}
-fn call_is_qualified(expr: &Expr<'_>) -> bool {
- if let ExprKind::Path(path) = &expr.kind {
- match path {
- QPath::Resolved(_, path) => path.segments.last().map_or(false, |segment| segment.args.is_some()),
- QPath::TypeRelative(_, segment) => segment.args.is_some(),
- QPath::LangItem(..) => false,
- }
- } else {
- false
- }
-}
-
-// Checks whether:
-// * child is an expression of the form `&e` in an argument position requiring an `impl Trait`
-// * `e`'s type implements `Trait` and is copyable
-// If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`.
-// The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to
-// be moved, but it cannot be.
-#[expect(clippy::too_many_arguments, clippy::too_many_lines)]
-fn needless_borrow_impl_arg_position<'tcx>(
+/// Checks for the number of borrow expressions which can be removed from the given expression
+/// where the expression is used as an argument to a function expecting a generic type.
+///
+/// The following constraints will be checked:
+/// * The borrowed expression meets all the generic type's constraints.
+/// * The generic type appears only once in the functions signature.
+/// * The borrowed value will not be moved if it is used later in the function.
+#[expect(clippy::too_many_arguments)]
+fn needless_borrow_generic_arg_count<'tcx>(
cx: &LateContext<'tcx>,
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
- parent: &Expr<'tcx>,
+ fn_id: DefId,
+ callee_args: &'tcx List<GenericArg<'tcx>>,
arg_index: usize,
param_ty: ParamTy,
mut expr: &Expr<'tcx>,
- precedence: i8,
msrv: &Msrv,
-) -> Position {
+) -> usize {
let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
- let Some(callee_def_id) = fn_def_id(cx, parent) else { return Position::Other(precedence) };
- let fn_sig = cx.tcx.fn_sig(callee_def_id).subst_identity().skip_binder();
- let substs_with_expr_ty = cx
- .typeck_results()
- .node_substs(if let ExprKind::Call(callee, _) = parent.kind {
- callee.hir_id
- } else {
- parent.hir_id
- });
-
- let predicates = cx.tcx.param_env(callee_def_id).caller_bounds();
+ let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder();
+ let predicates = cx.tcx.param_env(fn_id).caller_bounds();
let projection_predicates = predicates
.iter()
.filter_map(|predicate| {
@@ -1168,7 +1038,7 @@ fn needless_borrow_impl_arg_position<'tcx>(
|| cx.tcx.is_diagnostic_item(sym::Any, trait_def_id)
})
{
- return Position::Other(precedence);
+ return 0;
}
// See:
@@ -1176,14 +1046,14 @@ fn needless_borrow_impl_arg_position<'tcx>(
// - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1292225232
if projection_predicates
.iter()
- .any(|projection_predicate| is_mixed_projection_predicate(cx, callee_def_id, projection_predicate))
+ .any(|projection_predicate| is_mixed_projection_predicate(cx, fn_id, projection_predicate))
{
- return Position::Other(precedence);
+ return 0;
}
- // `substs_with_referent_ty` can be constructed outside of `check_referent` because the same
+ // `args_with_referent_ty` can be constructed outside of `check_referent` because the same
// elements are modified each time `check_referent` is called.
- let mut substs_with_referent_ty = substs_with_expr_ty.to_vec();
+ let mut args_with_referent_ty = callee_args.to_vec();
let mut check_reference_and_referent = |reference, referent| {
let referent_ty = cx.typeck_results().expr_ty(referent);
@@ -1207,7 +1077,7 @@ fn needless_borrow_impl_arg_position<'tcx>(
fn_sig,
arg_index,
&projection_predicates,
- &mut substs_with_referent_ty,
+ &mut args_with_referent_ty,
) {
return false;
}
@@ -1216,34 +1086,29 @@ fn needless_borrow_impl_arg_position<'tcx>(
if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
&& cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id)
&& let ty::Param(param_ty) = trait_predicate.self_ty().kind()
- && let GenericArgKind::Type(ty) = substs_with_referent_ty[param_ty.index as usize].unpack()
+ && let GenericArgKind::Type(ty) = args_with_referent_ty[param_ty.index as usize].unpack()
&& ty.is_array()
&& !msrv.meets(msrvs::ARRAY_INTO_ITERATOR)
{
return false;
}
- let predicate = EarlyBinder::bind(predicate).subst(cx.tcx, &substs_with_referent_ty);
+ let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, &args_with_referent_ty);
let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate);
let infcx = cx.tcx.infer_ctxt().build();
infcx.predicate_must_hold_modulo_regions(&obligation)
})
};
- let mut needless_borrow = false;
+ let mut count = 0;
while let ExprKind::AddrOf(_, _, referent) = expr.kind {
if !check_reference_and_referent(expr, referent) {
break;
}
expr = referent;
- needless_borrow = true;
- }
-
- if needless_borrow {
- Position::ImplArg(expr.hir_id)
- } else {
- Position::Other(precedence)
+ count += 1;
}
+ count
}
fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
@@ -1252,7 +1117,12 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
.in_definition_order()
.any(|assoc_item| {
if assoc_item.fn_has_self_parameter {
- let self_ty = cx.tcx.fn_sig(assoc_item.def_id).subst_identity().skip_binder().inputs()[0];
+ let self_ty = cx
+ .tcx
+ .fn_sig(assoc_item.def_id)
+ .instantiate_identity()
+ .skip_binder()
+ .inputs()[0];
matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut))
} else {
false
@@ -1301,7 +1171,7 @@ fn referent_used_exactly_once<'tcx>(
&& let [location] = *local_assignments(mir, local).as_slice()
&& let Some(statement) = mir.basic_blocks[location.block].statements.get(location.statement_index)
&& let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind
- && !place.has_deref()
+ && !place.is_indirect_first_projection()
// Ensure not in a loop (https://github.com/rust-lang/rust-clippy/issues/9710)
&& TriColorDepthFirstSearch::new(&mir.basic_blocks).run_from(location.block, &mut CycleDetector).is_none()
{
@@ -1323,7 +1193,7 @@ fn referent_used_exactly_once<'tcx>(
}
}
-// Iteratively replaces `param_ty` with `new_ty` in `substs`, and similarly for each resulting
+// Iteratively replaces `param_ty` with `new_ty` in `args`, and similarly for each resulting
// projected type that is a type parameter. Returns `false` if replacing the types would have an
// effect on the function signature beyond substituting `new_ty` for `param_ty`.
// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757
@@ -1334,11 +1204,11 @@ fn replace_types<'tcx>(
fn_sig: FnSig<'tcx>,
arg_index: usize,
projection_predicates: &[ProjectionPredicate<'tcx>],
- substs: &mut [ty::GenericArg<'tcx>],
+ args: &mut [ty::GenericArg<'tcx>],
) -> bool {
- let mut replaced = BitSet::new_empty(substs.len());
+ let mut replaced = BitSet::new_empty(args.len());
- let mut deque = VecDeque::with_capacity(substs.len());
+ let mut deque = VecDeque::with_capacity(args.len());
deque.push_back((param_ty, new_ty));
while let Some((param_ty, new_ty)) = deque.pop_front() {
@@ -1352,7 +1222,7 @@ fn replace_types<'tcx>(
return false;
}
- substs[param_ty.index as usize] = ty::GenericArg::from(new_ty);
+ args[param_ty.index as usize] = ty::GenericArg::from(new_ty);
// The `replaced.insert(...)` check provides some protection against infinite loops.
if replaced.insert(param_ty.index) {
@@ -1367,7 +1237,7 @@ fn replace_types<'tcx>(
));
if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
- && substs[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty)
+ && args[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty)
{
deque.push_back((*term_param_ty, projected_ty));
}
@@ -1379,95 +1249,6 @@ fn replace_types<'tcx>(
true
}
-struct TyPosition<'tcx> {
- position: Position,
- ty: Option<Ty<'tcx>>,
-}
-impl From<Position> for TyPosition<'_> {
- fn from(position: Position) -> Self {
- Self { position, ty: None }
- }
-}
-impl<'tcx> TyPosition<'tcx> {
- fn new_deref_stable_for_result(precedence: i8, ty: Ty<'tcx>) -> Self {
- Self {
- position: Position::ReborrowStable(precedence),
- ty: Some(ty),
- }
- }
- fn position_for_result(self, cx: &LateContext<'tcx>) -> Position {
- match (self.position, self.ty) {
- (Position::ReborrowStable(precedence), Some(ty)) => {
- Position::DerefStable(precedence, ty.is_sized(cx.tcx, cx.param_env))
- },
- (position, _) => position,
- }
- }
- fn position_for_arg(self) -> Position {
- self.position
- }
-}
-
-// Checks whether a type is stable when switching to auto dereferencing,
-fn ty_auto_deref_stability<'tcx>(
- tcx: TyCtxt<'tcx>,
- param_env: ParamEnv<'tcx>,
- ty: Ty<'tcx>,
- precedence: i8,
-) -> TyPosition<'tcx> {
- let ty::Ref(_, mut ty, _) = *ty.kind() else {
- return Position::Other(precedence).into();
- };
-
- ty = tcx.try_normalize_erasing_regions(param_env, ty).unwrap_or(ty);
-
- loop {
- break match *ty.kind() {
- ty::Ref(_, ref_ty, _) => {
- ty = ref_ty;
- continue;
- },
- ty::Param(_) => TyPosition::new_deref_stable_for_result(precedence, ty),
- ty::Alias(ty::Weak, _) => unreachable!("should have been normalized away above"),
- ty::Alias(ty::Inherent, _) => unreachable!("inherent projection should have been normalized away above"),
- ty::Alias(ty::Projection, _) if ty.has_non_region_param() => {
- TyPosition::new_deref_stable_for_result(precedence, ty)
- },
- ty::Infer(_)
- | ty::Error(_)
- | ty::Bound(..)
- | ty::Alias(ty::Opaque, ..)
- | ty::Placeholder(_)
- | ty::Dynamic(..) => Position::ReborrowStable(precedence).into(),
- ty::Adt(..) if ty.has_placeholders() || ty.has_opaque_types() => {
- Position::ReborrowStable(precedence).into()
- },
- ty::Adt(_, substs) if substs.has_non_region_param() => {
- TyPosition::new_deref_stable_for_result(precedence, ty)
- },
- ty::Bool
- | ty::Char
- | ty::Int(_)
- | ty::Uint(_)
- | ty::Array(..)
- | ty::Float(_)
- | ty::RawPtr(..)
- | ty::FnPtr(_) => Position::DerefStable(precedence, true).into(),
- ty::Str | ty::Slice(..) => Position::DerefStable(precedence, false).into(),
- ty::Adt(..)
- | ty::Foreign(_)
- | ty::FnDef(..)
- | ty::Generator(..)
- | ty::GeneratorWitness(..)
- | ty::GeneratorWitnessMIR(..)
- | ty::Closure(..)
- | ty::Never
- | ty::Tuple(_)
- | ty::Alias(ty::Projection, _) => Position::DerefStable(precedence, ty.is_sized(tcx, param_env)).into(),
- };
- }
-}
-
fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool {
if let ty::Adt(adt, _) = *ty.kind() {
adt.is_struct() && adt.all_fields().any(|f| f.name == name)
@@ -1477,12 +1258,12 @@ fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool {
}
#[expect(clippy::needless_pass_by_value, clippy::too_many_lines)]
-fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) {
+fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData<'tcx>) {
match state {
State::DerefMethod {
ty_changed_count,
- is_final_ufcs,
- target_mut,
+ is_ufcs,
+ mutbl,
} => {
let mut app = Applicability::MachineApplicable;
let (expr_str, _expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
@@ -1497,12 +1278,12 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
};
let addr_of_str = if ty_changed_count < ref_count {
// Check if a reborrow from &mut T -> &T is required.
- if target_mut == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
+ if mutbl == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
"&*"
} else {
""
}
- } else if target_mut == Mutability::Mut {
+ } else if mutbl == Mutability::Mut {
"&mut "
} else {
"&"
@@ -1519,7 +1300,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
*/
// Fix #10850, do not lint if it's `Foo::deref` instead of `foo.deref()`.
- if is_final_ufcs {
+ if is_ufcs {
return;
}
@@ -1527,24 +1308,30 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
cx,
EXPLICIT_DEREF_METHODS,
data.span,
- match target_mut {
+ match mutbl {
Mutability::Not => "explicit `deref` method call",
Mutability::Mut => "explicit `deref_mut` method call",
},
- "try this",
+ "try",
format!("{addr_of_str}{deref_str}{expr_str}"),
app,
);
},
State::DerefedBorrow(state) => {
let mut app = Applicability::MachineApplicable;
- let snip_expr = state.snip_expr.map_or(expr, |hir_id| cx.tcx.hir().expect_expr(hir_id));
- let (snip, snip_is_macro) = snippet_with_context(cx, snip_expr.span, data.span.ctxt(), "..", &mut app);
+ let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| {
- let calls_field = matches!(expr.kind, ExprKind::Field(..)) && matches!(data.position, Position::Callee);
+ let (precedence, calls_field) = match get_parent_node(cx.tcx, data.hir_id) {
+ Some(Node::Expr(e)) => match e.kind {
+ ExprKind::Call(callee, _) if callee.hir_id != data.hir_id => (0, false),
+ ExprKind::Call(..) => (PREC_POSTFIX, matches!(expr.kind, ExprKind::Field(..))),
+ _ => (e.precedence().order(), false),
+ },
+ _ => (0, false),
+ };
let sugg = if !snip_is_macro
+ && (calls_field || expr.precedence().order() < precedence)
&& !has_enclosing_paren(&snip)
- && (expr.precedence().order() < data.position.precedence() || calls_field)
{
format!("({snip})")
} else {
@@ -1561,7 +1348,8 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
| ExprKind::If(..)
| ExprKind::Loop(..)
| ExprKind::Match(..)
- ) && matches!(data.position, Position::DerefStable(_, true))
+ ) && let ty::Ref(_, ty, _) = data.adjusted_ty.kind()
+ && ty.is_sized(cx.tcx, cx.param_env)
{
// Rustc bug: auto deref doesn't work on block expression when targeting sized types.
return;
@@ -1574,9 +1362,9 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
Mutability::Not => "&",
Mutability::Mut => "&mut ",
};
- (prefix, 0)
+ (prefix, PREC_PREFIX)
} else {
- ("", data.position.precedence())
+ ("", 0)
};
span_lint_hir_and_then(
cx,
@@ -1593,7 +1381,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
} else {
format!("{prefix}{snip}")
};
- diag.span_suggestion(data.span, "try this", sugg, app);
+ diag.span_suggestion(data.span, "try", sugg, app);
},
);
},
@@ -1605,7 +1393,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
| ExprKind::If(..)
| ExprKind::Loop(..)
| ExprKind::Match(..)
- ) && matches!(data.position, Position::DerefStable(_, true))
+ ) && data.adjusted_ty.is_sized(cx.tcx, cx.param_env)
{
// Rustc bug: auto deref doesn't work on block expression when targeting sized types.
return;
@@ -1620,7 +1408,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
|diag| {
let mut app = Applicability::MachineApplicable;
let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
- diag.span_suggestion(data.span, "try this", snip.into_owned(), app);
+ diag.span_suggestion(data.span, "try", snip.into_owned(), app);
},
);
},
diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
index 020ffe7f8..9a85cc4ce 100644
--- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
@@ -3,14 +3,13 @@ use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::indent_of;
use clippy_utils::{is_default_equivalent, peel_blocks};
use rustc_errors::Applicability;
+use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
use rustc_hir::{
- self as hir,
- def::{CtorKind, CtorOf, DefKind, Res},
- Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind,
+ self as hir, Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::adjustment::{Adjust, PointerCoercion};
-use rustc_middle::ty::{self, Adt, AdtDef, SubstsRef, Ty, TypeckResults};
+use rustc_middle::ty::{self, Adt, AdtDef, GenericArgsRef, Ty, TypeckResults};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::sym;
@@ -80,7 +79,7 @@ fn is_path_self(e: &Expr<'_>) -> bool {
fn contains_trait_object(ty: Ty<'_>) -> bool {
match ty.kind() {
ty::Ref(_, ty, _) => contains_trait_object(*ty),
- ty::Adt(def, substs) => def.is_box() && substs[0].as_type().map_or(false, contains_trait_object),
+ ty::Adt(def, args) => def.is_box() && args[0].as_type().map_or(false, contains_trait_object),
ty::Dynamic(..) => true,
_ => false,
}
@@ -92,18 +91,19 @@ fn check_struct<'tcx>(
self_ty: &hir::Ty<'_>,
func_expr: &Expr<'_>,
adt_def: AdtDef<'_>,
- substs: SubstsRef<'_>,
+ ty_args: GenericArgsRef<'_>,
typeck_results: &'tcx TypeckResults<'tcx>,
) {
if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind {
if let Some(PathSegment { args, .. }) = p.segments.last() {
let args = args.map(|a| a.args).unwrap_or(&[]);
- // substs contains the generic parameters of the type declaration, while args contains the arguments
- // used at instantiation time. If both len are not equal, it means that some parameters were not
- // provided (which means that the default values were used); in this case we will not risk
- // suggesting too broad a rewrite. We won't either if any argument is a type or a const.
- if substs.len() != args.len() || args.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))) {
+ // ty_args contains the generic parameters of the type declaration, while args contains the
+ // arguments used at instantiation time. If both len are not equal, it means that some
+ // parameters were not provided (which means that the default values were used); in this
+ // case we will not risk suggesting too broad a rewrite. We won't either if any argument
+ // is a type or a const.
+ if ty_args.len() != args.len() || args.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))) {
return;
}
}
@@ -214,7 +214,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir);
if let ImplItemKind::Fn(_, b) = &impl_item.kind;
if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b);
- if let &Adt(adt_def, substs) = cx.tcx.type_of(item.owner_id).subst_identity().kind();
+ if let &Adt(adt_def, args) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind();
if let attrs = cx.tcx.hir().attrs(item.hir_id());
if !attrs.iter().any(|attr| attr.doc_str().is_some());
if let child_attrs = cx.tcx.hir().attrs(impl_item_hir);
@@ -222,7 +222,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
then {
if adt_def.is_struct() {
- check_struct(cx, item, self_ty, func_expr, adt_def, substs, cx.tcx.typeck_body(*b));
+ check_struct(cx, item, self_ty, func_expr, adt_def, args, cx.tcx.typeck_body(*b));
} else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) {
check_enum(cx, item, func_expr, adt_def);
}
diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs
index a005a360e..d3311792c 100644
--- a/src/tools/clippy/clippy_lints/src/derive.rs
+++ b/src/tools/clippy/clippy_lints/src/derive.rs
@@ -1,21 +1,19 @@
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, implements_trait_with_env, is_copy};
-use clippy_utils::{is_lint_allowed, match_def_path};
+use clippy_utils::{is_lint_allowed, match_def_path, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor};
use rustc_hir::{
- self as hir, BlockCheckMode, BodyId, Constness, Expr, ExprKind, FnDecl, Impl, Item, ItemKind, UnsafeSource,
- Unsafety,
+ self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Impl, Item, ItemKind, UnsafeSource, Unsafety,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter;
use rustc_middle::traits::Reveal;
use rustc_middle::ty::{
- self, BoundConstness, ClauseKind, GenericArgKind, GenericParamDefKind, ImplPolarity, ParamEnv, ToPredicate,
- TraitPredicate, Ty, TyCtxt,
+ self, ClauseKind, GenericArgKind, GenericParamDefKind, ImplPolarity, ParamEnv, ToPredicate, TraitPredicate, Ty,
+ TyCtxt,
};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::def_id::LocalDefId;
@@ -211,7 +209,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive {
..
}) = item.kind
{
- let ty = cx.tcx.type_of(item.owner_id).subst_identity();
+ let ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
let is_automatically_derived = cx.tcx.has_attr(item.owner_id, sym::automatically_derived);
check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
@@ -252,7 +250,7 @@ fn check_hash_peq<'tcx>(
// Only care about `impl PartialEq<Foo> for Foo`
// For `impl PartialEq<B> for A, input_types is [A, B]
- if trait_ref.subst_identity().substs.type_at(1) == ty {
+ if trait_ref.instantiate_identity().args.type_at(1) == ty {
span_lint_and_then(
cx,
DERIVED_HASH_WITH_MANUAL_EQ,
@@ -300,7 +298,7 @@ fn check_ord_partial_ord<'tcx>(
// Only care about `impl PartialOrd<Foo> for Foo`
// For `impl PartialOrd<B> for A, input_types is [A, B]
- if trait_ref.subst_identity().substs.type_at(1) == ty {
+ if trait_ref.instantiate_identity().args.type_at(1) == ty {
let mess = if partial_ord_is_automatically_derived {
"you are implementing `Ord` explicitly but have derived `PartialOrd`"
} else {
@@ -334,7 +332,9 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h
Some(id) if trait_ref.trait_def_id() == Some(id) => id,
_ => return,
};
- let Some(copy_id) = cx.tcx.lang_items().copy_trait() else { return };
+ let Some(copy_id) = cx.tcx.lang_items().copy_trait() else {
+ return;
+ };
let (ty_adt, ty_subs) = match *ty.kind() {
// Unions can't derive clone.
ty::Adt(adt, subs) if !adt.is_union() => (adt, subs),
@@ -345,9 +345,10 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h
if !is_copy(cx, ty) {
if ty_subs.non_erasable_generics().next().is_some() {
let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(&copy_id).map_or(false, |impls| {
- impls
- .iter()
- .any(|&id| matches!(cx.tcx.type_of(id).subst_identity().kind(), ty::Adt(adt, _) if ty_adt.did() == adt.did()))
+ impls.iter().any(|&id| {
+ matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _)
+ if ty_adt.did() == adt.did())
+ })
});
if !has_copy_impl {
return;
@@ -464,18 +465,18 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint.
fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
if_chain! {
- if let ty::Adt(adt, substs) = ty.kind();
+ if let ty::Adt(adt, args) = ty.kind();
if cx.tcx.visibility(adt.did()).is_public();
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);
let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id);
- if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, []);
+ if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]);
// If all of our fields implement `Eq`, we can implement `Eq` too
if adt
.all_fields()
- .map(|f| f.ty(cx.tcx, substs))
- .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, []));
+ .map(|f| f.ty(cx.tcx, args))
+ .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]));
then {
span_lint_and_sugg(
cx,
@@ -506,7 +507,6 @@ fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) ->
if let ClauseKind::Trait(p) = p.kind().skip_binder()
&& p.trait_ref.def_id == eq_trait_id
&& let ty::Param(self_ty) = p.trait_ref.self_ty().kind()
- && p.constness == BoundConstness::NotConst
{
// Flag types which already have an `Eq` bound.
params[self_ty.index as usize].1 = false;
@@ -518,13 +518,11 @@ fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) ->
params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| {
ClauseKind::Trait(TraitPredicate {
trait_ref: ty::TraitRef::new(tcx, eq_trait_id, [tcx.mk_param_from_def(param)]),
- constness: BoundConstness::NotConst,
polarity: ImplPolarity::Positive,
})
.to_predicate(tcx)
}),
)),
Reveal::UserFacing,
- Constness::NotConst,
)
}
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs
index ca8671c8f..95d3f7547 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs
@@ -94,7 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
path_def_id(cx, expr)
};
let Some(def_id) = uncalled_path.or_else(|| fn_def_id(cx, expr)) else {
- return
+ return;
};
let conf = match self.disallowed.get(&def_id) {
Some(&index) => &self.conf_disallowed[index],
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_names.rs b/src/tools/clippy/clippy_lints/src/disallowed_names.rs
index 6e6615f08..04c2d4413 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_names.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_names.rs
@@ -1,4 +1,5 @@
-use clippy_utils::{diagnostics::span_lint, is_test_module_or_function};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::is_test_module_or_function;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::{Item, Pat, PatKind};
use rustc_lint::{LateContext, LateLintPass};
diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs
index 87d88f707..e29ab634c 100644
--- a/src/tools/clippy/clippy_lints/src/doc.rs
+++ b/src/tools/clippy/clippy_lints/src/doc.rs
@@ -16,7 +16,7 @@ use rustc_ast::token::CommentKind;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sync::Lrc;
use rustc_errors::emitter::EmitterWriter;
-use rustc_errors::{Applicability, Handler, SuggestionStyle, TerminalUrl};
+use rustc_errors::{Applicability, Handler, SuggestionStyle};
use rustc_hir as hir;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{AnonConst, Expr};
@@ -31,9 +31,8 @@ use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::edition::Edition;
use rustc_span::source_map::{BytePos, FilePathMapping, SourceMap, Span};
use rustc_span::{sym, FileName, Pos};
-use std::io;
use std::ops::Range;
-use std::thread;
+use std::{io, thread};
use url::Url;
declare_clippy_lint! {
@@ -295,7 +294,9 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
let attrs = cx.tcx.hir().attrs(item.hir_id());
- let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return };
+ let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else {
+ return;
+ };
match item.kind {
hir::ItemKind::Fn(ref sig, _, body_id) => {
if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
@@ -339,7 +340,9 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
let attrs = cx.tcx.hir().attrs(item.hir_id());
- let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return };
+ let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else {
+ return;
+ };
if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind {
if !in_external_macro(cx.tcx.sess, item.span) {
lint_for_missing_headers(cx, item.owner_id, sig, headers, None, None);
@@ -349,7 +352,9 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
let attrs = cx.tcx.hir().attrs(item.hir_id());
- let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return };
+ let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else {
+ return;
+ };
if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) {
return;
}
@@ -711,20 +716,8 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
let fallback_bundle =
rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
- let emitter = EmitterWriter::new(
- Box::new(io::sink()),
- None,
- None,
- fallback_bundle,
- false,
- false,
- false,
- None,
- false,
- false,
- TerminalUrl::No,
- );
- let handler = Handler::with_emitter(false, None, Box::new(emitter));
+ let emitter = EmitterWriter::new(Box::new(io::sink()), fallback_bundle);
+ let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings();
let sess = ParseSess::with_span_handler(handler, sm);
let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) {
diff --git a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
index 976ce47e8..14122abbf 100644
--- a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
@@ -1,7 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_note;
-use clippy_utils::get_parent_node;
-use clippy_utils::is_must_use_func_call;
use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item};
+use clippy_utils::{get_parent_node, is_must_use_func_call};
use rustc_hir::{Arm, Expr, ExprKind, LangItem, Node};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
diff --git a/src/tools/clippy/clippy_lints/src/empty_drop.rs b/src/tools/clippy/clippy_lints/src/empty_drop.rs
index ec063c0f7..209fb66fa 100644
--- a/src/tools/clippy/clippy_lints/src/empty_drop.rs
+++ b/src/tools/clippy/clippy_lints/src/empty_drop.rs
@@ -1,4 +1,5 @@
-use clippy_utils::{diagnostics::span_lint_and_sugg, peel_blocks};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::peel_blocks;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Body, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node};
diff --git a/src/tools/clippy/clippy_lints/src/empty_enum.rs b/src/tools/clippy/clippy_lints/src/empty_enum.rs
index d94664daa..1701d0611 100644
--- a/src/tools/clippy/clippy_lints/src/empty_enum.rs
+++ b/src/tools/clippy/clippy_lints/src/empty_enum.rs
@@ -49,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for EmptyEnum {
}
if let ItemKind::Enum(..) = item.kind {
- let ty = cx.tcx.type_of(item.owner_id).subst_identity();
+ let ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
let adt = ty.ty_adt_def().expect("already checked whether this is an enum");
if adt.variants().is_empty() {
span_lint_and_help(
diff --git a/src/tools/clippy/clippy_lints/src/empty_structs_with_brackets.rs b/src/tools/clippy/clippy_lints/src/empty_structs_with_brackets.rs
index c3a020433..282157181 100644
--- a/src/tools/clippy/clippy_lints/src/empty_structs_with_brackets.rs
+++ b/src/tools/clippy/clippy_lints/src/empty_structs_with_brackets.rs
@@ -1,4 +1,5 @@
-use clippy_utils::{diagnostics::span_lint_and_then, source::snippet_opt};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_opt;
use rustc_ast::ast::{Item, ItemKind, VariantData};
use rustc_errors::Applicability;
use rustc_lexer::TokenKind;
diff --git a/src/tools/clippy/clippy_lints/src/endian_bytes.rs b/src/tools/clippy/clippy_lints/src/endian_bytes.rs
index f47098783..dda14b4df 100644
--- a/src/tools/clippy/clippy_lints/src/endian_bytes.rs
+++ b/src/tools/clippy/clippy_lints/src/endian_bytes.rs
@@ -1,8 +1,10 @@
use crate::Lint;
-use clippy_utils::{diagnostics::span_lint_and_then, is_lint_allowed};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::is_lint_allowed;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::{lint::in_external_macro, ty::Ty};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::Ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Symbol;
use std::borrow::Cow;
diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs
index ee5a875ad..6197b5b19 100644
--- a/src/tools/clippy/clippy_lints/src/entry.rs
+++ b/src/tools/clippy/clippy_lints/src/entry.rs
@@ -1,18 +1,14 @@
-use clippy_utils::higher;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context};
use clippy_utils::{
- can_move_expr_to_closure_no_visit,
- diagnostics::span_lint_and_sugg,
- is_expr_final_block_expr, is_expr_used_or_unified, match_def_path, paths, peel_hir_expr_while,
- source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context},
- SpanlessEq,
+ can_move_expr_to_closure_no_visit, higher, is_expr_final_block_expr, is_expr_used_or_unified, match_def_path,
+ paths, peel_hir_expr_while, SpanlessEq,
};
use core::fmt::{self, Write};
use rustc_errors::Applicability;
-use rustc_hir::{
- hir_id::HirIdSet,
- intravisit::{walk_expr, Visitor},
- Block, Expr, ExprKind, Guard, HirId, Let, Pat, Stmt, StmtKind, UnOp,
-};
+use rustc_hir::hir_id::HirIdSet;
+use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::{Block, Expr, ExprKind, Guard, HirId, Let, Pat, Stmt, StmtKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{Span, SyntaxContext, DUMMY_SP};
@@ -69,16 +65,21 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
return;
}
- let Some(higher::If { cond: cond_expr, then: then_expr, r#else: else_expr }) = higher::If::hir(expr) else {
- return
+ let Some(higher::If {
+ cond: cond_expr,
+ then: then_expr,
+ r#else: else_expr,
+ }) = higher::If::hir(expr)
+ else {
+ return;
};
let Some((map_ty, contains_expr)) = try_parse_contains(cx, cond_expr) else {
- return
+ return;
};
let Some(then_search) = find_insert_calls(cx, &contains_expr, then_expr) else {
- return
+ return;
};
let mut app = Applicability::MachineApplicable;
@@ -186,7 +187,7 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
MAP_ENTRY,
expr.span,
&format!("usage of `contains_key` followed by `insert` on a `{}`", map_ty.name()),
- "try this",
+ "try",
sugg,
app,
);
diff --git a/src/tools/clippy/clippy_lints/src/enum_clike.rs b/src/tools/clippy/clippy_lints/src/enum_clike.rs
index d85650712..96c5c7fc5 100644
--- a/src/tools/clippy/clippy_lints/src/enum_clike.rs
+++ b/src/tools/clippy/clippy_lints/src/enum_clike.rs
@@ -45,7 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant {
for var in def.variants {
if let Some(anon_const) = &var.disr_expr {
let def_id = cx.tcx.hir().body_owner_def_id(anon_const.body);
- let mut ty = cx.tcx.type_of(def_id.to_def_id()).subst_identity();
+ let mut ty = cx.tcx.type_of(def_id.to_def_id()).instantiate_identity();
let constant = cx
.tcx
.const_eval_poly(def_id.to_def_id())
diff --git a/src/tools/clippy/clippy_lints/src/error_impl_error.rs b/src/tools/clippy/clippy_lints/src/error_impl_error.rs
new file mode 100644
index 000000000..379af9b22
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/error_impl_error.rs
@@ -0,0 +1,87 @@
+use clippy_utils::diagnostics::{span_lint, span_lint_hir_and_then};
+use clippy_utils::path_res;
+use clippy_utils::ty::implements_trait;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::{Item, ItemKind};
+use rustc_hir_analysis::hir_ty_to_ty;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::Visibility;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for types named `Error` that implement `Error`.
+ ///
+ /// ### Why is this bad?
+ /// It can become confusing when a codebase has 20 types all named `Error`, requiring either
+ /// aliasing them in the `use` statement or qualifying them like `my_module::Error`. This
+ /// hinders comprehension, as it requires you to memorize every variation of importing `Error`
+ /// used across a codebase.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// #[derive(Debug)]
+ /// pub enum Error { ... }
+ ///
+ /// impl std::fmt::Display for Error { ... }
+ ///
+ /// impl std::error::Error for Error { ... }
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub ERROR_IMPL_ERROR,
+ restriction,
+ "exported types named `Error` that implement `Error`"
+}
+declare_lint_pass!(ErrorImplError => [ERROR_IMPL_ERROR]);
+
+impl<'tcx> LateLintPass<'tcx> for ErrorImplError {
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
+ let Some(error_def_id) = cx.tcx.get_diagnostic_item(sym::Error) else {
+ return;
+ };
+
+ match item.kind {
+ ItemKind::TyAlias(ty, _) if implements_trait(cx, hir_ty_to_ty(cx.tcx, ty), error_def_id, &[])
+ && item.ident.name == sym::Error
+ && is_visible_outside_module(cx, item.owner_id.def_id) =>
+ {
+ span_lint(
+ cx,
+ ERROR_IMPL_ERROR,
+ item.ident.span,
+ "exported type alias named `Error` that implements `Error`",
+ );
+ },
+ ItemKind::Impl(imp) if let Some(trait_def_id) = imp.of_trait.and_then(|t| t.trait_def_id())
+ && error_def_id == trait_def_id
+ && let Some(def_id) = path_res(cx, imp.self_ty).opt_def_id().and_then(DefId::as_local)
+ && let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id)
+ && let Some(ident) = cx.tcx.opt_item_ident(def_id.to_def_id())
+ && ident.name == sym::Error
+ && is_visible_outside_module(cx, def_id) =>
+ {
+ span_lint_hir_and_then(
+ cx,
+ ERROR_IMPL_ERROR,
+ hir_id,
+ ident.span,
+ "exported type named `Error` that implements `Error`",
+ |diag| {
+ diag.span_note(item.span, "`Error` was implemented here");
+ }
+ );
+ }
+ _ => {},
+ }
+ }
+}
+
+/// Do not lint private `Error`s, i.e., ones without any `pub` (minus `pub(self)` of course) and
+/// which aren't reexported
+fn is_visible_outside_module(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
+ !matches!(
+ cx.tcx.visibility(def_id),
+ Visibility::Restricted(mod_def_id) if cx.tcx.parent_module_from_def_id(def_id).to_def_id() == mod_def_id
+ )
+}
diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs
index a51a8ee09..dbe3453e7 100644
--- a/src/tools/clippy/clippy_lints/src/escape.rs
+++ b/src/tools/clippy/clippy_lints/src/escape.rs
@@ -1,6 +1,5 @@
use clippy_utils::diagnostics::span_lint_hir;
-use rustc_hir::intravisit;
-use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind};
+use rustc_hir::{self, intravisit, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind};
use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
index 58e62d1f3..38066503c 100644
--- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs
+++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
@@ -1,19 +1,22 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::higher::VecArgs;
use clippy_utils::source::snippet_opt;
-use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
-use clippy_utils::usage::local_used_after_expr;
+use clippy_utils::ty::type_diagnostic_name;
+use clippy_utils::usage::{local_used_after_expr, local_used_in};
use clippy_utils::{higher, is_adjusted, path_to_local, path_to_local_id};
-use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
-use rustc_hir::{Closure, Expr, ExprKind, Param, PatKind, Unsafety};
+use rustc_hir::{BindingAnnotation, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, TyKind, Unsafety};
+use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
-use rustc_middle::ty::binding::BindingMode;
-use rustc_middle::ty::{self, EarlyBinder, SubstsRef, Ty, TypeVisitableExt};
+use rustc_middle::ty::{
+ self, Binder, ClosureArgs, ClosureKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
+ ImplPolarity, List, Region, RegionKind, Ty, TypeVisitableExt, TypeckResults,
+};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym;
+use rustc_target::spec::abi::Abi;
+use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
declare_clippy_lint! {
/// ### What it does
@@ -72,14 +75,18 @@ declare_clippy_lint! {
declare_lint_pass!(EtaReduction => [REDUNDANT_CLOSURE, REDUNDANT_CLOSURE_FOR_METHOD_CALLS]);
impl<'tcx> LateLintPass<'tcx> for EtaReduction {
+ #[allow(clippy::too_many_lines)]
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if expr.span.from_expansion() {
+ let body = if let ExprKind::Closure(c) = expr.kind
+ && c.fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer))
+ && matches!(c.fn_decl.output, FnRetTy::DefaultReturn(_))
+ && !expr.span.from_expansion()
+ {
+ cx.tcx.hir().body(c.body)
+ } else {
return;
- }
- let body = match expr.kind {
- ExprKind::Closure(&Closure { body, .. }) => cx.tcx.hir().body(body),
- _ => return,
};
+
if body.value.span.from_expansion() {
if body.params.is_empty() {
if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, body.value) {
@@ -99,149 +106,217 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
return;
}
- let closure_ty = cx.typeck_results().expr_ty(expr);
+ let typeck = cx.typeck_results();
+ let closure = if let ty::Closure(_, closure_subs) = typeck.expr_ty(expr).kind() {
+ closure_subs.as_closure()
+ } else {
+ return;
+ };
- if_chain!(
- if !is_adjusted(cx, body.value);
- if let ExprKind::Call(callee, args) = body.value.kind;
- if let ExprKind::Path(_) = callee.kind;
- if check_inputs(cx, body.params, None, args);
- let callee_ty = cx.typeck_results().expr_ty_adjusted(callee);
- let call_ty = cx.typeck_results().type_dependent_def_id(body.value.hir_id)
- .map_or(callee_ty, |id| cx.tcx.type_of(id).subst_identity());
- if check_sig(cx, closure_ty, call_ty);
- let substs = cx.typeck_results().node_substs(callee.hir_id);
- // This fixes some false positives that I don't entirely understand
- if substs.is_empty() || !cx.typeck_results().expr_ty(expr).has_late_bound_regions();
- // A type param function ref like `T::f` is not 'static, however
- // it is if cast like `T::f as fn()`. This seems like a rustc bug.
- if !substs.types().any(|t| matches!(t.kind(), ty::Param(_)));
- let callee_ty_unadjusted = cx.typeck_results().expr_ty(callee).peel_refs();
- if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Arc);
- if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Rc);
- if let ty::Closure(_, substs) = *closure_ty.kind();
- // Don't lint if this is an inclusive range expression.
- // They desugar to a call to `RangeInclusiveNew` which would have odd suggestions. (#10684)
- if !matches!(higher::Range::hir(body.value), Some(higher::Range {
- start: Some(_),
- end: Some(_),
- limits: rustc_ast::RangeLimits::Closed
- }));
- then {
- span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
- if let Some(mut snippet) = snippet_opt(cx, callee.span) {
- if let Some(fn_mut_id) = cx.tcx.lang_items().fn_mut_trait()
- && let args = cx.tcx.erase_late_bound_regions(substs.as_closure().sig()).inputs()
- && implements_trait(
- cx,
- callee_ty.peel_refs(),
- fn_mut_id,
- &args.iter().copied().map(Into::into).collect::<Vec<_>>(),
- )
- && path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr))
- {
- // Mutable closure is used after current expr; we cannot consume it.
- snippet = format!("&mut {snippet}");
- }
+ if is_adjusted(cx, body.value) {
+ return;
+ }
- diag.span_suggestion(
- expr.span,
- "replace the closure with the function itself",
- snippet,
- Applicability::MachineApplicable,
- );
- }
- });
- }
- );
+ match body.value.kind {
+ ExprKind::Call(callee, args)
+ if matches!(callee.kind, ExprKind::Path(QPath::Resolved(..) | QPath::TypeRelative(..))) =>
+ {
+ let callee_ty = typeck.expr_ty(callee).peel_refs();
+ if matches!(
+ type_diagnostic_name(cx, callee_ty),
+ Some(sym::Arc | sym::Rc)
+ ) || !check_inputs(typeck, body.params, None, args) {
+ return;
+ }
+ let callee_ty_adjusted = typeck.expr_adjustments(callee).last().map_or(
+ callee_ty,
+ |a| a.target.peel_refs(),
+ );
- if_chain!(
- if !is_adjusted(cx, body.value);
- if let ExprKind::MethodCall(path, receiver, args, _) = body.value.kind;
- if check_inputs(cx, body.params, Some(receiver), args);
- let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap();
- let substs = cx.typeck_results().node_substs(body.value.hir_id);
- let call_ty = cx.tcx.type_of(method_def_id).subst(cx.tcx, substs);
- if check_sig(cx, closure_ty, call_ty);
- then {
- span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| {
- let name = get_ufcs_type_name(cx, method_def_id, substs);
- diag.span_suggestion(
+ let sig = match callee_ty_adjusted.kind() {
+ ty::FnDef(def, _) => cx.tcx.fn_sig(def).skip_binder().skip_binder(),
+ ty::FnPtr(sig) => sig.skip_binder(),
+ ty::Closure(_, subs) => cx
+ .tcx
+ .signature_unclosure(subs.as_closure().sig(), Unsafety::Normal)
+ .skip_binder(),
+ _ => {
+ if typeck.type_dependent_def_id(body.value.hir_id).is_some()
+ && let subs = typeck.node_args(body.value.hir_id)
+ && let output = typeck.expr_ty(body.value)
+ && let ty::Tuple(tys) = *subs.type_at(1).kind()
+ {
+ cx.tcx.mk_fn_sig(tys, output, false, Unsafety::Normal, Abi::Rust)
+ } else {
+ return;
+ }
+ },
+ };
+ if check_sig(cx, closure, sig)
+ && let generic_args = typeck.node_args(callee.hir_id)
+ // Given some trait fn `fn f() -> ()` and some type `T: Trait`, `T::f` is not
+ // `'static` unless `T: 'static`. The cast `T::f as fn()` will, however, result
+ // in a type which is `'static`.
+ // For now ignore all callee types which reference a type parameter.
+ && !generic_args.types().any(|t| matches!(t.kind(), ty::Param(_)))
+ {
+ span_lint_and_then(
+ cx,
+ REDUNDANT_CLOSURE,
expr.span,
- "replace the closure with the method itself",
- format!("{name}::{}", path.ident.name),
- Applicability::MachineApplicable,
+ "redundant closure",
+ |diag| {
+ if let Some(mut snippet) = snippet_opt(cx, callee.span) {
+ if let Ok((ClosureKind::FnMut, _))
+ = cx.tcx.infer_ctxt().build().type_implements_fn_trait(
+ cx.param_env,
+ Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
+ ImplPolarity::Positive,
+ ) && path_to_local(callee)
+ .map_or(
+ false,
+ |l| local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr),
+ )
+ {
+ // Mutable closure is used after current expr; we cannot consume it.
+ snippet = format!("&mut {snippet}");
+ }
+ diag.span_suggestion(
+ expr.span,
+ "replace the closure with the function itself",
+ snippet,
+ Applicability::MachineApplicable,
+ );
+ }
+ }
);
- })
- }
- );
+ }
+ },
+ ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => {
+ if let Some(method_def_id) = typeck.type_dependent_def_id(body.value.hir_id)
+ && check_sig(cx, closure, cx.tcx.fn_sig(method_def_id).skip_binder().skip_binder())
+ {
+ span_lint_and_then(
+ cx,
+ REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
+ expr.span,
+ "redundant closure",
+ |diag| {
+ let args = typeck.node_args(body.value.hir_id);
+ let name = get_ufcs_type_name(cx, method_def_id, args);
+ diag.span_suggestion(
+ expr.span,
+ "replace the closure with the method itself",
+ format!("{}::{}", name, path.ident.name),
+ Applicability::MachineApplicable,
+ );
+ },
+ );
+ }
+ },
+ _ => (),
+ }
}
}
fn check_inputs(
- cx: &LateContext<'_>,
+ typeck: &TypeckResults<'_>,
params: &[Param<'_>],
- receiver: Option<&Expr<'_>>,
- call_args: &[Expr<'_>],
+ self_arg: Option<&Expr<'_>>,
+ args: &[Expr<'_>],
) -> bool {
- if receiver.map_or(params.len() != call_args.len(), |_| params.len() != call_args.len() + 1) {
- return false;
+ params.len() == self_arg.map_or(0, |_| 1) + args.len()
+ && params.iter().zip(self_arg.into_iter().chain(args)).all(|(p, arg)| {
+ matches!(
+ p.pat.kind,PatKind::Binding(BindingAnnotation::NONE, id, _, None)
+ if path_to_local_id(arg, id)
+ )
+ // Only allow adjustments which change regions (i.e. re-borrowing).
+ && typeck
+ .expr_adjustments(arg)
+ .last()
+ .map_or(true, |a| a.target == typeck.expr_ty(arg))
+ })
+}
+
+fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure: ClosureArgs<'tcx>, call_sig: FnSig<'_>) -> bool {
+ call_sig.unsafety == Unsafety::Normal
+ && !has_late_bound_to_non_late_bound_regions(
+ cx.tcx
+ .signature_unclosure(closure.sig(), Unsafety::Normal)
+ .skip_binder(),
+ call_sig,
+ )
+}
+
+/// This walks through both signatures and checks for any time a late-bound region is expected by an
+/// `impl Fn` type, but the target signature does not have a late-bound region in the same position.
+///
+/// This is needed because rustc is unable to late bind early-bound regions in a function signature.
+fn has_late_bound_to_non_late_bound_regions(from_sig: FnSig<'_>, to_sig: FnSig<'_>) -> bool {
+ fn check_region(from_region: Region<'_>, to_region: Region<'_>) -> bool {
+ matches!(from_region.kind(), RegionKind::ReLateBound(..))
+ && !matches!(to_region.kind(), RegionKind::ReLateBound(..))
}
- let binding_modes = cx.typeck_results().pat_binding_modes();
- let check_inputs = |param: &Param<'_>, arg| {
- match param.pat.kind {
- PatKind::Binding(_, id, ..) if path_to_local_id(arg, id) => {},
- _ => return false,
- }
- // checks that parameters are not bound as `ref` or `ref mut`
- if let Some(BindingMode::BindByReference(_)) = binding_modes.get(param.pat.hir_id) {
- return false;
- }
- match *cx.typeck_results().expr_adjustments(arg) {
- [] => true,
- [
- Adjustment {
- kind: Adjust::Deref(None),
- ..
+ fn check_subs(from_subs: &[GenericArg<'_>], to_subs: &[GenericArg<'_>]) -> bool {
+ if from_subs.len() != to_subs.len() {
+ return true;
+ }
+ for (from_arg, to_arg) in to_subs.iter().zip(from_subs) {
+ match (from_arg.unpack(), to_arg.unpack()) {
+ (GenericArgKind::Lifetime(from_region), GenericArgKind::Lifetime(to_region)) => {
+ if check_region(from_region, to_region) {
+ return true;
+ }
},
- Adjustment {
- kind: Adjust::Borrow(AutoBorrow::Ref(_, mu2)),
- ..
+ (GenericArgKind::Type(from_ty), GenericArgKind::Type(to_ty)) => {
+ if check_ty(from_ty, to_ty) {
+ return true;
+ }
},
- ] => {
- // re-borrow with the same mutability is allowed
- let ty = cx.typeck_results().expr_ty(arg);
- matches!(*ty.kind(), ty::Ref(.., mu1) if mu1 == mu2.into())
- },
- _ => false,
+ (GenericArgKind::Const(_), GenericArgKind::Const(_)) => (),
+ _ => return true,
+ }
}
- };
- std::iter::zip(params, receiver.into_iter().chain(call_args.iter())).all(|(param, arg)| check_inputs(param, arg))
-}
-
-fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tcx>) -> bool {
- let call_sig = call_ty.fn_sig(cx.tcx);
- if call_sig.unsafety() == Unsafety::Unsafe {
- return false;
+ false
}
- if !closure_ty.has_late_bound_regions() {
- return true;
+
+ fn check_ty(from_ty: Ty<'_>, to_ty: Ty<'_>) -> bool {
+ match (from_ty.kind(), to_ty.kind()) {
+ (&ty::Adt(_, from_subs), &ty::Adt(_, to_subs)) => check_subs(from_subs, to_subs),
+ (&ty::Array(from_ty, _), &ty::Array(to_ty, _)) | (&ty::Slice(from_ty), &ty::Slice(to_ty)) => {
+ check_ty(from_ty, to_ty)
+ },
+ (&ty::Ref(from_region, from_ty, _), &ty::Ref(to_region, to_ty, _)) => {
+ check_region(from_region, to_region) || check_ty(from_ty, to_ty)
+ },
+ (&ty::Tuple(from_tys), &ty::Tuple(to_tys)) => {
+ from_tys.len() != to_tys.len()
+ || from_tys
+ .iter()
+ .zip(to_tys)
+ .any(|(from_ty, to_ty)| check_ty(from_ty, to_ty))
+ },
+ _ => from_ty.has_late_bound_regions(),
+ }
}
- let ty::Closure(_, substs) = closure_ty.kind() else {
- return false;
- };
- let closure_sig = cx.tcx.signature_unclosure(substs.as_closure().sig(), Unsafety::Normal);
- cx.tcx.erase_late_bound_regions(closure_sig) == cx.tcx.erase_late_bound_regions(call_sig)
+
+ assert!(from_sig.inputs_and_output.len() == to_sig.inputs_and_output.len());
+ from_sig
+ .inputs_and_output
+ .iter()
+ .zip(to_sig.inputs_and_output)
+ .any(|(from_ty, to_ty)| check_ty(from_ty, to_ty))
}
-fn get_ufcs_type_name<'tcx>(cx: &LateContext<'tcx>, method_def_id: DefId, substs: SubstsRef<'tcx>) -> String {
+fn get_ufcs_type_name<'tcx>(cx: &LateContext<'tcx>, method_def_id: DefId, args: GenericArgsRef<'tcx>) -> String {
let assoc_item = cx.tcx.associated_item(method_def_id);
let def_id = assoc_item.container_id(cx.tcx);
match assoc_item.container {
ty::TraitContainer => cx.tcx.def_path_str(def_id),
ty::ImplContainer => {
- let ty = cx.tcx.type_of(def_id).skip_binder();
+ let ty = cx.tcx.type_of(def_id).instantiate_identity();
match ty.kind() {
ty::Adt(adt, _) => cx.tcx.def_path_str(adt.did()),
ty::Array(..)
@@ -251,7 +326,7 @@ fn get_ufcs_type_name<'tcx>(cx: &LateContext<'tcx>, method_def_id: DefId, substs
| ty::Ref(..)
| ty::Slice(_)
| ty::Tuple(_) => {
- format!("<{}>", EarlyBinder::bind(ty).subst(cx.tcx, substs))
+ format!("<{}>", EarlyBinder::bind(ty).instantiate(cx.tcx, args))
},
_ => ty.to_string(),
}
diff --git a/src/tools/clippy/clippy_lints/src/excessive_nesting.rs b/src/tools/clippy/clippy_lints/src/excessive_nesting.rs
index d04d833e6..8911f1872 100644
--- a/src/tools/clippy/clippy_lints/src/excessive_nesting.rs
+++ b/src/tools/clippy/clippy_lints/src/excessive_nesting.rs
@@ -1,9 +1,8 @@
-use clippy_utils::{diagnostics::span_lint_and_help, source::snippet};
-use rustc_ast::{
- node_id::NodeSet,
- visit::{walk_block, walk_item, Visitor},
- Block, Crate, Inline, Item, ItemKind, ModKind, NodeId,
-};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::snippet;
+use rustc_ast::node_id::NodeSet;
+use rustc_ast::visit::{walk_block, walk_item, Visitor};
+use rustc_ast::{Block, Crate, Inline, Item, ItemKind, ModKind, NodeId};
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs
index 315df6c71..4b9ca8c91 100644
--- a/src/tools/clippy/clippy_lints/src/explicit_write.rs
+++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs
@@ -100,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
EXPLICIT_WRITE,
expr.span,
&format!("use of `{used}.unwrap()`"),
- "try this",
+ "try",
format!("{prefix}{sugg_mac}!({inputs_snippet})"),
applicability,
);
diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
index 126bed678..c18006a71 100644
--- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
+++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
@@ -1,6 +1,5 @@
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
-use clippy_utils::is_from_proc_macro;
-use clippy_utils::trait_ref_of_method;
+use clippy_utils::{is_from_proc_macro, trait_ref_of_method};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_impl_item, walk_item, walk_param_bound, walk_ty, Visitor};
@@ -12,10 +11,8 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::hir::nested_filter;
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::{
- def_id::{DefId, LocalDefId},
- Span,
-};
+use rustc_span::def_id::{DefId, LocalDefId};
+use rustc_span::Span;
declare_clippy_lint! {
/// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
index 5e0fcd743..29e5315f8 100644
--- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
+++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
@@ -1,10 +1,8 @@
-use clippy_utils::consts::{
- constant, constant_simple, Constant,
- Constant::{Int, F32, F64},
-};
+use clippy_utils::consts::Constant::{Int, F32, F64};
+use clippy_utils::consts::{constant, constant_simple, Constant};
+use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::{
- diagnostics::span_lint_and_sugg, eq_expr_value, get_parent_expr, higher, in_constant, is_no_std_crate,
- numeric_literal, peel_blocks, sugg,
+ eq_expr_value, get_parent_expr, higher, in_constant, is_no_std_crate, numeric_literal, peel_blocks, sugg,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/fn_null_check.rs b/src/tools/clippy/clippy_lints/src/fn_null_check.rs
deleted file mode 100644
index 521045a9f..000000000
--- a/src/tools/clippy/clippy_lints/src/fn_null_check.rs
+++ /dev/null
@@ -1,102 +0,0 @@
-use clippy_utils::consts::{constant, Constant};
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::{is_integer_literal, is_path_diagnostic_item};
-use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for comparing a function pointer to null.
- ///
- /// ### Why is this bad?
- /// Function pointers are assumed to not be null.
- ///
- /// ### Example
- /// ```rust,ignore
- /// let fn_ptr: fn() = /* somehow obtained nullable function pointer */
- ///
- /// if (fn_ptr as *const ()).is_null() { ... }
- /// ```
- /// Use instead:
- /// ```rust,ignore
- /// let fn_ptr: Option<fn()> = /* somehow obtained nullable function pointer */
- ///
- /// if fn_ptr.is_none() { ... }
- /// ```
- #[clippy::version = "1.68.0"]
- pub FN_NULL_CHECK,
- correctness,
- "`fn()` type assumed to be nullable"
-}
-declare_lint_pass!(FnNullCheck => [FN_NULL_CHECK]);
-
-fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) {
- span_lint_and_help(
- cx,
- FN_NULL_CHECK,
- expr.span,
- "function pointer assumed to be nullable, even though it isn't",
- None,
- "try wrapping your function pointer type in `Option<T>` instead, and using `is_none` to check for null pointer value",
- );
-}
-
-fn is_fn_ptr_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
- if let ExprKind::Cast(cast_expr, cast_ty) = expr.kind
- && let TyKind::Ptr(_) = cast_ty.kind
- {
- cx.typeck_results().expr_ty_adjusted(cast_expr).is_fn()
- } else {
- false
- }
-}
-
-impl<'tcx> LateLintPass<'tcx> for FnNullCheck {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- match expr.kind {
- // Catching:
- // (fn_ptr as *<const/mut> <ty>).is_null()
- ExprKind::MethodCall(method_name, receiver, _, _)
- if method_name.ident.as_str() == "is_null" && is_fn_ptr_cast(cx, receiver) =>
- {
- lint_expr(cx, expr);
- },
-
- ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Eq) => {
- let to_check: &Expr<'_>;
- if is_fn_ptr_cast(cx, left) {
- to_check = right;
- } else if is_fn_ptr_cast(cx, right) {
- to_check = left;
- } else {
- return;
- }
-
- match to_check.kind {
- // Catching:
- // (fn_ptr as *<const/mut> <ty>) == (0 as <ty>)
- ExprKind::Cast(cast_expr, _) if is_integer_literal(cast_expr, 0) => {
- lint_expr(cx, expr);
- },
-
- // Catching:
- // (fn_ptr as *<const/mut> <ty>) == std::ptr::null()
- ExprKind::Call(func, []) if is_path_diagnostic_item(cx, func, sym::ptr_null) => {
- lint_expr(cx, expr);
- },
-
- // Catching:
- // (fn_ptr as *<const/mut> <ty>) == <const that evaluates to null_ptr>
- _ if matches!(constant(cx, cx.typeck_results(), to_check), Some(Constant::RawPtr(0))) => {
- lint_expr(cx, expr);
- },
-
- _ => {},
- }
- },
- _ => {},
- }
- }
-}
diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs
index d34d6e927..f4f8bdc2c 100644
--- a/src/tools/clippy/clippy_lints/src/format.rs
+++ b/src/tools/clippy/clippy_lints/src/format.rs
@@ -43,7 +43,9 @@ declare_lint_pass!(UselessFormat => [USELESS_FORMAT]);
impl<'tcx> LateLintPass<'tcx> for UselessFormat {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
+ let Some(macro_call) = root_macro_call_first_node(cx, expr) else {
+ return;
+ };
if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) {
return;
}
diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs
index 08e45ed7d..01c714c41 100644
--- a/src/tools/clippy/clippy_lints/src/format_args.rs
+++ b/src/tools/clippy/clippy_lints/src/format_args.rs
@@ -14,10 +14,8 @@ use rustc_ast::{
FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions,
FormatPlaceholder, FormatTrait,
};
-use rustc_errors::{
- Applicability,
- SuggestionStyle::{CompletelyHidden, ShowCode},
-};
+use rustc_errors::Applicability;
+use rustc_errors::SuggestionStyle::{CompletelyHidden, ShowCode};
use rustc_hir::{Expr, ExprKind, LangItem};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
@@ -188,7 +186,9 @@ impl FormatArgs {
impl<'tcx> LateLintPass<'tcx> for FormatArgs {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
- let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
+ let Some(macro_call) = root_macro_call_first_node(cx, expr) else {
+ return;
+ };
if !is_format_macro(cx, macro_call.def_id) {
return;
}
diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs
index 3ddee1842..76369bccf 100644
--- a/src/tools/clippy/clippy_lints/src/format_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/format_impl.rs
@@ -7,8 +7,8 @@ use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::Span;
-use rustc_span::{sym, symbol::kw, Symbol};
+use rustc_span::symbol::kw;
+use rustc_span::{sym, Span, Symbol};
declare_clippy_lint! {
/// ### What it does
@@ -127,7 +127,9 @@ impl<'tcx> LateLintPass<'tcx> for FormatImpl {
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- let Some(format_trait_impl) = self.format_trait_impl else { return };
+ let Some(format_trait_impl) = self.format_trait_impl else {
+ return;
+ };
if format_trait_impl.name == sym::Display {
check_to_string_in_display(cx, expr);
diff --git a/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs b/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs
new file mode 100644
index 000000000..419c77343
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs
@@ -0,0 +1,99 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_errors::Applicability;
+use rustc_hir::Item;
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for outer doc comments written with 4 forward slashes (`////`).
+ ///
+ /// ### Why is this bad?
+ /// This is (probably) a typo, and results in it not being a doc comment; just a regular
+ /// comment.
+ ///
+ /// ### Example
+ /// ```rust
+ /// //// My amazing data structure
+ /// pub struct Foo {
+ /// // ...
+ /// }
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// /// My amazing data structure
+ /// pub struct Foo {
+ /// // ...
+ /// }
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub FOUR_FORWARD_SLASHES,
+ suspicious,
+ "comments with 4 forward slashes (`////`) likely intended to be doc comments (`///`)"
+}
+declare_lint_pass!(FourForwardSlashes => [FOUR_FORWARD_SLASHES]);
+
+impl<'tcx> LateLintPass<'tcx> for FourForwardSlashes {
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
+ if item.span.from_expansion() {
+ return;
+ }
+ let sm = cx.sess().source_map();
+ let mut span = cx
+ .tcx
+ .hir()
+ .attrs(item.hir_id())
+ .iter()
+ .fold(item.span.shrink_to_lo(), |span, attr| span.to(attr.span));
+ let (Some(file), _, _, end_line, _) = sm.span_to_location_info(span) else {
+ return;
+ };
+ let mut bad_comments = vec![];
+ for line in (0..end_line.saturating_sub(1)).rev() {
+ let Some(contents) = file.get_line(line).map(|c| c.trim().to_owned()) else {
+ return;
+ };
+ // Keep searching until we find the next item
+ if !contents.is_empty() && !contents.starts_with("//") && !contents.starts_with("#[") {
+ break;
+ }
+
+ if contents.starts_with("////") && !matches!(contents.chars().nth(4), Some('/' | '!')) {
+ let bounds = file.line_bounds(line);
+ let line_span = Span::with_root_ctxt(bounds.start, bounds.end);
+ span = line_span.to(span);
+ bad_comments.push((line_span, contents));
+ }
+ }
+
+ if !bad_comments.is_empty() {
+ span_lint_and_then(
+ cx,
+ FOUR_FORWARD_SLASHES,
+ span,
+ "this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't",
+ |diag| {
+ let msg = if bad_comments.len() == 1 {
+ "make this a doc comment by removing one `/`"
+ } else {
+ "turn these into doc comments by removing one `/`"
+ };
+
+ diag.multipart_suggestion(
+ msg,
+ bad_comments
+ .into_iter()
+ // It's a little unfortunate but the span includes the `\n` yet the contents
+ // do not, so we must add it back. If some codebase uses `\r\n` instead they
+ // will need normalization but it should be fine
+ .map(|(span, c)| (span, c.replacen("////", "///", 1) + "\n"))
+ .collect(),
+ Applicability::MachineApplicable,
+ );
+ },
+ );
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs
index 92d67ef35..2b899e21e 100644
--- a/src/tools/clippy/clippy_lints/src/from_over_into.rs
+++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs
@@ -10,7 +10,8 @@ use rustc_hir::{
TyKind,
};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::{hir::nested_filter::OnlyBodies, ty};
+use rustc_middle::hir::nested_filter::OnlyBodies;
+use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::{kw, sym};
use rustc_span::{Span, Symbol};
@@ -76,9 +77,10 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto {
&& let Some(into_trait_seg) = hir_trait_ref.path.segments.last()
// `impl Into<target_ty> for self_ty`
&& let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args
- && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(ty::EarlyBinder::subst_identity)
+ && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id)
+ .map(ty::EarlyBinder::instantiate_identity)
&& cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id)
- && !matches!(middle_trait_ref.substs.type_at(1).kind(), ty::Alias(ty::Opaque, _))
+ && !matches!(middle_trait_ref.args.type_at(1).kind(), ty::Alias(ty::Opaque, _))
{
span_lint_and_then(
cx,
@@ -163,10 +165,14 @@ fn convert_to_from(
return None;
}
let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id);
- let ImplItemKind::Fn(ref sig, body_id) = impl_item.kind else { return None };
+ let ImplItemKind::Fn(ref sig, body_id) = impl_item.kind else {
+ return None;
+ };
let body = cx.tcx.hir().body(body_id);
let [input] = body.params else { return None };
- let PatKind::Binding(.., self_ident, None) = input.pat.kind else { return None };
+ let PatKind::Binding(.., self_ident, None) = input.pat.kind else {
+ return None;
+ };
let from = snippet_opt(cx, self_ty.span)?;
let into = snippet_opt(cx, target_ty.span)?;
diff --git a/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs b/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs
index 096508dc4..5e859d97c 100644
--- a/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs
@@ -4,8 +4,7 @@ use clippy_utils::{match_def_path, path_def_id, paths};
use rustc_hir::def_id::DefId;
use rustc_hir::{Expr, ExprKind, QPath};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::RawPtr;
-use rustc_middle::ty::TypeAndMut;
+use rustc_middle::ty::{RawPtr, TypeAndMut};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
diff --git a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
index d3d0d91c1..597fca888 100644
--- a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
@@ -1,6 +1,8 @@
-use clippy_utils::{diagnostics::span_lint_and_then, is_in_test_function};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::is_in_test_function;
-use rustc_hir::{intravisit::FnKind, Body, HirId};
+use rustc_hir::intravisit::FnKind;
+use rustc_hir::{Body, HirId};
use rustc_lint::LateContext;
use rustc_span::Span;
diff --git a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
index b244b9133..18f7368da 100644
--- a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
@@ -1,7 +1,8 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet;
use rustc_errors::Applicability;
-use rustc_hir::{intravisit::FnKind, Body, ExprKind, FnDecl, ImplicitSelfKind, Unsafety};
+use rustc_hir::intravisit::FnKind;
+use rustc_hir::{Body, ExprKind, FnDecl, ImplicitSelfKind, Unsafety};
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_span::Span;
@@ -12,8 +13,8 @@ use super::MISNAMED_GETTERS;
pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body: &Body<'_>, span: Span) {
let FnKind::Method(ref ident, sig) = kind else {
- return;
- };
+ return;
+ };
// Takes only &(mut) self
if decl.inputs.len() != 1 {
@@ -25,8 +26,8 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body:
let name = match decl.implicit_self {
ImplicitSelfKind::MutRef => {
let Some(name) = name.strip_suffix("_mut") else {
- return;
- };
+ return;
+ };
name
},
ImplicitSelfKind::Imm | ImplicitSelfKind::Mut | ImplicitSelfKind::ImmRef => name,
@@ -76,7 +77,7 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body:
for adjusted_type in iter::once(typeck_results.expr_ty(self_data))
.chain(typeck_results.expr_adjustments(self_data).iter().map(|adj| adj.target))
{
- let ty::Adt(def,_) = adjusted_type.kind() else {
+ let ty::Adt(def, _) = adjusted_type.kind() else {
continue;
};
@@ -91,13 +92,15 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body:
}
let Some(used_field) = used_field else {
- // Can happen if the field access is a tuple. We don't lint those because the getter name could not start with a number.
+ // Can happen if the field access is a tuple. We don't lint those because the getter name could not
+ // start with a number.
return;
};
let Some(correct_field) = correct_field else {
// There is no field corresponding to the getter name.
- // FIXME: This can be a false positive if the correct field is reachable through deeper autodereferences than used_field is
+ // FIXME: This can be a false positive if the correct field is reachable through deeper
+ // autodereferences than used_field is
return;
};
diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs
index d0ad26282..57df5683c 100644
--- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs
@@ -1,14 +1,13 @@
use hir::FnSig;
use rustc_ast::ast::Attribute;
use rustc_errors::Applicability;
+use rustc_hir::def::Res;
use rustc_hir::def_id::DefIdSet;
-use rustc_hir::{self as hir, def::Res, QPath};
+use rustc_hir::{self as hir, QPath};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LintContext};
-use rustc_middle::{
- lint::in_external_macro,
- ty::{self, Ty},
-};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::{self, Ty};
use rustc_span::{sym, Span, Symbol};
use clippy_utils::attrs::is_proc_macro;
@@ -198,14 +197,14 @@ fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, tys: &mut DefIdSet)
match *ty.kind() {
// primitive types are never mutable
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false,
- ty::Adt(adt, substs) => {
+ ty::Adt(adt, args) => {
tys.insert(adt.did()) && !ty.is_freeze(cx.tcx, cx.param_env)
|| KNOWN_WRAPPER_TYS
.iter()
.any(|&sym| cx.tcx.is_diagnostic_item(sym, adt.did()))
- && substs.types().any(|ty| is_mutable_ty(cx, ty, tys))
+ && args.types().any(|ty| is_mutable_ty(cx, ty, tys))
},
- ty::Tuple(substs) => substs.iter().any(|ty| is_mutable_ty(cx, ty, tys)),
+ ty::Tuple(args) => args.iter().any(|ty| is_mutable_ty(cx, ty, tys)),
ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, tys),
ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => {
mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, tys)
@@ -222,7 +221,7 @@ fn is_mutated_static(e: &hir::Expr<'_>) -> bool {
match e.kind {
Path(QPath::Resolved(_, path)) => !matches!(path.res, Res::Local(_)),
Path(_) => true,
- Field(inner, _) | Index(inner, _) => is_mutated_static(inner),
+ Field(inner, _) | Index(inner, _, _) => is_mutated_static(inner),
_ => false,
}
}
diff --git a/src/tools/clippy/clippy_lints/src/functions/result.rs b/src/tools/clippy/clippy_lints/src/functions/result.rs
index fa2a9b30c..90fc0d4f6 100644
--- a/src/tools/clippy/clippy_lints/src/functions/result.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/result.rs
@@ -21,11 +21,11 @@ fn result_err_ty<'tcx>(
) -> Option<(&'tcx hir::Ty<'tcx>, Ty<'tcx>)> {
if !in_external_macro(cx.sess(), item_span)
&& let hir::FnRetTy::Return(hir_ty) = decl.output
- && let ty = cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).subst_identity().output())
+ && let ty = cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).instantiate_identity().output())
&& is_type_diagnostic_item(cx, ty, sym::Result)
- && let ty::Adt(_, substs) = ty.kind()
+ && let ty::Adt(_, args) = ty.kind()
{
- let err_ty = substs.type_at(1);
+ let err_ty = args.type_at(1);
Some((hir_ty, err_ty))
} else {
None
diff --git a/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs b/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs
index bd473ac7e..34f1bf3b2 100644
--- a/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs
@@ -23,7 +23,7 @@ pub(super) fn check_fn(
}
let Some(code_snippet) = snippet_opt(cx, body.value.span) else {
- return
+ return;
};
let mut line_count: u64 = 0;
let mut in_comment = false;
diff --git a/src/tools/clippy/clippy_lints/src/future_not_send.rs b/src/tools/clippy/clippy_lints/src/future_not_send.rs
index 818ebd113..621415c88 100644
--- a/src/tools/clippy/clippy_lints/src/future_not_send.rs
+++ b/src/tools/clippy/clippy_lints/src/future_not_send.rs
@@ -63,10 +63,10 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
return;
}
let ret_ty = return_ty(cx, cx.tcx.hir().local_def_id_to_hir_id(fn_def_id).expect_owner());
- if let ty::Alias(ty::Opaque, AliasTy { def_id, substs, .. }) = *ret_ty.kind() {
+ if let ty::Alias(ty::Opaque, AliasTy { def_id, args, .. }) = *ret_ty.kind() {
let preds = cx.tcx.explicit_item_bounds(def_id);
let mut is_future = false;
- for (p, _span) in preds.subst_iter_copied(cx.tcx, substs) {
+ for (p, _span) in preds.iter_instantiated_copied(cx.tcx, args) {
if let Some(trait_pred) = p.as_trait_clause() {
if Some(trait_pred.skip_binder().trait_ref.def_id) == cx.tcx.lang_items().future_trait() {
is_future = true;
diff --git a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
index 9ea8c494c..e614a8f69 100644
--- a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
+++ b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
@@ -1,7 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::higher;
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::SpanlessEq;
+use clippy_utils::{higher, SpanlessEq};
use if_chain::if_chain;
use rustc_errors::Diagnostic;
use rustc_hir::intravisit::{self as visit, Visitor};
diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
index 725bd3d54..ab6ad3f3b 100644
--- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
+++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
@@ -119,7 +119,13 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
fn stmts_contains_early_return(stmts: &[Stmt<'_>]) -> bool {
stmts.iter().any(|stmt| {
- let Stmt { kind: StmtKind::Semi(e), .. } = stmt else { return false };
+ let Stmt {
+ kind: StmtKind::Semi(e),
+ ..
+ } = stmt
+ else {
+ return false;
+ };
contains_return(e)
})
diff --git a/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs b/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs
new file mode 100644
index 000000000..c635120b8
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs
@@ -0,0 +1,52 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use hir::PatKind;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `_` in patterns of type `()`.
+ ///
+ /// ### Why is this bad?
+ /// Matching with `()` explicitly instead of `_` outlines
+ /// the fact that the pattern contains no data. Also it
+ /// would detect a type change that `_` would ignore.
+ ///
+ /// ### Example
+ /// ```rust
+ /// match std::fs::create_dir("tmp-work-dir") {
+ /// Ok(_) => println!("Working directory created"),
+ /// Err(s) => eprintln!("Could not create directory: {s}"),
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// match std::fs::create_dir("tmp-work-dir") {
+ /// Ok(()) => println!("Working directory created"),
+ /// Err(s) => eprintln!("Could not create directory: {s}"),
+ /// }
+ /// ```
+ #[clippy::version = "1.73.0"]
+ pub IGNORED_UNIT_PATTERNS,
+ pedantic,
+ "suggest replacing `_` by `()` in patterns where appropriate"
+}
+declare_lint_pass!(IgnoredUnitPatterns => [IGNORED_UNIT_PATTERNS]);
+
+impl<'tcx> LateLintPass<'tcx> for IgnoredUnitPatterns {
+ fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx hir::Pat<'tcx>) {
+ if matches!(pat.kind, PatKind::Wild) && cx.typeck_results().pat_ty(pat).is_unit() {
+ span_lint_and_sugg(
+ cx,
+ IGNORED_UNIT_PATTERNS,
+ pat.span,
+ "matching over `()` is more explicit",
+ "use `()` instead of `_`",
+ String::from("()"),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/implicit_return.rs b/src/tools/clippy/clippy_lints/src/implicit_return.rs
index 372b6ead3..a6b035d51 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_return.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_return.rs
@@ -1,9 +1,7 @@
-use clippy_utils::{
- diagnostics::span_lint_hir_and_then,
- get_async_fn_body, is_async_fn,
- source::{snippet_with_applicability, snippet_with_context, walk_span_to_context},
- visitors::for_each_expr,
-};
+use clippy_utils::diagnostics::span_lint_hir_and_then;
+use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context};
+use clippy_utils::visitors::for_each_expr;
+use clippy_utils::{get_async_fn_body, is_async_fn};
use core::ops::ControlFlow;
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
index 1e99b6faa..b99d45446 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
@@ -102,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
if let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id);
if let Some(impl_id) = cx.tcx.impl_of_method(const_id);
if let None = cx.tcx.impl_trait_ref(impl_id); // An inherent impl
- if cx.tcx.type_of(impl_id).subst_identity().is_integral();
+ if cx.tcx.type_of(impl_id).instantiate_identity().is_integral();
then {
print_lint_and_sugg(cx, var_name, expr)
}
@@ -115,7 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
if let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id);
if let Some(impl_id) = cx.tcx.impl_of_method(func_id);
if let None = cx.tcx.impl_trait_ref(impl_id); // An inherent impl
- if cx.tcx.type_of(impl_id).subst_identity().is_integral();
+ if cx.tcx.type_of(impl_id).instantiate_identity().is_integral();
then {
print_lint_and_sugg(cx, var_name, expr)
}
diff --git a/src/tools/clippy/clippy_lints/src/incorrect_impls.rs b/src/tools/clippy/clippy_lints/src/incorrect_impls.rs
index 7b95116ee..3c59b839a 100644
--- a/src/tools/clippy/clippy_lints/src/incorrect_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/incorrect_impls.rs
@@ -1,11 +1,15 @@
-use clippy_utils::{diagnostics::span_lint_and_sugg, get_parent_node, last_path_segment, ty::implements_trait};
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::paths::ORD_CMP;
+use clippy_utils::ty::implements_trait;
+use clippy_utils::{get_parent_node, is_res_lang_ctor, last_path_segment, match_def_path, path_res, std_or_core};
use rustc_errors::Applicability;
-use rustc_hir::{ExprKind, ImplItem, ImplItemKind, ItemKind, Node, UnOp};
-use rustc_hir_analysis::hir_ty_to_ty;
+use rustc_hir::def_id::LocalDefId;
+use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, ItemKind, LangItem, Node, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::EarlyBinder;
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::{sym, symbol};
+use rustc_span::sym;
+use rustc_span::symbol::kw;
declare_clippy_lint! {
/// ### What it does
@@ -46,25 +50,84 @@ declare_clippy_lint! {
correctness,
"manual implementation of `Clone` on a `Copy` type"
}
-declare_lint_pass!(IncorrectImpls => [INCORRECT_CLONE_IMPL_ON_COPY_TYPE]);
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for manual implementations of both `PartialOrd` and `Ord` when only `Ord` is
+ /// necessary.
+ ///
+ /// ### Why is this bad?
+ /// If both `PartialOrd` and `Ord` are implemented, they must agree. This is commonly done by
+ /// wrapping the result of `cmp` in `Some` for `partial_cmp`. Not doing this may silently
+ /// introduce an error upon refactoring.
+ ///
+ /// ### Known issues
+ /// Code that calls the `.into()` method instead will be flagged as incorrect, despite `.into()`
+ /// wrapping it in `Some`.
+ ///
+ /// ### Limitations
+ /// Will not lint if `Self` and `Rhs` do not have the same type.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # use std::cmp::Ordering;
+ /// #[derive(Eq, PartialEq)]
+ /// struct A(u32);
+ ///
+ /// impl Ord for A {
+ /// fn cmp(&self, other: &Self) -> Ordering {
+ /// // ...
+ /// # todo!();
+ /// }
+ /// }
+ ///
+ /// impl PartialOrd for A {
+ /// fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ /// // ...
+ /// # todo!();
+ /// }
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # use std::cmp::Ordering;
+ /// #[derive(Eq, PartialEq)]
+ /// struct A(u32);
+ ///
+ /// impl Ord for A {
+ /// fn cmp(&self, other: &Self) -> Ordering {
+ /// // ...
+ /// # todo!();
+ /// }
+ /// }
+ ///
+ /// impl PartialOrd for A {
+ /// fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ /// Some(self.cmp(other))
+ /// }
+ /// }
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE,
+ correctness,
+ "manual implementation of `PartialOrd` when `Ord` is already implemented"
+}
+declare_lint_pass!(IncorrectImpls => [INCORRECT_CLONE_IMPL_ON_COPY_TYPE, INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE]);
impl LateLintPass<'_> for IncorrectImpls {
- #[expect(clippy::needless_return)]
+ #[expect(clippy::too_many_lines)]
fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
- let node = get_parent_node(cx.tcx, impl_item.hir_id());
- let Some(Node::Item(item)) = node else {
- return;
- };
- let ItemKind::Impl(imp) = item.kind else {
+ let Some(Node::Item(item)) = get_parent_node(cx.tcx, impl_item.hir_id()) else {
return;
};
let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) else {
return;
};
- let trait_impl_def_id = trait_impl.def_id;
if cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) {
return;
}
+ let ItemKind::Impl(_) = item.kind else {
+ return;
+ };
let ImplItemKind::Fn(_, impl_item_id) = cx.tcx.hir().impl_item(impl_item.impl_item_id()).kind else {
return;
};
@@ -72,15 +135,12 @@ impl LateLintPass<'_> for IncorrectImpls {
let ExprKind::Block(block, ..) = body.value.kind else {
return;
};
- // Above is duplicated from the `duplicate_manual_partial_ord_impl` branch.
- // Remove it while solving conflicts once that PR is merged.
- // Actual implementation; remove this comment once aforementioned PR is merged
- if cx.tcx.is_diagnostic_item(sym::Clone, trait_impl_def_id)
+ if cx.tcx.is_diagnostic_item(sym::Clone, trait_impl.def_id)
&& let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy)
&& implements_trait(
cx,
- hir_ty_to_ty(cx.tcx, imp.self_ty),
+ trait_impl.self_ty(),
copy_def_id,
&[],
)
@@ -88,9 +148,9 @@ impl LateLintPass<'_> for IncorrectImpls {
if impl_item.ident.name == sym::clone {
if block.stmts.is_empty()
&& let Some(expr) = block.expr
- && let ExprKind::Unary(UnOp::Deref, inner) = expr.kind
- && let ExprKind::Path(qpath) = inner.kind
- && last_path_segment(&qpath).ident.name == symbol::kw::SelfLower
+ && let ExprKind::Unary(UnOp::Deref, deref) = expr.kind
+ && let ExprKind::Path(qpath) = deref.kind
+ && last_path_segment(&qpath).ident.name == kw::SelfLower
{} else {
span_lint_and_sugg(
cx,
@@ -112,7 +172,7 @@ impl LateLintPass<'_> for IncorrectImpls {
INCORRECT_CLONE_IMPL_ON_COPY_TYPE,
impl_item.span,
"incorrect implementation of `clone_from` on a `Copy` type",
- "remove this",
+ "remove it",
String::new(),
Applicability::MaybeIncorrect,
);
@@ -120,5 +180,116 @@ impl LateLintPass<'_> for IncorrectImpls {
return;
}
}
+
+ if cx.tcx.is_diagnostic_item(sym::PartialOrd, trait_impl.def_id)
+ && impl_item.ident.name == sym::partial_cmp
+ && let Some(ord_def_id) = cx
+ .tcx
+ .diagnostic_items(trait_impl.def_id.krate)
+ .name_to_id
+ .get(&sym::Ord)
+ && implements_trait(
+ cx,
+ trait_impl.self_ty(),
+ *ord_def_id,
+ &[],
+ )
+ {
+ // If the `cmp` call likely needs to be fully qualified in the suggestion
+ // (like `std::cmp::Ord::cmp`). It's unfortunate we must put this here but we can't
+ // access `cmp_expr` in the suggestion without major changes, as we lint in `else`.
+ let mut needs_fully_qualified = false;
+
+ if block.stmts.is_empty()
+ && let Some(expr) = block.expr
+ && let ExprKind::Call(
+ Expr {
+ kind: ExprKind::Path(some_path),
+ hir_id: some_hir_id,
+ ..
+ },
+ [cmp_expr],
+ ) = expr.kind
+ && is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome)
+ // Fix #11178, allow `Self::cmp(self, ..)` too
+ && self_cmp_call(cx, cmp_expr, impl_item.owner_id.def_id, &mut needs_fully_qualified)
+ {} else {
+ // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid
+ // suggestion tons more complex.
+ if let [lhs, rhs, ..] = trait_impl.args.as_slice() && lhs != rhs {
+ return;
+ }
+
+ span_lint_and_then(
+ cx,
+ INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE,
+ item.span,
+ "incorrect implementation of `partial_cmp` on an `Ord` type",
+ |diag| {
+ let [_, other] = body.params else {
+ return;
+ };
+ let Some(std_or_core) = std_or_core(cx) else {
+ return;
+ };
+
+ let suggs = match (other.pat.simple_ident(), needs_fully_qualified) {
+ (Some(other_ident), true) => vec![(
+ block.span,
+ format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, {})) }}", other_ident.name),
+ )],
+ (Some(other_ident), false) => {
+ vec![(block.span, format!("{{ Some(self.cmp({})) }}", other_ident.name))]
+ },
+ (None, true) => vec![
+ (
+ block.span,
+ format!("{{ Some({std_or_core}::cmp::Ord::cmp(self, other)) }}"),
+ ),
+ (other.pat.span, "other".to_owned()),
+ ],
+ (None, false) => vec![
+ (block.span, "{ Some(self.cmp(other)) }".to_owned()),
+ (other.pat.span, "other".to_owned()),
+ ],
+ };
+
+ diag.multipart_suggestion(
+ "change this to",
+ suggs,
+ Applicability::Unspecified,
+ );
+ }
+ );
+ }
+ }
+ }
+}
+
+/// Returns whether this is any of `self.cmp(..)`, `Self::cmp(self, ..)` or `Ord::cmp(self, ..)`.
+fn self_cmp_call<'tcx>(
+ cx: &LateContext<'tcx>,
+ cmp_expr: &'tcx Expr<'tcx>,
+ def_id: LocalDefId,
+ needs_fully_qualified: &mut bool,
+) -> bool {
+ match cmp_expr.kind {
+ ExprKind::Call(path, [_self, _other]) => path_res(cx, path)
+ .opt_def_id()
+ .is_some_and(|def_id| match_def_path(cx, def_id, &ORD_CMP)),
+ ExprKind::MethodCall(_, _, [_other], ..) => {
+ // We can set this to true here no matter what as if it's a `MethodCall` and goes to the
+ // `else` branch, it must be a method named `cmp` that isn't `Ord::cmp`
+ *needs_fully_qualified = true;
+
+ // It's a bit annoying but `typeck_results` only gives us the CURRENT body, which we
+ // have none, not of any `LocalDefId` we want, so we must call the query itself to avoid
+ // an immediate ICE
+ cx.tcx
+ .typeck(def_id)
+ .type_dependent_def_id(cmp_expr.hir_id)
+ .is_some_and(|def_id| match_def_path(cx, def_id, &ORD_CMP))
+ },
+ _ => false,
}
}
diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
index 7a269e98f..f507f45d5 100644
--- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
+++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
@@ -13,7 +13,8 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter;
use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::{symbol::Ident, Span};
+use rustc_span::symbol::Ident;
+use rustc_span::Span;
declare_clippy_lint! {
/// ### What it does
@@ -253,7 +254,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> {
// Checking for slice indexing
let parent_id = map.parent_id(expr.hir_id);
if let Some(hir::Node::Expr(parent_expr)) = map.find(parent_id);
- if let hir::ExprKind::Index(_, index_expr) = parent_expr.kind;
+ if let hir::ExprKind::Index(_, index_expr, _) = parent_expr.kind;
if let Some(Constant::Int(index_value)) = constant(cx, cx.typeck_results(), index_expr);
if let Ok(index_value) = index_value.try_into();
if index_value < max_suggested_slice;
diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
index 22c14d9b0..4f4f57177 100644
--- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
+++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
@@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
return;
}
- if let ExprKind::Index(array, index) = &expr.kind {
+ if let ExprKind::Index(array, index, _) = &expr.kind {
let note = "the suggestion might not be applicable in constant blocks";
let ty = cx.typeck_results().expr_ty(array).peel_refs();
if let Some(range) = higher::Range::hir(index) {
diff --git a/src/tools/clippy/clippy_lints/src/inherent_impl.rs b/src/tools/clippy/clippy_lints/src/inherent_impl.rs
index 7c41699f3..3d1113ff9 100644
--- a/src/tools/clippy/clippy_lints/src/inherent_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/inherent_impl.rs
@@ -3,7 +3,8 @@
use clippy_utils::diagnostics::span_lint_and_note;
use clippy_utils::is_lint_allowed;
use rustc_data_structures::fx::FxHashMap;
-use rustc_hir::{def_id::LocalDefId, Item, ItemKind, Node};
+use rustc_hir::def_id::LocalDefId;
+use rustc_hir::{Item, ItemKind, Node};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span;
@@ -66,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl {
)
}) {
for impl_id in impl_ids.iter().map(|id| id.expect_local()) {
- let impl_ty = cx.tcx.type_of(impl_id).subst_identity();
+ let impl_ty = cx.tcx.type_of(impl_id).instantiate_identity();
match type_map.entry(impl_ty) {
Entry::Vacant(e) => {
// Store the id for the first impl block of this type. The span is retrieved lazily.
diff --git a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs
index d43e5cc9b..bc4ec33b7 100644
--- a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs
+++ b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs
@@ -1,11 +1,11 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::{implements_trait, is_type_lang_item};
use clippy_utils::{return_ty, trait_ref_of_method};
-use if_chain::if_chain;
-use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind, LangItem};
+use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind, LangItem, Unsafety};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
+use rustc_target::spec::abi::Abi;
declare_clippy_lint! {
/// ### What it does
@@ -95,24 +95,23 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString {
return;
}
- if_chain! {
- // Check if item is a method, called to_string and has a parameter 'self'
- if let ImplItemKind::Fn(ref signature, _) = impl_item.kind;
- if impl_item.ident.name == sym::to_string;
- let decl = &signature.decl;
- if decl.implicit_self.has_implicit_self();
- if decl.inputs.len() == 1;
- if impl_item.generics.params.iter().all(|p| matches!(p.kind, GenericParamKind::Lifetime { .. }));
-
+ // Check if item is a method called `to_string` and has a parameter 'self'
+ if let ImplItemKind::Fn(ref signature, _) = impl_item.kind
+ // #11201
+ && let header = signature.header
+ && header.unsafety == Unsafety::Normal
+ && header.abi == Abi::Rust
+ && impl_item.ident.name == sym::to_string
+ && let decl = signature.decl
+ && decl.implicit_self.has_implicit_self()
+ && decl.inputs.len() == 1
+ && impl_item.generics.params.iter().all(|p| matches!(p.kind, GenericParamKind::Lifetime { .. }))
// Check if return type is String
- if is_type_lang_item(cx, return_ty(cx, impl_item.owner_id), LangItem::String);
-
+ && is_type_lang_item(cx, return_ty(cx, impl_item.owner_id), LangItem::String)
// Filters instances of to_string which are required by a trait
- if trait_ref_of_method(cx, impl_item.owner_id.def_id).is_none();
-
- then {
- show_lint(cx, impl_item);
- }
+ && trait_ref_of_method(cx, impl_item.owner_id.def_id).is_none()
+ {
+ show_lint(cx, impl_item);
}
}
}
diff --git a/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs b/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs
index 7e1548531..b00fa104f 100644
--- a/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs
+++ b/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs
@@ -50,7 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for NumberedFields {
&& fields
.iter()
.all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit))
- && !matches!(cx.qpath_res(path, e.hir_id), Res::Def(DefKind::TyAlias, ..))
+ && !matches!(cx.qpath_res(path, e.hir_id), Res::Def(DefKind::TyAlias { .. }, ..))
{
let expr_spans = fields
.iter()
@@ -71,7 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for NumberedFields {
INIT_NUMBERED_FIELDS,
e.span,
"used a field initializer for a tuple struct",
- "try this instead",
+ "try",
snippet,
appl,
);
diff --git a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
index 34e999158..8df7dfb8b 100644
--- a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
+++ b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
@@ -7,7 +7,8 @@ use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::{source_map::Spanned, sym};
+use rustc_span::source_map::Spanned;
+use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs
index 40378ee82..55a43e915 100644
--- a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs
+++ b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs
@@ -1,4 +1,5 @@
-use clippy_utils::{diagnostics::span_lint_and_help, is_from_proc_macro, is_in_cfg_test};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::{is_from_proc_macro, is_in_cfg_test};
use rustc_hir::{HirId, ItemId, ItemKind, Mod};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
@@ -32,7 +33,7 @@ declare_clippy_lint! {
/// // [...]
/// }
/// ```
- #[clippy::version = "1.70.0"]
+ #[clippy::version = "1.71.0"]
pub ITEMS_AFTER_TEST_MODULE,
style,
"An item was found after the testing module `tests`"
diff --git a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs
index c924d7361..066d2c4b7 100644
--- a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs
+++ b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs
@@ -1,5 +1,8 @@
-use clippy_utils::{diagnostics::span_lint, get_parent_node, ty::implements_trait};
-use rustc_hir::{def_id::LocalDefId, FnSig, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::get_parent_node;
+use clippy_utils::ty::implements_trait;
+use rustc_hir::def_id::LocalDefId;
+use rustc_hir::{FnSig, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym;
@@ -68,7 +71,7 @@ fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefI
if sig.decl.implicit_self.has_implicit_self() {
let ret_ty = cx
.tcx
- .erase_late_bound_regions(cx.tcx.fn_sig(fn_id).subst_identity().output());
+ .erase_late_bound_regions(cx.tcx.fn_sig(fn_id).instantiate_identity().output());
let ret_ty = cx
.tcx
.try_normalize_erasing_regions(cx.param_env, ret_ty)
diff --git a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs
index 4dc750c03..9b26c3573 100644
--- a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs
+++ b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs
@@ -50,7 +50,11 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
if_chain! {
if !item.span.from_expansion();
- if let ItemKind::Const(hir_ty, _) = &item.kind;
+ if let ItemKind::Const(hir_ty, generics, _) = &item.kind;
+ // Since static items may not have generics, skip generic const items.
+ // FIXME(generic_const_items): I don't think checking `generics.hwcp` suffices as it
+ // doesn't account for empty where-clauses that only consist of keyword `where` IINM.
+ if generics.params.is_empty() && !generics.has_where_clause_predicates;
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
if let ty::Array(element_type, cst) = ty.kind();
if let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind();
diff --git a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs
index 1c99bd2f3..b22b57a30 100644
--- a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs
+++ b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs
@@ -1,10 +1,8 @@
//! lint when there is a large size difference between variants on an enum
+use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{
- diagnostics::span_lint_and_then,
- ty::{approx_ty_size, is_copy, AdtVariantInfo},
-};
+use clippy_utils::ty::{approx_ty_size, is_copy, AdtVariantInfo};
use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
@@ -83,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
return;
}
if let ItemKind::Enum(ref def, _) = item.kind {
- let ty = cx.tcx.type_of(item.owner_id).subst_identity();
+ let ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
let Adt(adt, subst) = ty.kind() else {
panic!("already checked whether this is an enum")
};
@@ -169,8 +167,8 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
}
fn maybe_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
- if let Adt(_def, substs) = ty.kind()
- && substs.types().next().is_some()
+ if let Adt(_def, args) = ty.kind()
+ && args.types().next().is_some()
&& let Some(copy_trait) = cx.tcx.lang_items().copy_trait()
{
return cx.tcx.non_blanket_impls_for_ty(copy_trait, ty).next().is_some();
diff --git a/src/tools/clippy/clippy_lints/src/large_futures.rs b/src/tools/clippy/clippy_lints/src/large_futures.rs
index 087c4a652..d67d58993 100644
--- a/src/tools/clippy/clippy_lints/src/large_futures.rs
+++ b/src/tools/clippy/clippy_lints/src/large_futures.rs
@@ -1,5 +1,6 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
-use clippy_utils::{diagnostics::span_lint_and_sugg, ty::implements_trait};
+use clippy_utils::ty::implements_trait;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath};
use rustc_lint::{LateContext, LateLintPass};
diff --git a/src/tools/clippy/clippy_lints/src/large_include_file.rs b/src/tools/clippy/clippy_lints/src/large_include_file.rs
index 424c0d9e7..566901de3 100644
--- a/src/tools/clippy/clippy_lints/src/large_include_file.rs
+++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs
@@ -2,8 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_note;
use clippy_utils::is_lint_allowed;
use clippy_utils::macros::root_macro_call_first_node;
use rustc_ast::LitKind;
-use rustc_hir::Expr;
-use rustc_hir::ExprKind;
+use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::sym;
diff --git a/src/tools/clippy/clippy_lints/src/large_stack_frames.rs b/src/tools/clippy/clippy_lints/src/large_stack_frames.rs
index 9c0cc978a..7aa1446d5 100644
--- a/src/tools/clippy/clippy_lints/src/large_stack_frames.rs
+++ b/src/tools/clippy/clippy_lints/src/large_stack_frames.rs
@@ -4,11 +4,9 @@ use clippy_utils::diagnostics::span_lint_and_note;
use clippy_utils::fn_has_unsatisfiable_preds;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::FnKind;
-use rustc_hir::Body;
-use rustc_hir::FnDecl;
+use rustc_hir::{Body, FnDecl};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::declare_tool_lint;
-use rustc_session::impl_lint_pass;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;
declare_clippy_lint! {
diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs
index 17bd89efa..deba232bd 100644
--- a/src/tools/clippy/clippy_lints/src/len_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/len_zero.rs
@@ -1,22 +1,22 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet_with_context;
-use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators, sugg::Sugg};
+use clippy_utils::sugg::Sugg;
+use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
-use rustc_hir::def_id::DefIdSet;
+use rustc_hir::def::Res;
+use rustc_hir::def_id::{DefId, DefIdSet};
use rustc_hir::{
- def::Res, def_id::DefId, lang_items::LangItem, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg,
- GenericBound, ImplItem, ImplItemKind, ImplicitSelfKind, Item, ItemKind, Mutability, Node, PathSegment, PrimTy,
- QPath, TraitItemRef, TyKind, TypeBindingKind,
+ AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind,
+ ImplicitSelfKind, Item, ItemKind, LangItem, Mutability, Node, PatKind, PathSegment, PrimTy, QPath, TraitItemRef,
+ TyKind, TypeBindingKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, AssocKind, FnSig, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::{
- source_map::{Span, Spanned, Symbol},
- symbol::sym,
-};
+use rustc_span::source_map::{Span, Spanned, Symbol};
+use rustc_span::symbol::sym;
declare_clippy_lint! {
/// ### What it does
@@ -145,7 +145,10 @@ impl<'tcx> LateLintPass<'tcx> for LenZero {
if let Some(local_id) = ty_id.as_local();
let ty_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id);
if !is_lint_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id);
- if let Some(output) = parse_len_output(cx, cx.tcx.fn_sig(item.owner_id).subst_identity().skip_binder());
+ if let Some(output) = parse_len_output(
+ cx,
+ cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder()
+ );
then {
let (name, kind) = match cx.tcx.hir().find(ty_hir_id) {
Some(Node::ForeignItem(x)) => (x.ident.name, "extern type"),
@@ -167,6 +170,31 @@ impl<'tcx> LateLintPass<'tcx> for LenZero {
return;
}
+ if let ExprKind::Let(lt) = expr.kind
+ && has_is_empty(cx, lt.init)
+ && match lt.pat.kind {
+ PatKind::Slice([], None, []) => true,
+ PatKind::Lit(lit) if is_empty_string(lit) => true,
+ _ => false,
+ }
+ {
+ let mut applicability = Applicability::MachineApplicable;
+
+ let lit1 = peel_ref_operators(cx, lt.init);
+ let lit_str =
+ Sugg::hir_with_context(cx, lit1, lt.span.ctxt(), "_", &mut applicability).maybe_par();
+
+ span_lint_and_sugg(
+ cx,
+ COMPARISON_TO_EMPTY,
+ lt.span,
+ "comparison to empty slice using `if let`",
+ "using `is_empty` is clearer and more explicit",
+ format!("{lit_str}.is_empty()"),
+ applicability,
+ );
+ }
+
if let ExprKind::Binary(Spanned { node: cmp, .. }, left, right) = expr.kind {
// expr.span might contains parenthesis, see issue #10529
let actual_span = left.span.with_hi(right.span.hi());
@@ -425,7 +453,7 @@ fn check_for_is_empty(
if !(is_empty.fn_has_self_parameter
&& check_is_empty_sig(
cx,
- cx.tcx.fn_sig(is_empty.def_id).subst_identity().skip_binder(),
+ cx.tcx.fn_sig(is_empty.def_id).instantiate_identity().skip_binder(),
self_kind,
output,
)) =>
diff --git a/src/tools/clippy/clippy_lints/src/let_if_seq.rs b/src/tools/clippy/clippy_lints/src/let_if_seq.rs
index db41bc67d..2f6f36c39 100644
--- a/src/tools/clippy/clippy_lints/src/let_if_seq.rs
+++ b/src/tools/clippy/clippy_lints/src/let_if_seq.rs
@@ -1,6 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::path_to_local_id;
use clippy_utils::source::snippet;
-use clippy_utils::{path_to_local_id, visitors::is_local_used};
+use clippy_utils::visitors::is_local_used;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs
index e66141809..e7c875ab3 100644
--- a/src/tools/clippy/clippy_lints/src/let_underscore.rs
+++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs
@@ -1,12 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::is_from_proc_macro;
use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type};
-use clippy_utils::{is_must_use_func_call, paths};
+use clippy_utils::{is_from_proc_macro, is_must_use_func_call, paths};
use rustc_hir::{Local, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::subst::GenericArgKind;
-use rustc_middle::ty::IsSuggestable;
+use rustc_middle::ty::{GenericArgKind, IsSuggestable};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{BytePos, Span};
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 87329ee5e..358004cf4 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -65,6 +65,7 @@ mod declared_lints;
mod renamed_lints;
// begin lints modules, do not remove this comment, it’s used in `update_lints`
+mod absolute_paths;
mod allow_attributes;
mod almost_complete_range;
mod approx_const;
@@ -120,6 +121,7 @@ mod entry;
mod enum_clike;
mod enum_variants;
mod equatable_if_let;
+mod error_impl_error;
mod escape;
mod eta_reduction;
mod excessive_bools;
@@ -131,12 +133,12 @@ mod extra_unused_type_parameters;
mod fallible_impl_from;
mod float_literal;
mod floating_point_arithmetic;
-mod fn_null_check;
mod format;
mod format_args;
mod format_impl;
mod format_push_string;
mod formatting;
+mod four_forward_slashes;
mod from_over_into;
mod from_raw_with_void_ptr;
mod from_str_radix_10;
@@ -145,6 +147,7 @@ mod future_not_send;
mod if_let_mutex;
mod if_not_else;
mod if_then_some_else_none;
+mod ignored_unit_patterns;
mod implicit_hasher;
mod implicit_return;
mod implicit_saturating_add;
@@ -184,6 +187,7 @@ mod manual_assert;
mod manual_async_fn;
mod manual_bits;
mod manual_clamp;
+mod manual_float_methods;
mod manual_is_ascii_check;
mod manual_let_else;
mod manual_main_separator_str;
@@ -229,6 +233,7 @@ mod needless_for_each;
mod needless_if;
mod needless_late_init;
mod needless_parens_on_range_literals;
+mod needless_pass_by_ref_mut;
mod needless_pass_by_value;
mod needless_question_mark;
mod needless_update;
@@ -271,6 +276,7 @@ mod redundant_clone;
mod redundant_closure_call;
mod redundant_else;
mod redundant_field_names;
+mod redundant_locals;
mod redundant_pub_crate;
mod redundant_slicing;
mod redundant_static_lifetimes;
@@ -346,11 +352,10 @@ mod zero_div_zero;
mod zero_sized_map_values;
// end lints modules, do not remove this comment, it’s used in `update_lints`
+use crate::utils::conf::metadata::get_configuration_metadata;
+use crate::utils::conf::TryConf;
pub use crate::utils::conf::{lookup_conf_file, Conf};
-use crate::utils::{
- conf::{metadata::get_configuration_metadata, TryConf},
- FindAll,
-};
+use crate::utils::FindAll;
/// Register all pre expansion lints
///
@@ -663,7 +668,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
});
store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv())));
let matches_for_let_else = conf.matches_for_let_else;
- store.register_late_pass(move |_| Box::new(manual_let_else::ManualLetElse::new(msrv(), matches_for_let_else)));
store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv())));
store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv())));
store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv())));
@@ -723,7 +727,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(drop_forget_ref::DropForgetRef));
store.register_late_pass(|_| Box::new(empty_enum::EmptyEnum));
store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons));
- store.register_late_pass(|_| Box::new(regex::Regex));
+ store.register_late_pass(|_| Box::<regex::Regex>::default());
let ignore_interior_mutability = conf.ignore_interior_mutability.clone();
store.register_late_pass(move |_| Box::new(copies::CopyAndPaste::new(ignore_interior_mutability.clone())));
store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator));
@@ -772,7 +776,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::<useless_conversion::UselessConversion>::default());
store.register_late_pass(|_| Box::new(implicit_hasher::ImplicitHasher));
store.register_late_pass(|_| Box::new(fallible_impl_from::FallibleImplFrom));
- store.register_late_pass(|_| Box::<question_mark::QuestionMark>::default());
+ store.register_late_pass(move |_| Box::new(question_mark::QuestionMark::new(msrv(), matches_for_let_else)));
store.register_late_pass(|_| Box::new(question_mark_used::QuestionMarkUsed));
store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings));
store.register_late_pass(|_| Box::new(suspicious_trait_impl::SuspiciousImpl));
@@ -910,7 +914,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv())));
store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison));
store.register_early_pass(move || Box::new(module_style::ModStyle));
- store.register_late_pass(|_| Box::new(unused_async::UnusedAsync));
+ store.register_late_pass(|_| Box::<unused_async::UnusedAsync>::default());
let disallowed_types = conf.disallowed_types.clone();
store.register_late_pass(move |_| Box::new(disallowed_types::DisallowedTypes::new(disallowed_types.clone())));
let import_renames = conf.enforced_import_renames.clone();
@@ -1003,7 +1007,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
semicolon_outside_block_ignore_multiline,
))
});
- store.register_late_pass(|_| Box::new(fn_null_check::FnNullCheck));
store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse));
store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef));
store.register_late_pass(|_| Box::new(multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock));
@@ -1058,6 +1061,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
let stack_size_threshold = conf.stack_size_threshold;
store.register_late_pass(move |_| Box::new(large_stack_frames::LargeStackFrames::new(stack_size_threshold)));
store.register_late_pass(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit));
+ store.register_late_pass(move |_| {
+ Box::new(needless_pass_by_ref_mut::NeedlessPassByRefMut::new(
+ avoid_breaking_exported_api,
+ ))
+ });
store.register_late_pass(|_| Box::new(incorrect_impls::IncorrectImpls));
store.register_late_pass(move |_| {
Box::new(single_call_fn::SingleCallFn {
@@ -1074,6 +1082,19 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(manual_range_patterns::ManualRangePatterns));
store.register_early_pass(|| Box::new(visibility::Visibility));
store.register_late_pass(move |_| Box::new(tuple_array_conversions::TupleArrayConversions { msrv: msrv() }));
+ store.register_late_pass(|_| Box::new(manual_float_methods::ManualFloatMethods));
+ store.register_late_pass(|_| Box::new(four_forward_slashes::FourForwardSlashes));
+ store.register_late_pass(|_| Box::new(error_impl_error::ErrorImplError));
+ let absolute_paths_max_segments = conf.absolute_paths_max_segments;
+ let absolute_paths_allowed_crates = conf.absolute_paths_allowed_crates.clone();
+ store.register_late_pass(move |_| {
+ Box::new(absolute_paths::AbsolutePaths {
+ absolute_paths_max_segments,
+ absolute_paths_allowed_crates: absolute_paths_allowed_crates.clone(),
+ })
+ });
+ store.register_late_pass(|_| Box::new(redundant_locals::RedundantLocals));
+ store.register_late_pass(|_| Box::new(ignored_unit_patterns::IgnoredUnitPatterns));
// add lints here, do not remove this comment, it's used in `new_lint`
}
diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs
index 852f67365..0004a150d 100644
--- a/src/tools/clippy/clippy_lints/src/lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs
@@ -15,6 +15,7 @@ use rustc_hir::{
PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::hir::map::Map;
use rustc_middle::hir::nested_filter as middle_nested_filter;
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -620,7 +621,7 @@ impl<'cx, 'tcx, F> Visitor<'tcx> for LifetimeChecker<'cx, 'tcx, F>
where
F: NestedFilter<'tcx>,
{
- type Map = rustc_middle::hir::map::Map<'tcx>;
+ type Map = Map<'tcx>;
type NestedFilter = F;
// for lifetimes as parameters of generics
diff --git a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs
index 09b2032e2..49425ff0a 100644
--- a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs
+++ b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs
@@ -1,7 +1,6 @@
-use clippy_utils::{
- diagnostics::span_lint_and_then, is_diag_item_method, is_trait_method, match_def_path, path_to_local_id, paths,
- ty::match_type,
-};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::ty::match_type;
+use clippy_utils::{is_diag_item_method, is_trait_method, match_def_path, path_to_local_id, paths};
use rustc_errors::Applicability;
use rustc_hir::{Body, Closure, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs
index dadcd9c51..09ca03173 100644
--- a/src/tools/clippy/clippy_lints/src/literal_representation.rs
+++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs
@@ -264,7 +264,7 @@ impl LiteralDigitGrouping {
return;
}
- if Self::is_literal_uuid_formatted(&mut num_lit) {
+ if Self::is_literal_uuid_formatted(&num_lit) {
return;
}
@@ -376,7 +376,7 @@ impl LiteralDigitGrouping {
///
/// Returns `true` if the radix is hexadecimal, and the groups match the
/// UUID format of 8-4-4-4-12.
- fn is_literal_uuid_formatted(num_lit: &mut NumericLiteral<'_>) -> bool {
+ fn is_literal_uuid_formatted(num_lit: &NumericLiteral<'_>) -> bool {
if num_lit.radix != Radix::Hexadecimal {
return false;
}
diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
index 5c5a4cfce..7b8c88235 100644
--- a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
@@ -109,7 +109,7 @@ fn is_ref_iterable<'tcx>(
&& let sig = cx.tcx.liberate_late_bound_regions(fn_id, cx.tcx.fn_sig(fn_id).skip_binder())
&& let &[req_self_ty, req_res_ty] = &**sig.inputs_and_output
&& let param_env = cx.tcx.param_env(fn_id)
- && implements_trait_with_env(cx.tcx, param_env, req_self_ty, trait_id, [])
+ && implements_trait_with_env(cx.tcx, param_env, req_self_ty, trait_id, &[])
&& let Some(into_iter_ty) =
make_normalized_projection_with_regions(cx.tcx, param_env, trait_id, sym!(IntoIter), [req_self_ty])
&& let req_res_ty = normalize_with_regions(cx.tcx, param_env, req_res_ty)
@@ -125,7 +125,7 @@ fn is_ref_iterable<'tcx>(
}
let res_ty = cx.tcx.erase_regions(EarlyBinder::bind(req_res_ty)
- .subst(cx.tcx, typeck.node_substs(call_expr.hir_id)));
+ .instantiate(cx.tcx, typeck.node_args(call_expr.hir_id)));
let mutbl = if let ty::Ref(_, _, mutbl) = *req_self_ty.kind() {
Some(mutbl)
} else {
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs
index 4bb9936e9..0aaa66e6b 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs
@@ -1,14 +1,14 @@
use super::utils::make_iterator_snippet;
use super::MANUAL_FIND;
-use clippy_utils::{
- diagnostics::span_lint_and_then, higher, is_res_lang_ctor, path_res, peel_blocks_with_stmt,
- source::snippet_with_applicability, ty::implements_trait,
-};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::implements_trait;
+use clippy_utils::{higher, is_res_lang_ctor, path_res, peel_blocks_with_stmt};
use if_chain::if_chain;
use rustc_errors::Applicability;
-use rustc_hir::{
- def::Res, lang_items::LangItem, BindingAnnotation, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind,
-};
+use rustc_hir::def::Res;
+use rustc_hir::lang_items::LangItem;
+use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind};
use rustc_lint::LateContext;
use rustc_span::source_map::Span;
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
index 1e02a30e3..559a2c03f 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
@@ -1,9 +1,8 @@
use super::utils::make_iterator_snippet;
use super::MANUAL_FLATTEN;
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::higher;
use clippy_utils::visitors::is_local_used;
-use clippy_utils::{path_to_local_id, peel_blocks_with_stmt};
+use clippy_utils::{higher, path_to_local_id, peel_blocks_with_stmt};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs
index 7d1f8ef29..d3fd0e863 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs
@@ -60,8 +60,8 @@ pub(super) fn check<'tcx>(
o.and_then(|(lhs, rhs)| {
let rhs = fetch_cloned_expr(rhs);
if_chain! {
- if let ExprKind::Index(base_left, idx_left) = lhs.kind;
- if let ExprKind::Index(base_right, idx_right) = rhs.kind;
+ if let ExprKind::Index(base_left, idx_left, _) = lhs.kind;
+ if let ExprKind::Index(base_right, idx_right, _) = rhs.kind;
if let Some(ty) = get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_left));
if get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_right)).is_some();
if let Some((start_left, offset_left)) = get_details_from_idx(cx, idx_left, &starts);
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs b/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs
index cb9c84be4..ca584a454 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs
@@ -1,9 +1,6 @@
-use clippy_utils::{
- diagnostics::{multispan_sugg_with_applicability, span_lint_and_then},
- match_def_path, paths,
- source::snippet,
- SpanlessEq,
-};
+use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then};
+use clippy_utils::source::snippet;
+use clippy_utils::{match_def_path, paths, SpanlessEq};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Pat, Stmt, StmtKind, UnOp};
use rustc_lint::LateContext;
diff --git a/src/tools/clippy/clippy_lints/src/loops/missing_spin_loop.rs b/src/tools/clippy/clippy_lints/src/loops/missing_spin_loop.rs
index 8412875b1..7b7d19c75 100644
--- a/src/tools/clippy/clippy_lints/src/loops/missing_spin_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/missing_spin_loop.rs
@@ -35,7 +35,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &'
if let ExprKind::Block(Block { stmts: [], expr: None, ..}, _) = body.kind;
if let ExprKind::MethodCall(method, callee, ..) = unpack_cond(cond).kind;
if [sym::load, sym::compare_exchange, sym::compare_exchange_weak].contains(&method.ident.name);
- if let ty::Adt(def, _substs) = cx.typeck_results().expr_ty(callee).kind();
+ if let ty::Adt(def, _args) = cx.typeck_results().expr_ty(callee).kind();
if cx.tcx.is_diagnostic_item(sym::AtomicBool, def.did());
then {
span_lint_and_sugg(
@@ -43,7 +43,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &'
MISSING_SPIN_LOOP,
body.span,
"busy-waiting loop should at least have a spin loop hint",
- "try this",
+ "try",
(if is_no_std_crate(cx) {
"{ core::hint::spin_loop() }"
} else {
diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs
index 529189b52..ffd29ab76 100644
--- a/src/tools/clippy/clippy_lints/src/loops/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs
@@ -601,7 +601,7 @@ declare_clippy_lint! {
/// // use `number`
/// }
/// ```
- #[clippy::version = "1.70.0"]
+ #[clippy::version = "1.71.0"]
pub MANUAL_WHILE_LET_SOME,
style,
"checking for emptiness of a `Vec` in the loop condition and popping an element in the body"
diff --git a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
index 4dae93f60..b83d148b5 100644
--- a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
@@ -7,7 +7,8 @@ use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Node, PatKind};
use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::LateContext;
-use rustc_middle::{mir::FakeReadCause, ty};
+use rustc_middle::mir::FakeReadCause;
+use rustc_middle::ty;
use rustc_span::source_map::Span;
pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) {
diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
index cb4465675..c4af46b8f 100644
--- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
@@ -319,7 +319,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
if_chain! {
// an index op
- if let ExprKind::Index(seqexpr, idx) = expr.kind;
+ if let ExprKind::Index(seqexpr, idx, _) = expr.kind;
if !self.check(idx, seqexpr, expr);
then {
return;
@@ -370,7 +370,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
ExprKind::MethodCall(_, receiver, args, _) => {
let def_id = self.cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
for (ty, expr) in iter::zip(
- self.cx.tcx.fn_sig(def_id).subst_identity().inputs().skip_binder(),
+ self.cx.tcx.fn_sig(def_id).instantiate_identity().inputs().skip_binder(),
std::iter::once(receiver).chain(args.iter()),
) {
self.prefer_mutable = false;
diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
index ee338c6be..cc19ac55e 100644
--- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
@@ -1,9 +1,9 @@
use super::utils::make_iterator_snippet;
use super::NEVER_LOOP;
-use clippy_utils::consts::constant;
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher::ForLoop;
use clippy_utils::source::snippet;
-use clippy_utils::{consts::Constant, diagnostics::span_lint_and_then};
use rustc_errors::Applicability;
use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind};
use rustc_lint::LateContext;
@@ -162,7 +162,9 @@ fn never_loop_expr<'tcx>(
ExprKind::Binary(_, e1, e2)
| ExprKind::Assign(e1, e2, _)
| ExprKind::AssignOp(_, e1, e2)
- | ExprKind::Index(e1, e2) => never_loop_expr_all(cx, &mut [e1, e2].iter().copied(), ignore_ids, main_loop_id),
+ | ExprKind::Index(e1, e2, _) => {
+ never_loop_expr_all(cx, &mut [e1, e2].iter().copied(), ignore_ids, main_loop_id)
+ },
ExprKind::Loop(b, _, _, _) => {
// Break can come from the inner loop so remove them.
absorb_break(never_loop_block(cx, b, ignore_ids, main_loop_id))
diff --git a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs
index 744fd61bd..dfb800ccf 100644
--- a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs
@@ -9,6 +9,7 @@ use rustc_errors::Applicability;
use rustc_hir::{is_range_literal, BorrowKind, Expr, ExprKind, Pat};
use rustc_lint::LateContext;
use rustc_span::edition::Edition;
+use rustc_span::sym;
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
@@ -51,7 +52,7 @@ pub(super) fn check<'tcx>(
},
[],
_,
- ) if method.ident.name.as_str() == "iter_mut" => (arg, "&mut "),
+ ) if method.ident.name == sym::iter_mut => (arg, "&mut "),
ExprKind::MethodCall(
method,
Expr {
diff --git a/src/tools/clippy/clippy_lints/src/loops/utils.rs b/src/tools/clippy/clippy_lints/src/loops/utils.rs
index 28ee24309..6edca2d55 100644
--- a/src/tools/clippy/clippy_lints/src/loops/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/utils.rs
@@ -76,7 +76,7 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> {
ExprKind::Assign(lhs, _, _) if lhs.hir_id == expr.hir_id => {
*state = IncrementVisitorVarState::DontWarn;
},
- ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => {
+ ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) => {
*state = IncrementVisitorVarState::DontWarn;
},
_ => (),
@@ -226,7 +226,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
InitializeVisitorState::DontWarn
}
},
- ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => {
+ ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) => {
self.state = InitializeVisitorState::DontWarn;
},
_ => (),
diff --git a/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs b/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs
index d1a1f773f..7f24f3c5d 100644
--- a/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs
@@ -6,8 +6,7 @@ use if_chain::if_chain;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefIdMap;
use rustc_hir::intravisit::{walk_expr, Visitor};
-use rustc_hir::HirIdSet;
-use rustc_hir::{Expr, ExprKind, QPath};
+use rustc_hir::{Expr, ExprKind, HirIdSet, QPath};
use rustc_lint::LateContext;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) {
diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs
index 55989f8a4..5153070cf 100644
--- a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs
@@ -1,18 +1,18 @@
use super::WHILE_LET_ON_ITERATOR;
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::higher;
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{
- get_enclosing_loop_or_multi_call_closure, is_refutable, is_res_lang_ctor, is_trait_method, visitors::is_res_used,
-};
+use clippy_utils::visitors::is_res_used;
+use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable, is_res_lang_ctor, is_trait_method};
use if_chain::if_chain;
use rustc_errors::Applicability;
+use rustc_hir::def::Res;
use rustc_hir::intravisit::{walk_expr, Visitor};
-use rustc_hir::{def::Res, Closure, Expr, ExprKind, HirId, LangItem, Local, Mutability, PatKind, UnOp};
+use rustc_hir::{Closure, Expr, ExprKind, HirId, LangItem, Local, Mutability, PatKind, UnOp};
use rustc_lint::LateContext;
use rustc_middle::hir::nested_filter::OnlyBodies;
use rustc_middle::ty::adjustment::Adjust;
-use rustc_span::{symbol::sym, Symbol};
+use rustc_span::symbol::sym;
+use rustc_span::Symbol;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let (scrutinee_expr, iter_expr_struct, iter_expr, some_pat, loop_expr) = if_chain! {
@@ -113,7 +113,7 @@ fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExp
// Shouldn't have side effects, but there's no way to trace which field is used. So forget which fields have
// already been seen.
- ExprKind::Index(base, idx) if !idx.can_have_side_effects() => {
+ ExprKind::Index(base, idx, _) if !idx.can_have_side_effects() => {
can_move = false;
fields.clear();
e = base;
@@ -332,7 +332,7 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: &
if let Some(e) = get_enclosing_loop_or_multi_call_closure(cx, loop_expr) {
let Res::Local(local_id) = iter_expr.path else {
- return true
+ return true;
};
let mut v = NestedLoopVisitor {
cx,
diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs
index 8e322a979..9b158f18f 100644
--- a/src/tools/clippy/clippy_lints/src/macro_use.rs
+++ b/src/tools/clippy/clippy_lints/src/macro_use.rs
@@ -8,7 +8,8 @@ use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::{edition::Edition, sym, Span};
+use rustc_span::edition::Edition;
+use rustc_span::{sym, Span};
declare_clippy_lint! {
/// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs
index 4629b22d1..6c7c57ba1 100644
--- a/src/tools/clippy/clippy_lints/src/manual_bits.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs
@@ -110,7 +110,7 @@ fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<
if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id();
if cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id);
then {
- cx.typeck_results().node_substs(count_func.hir_id).types().next().map(|resolved_ty| (*real_ty, resolved_ty))
+ cx.typeck_results().node_args(count_func.hir_id).types().next().map(|resolved_ty| (*real_ty, resolved_ty))
} else {
None
}
diff --git a/src/tools/clippy/clippy_lints/src/manual_clamp.rs b/src/tools/clippy/clippy_lints/src/manual_clamp.rs
index 440362b96..e75666e61 100644
--- a/src/tools/clippy/clippy_lints/src/manual_clamp.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_clamp.rs
@@ -4,21 +4,19 @@ use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::implements_trait;
use clippy_utils::visitors::is_const_evaluatable;
-use clippy_utils::MaybePath;
use clippy_utils::{
eq_expr_value, in_constant, is_diag_trait_item, is_trait_method, path_res, path_to_local_id, peel_blocks,
- peel_blocks_with_stmt,
+ peel_blocks_with_stmt, MaybePath,
};
use itertools::Itertools;
-use rustc_errors::Applicability;
-use rustc_errors::Diagnostic;
-use rustc_hir::{
- def::Res, Arm, BinOpKind, Block, Expr, ExprKind, Guard, HirId, PatKind, PathSegment, PrimTy, QPath, StmtKind,
-};
+use rustc_errors::{Applicability, Diagnostic};
+use rustc_hir::def::Res;
+use rustc_hir::{Arm, BinOpKind, Block, Expr, ExprKind, Guard, HirId, PatKind, PathSegment, PrimTy, QPath, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::Ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::{symbol::sym, Span};
+use rustc_span::symbol::sym;
+use rustc_span::Span;
use std::ops::Deref;
declare_clippy_lint! {
diff --git a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs
new file mode 100644
index 000000000..88db7ae6a
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs
@@ -0,0 +1,175 @@
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::{is_from_proc_macro, path_to_local};
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Constness, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for manual `is_infinite` reimplementations
+ /// (i.e., `x == <float>::INFINITY || x == <float>::NEG_INFINITY`).
+ ///
+ /// ### Why is this bad?
+ /// The method `is_infinite` is shorter and more readable.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let x = 1.0f32;
+ /// if x == f32::INFINITY || x == f32::NEG_INFINITY {}
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # let x = 1.0f32;
+ /// if x.is_infinite() {}
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub MANUAL_IS_INFINITE,
+ style,
+ "use dedicated method to check if a float is infinite"
+}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for manual `is_finite` reimplementations
+ /// (i.e., `x != <float>::INFINITY && x != <float>::NEG_INFINITY`).
+ ///
+ /// ### Why is this bad?
+ /// The method `is_finite` is shorter and more readable.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let x = 1.0f32;
+ /// if x != f32::INFINITY && x != f32::NEG_INFINITY {}
+ /// if x.abs() < f32::INFINITY {}
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # let x = 1.0f32;
+ /// if x.is_finite() {}
+ /// if x.is_finite() {}
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub MANUAL_IS_FINITE,
+ style,
+ "use dedicated method to check if a float is finite"
+}
+declare_lint_pass!(ManualFloatMethods => [MANUAL_IS_INFINITE, MANUAL_IS_FINITE]);
+
+#[derive(Clone, Copy)]
+enum Variant {
+ ManualIsInfinite,
+ ManualIsFinite,
+}
+
+impl Variant {
+ pub fn lint(self) -> &'static Lint {
+ match self {
+ Self::ManualIsInfinite => MANUAL_IS_INFINITE,
+ Self::ManualIsFinite => MANUAL_IS_FINITE,
+ }
+ }
+
+ pub fn msg(self) -> &'static str {
+ match self {
+ Self::ManualIsInfinite => "manually checking if a float is infinite",
+ Self::ManualIsFinite => "manually checking if a float is finite",
+ }
+ }
+}
+
+impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+ if !in_external_macro(cx.sess(), expr.span)
+ && (
+ matches!(cx.tcx.constness(cx.tcx.hir().enclosing_body_owner(expr.hir_id)), Constness::NotConst)
+ || cx.tcx.features().active(sym!(const_float_classify))
+ ) && let ExprKind::Binary(kind, lhs, rhs) = expr.kind
+ && let ExprKind::Binary(lhs_kind, lhs_lhs, lhs_rhs) = lhs.kind
+ && let ExprKind::Binary(rhs_kind, rhs_lhs, rhs_rhs) = rhs.kind
+ // Checking all possible scenarios using a function would be a hopeless task, as we have
+ // 16 possible alignments of constants/operands. For now, let's use `partition`.
+ && let (operands, constants) = [lhs_lhs, lhs_rhs, rhs_lhs, rhs_rhs]
+ .into_iter()
+ .partition::<Vec<&Expr<'_>>, _>(|i| path_to_local(i).is_some())
+ && let [first, second] = &*operands
+ && let Some([const_1, const_2]) = constants
+ .into_iter()
+ .map(|i| constant(cx, cx.typeck_results(), i))
+ .collect::<Option<Vec<_>>>()
+ .as_deref()
+ && path_to_local(first).is_some_and(|f| path_to_local(second).is_some_and(|s| f == s))
+ // The actual infinity check, we also allow `NEG_INFINITY` before` INFINITY` just in
+ // case somebody does that for some reason
+ && (is_infinity(const_1) && is_neg_infinity(const_2)
+ || is_neg_infinity(const_1) && is_infinity(const_2))
+ && !is_from_proc_macro(cx, expr)
+ && let Some(local_snippet) = snippet_opt(cx, first.span)
+ {
+ let variant = match (kind.node, lhs_kind.node, rhs_kind.node) {
+ (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Eq) => Variant::ManualIsInfinite,
+ (BinOpKind::And, BinOpKind::Ne, BinOpKind::Ne) => Variant::ManualIsFinite,
+ _ => return,
+ };
+
+ span_lint_and_then(
+ cx,
+ variant.lint(),
+ expr.span,
+ variant.msg(),
+ |diag| {
+ match variant {
+ Variant::ManualIsInfinite => {
+ diag.span_suggestion(
+ expr.span,
+ "use the dedicated method instead",
+ format!("{local_snippet}.is_infinite()"),
+ Applicability::MachineApplicable,
+ );
+ },
+ Variant::ManualIsFinite => {
+ // TODO: There's probably some better way to do this, i.e., create
+ // multiple suggestions with notes between each of them
+ diag.span_suggestion_verbose(
+ expr.span,
+ "use the dedicated method instead",
+ format!("{local_snippet}.is_finite()"),
+ Applicability::MaybeIncorrect,
+ )
+ .span_suggestion_verbose(
+ expr.span,
+ "this will alter how it handles NaN; if that is a problem, use instead",
+ format!("{local_snippet}.is_finite() || {local_snippet}.is_nan()"),
+ Applicability::MaybeIncorrect,
+ )
+ .span_suggestion_verbose(
+ expr.span,
+ "or, for conciseness",
+ format!("!{local_snippet}.is_infinite()"),
+ Applicability::MaybeIncorrect,
+ );
+ },
+ }
+ },
+ );
+ }
+ }
+}
+
+fn is_infinity(constant: &Constant<'_>) -> bool {
+ match constant {
+ Constant::F32(float) => *float == f32::INFINITY,
+ Constant::F64(float) => *float == f64::INFINITY,
+ _ => false,
+ }
+}
+
+fn is_neg_infinity(constant: &Constant<'_>) -> bool {
+ match constant {
+ Constant::F32(float) => *float == f32::NEG_INFINITY,
+ Constant::F64(float) => *float == f64::NEG_INFINITY,
+ _ => false,
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
index 31264261f..f26442447 100644
--- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
@@ -1,12 +1,16 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::macros::root_macro_call;
use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::{diagnostics::span_lint_and_sugg, higher, in_constant, macros::root_macro_call, sugg::Sugg};
+use clippy_utils::sugg::Sugg;
+use clippy_utils::{higher, in_constant};
use rustc_ast::ast::RangeLimits;
use rustc_ast::LitKind::{Byte, Char};
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, PatKind, RangeEnd};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::{def_id::DefId, sym, Span};
+use rustc_span::def_id::DefId;
+use rustc_span::{sym, Span};
declare_clippy_lint! {
/// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs
index 59e421c16..c531137b7 100644
--- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs
@@ -1,18 +1,17 @@
+use crate::question_mark::{QuestionMark, QUESTION_MARK};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher::IfLetOrMatch;
-use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::peel_blocks;
use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::{Descend, Visitable};
-use if_chain::if_chain;
+use clippy_utils::{is_lint_allowed, msrvs, pat_and_expr_can_be_question_mark, peel_blocks};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{Expr, ExprKind, HirId, ItemId, Local, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind, Ty};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro;
-use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_session::declare_tool_lint;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
use serde::Deserialize;
@@ -50,25 +49,8 @@ declare_clippy_lint! {
"manual implementation of a let...else statement"
}
-pub struct ManualLetElse {
- msrv: Msrv,
- matches_behaviour: MatchLintBehaviour,
-}
-
-impl ManualLetElse {
- #[must_use]
- pub fn new(msrv: Msrv, matches_behaviour: MatchLintBehaviour) -> Self {
- Self {
- msrv,
- matches_behaviour,
- }
- }
-}
-
-impl_lint_pass!(ManualLetElse => [MANUAL_LET_ELSE]);
-
-impl<'tcx> LateLintPass<'tcx> for ManualLetElse {
- fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &'tcx Stmt<'tcx>) {
+impl<'tcx> QuestionMark {
+ pub(crate) fn check_manual_let_else(&mut self, cx: &LateContext<'_>, stmt: &'tcx Stmt<'tcx>) {
if !self.msrv.meets(msrvs::LET_ELSE) || in_external_macro(cx.sess(), stmt.span) {
return;
}
@@ -81,11 +63,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualLetElse {
let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init)
{
match if_let_or_match {
- IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => if_chain! {
- if let Some(ident_map) = expr_simple_identity_map(local.pat, let_pat, if_then);
- if let Some(if_else) = if_else;
- if expr_diverges(cx, if_else);
- then {
+ IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => {
+ if
+ let Some(ident_map) = expr_simple_identity_map(local.pat, let_pat, if_then) &&
+ let Some(if_else) = if_else &&
+ expr_diverges(cx, if_else) &&
+ let qm_allowed = is_lint_allowed(cx, QUESTION_MARK, stmt.hir_id) &&
+ (qm_allowed || pat_and_expr_can_be_question_mark(cx, let_pat, if_else).is_none())
+ {
emit_manual_let_else(cx, stmt.span, if_let_expr, &ident_map, let_pat, if_else);
}
},
@@ -128,8 +113,6 @@ impl<'tcx> LateLintPass<'tcx> for ManualLetElse {
}
};
}
-
- extract_msrv_attr!(LateContext);
}
fn emit_manual_let_else(
@@ -208,7 +191,9 @@ fn replace_in_pattern(
match pat.kind {
PatKind::Binding(_ann, _id, binding_name, opt_subpt) => {
- let Some(pat_to_put) = ident_map.get(&binding_name.name) else { break 'a };
+ let Some(pat_to_put) = ident_map.get(&binding_name.name) else {
+ break 'a;
+ };
let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app);
if let Some(subpt) = opt_subpt {
let subpt = replace_in_pattern(cx, span, ident_map, subpt, app, false);
diff --git a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs
index 65ff55520..39d8b20d3 100644
--- a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs
@@ -2,12 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
use rustc_ast::LitKind;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
-use rustc_hir::Expr;
-use rustc_hir::ExprKind;
-use rustc_hir::PatKind;
-use rustc_hir::RangeEnd;
-use rustc_lint::LintContext;
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd, UnOp};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -19,6 +15,10 @@ declare_clippy_lint! {
/// ### Why is this bad?
/// Using an explicit range is more concise and easier to read.
///
+ /// ### Known issues
+ /// This lint intentionally does not handle numbers greater than `i128::MAX` for `u128` literals
+ /// in order to support negative numbers.
+ ///
/// ### Example
/// ```rust
/// let x = 6;
@@ -36,11 +36,14 @@ declare_clippy_lint! {
}
declare_lint_pass!(ManualRangePatterns => [MANUAL_RANGE_PATTERNS]);
-fn expr_as_u128(expr: &Expr<'_>) -> Option<u128> {
- if let ExprKind::Lit(lit) = expr.kind
+fn expr_as_i128(expr: &Expr<'_>) -> Option<i128> {
+ if let ExprKind::Unary(UnOp::Neg, expr) = expr.kind {
+ expr_as_i128(expr).map(|num| -num)
+ } else if let ExprKind::Lit(lit) = expr.kind
&& let LitKind::Int(num, _) = lit.node
{
- Some(num)
+ // Intentionally not handling numbers greater than i128::MAX (for u128 literals) for now.
+ num.try_into().ok()
} else {
None
}
@@ -56,22 +59,22 @@ impl LateLintPass<'_> for ManualRangePatterns {
if let PatKind::Or(pats) = pat.kind
&& pats.len() >= 3
{
- let mut min = u128::MAX;
- let mut max = 0;
+ let mut min = i128::MAX;
+ let mut max = i128::MIN;
let mut numbers_found = FxHashSet::default();
let mut ranges_found = Vec::new();
for pat in pats {
if let PatKind::Lit(lit) = pat.kind
- && let Some(num) = expr_as_u128(lit)
+ && let Some(num) = expr_as_i128(lit)
{
numbers_found.insert(num);
min = min.min(num);
max = max.max(num);
} else if let PatKind::Range(Some(left), Some(right), end) = pat.kind
- && let Some(left) = expr_as_u128(left)
- && let Some(right) = expr_as_u128(right)
+ && let Some(left) = expr_as_i128(left)
+ && let Some(right) = expr_as_i128(right)
&& right >= left
{
min = min.min(left);
diff --git a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
index aafee9271..0e89ca132 100644
--- a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
@@ -119,7 +119,7 @@ fn check_for_either_unsigned_int_constant<'a>(
}
fn check_for_unsigned_int_constant<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option<u128> {
- let Some(int_const) = constant_full_int(cx, cx.typeck_results(), expr) else { return None };
+ let int_const = constant_full_int(cx, cx.typeck_results(), expr)?;
match int_const {
FullInt::S(s) => s.try_into().ok(),
FullInt::U(u) => Some(u),
diff --git a/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs
index 703a6b258..f97600b53 100644
--- a/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs
@@ -92,7 +92,7 @@ fn simplify_half<'tcx>(
&& let ExprKind::Path(ref func_qpath) = func.kind
&& let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id()
&& cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id)
- && let Some(ty2) = cx.typeck_results().node_substs(func.hir_id).types().next()
+ && let Some(ty2) = cx.typeck_results().node_args(func.hir_id).types().next()
// T1 == T2?
&& *ty1 == ty2
{
diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs
index 93d977a5c..201bb56ef 100644
--- a/src/tools/clippy/clippy_lints/src/manual_strip.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs
@@ -8,8 +8,7 @@ use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_hir::def::Res;
use rustc_hir::intravisit::{walk_expr, Visitor};
-use rustc_hir::BinOpKind;
-use rustc_hir::{BorrowKind, Expr, ExprKind};
+use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -205,7 +204,7 @@ fn find_stripping<'tcx>(
if_chain! {
if is_ref_str(self.cx, ex);
let unref = peel_ref(ex);
- if let ExprKind::Index(indexed, index) = &unref.kind;
+ if let ExprKind::Index(indexed, index, _) = &unref.kind;
if let Some(higher::Range { start, end, .. }) = higher::Range::hir(index);
if let ExprKind::Path(path) = &indexed.kind;
if self.cx.qpath_res(path, ex.hir_id) == self.target;
diff --git a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs
index edcab6968..f0a0f482a 100644
--- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs
@@ -104,7 +104,7 @@ fn is_unit_function(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
let ty = cx.typeck_results().expr_ty(expr);
if let ty::FnDef(id, _) = *ty.kind() {
- if let Some(fn_type) = cx.tcx.fn_sig(id).subst_identity().no_bound_vars() {
+ if let Some(fn_type) = cx.tcx.fn_sig(id).instantiate_identity().no_bound_vars() {
return is_unit_type(fn_type.output());
}
}
@@ -226,7 +226,7 @@ fn lint_map_unit_fn(
);
span_lint_and_then(cx, lint, expr.span, &msg, |diag| {
- diag.span_suggestion(stmt.span, "try this", suggestion, applicability);
+ diag.span_suggestion(stmt.span, "try", suggestion, applicability);
});
} else if let Some((binding, closure_expr)) = unit_closure(cx, fn_arg) {
let msg = suggestion_msg("closure", map_type);
@@ -241,7 +241,7 @@ fn lint_map_unit_fn(
snippet_with_applicability(cx, var_arg.span, "_", &mut applicability),
snippet_with_context(cx, reduced_expr_span, var_arg.span.ctxt(), "_", &mut applicability).0,
);
- diag.span_suggestion(stmt.span, "try this", suggestion, applicability);
+ diag.span_suggestion(stmt.span, "try", suggestion, applicability);
} else {
let suggestion = format!(
"if let {0}({1}) = {2} {{ ... }}",
@@ -249,7 +249,7 @@ fn lint_map_unit_fn(
snippet(cx, binding.pat.span, "_"),
snippet(cx, var_arg.span, "_"),
);
- diag.span_suggestion(stmt.span, "try this", suggestion, Applicability::HasPlaceholders);
+ diag.span_suggestion(stmt.span, "try", suggestion, Applicability::HasPlaceholders);
}
});
}
diff --git a/src/tools/clippy/clippy_lints/src/match_result_ok.rs b/src/tools/clippy/clippy_lints/src/match_result_ok.rs
index 6ec978403..841c020f2 100644
--- a/src/tools/clippy/clippy_lints/src/match_result_ok.rs
+++ b/src/tools/clippy/clippy_lints/src/match_result_ok.rs
@@ -1,8 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::higher;
-use clippy_utils::is_res_lang_ctor;
use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{higher, is_res_lang_ctor};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem, PatKind};
diff --git a/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs b/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs
index d18c92cab..3329f93b7 100644
--- a/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs
@@ -28,7 +28,7 @@ pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool {
local.span,
"you seem to be trying to use `match` to destructure a single infallible pattern. \
Consider using `let`",
- "try this",
+ "try",
format!(
"let {}({}{}) = {};",
snippet_with_applicability(cx, variant_name.span, "..", &mut applicability),
diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs b/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs
index f6bf0e7aa..e0181a475 100644
--- a/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs
@@ -143,7 +143,7 @@ fn check<'tcx>(
MANUAL_FILTER,
expr.span,
"manual implementation of `Option::filter`",
- "try this",
+ "try",
if sugg_info.needs_brackets {
format!(
"{{ {}{}.filter({body_str}) }}",
diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_map.rs b/src/tools/clippy/clippy_lints/src/matches/manual_map.rs
index aaba23967..ed3d8b09f 100644
--- a/src/tools/clippy/clippy_lints/src/matches/manual_map.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/manual_map.rs
@@ -58,7 +58,7 @@ fn check<'tcx>(
MANUAL_MAP,
expr.span,
"manual implementation of `Option::map`",
- "try this",
+ "try",
if sugg_info.needs_brackets {
format!(
"{{ {}{}.map({}) }}",
diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
index 5b7644a53..6b611f567 100644
--- a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
@@ -1,14 +1,17 @@
-use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF};
+use crate::map_unit_fn::OPTION_MAP_UNIT_FN;
+use crate::matches::MATCH_AS_REF;
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
+use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{is_copy, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function};
use clippy_utils::{
can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id,
- peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, sugg::Sugg, CaptureKind,
+ peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, CaptureKind,
};
use rustc_ast::util::parser::PREC_POSTFIX;
use rustc_errors::Applicability;
+use rustc_hir::def::Res;
use rustc_hir::LangItem::{OptionNone, OptionSome};
-use rustc_hir::{def::Res, BindingAnnotation, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath};
+use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath};
use rustc_lint::LateContext;
use rustc_span::{sym, SyntaxContext};
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs b/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs
index 2818f030b..d51cca040 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs
@@ -27,10 +27,10 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr:
let input_ty = cx.typeck_results().expr_ty(ex);
let cast = if_chain! {
- if let ty::Adt(_, substs) = input_ty.kind();
- let input_ty = substs.type_at(0);
- if let ty::Adt(_, substs) = output_ty.kind();
- let output_ty = substs.type_at(0);
+ if let ty::Adt(_, args) = input_ty.kind();
+ let input_ty = args.type_at(0);
+ if let ty::Adt(_, args) = output_ty.kind();
+ let output_ty = args.type_at(0);
if let ty::Ref(_, output_ty, _) = *output_ty.kind();
if input_ty != output_ty;
then {
@@ -46,7 +46,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr:
MATCH_AS_REF,
expr.span,
&format!("use `{suggestion}()` instead"),
- "try this",
+ "try",
format!(
"{}.{suggestion}(){cast}",
snippet_with_applicability(cx, ex.span, "_", &mut applicability),
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs
index 0064619ef..e2ddf11ab 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs
@@ -1,9 +1,7 @@
use super::REDUNDANT_PATTERN_MATCHING;
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_lint_allowed;
-use clippy_utils::is_wild;
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::span_contains_comment;
+use clippy_utils::{is_lint_allowed, is_wild, span_contains_comment};
use rustc_ast::{Attribute, LitKind};
use rustc_errors::Applicability;
use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat, PatKind, QPath};
@@ -139,7 +137,7 @@ where
MATCH_LIKE_MATCHES_MACRO,
expr.span,
&format!("{} expression looks like `matches!` macro", if is_if_let { "if let .. else" } else { "match" }),
- "try this",
+ "try",
format!(
"{}matches!({}, {pat_and_guard})",
if b0 { "" } else { "!" },
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_on_vec_items.rs b/src/tools/clippy/clippy_lints/src/matches/match_on_vec_items.rs
index 2917f85c4..bd53ebd48 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_on_vec_items.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_on_vec_items.rs
@@ -12,7 +12,7 @@ use super::MATCH_ON_VEC_ITEMS;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>) {
if_chain! {
if let Some(idx_expr) = is_vec_indexing(cx, scrutinee);
- if let ExprKind::Index(vec, idx) = idx_expr.kind;
+ if let ExprKind::Index(vec, idx, _) = idx_expr.kind;
then {
// FIXME: could be improved to suggest surrounding every pattern with Some(_),
@@ -22,7 +22,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>) {
MATCH_ON_VEC_ITEMS,
scrutinee.span,
"indexing into a vector may panic",
- "try this",
+ "try",
format!(
"{}.get({})",
snippet(cx, vec.span, ".."),
@@ -36,7 +36,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>) {
fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
if_chain! {
- if let ExprKind::Index(array, index) = expr.kind;
+ if let ExprKind::Index(array, index, _) = expr.kind;
if is_vector(cx, array);
if !is_full_range(cx, index);
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
index 3d2fbea63..6fc79fadd 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
@@ -2,8 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet;
use clippy_utils::{is_lint_allowed, path_to_local, search_same, SpanlessEq, SpanlessHash};
use core::cmp::Ordering;
-use core::iter;
-use core::slice;
+use core::{iter, slice};
use rustc_arena::DroplessArena;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
@@ -240,7 +239,7 @@ impl<'a> NormalizedPat<'a> {
},
PatKind::TupleStruct(ref path, pats, wild_idx) => {
let Some(adt) = cx.typeck_results().pat_ty(pat).ty_adt_def() else {
- return Self::Wild
+ return Self::Wild;
};
let (var_id, variant) = if adt.is_enum() {
match cx.qpath_res(path, pat.hir_id).opt_def_id() {
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs
index 3126b5901..8d22ceb47 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs
@@ -143,7 +143,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
wildcard_span,
"wildcard matches only a single variant and will also match any future added variants",
- "try this",
+ "try",
format_suggestion(x),
Applicability::MaybeIncorrect,
),
@@ -161,7 +161,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
WILDCARD_ENUM_MATCH_ARM,
wildcard_span,
message,
- "try this",
+ "try",
suggestions.join(" | "),
Applicability::MaybeIncorrect,
);
diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs
index 00fa3eb9b..930386a60 100644
--- a/src/tools/clippy/clippy_lints/src/matches/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs
@@ -16,6 +16,7 @@ mod match_wild_enum;
mod match_wild_err_arm;
mod needless_match;
mod overlapping_arms;
+mod redundant_guards;
mod redundant_pattern_match;
mod rest_pat_in_fully_bound_struct;
mod significant_drop_in_scrutinee;
@@ -936,6 +937,36 @@ declare_clippy_lint! {
"reimplementation of `filter`"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for unnecessary guards in match expressions.
+ ///
+ /// ### Why is this bad?
+ /// It's more complex and much less readable. Making it part of the pattern can improve
+ /// exhaustiveness checking as well.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// match x {
+ /// Some(x) if matches!(x, Some(1)) => ..,
+ /// Some(x) if x == Some(2) => ..,
+ /// _ => todo!(),
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust,ignore
+ /// match x {
+ /// Some(Some(1)) => ..,
+ /// Some(Some(2)) => ..,
+ /// _ => todo!(),
+ /// }
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub REDUNDANT_GUARDS,
+ complexity,
+ "checks for unnecessary guards in match expressions"
+}
+
#[derive(Default)]
pub struct Matches {
msrv: Msrv,
@@ -978,6 +1009,7 @@ impl_lint_pass!(Matches => [
TRY_ERR,
MANUAL_MAP,
MANUAL_FILTER,
+ REDUNDANT_GUARDS,
]);
impl<'tcx> LateLintPass<'tcx> for Matches {
@@ -1006,7 +1038,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
wild_in_or_pats::check(cx, arms);
}
- if source == MatchSource::TryDesugar {
+ if let MatchSource::TryDesugar(_) = source {
try_err::check(cx, expr, ex);
}
@@ -1025,6 +1057,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
needless_match::check_match(cx, ex, arms, expr);
match_on_vec_items::check(cx, ex);
match_str_case_mismatch::check(cx, ex, arms);
+ redundant_guards::check(cx, arms);
if !in_constant(cx, expr.hir_id) {
manual_unwrap_or::check(cx, expr, ex, arms);
@@ -1125,8 +1158,8 @@ fn contains_cfg_arm(cx: &LateContext<'_>, e: &Expr<'_>, scrutinee: &Expr<'_>, ar
//|^
let found = arm_spans.try_fold(start, |start, range| {
let Some((end, next_start)) = range else {
- // Shouldn't happen as macros can't expand to match arms, but treat this as though a `cfg` attribute were
- // found.
+ // Shouldn't happen as macros can't expand to match arms, but treat this as though a `cfg` attribute
+ // were found.
return Err(());
};
let span = SpanData {
diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs
new file mode 100644
index 000000000..29af48123
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs
@@ -0,0 +1,196 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::path_to_local;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::visitors::{for_each_expr, is_local_used};
+use rustc_ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, Guard, MatchSource, Node, Pat, PatKind};
+use rustc_lint::LateContext;
+use rustc_span::Span;
+use std::ops::ControlFlow;
+
+use super::REDUNDANT_GUARDS;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
+ for outer_arm in arms {
+ let Some(guard) = outer_arm.guard else {
+ continue;
+ };
+
+ // `Some(x) if matches!(x, y)`
+ if let Guard::If(if_expr) = guard
+ && let ExprKind::Match(
+ scrutinee,
+ [
+ arm,
+ Arm {
+ pat: Pat {
+ kind: PatKind::Wild,
+ ..
+ },
+ ..
+ },
+ ],
+ MatchSource::Normal,
+ ) = if_expr.kind
+ {
+ emit_redundant_guards(
+ cx,
+ outer_arm,
+ if_expr.span,
+ scrutinee,
+ arm.pat.span,
+ arm.guard,
+ );
+ }
+ // `Some(x) if let Some(2) = x`
+ else if let Guard::IfLet(let_expr) = guard {
+ emit_redundant_guards(
+ cx,
+ outer_arm,
+ let_expr.span,
+ let_expr.init,
+ let_expr.pat.span,
+ None,
+ );
+ }
+ // `Some(x) if x == Some(2)`
+ else if let Guard::If(if_expr) = guard
+ && let ExprKind::Binary(bin_op, local, pat) = if_expr.kind
+ && matches!(bin_op.node, BinOpKind::Eq)
+ && expr_can_be_pat(cx, pat)
+ // Ensure they have the same type. If they don't, we'd need deref coercion which isn't
+ // possible (currently) in a pattern. In some cases, you can use something like
+ // `as_deref` or similar but in general, we shouldn't lint this as it'd create an
+ // extraordinary amount of FPs.
+ //
+ // This isn't necessary in the other two checks, as they must be a pattern already.
+ && cx.typeck_results().expr_ty(local) == cx.typeck_results().expr_ty(pat)
+ {
+ emit_redundant_guards(
+ cx,
+ outer_arm,
+ if_expr.span,
+ local,
+ pat.span,
+ None,
+ );
+ }
+ }
+}
+
+fn get_pat_binding<'tcx>(cx: &LateContext<'tcx>, guard_expr: &Expr<'_>, outer_arm: &Arm<'tcx>) -> Option<(Span, bool)> {
+ if let Some(local) = path_to_local(guard_expr) && !is_local_used(cx, outer_arm.body, local) {
+ let mut span = None;
+ let mut multiple_bindings = false;
+ // `each_binding` gives the `HirId` of the `Pat` itself, not the binding
+ outer_arm.pat.walk(|pat| {
+ if let PatKind::Binding(_, hir_id, _, _) = pat.kind
+ && hir_id == local
+ && span.replace(pat.span).is_some()
+ {
+ multiple_bindings = true;
+ return false;
+ }
+
+ true
+ });
+
+ // Ignore bindings from or patterns, like `First(x) | Second(x, _) | Third(x, _, _)`
+ if !multiple_bindings {
+ return span.map(|span| {
+ (
+ span,
+ !matches!(cx.tcx.hir().get_parent(local), Node::PatField(_)),
+ )
+ });
+ }
+ }
+
+ None
+}
+
+fn emit_redundant_guards<'tcx>(
+ cx: &LateContext<'tcx>,
+ outer_arm: &Arm<'tcx>,
+ guard_span: Span,
+ local: &Expr<'_>,
+ pat_span: Span,
+ inner_guard: Option<Guard<'_>>,
+) {
+ let mut app = Applicability::MaybeIncorrect;
+ let Some((pat_binding, can_use_shorthand)) = get_pat_binding(cx, local, outer_arm) else {
+ return;
+ };
+
+ span_lint_and_then(
+ cx,
+ REDUNDANT_GUARDS,
+ guard_span.source_callsite(),
+ "redundant guard",
+ |diag| {
+ let binding_replacement = snippet_with_applicability(cx, pat_span, "<binding_repl>", &mut app);
+ diag.multipart_suggestion_verbose(
+ "try",
+ vec![
+ if can_use_shorthand {
+ (pat_binding, binding_replacement.into_owned())
+ } else {
+ (pat_binding.shrink_to_hi(), format!(": {binding_replacement}"))
+ },
+ (
+ guard_span.source_callsite().with_lo(outer_arm.pat.span.hi()),
+ inner_guard.map_or_else(String::new, |guard| {
+ let (prefix, span) = match guard {
+ Guard::If(e) => ("if", e.span),
+ Guard::IfLet(l) => ("if let", l.span),
+ };
+
+ format!(
+ " {prefix} {}",
+ snippet_with_applicability(cx, span, "<guard>", &mut app),
+ )
+ }),
+ ),
+ ],
+ app,
+ );
+ },
+ );
+}
+
+/// Checks if the given `Expr` can also be represented as a `Pat`.
+///
+/// All literals generally also work as patterns, however float literals are special.
+/// They are currently (as of 2023/08/08) still allowed in patterns, but that will become
+/// an error in the future, and rustc already actively warns against this (see rust#41620),
+/// so we don't consider those as usable within patterns for linting purposes.
+fn expr_can_be_pat(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ for_each_expr(expr, |expr| {
+ if match expr.kind {
+ ExprKind::ConstBlock(..) => cx.tcx.features().inline_const_pat,
+ ExprKind::Call(c, ..) if let ExprKind::Path(qpath) = c.kind => {
+ // Allow ctors
+ matches!(cx.qpath_res(&qpath, c.hir_id), Res::Def(DefKind::Ctor(..), ..))
+ },
+ ExprKind::Path(qpath) => {
+ matches!(
+ cx.qpath_res(&qpath, expr.hir_id),
+ Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Ctor(..), ..),
+ )
+ },
+ ExprKind::AddrOf(..)
+ | ExprKind::Array(..)
+ | ExprKind::Tup(..)
+ | ExprKind::Struct(..) => true,
+ ExprKind::Lit(lit) if !matches!(lit.node, LitKind::Float(..)) => true,
+ _ => false,
+ } {
+ return ControlFlow::Continue(());
+ }
+
+ ControlFlow::Break(())
+ })
+ .is_none()
+}
diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
index 479cfd835..9a7c00823 100644
--- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
@@ -3,17 +3,19 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet, walk_span_to_context};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop};
-use clippy_utils::visitors::any_temporaries_need_ordered_drop;
+use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr};
use clippy_utils::{higher, is_expn_of, is_trait_method};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
-use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp};
+use rustc_hir::{Arm, Expr, ExprKind, Guard, Node, Pat, PatKind, QPath, UnOp};
use rustc_lint::LateContext;
-use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
+use rustc_middle::ty::{self, GenericArgKind, Ty};
use rustc_span::{sym, Symbol};
+use std::fmt::Write;
+use std::ops::ControlFlow;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
@@ -45,49 +47,39 @@ fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option<Ty<'_>> {
}
}
-fn find_sugg_for_if_let<'tcx>(
+fn find_method_and_type<'tcx>(
cx: &LateContext<'tcx>,
- expr: &'tcx Expr<'_>,
- let_pat: &Pat<'_>,
- let_expr: &'tcx Expr<'_>,
- keyword: &'static str,
- has_else: bool,
-) {
- // also look inside refs
- // if we have &None for example, peel it so we can detect "if let None = x"
- let check_pat = match let_pat.kind {
- PatKind::Ref(inner, _mutability) => inner,
- _ => let_pat,
- };
- let op_ty = cx.typeck_results().expr_ty(let_expr);
- // Determine which function should be used, and the type contained by the corresponding
- // variant.
- let (good_method, inner_ty) = match check_pat.kind {
+ check_pat: &Pat<'_>,
+ op_ty: Ty<'tcx>,
+) -> Option<(&'static str, Ty<'tcx>)> {
+ match check_pat.kind {
PatKind::TupleStruct(ref qpath, args, rest) => {
let is_wildcard = matches!(args.first().map(|p| &p.kind), Some(PatKind::Wild));
let is_rest = matches!((args, rest.as_opt_usize()), ([], Some(_)));
if is_wildcard || is_rest {
let res = cx.typeck_results().qpath_res(qpath, check_pat.hir_id);
- let Some(id) = res.opt_def_id().map(|ctor_id| cx.tcx.parent(ctor_id)) else { return };
+ let Some(id) = res.opt_def_id().map(|ctor_id| cx.tcx.parent(ctor_id)) else {
+ return None;
+ };
let lang_items = cx.tcx.lang_items();
if Some(id) == lang_items.result_ok_variant() {
- ("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty))
+ Some(("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty)))
} else if Some(id) == lang_items.result_err_variant() {
- ("is_err()", try_get_generic_ty(op_ty, 1).unwrap_or(op_ty))
+ Some(("is_err()", try_get_generic_ty(op_ty, 1).unwrap_or(op_ty)))
} else if Some(id) == lang_items.option_some_variant() {
- ("is_some()", op_ty)
+ Some(("is_some()", op_ty))
} else if Some(id) == lang_items.poll_ready_variant() {
- ("is_ready()", op_ty)
+ Some(("is_ready()", op_ty))
} else if is_pat_variant(cx, check_pat, qpath, Item::Diag(sym::IpAddr, sym!(V4))) {
- ("is_ipv4()", op_ty)
+ Some(("is_ipv4()", op_ty))
} else if is_pat_variant(cx, check_pat, qpath, Item::Diag(sym::IpAddr, sym!(V6))) {
- ("is_ipv6()", op_ty)
+ Some(("is_ipv6()", op_ty))
} else {
- return;
+ None
}
} else {
- return;
+ None
}
},
PatKind::Path(ref path) => {
@@ -99,15 +91,37 @@ fn find_sugg_for_if_let<'tcx>(
} else if cx.tcx.lang_items().poll_pending_variant() == Some(variant_id) {
"is_pending()"
} else {
- return;
+ return None;
};
// `None` and `Pending` don't have an inner type.
- (method, cx.tcx.types.unit)
+ Some((method, cx.tcx.types.unit))
} else {
- return;
+ None
}
},
- _ => return,
+ _ => None,
+ }
+}
+
+fn find_sugg_for_if_let<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'_>,
+ let_pat: &Pat<'_>,
+ let_expr: &'tcx Expr<'_>,
+ keyword: &'static str,
+ has_else: bool,
+) {
+ // also look inside refs
+ // if we have &None for example, peel it so we can detect "if let None = x"
+ let check_pat = match let_pat.kind {
+ PatKind::Ref(inner, _mutability) => inner,
+ _ => let_pat,
+ };
+ let op_ty = cx.typeck_results().expr_ty(let_expr);
+ // Determine which function should be used, and the type contained by the corresponding
+ // variant.
+ let Some((good_method, inner_ty)) = find_method_and_type(cx, check_pat, op_ty) else {
+ return;
};
// If this is the last expression in a block or there is an else clause then the whole
@@ -175,7 +189,7 @@ fn find_sugg_for_if_let<'tcx>(
.maybe_par()
.to_string();
- diag.span_suggestion(span, "try this", format!("{keyword} {sugg}.{good_method}"), app);
+ diag.span_suggestion(span, "try", format!("{keyword} {sugg}.{good_method}"), app);
if needs_drop {
diag.note("this will change drop order of the result, as well as all temporaries");
@@ -189,30 +203,58 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op
if arms.len() == 2 {
let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind);
- if let Some(good_method) = found_good_method(cx, arms, node_pair) {
+ if let Some((good_method, maybe_guard)) = found_good_method(cx, arms, node_pair) {
let span = is_expn_of(expr.span, "matches").unwrap_or(expr.span.to(op.span));
let result_expr = match &op.kind {
ExprKind::AddrOf(_, _, borrowed) => borrowed,
_ => op,
};
+ let mut sugg = format!("{}.{good_method}", snippet(cx, result_expr.span, "_"));
+
+ if let Some(guard) = maybe_guard {
+ let Guard::If(guard) = *guard else { return }; // `...is_none() && let ...` is a syntax error
+
+ // wow, the HIR for match guards in `PAT if let PAT = expr && expr => ...` is annoying!
+ // `guard` here is `Guard::If` with the let expression somewhere deep in the tree of exprs,
+ // counter to the intuition that it should be `Guard::IfLet`, so we need another check
+ // to see that there aren't any let chains anywhere in the guard, as that would break
+ // if we suggest `t.is_none() && (let X = y && z)` for:
+ // `match t { None if let X = y && z => true, _ => false }`
+ let has_nested_let_chain = for_each_expr(guard, |expr| {
+ if matches!(expr.kind, ExprKind::Let(..)) {
+ ControlFlow::Break(())
+ } else {
+ ControlFlow::Continue(())
+ }
+ })
+ .is_some();
+
+ if has_nested_let_chain {
+ return;
+ }
+
+ let guard = Sugg::hir(cx, guard, "..");
+ let _ = write!(sugg, " && {}", guard.maybe_par());
+ }
+
span_lint_and_sugg(
cx,
REDUNDANT_PATTERN_MATCHING,
span,
&format!("redundant pattern matching, consider using `{good_method}`"),
- "try this",
- format!("{}.{good_method}", snippet(cx, result_expr.span, "_")),
+ "try",
+ sugg,
Applicability::MachineApplicable,
);
}
}
}
-fn found_good_method<'a>(
+fn found_good_method<'tcx>(
cx: &LateContext<'_>,
- arms: &[Arm<'_>],
+ arms: &'tcx [Arm<'tcx>],
node: (&PatKind<'_>, &PatKind<'_>),
-) -> Option<&'a str> {
+) -> Option<(&'static str, Option<&'tcx Guard<'tcx>>)> {
match node {
(
PatKind::TupleStruct(ref path_left, patterns_left, _),
@@ -298,7 +340,11 @@ fn get_ident(path: &QPath<'_>) -> Option<rustc_span::symbol::Ident> {
}
}
-fn get_good_method<'a>(cx: &LateContext<'_>, arms: &[Arm<'_>], path_left: &QPath<'_>) -> Option<&'a str> {
+fn get_good_method<'tcx>(
+ cx: &LateContext<'_>,
+ arms: &'tcx [Arm<'tcx>],
+ path_left: &QPath<'_>,
+) -> Option<(&'static str, Option<&'tcx Guard<'tcx>>)> {
if let Some(name) = get_ident(path_left) {
return match name.as_str() {
"Ok" => {
@@ -336,7 +382,9 @@ enum Item {
}
fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expected_item: Item) -> bool {
- let Some(id) = cx.typeck_results().qpath_res(path, pat.hir_id).opt_def_id() else { return false };
+ let Some(id) = cx.typeck_results().qpath_res(path, pat.hir_id).opt_def_id() else {
+ return false;
+ };
match expected_item {
Item::Lang(expected_lang_item) => cx
@@ -362,16 +410,16 @@ fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expecte
}
#[expect(clippy::too_many_arguments)]
-fn find_good_method_for_match<'a>(
+fn find_good_method_for_match<'a, 'tcx>(
cx: &LateContext<'_>,
- arms: &[Arm<'_>],
+ arms: &'tcx [Arm<'tcx>],
path_left: &QPath<'_>,
path_right: &QPath<'_>,
expected_item_left: Item,
expected_item_right: Item,
should_be_left: &'a str,
should_be_right: &'a str,
-) -> Option<&'a str> {
+) -> Option<(&'a str, Option<&'tcx Guard<'tcx>>)> {
let first_pat = arms[0].pat;
let second_pat = arms[1].pat;
@@ -389,22 +437,22 @@ fn find_good_method_for_match<'a>(
match body_node_pair {
(ExprKind::Lit(lit_left), ExprKind::Lit(lit_right)) => match (&lit_left.node, &lit_right.node) {
- (LitKind::Bool(true), LitKind::Bool(false)) => Some(should_be_left),
- (LitKind::Bool(false), LitKind::Bool(true)) => Some(should_be_right),
+ (LitKind::Bool(true), LitKind::Bool(false)) => Some((should_be_left, arms[0].guard.as_ref())),
+ (LitKind::Bool(false), LitKind::Bool(true)) => Some((should_be_right, arms[1].guard.as_ref())),
_ => None,
},
_ => None,
}
}
-fn find_good_method_for_matches_macro<'a>(
+fn find_good_method_for_matches_macro<'a, 'tcx>(
cx: &LateContext<'_>,
- arms: &[Arm<'_>],
+ arms: &'tcx [Arm<'tcx>],
path_left: &QPath<'_>,
expected_item_left: Item,
should_be_left: &'a str,
should_be_right: &'a str,
-) -> Option<&'a str> {
+) -> Option<(&'a str, Option<&'tcx Guard<'tcx>>)> {
let first_pat = arms[0].pat;
let body_node_pair = if is_pat_variant(cx, first_pat, path_left, expected_item_left) {
@@ -415,8 +463,8 @@ fn find_good_method_for_matches_macro<'a>(
match body_node_pair {
(ExprKind::Lit(lit_left), ExprKind::Lit(lit_right)) => match (&lit_left.node, &lit_right.node) {
- (LitKind::Bool(true), LitKind::Bool(false)) => Some(should_be_left),
- (LitKind::Bool(false), LitKind::Bool(true)) => Some(should_be_right),
+ (LitKind::Bool(true), LitKind::Bool(false)) => Some((should_be_left, arms[0].guard.as_ref())),
+ (LitKind::Bool(false), LitKind::Bool(true)) => Some((should_be_right, arms[1].guard.as_ref())),
_ => None,
},
_ => None,
diff --git a/src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs b/src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs
index d06bcdaa2..4efe93d4b 100644
--- a/src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs
@@ -10,7 +10,7 @@ pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) {
if !pat.span.from_expansion();
if let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind;
if let Some(def_id) = path.res.opt_def_id();
- let ty = cx.tcx.type_of(def_id).subst_identity();
+ let ty = cx.tcx.type_of(def_id).instantiate_identity();
if let ty::Adt(def, _) = ty.kind();
if def.is_struct() || def.is_union();
if fields.len() == def.non_enum_variant().fields.len();
diff --git a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
index 37528d9f7..ee0fdb353 100644
--- a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
@@ -6,8 +6,7 @@ use rustc_errors::{Applicability, Diagnostic};
use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{Arm, Expr, ExprKind, MatchSource};
use rustc_lint::{LateContext, LintContext};
-use rustc_middle::ty::subst::GenericArgKind;
-use rustc_middle::ty::{Ty, TypeAndMut};
+use rustc_middle::ty::{GenericArgKind, Ty, TypeAndMut};
use rustc_span::Span;
use super::SIGNIFICANT_DROP_IN_SCRUTINEE;
diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs
index 35627d6c6..6b05c6bff 100644
--- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs
@@ -136,7 +136,7 @@ fn report_single_pattern(
}
};
- span_lint_and_sugg(cx, lint, expr.span, msg, "try this", sugg, app);
+ span_lint_and_sugg(cx, lint, expr.span, msg, "try", sugg, app);
}
fn check_opt_like<'a>(
diff --git a/src/tools/clippy/clippy_lints/src/matches/try_err.rs b/src/tools/clippy/clippy_lints/src/matches/try_err.rs
index 3a7f1e034..0fd6f533d 100644
--- a/src/tools/clippy/clippy_lints/src/matches/try_err.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/try_err.rs
@@ -70,7 +70,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine
TRY_ERR,
expr.span,
"returning an `Err(_)` with the `?` operator",
- "try this",
+ "try",
suggestion,
applicability,
);
@@ -80,7 +80,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine
/// Finds function return type by examining return expressions in match arms.
fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option<Ty<'tcx>> {
- if let ExprKind::Match(_, arms, MatchSource::TryDesugar) = expr {
+ if let ExprKind::Match(_, arms, MatchSource::TryDesugar(_)) = expr {
for arm in *arms {
if let ExprKind::Ret(Some(ret)) = arm.body.kind {
return Some(cx.typeck_results().expr_ty(ret));
diff --git a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs
index 008533488..3a8cc4174 100644
--- a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs
@@ -1,7 +1,8 @@
use super::{contains_return, BIND_INSTEAD_OF_MAP};
use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::peel_blocks;
use clippy_utils::source::{snippet, snippet_with_context};
-use clippy_utils::{peel_blocks, visitors::find_all_ret_expressions};
+use clippy_utils::visitors::find_all_ret_expressions;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
@@ -87,7 +88,7 @@ pub(crate) trait BindInsteadOfMap {
BIND_INSTEAD_OF_MAP,
expr.span,
&msg,
- "try this",
+ "try",
note,
app,
);
@@ -124,7 +125,7 @@ pub(crate) trait BindInsteadOfMap {
span_lint_and_then(cx, BIND_INSTEAD_OF_MAP, expr.span, &msg, |diag| {
multispan_sugg_with_applicability(
diag,
- "try this",
+ "try",
Applicability::MachineApplicable,
std::iter::once((span, Self::GOOD_METHOD_NAME.into())).chain(
suggs
diff --git a/src/tools/clippy/clippy_lints/src/methods/bytecount.rs b/src/tools/clippy/clippy_lints/src/methods/bytecount.rs
index fef90f6eb..f490a7175 100644
--- a/src/tools/clippy/clippy_lints/src/methods/bytecount.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/bytecount.rs
@@ -45,7 +45,7 @@ pub(super) fn check<'tcx>(
let haystack = if let ExprKind::MethodCall(path, receiver, [], _) =
filter_recv.kind {
let p = path.ident.name;
- if p == sym::iter || p == sym!(iter_mut) {
+ if p == sym::iter || p == sym::iter_mut {
receiver
} else {
filter_recv
diff --git a/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs b/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs
index 46a20ad41..649fc46e4 100644
--- a/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs
@@ -17,7 +17,7 @@ pub(super) fn check<'tcx>(
if_chain! {
if let Some(bytes_id) = cx.typeck_results().type_dependent_def_id(count_recv.hir_id);
if let Some(impl_id) = cx.tcx.impl_of_method(bytes_id);
- if cx.tcx.type_of(impl_id).subst_identity().is_str();
+ if cx.tcx.type_of(impl_id).instantiate_identity().is_str();
let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs();
if ty.is_str() || is_type_lang_item(cx, ty, hir::LangItem::String);
then {
diff --git a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs
index 7711aa78b..d5897822e 100644
--- a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs
@@ -1,13 +1,13 @@
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_opt;
-use clippy_utils::source::{indent_of, reindent_multiline};
+use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
use clippy_utils::ty::is_type_lang_item;
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem};
use rustc_lint::LateContext;
-use rustc_span::{source_map::Spanned, Span};
+use rustc_span::source_map::Spanned;
+use rustc_span::Span;
use super::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS;
@@ -30,7 +30,7 @@ pub(super) fn check<'tcx>(
if_chain! {
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
- if cx.tcx.type_of(impl_id).subst_identity().is_str();
+ if cx.tcx.type_of(impl_id).instantiate_identity().is_str();
if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = arg.kind;
if (2..=6).contains(&ext_literal.as_str().len());
let ext_str = ext_literal.as_str();
diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs b/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs
index 079df2226..0e41f3c21 100644
--- a/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs
@@ -4,8 +4,7 @@ use clippy_utils::{method_chain_args, path_def_id};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
-use rustc_lint::LateContext;
-use rustc_lint::Lint;
+use rustc_lint::{LateContext, Lint};
use rustc_middle::ty;
/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs
index 8984b2cf8..c9d50a5b0 100644
--- a/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs
@@ -5,8 +5,7 @@ use if_chain::if_chain;
use rustc_ast::ast;
use rustc_errors::Applicability;
use rustc_hir as hir;
-use rustc_lint::LateContext;
-use rustc_lint::Lint;
+use rustc_lint::{LateContext, Lint};
/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`.
pub(super) fn check(
diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs
index 65fd50dff..eb4f003d3 100644
--- a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs
@@ -5,7 +5,9 @@ use clippy_utils::ty::is_copy;
use rustc_errors::Applicability;
use rustc_hir::{BindingAnnotation, ByRef, Expr, ExprKind, MatchSource, Node, PatKind, QPath};
use rustc_lint::LateContext;
-use rustc_middle::ty::{self, adjustment::Adjust, print::with_forced_trimmed_paths};
+use rustc_middle::ty::adjustment::Adjust;
+use rustc_middle::ty::print::with_forced_trimmed_paths;
+use rustc_middle::ty::{self};
use rustc_span::symbol::{sym, Symbol};
use super::CLONE_ON_COPY;
@@ -62,7 +64,7 @@ pub(super) fn check(
ExprKind::Path(QPath::LangItem(rustc_hir::LangItem::TryTraitBranch, _, _))
),
ExprKind::MethodCall(_, self_arg, ..) if expr.hir_id == self_arg.hir_id => true,
- ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
+ ExprKind::Match(_, _, MatchSource::TryDesugar(_) | MatchSource::AwaitDesugar)
| ExprKind::Field(..)
| ExprKind::Index(..) => true,
_ => false,
diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs
index 5e8ad0861..ddf3c9f27 100644
--- a/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs
@@ -42,7 +42,7 @@ pub(super) fn check(
CLONE_ON_REF_PTR,
expr.span,
"using `.clone()` on a ref-counted pointer",
- "try this",
+ "try",
format!("{caller_type}::<{}>::clone(&{snippet})", subst.type_at(0)),
app,
);
diff --git a/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs b/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs
index 5e01ed90f..5409ede60 100644
--- a/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs
@@ -8,8 +8,7 @@ use rustc_hir as hir;
use rustc_lint::LateContext;
use std::collections::VecDeque;
-use super::method_call;
-use super::COLLAPSIBLE_STR_REPLACE;
+use super::{method_call, COLLAPSIBLE_STR_REPLACE};
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
diff --git a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs
index d0c79dc11..6a82d8f75 100644
--- a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs
@@ -4,17 +4,12 @@ use clippy_utils::is_range_full;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_lang_item;
use rustc_errors::Applicability;
-use rustc_hir::Expr;
-use rustc_hir::ExprKind;
-use rustc_hir::LangItem;
-use rustc_hir::Path;
-use rustc_hir::QPath;
+use rustc_hir::{Expr, ExprKind, LangItem, Path, QPath};
use rustc_lint::LateContext;
use rustc_middle::query::Key;
use rustc_middle::ty;
use rustc_middle::ty::Ty;
-use rustc_span::sym;
-use rustc_span::Symbol;
+use rustc_span::{sym, Symbol};
/// Checks if both types match the given diagnostic item, e.g.:
///
diff --git a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs
index ae03da0d3..3d82441c0 100644
--- a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs
@@ -1,8 +1,7 @@
use super::ERR_EXPECT;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::ty::has_debug_impl;
-use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::ty::{has_debug_impl, is_type_diagnostic_item};
use rustc_errors::Applicability;
use rustc_lint::LateContext;
use rustc_middle::ty;
@@ -47,7 +46,7 @@ pub(super) fn check(
/// Given a `Result<T, E>` type, return its data (`T`).
fn get_data_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option<Ty<'a>> {
match ty.kind() {
- ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym::Result) => substs.types().next(),
+ ty::Adt(_, args) if is_type_diagnostic_item(cx, ty, sym::Result) => args.types().next(),
_ => None,
}
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
index 92d21bb89..d3e90e4bb 100644
--- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
@@ -70,7 +70,7 @@ pub(super) fn check<'tcx>(
if let hir::ExprKind::Path(ref p) = fun.kind {
match cx.qpath_res(p, fun.hir_id) {
hir::def::Res::Def(hir::def::DefKind::Fn | hir::def::DefKind::AssocFn, def_id) => matches!(
- cx.tcx.fn_sig(def_id).subst_identity().output().skip_binder().kind(),
+ cx.tcx.fn_sig(def_id).instantiate_identity().output().skip_binder().kind(),
ty::Ref(re, ..) if re.is_static(),
),
_ => false,
@@ -84,7 +84,7 @@ pub(super) fn check<'tcx>(
.type_dependent_def_id(arg.hir_id)
.map_or(false, |method_id| {
matches!(
- cx.tcx.fn_sig(method_id).subst_identity().output().skip_binder().kind(),
+ cx.tcx.fn_sig(method_id).instantiate_identity().output().skip_binder().kind(),
ty::Ref(re, ..) if re.is_static()
)
})
@@ -144,7 +144,7 @@ pub(super) fn check<'tcx>(
EXPECT_FUN_CALL,
span_replace_word,
&format!("use of `{name}` followed by a function call"),
- "try this",
+ "try",
format!("unwrap_or_else({closure_args} panic!({sugg}))"),
applicability,
);
@@ -162,7 +162,7 @@ pub(super) fn check<'tcx>(
EXPECT_FUN_CALL,
span_replace_word,
&format!("use of `{name}` followed by a function call"),
- "try this",
+ "try",
format!("unwrap_or_else({closure_args} {{ panic!(\"{{}}\", {arg_root_snippet}) }})"),
applicability,
);
diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_used.rs b/src/tools/clippy/clippy_lints/src/methods/expect_used.rs
deleted file mode 100644
index 614610335..000000000
--- a/src/tools/clippy/clippy_lints/src/methods/expect_used.rs
+++ /dev/null
@@ -1,44 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_in_cfg_test, is_in_test_function};
-use rustc_hir as hir;
-use rustc_lint::LateContext;
-use rustc_span::sym;
-
-use super::EXPECT_USED;
-
-/// lint use of `expect()` or `expect_err` for `Result` and `expect()` for `Option`.
-pub(super) fn check(
- cx: &LateContext<'_>,
- expr: &hir::Expr<'_>,
- recv: &hir::Expr<'_>,
- is_err: bool,
- 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) && !is_err {
- Some((EXPECT_USED, "an `Option`", "None", ""))
- } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) {
- Some((EXPECT_USED, "a `Result`", if is_err { "Ok" } else { "Err" }, "an "))
- } else {
- None
- };
-
- let method = if is_err { "expect_err" } else { "expect" };
-
- if allow_expect_in_tests && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) {
- return;
- }
-
- if let Some((lint, kind, none_value, none_prefix)) = mess {
- span_lint_and_help(
- cx,
- lint,
- expr.span,
- &format!("used `{method}()` on {kind} value"),
- None,
- &format!("if this value is {none_prefix}`{none_value}`, it will panic"),
- );
- }
-}
diff --git a/src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs
index 37b284635..495b26652 100644
--- a/src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/extend_with_drain.rs
@@ -31,7 +31,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg:
EXTEND_WITH_DRAIN,
expr.span,
"use of `extend` instead of `append` for adding the full range of a second vector",
- "try this",
+ "try",
format!(
"{}.append({}{})",
snippet_with_applicability(cx, recv.span, "..", &mut applicability),
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs
index fc80f2eea..c9eaa185a 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs
@@ -1,7 +1,9 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::macros::{is_panic, root_macro_call};
use clippy_utils::source::{indent_of, reindent_multiline, snippet};
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_trait_method, path_to_local_id, peel_blocks, SpanlessEq};
+use clippy_utils::{higher, is_trait_method, path_to_local_id, peel_blocks, SpanlessEq};
+use hir::{Body, HirId, MatchSource, Pat};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
@@ -10,12 +12,10 @@ use rustc_hir::{Closure, Expr, ExprKind, PatKind, PathSegment, QPath, UnOp};
use rustc_lint::LateContext;
use rustc_middle::ty::adjustment::Adjust;
use rustc_span::source_map::Span;
-use rustc_span::symbol::{sym, Symbol};
+use rustc_span::symbol::{sym, Ident, Symbol};
use std::borrow::Cow;
-use super::MANUAL_FILTER_MAP;
-use super::MANUAL_FIND_MAP;
-use super::OPTION_FILTER_MAP;
+use super::{MANUAL_FILTER_MAP, MANUAL_FIND_MAP, OPTION_FILTER_MAP};
fn is_method(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol) -> bool {
match &expr.kind {
@@ -50,6 +50,214 @@ fn is_option_filter_map(cx: &LateContext<'_>, filter_arg: &hir::Expr<'_>, map_ar
is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_some))
}
+#[derive(Debug, Copy, Clone)]
+enum OffendingFilterExpr<'tcx> {
+ /// `.filter(|opt| opt.is_some())`
+ IsSome {
+ /// The receiver expression
+ receiver: &'tcx Expr<'tcx>,
+ /// If `Some`, then this contains the span of an expression that possibly contains side
+ /// effects: `.filter(|opt| side_effect(opt).is_some())`
+ /// ^^^^^^^^^^^^^^^^
+ ///
+ /// We will use this later for warning the user that the suggested fix may change
+ /// the behavior.
+ side_effect_expr_span: Option<Span>,
+ },
+ /// `.filter(|res| res.is_ok())`
+ IsOk {
+ /// The receiver expression
+ receiver: &'tcx Expr<'tcx>,
+ /// See `IsSome`
+ side_effect_expr_span: Option<Span>,
+ },
+ /// `.filter(|enum| matches!(enum, Enum::A(_)))`
+ Matches {
+ /// The DefId of the variant being matched
+ variant_def_id: hir::def_id::DefId,
+ },
+}
+
+#[derive(Debug)]
+enum CalledMethod {
+ OptionIsSome,
+ ResultIsOk,
+}
+
+/// The result of checking a `map` call, returned by `OffendingFilterExpr::check_map_call`
+#[derive(Debug)]
+enum CheckResult<'tcx> {
+ Method {
+ map_arg: &'tcx Expr<'tcx>,
+ /// The method that was called inside of `filter`
+ method: CalledMethod,
+ /// See `OffendingFilterExpr::IsSome`
+ side_effect_expr_span: Option<Span>,
+ },
+ PatternMatching {
+ /// The span of the variant being matched
+ /// if let Some(s) = enum
+ /// ^^^^^^^
+ variant_span: Span,
+ /// if let Some(s) = enum
+ /// ^
+ variant_ident: Ident,
+ },
+}
+
+impl<'tcx> OffendingFilterExpr<'tcx> {
+ pub fn check_map_call(
+ &mut self,
+ cx: &LateContext<'tcx>,
+ map_body: &'tcx Body<'tcx>,
+ map_param_id: HirId,
+ filter_param_id: HirId,
+ is_filter_param_ref: bool,
+ ) -> Option<CheckResult<'tcx>> {
+ match *self {
+ OffendingFilterExpr::IsSome {
+ receiver,
+ side_effect_expr_span,
+ }
+ | OffendingFilterExpr::IsOk {
+ receiver,
+ side_effect_expr_span,
+ } => {
+ // check if closure ends with expect() or unwrap()
+ if let ExprKind::MethodCall(seg, map_arg, ..) = map_body.value.kind
+ && matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or)
+ // .map(|y| f(y).copied().unwrap())
+ // ~~~~
+ && let map_arg_peeled = match map_arg.kind {
+ ExprKind::MethodCall(method, original_arg, [], _) if acceptable_methods(method) => {
+ original_arg
+ },
+ _ => map_arg,
+ }
+ // .map(|y| y[.acceptable_method()].unwrap())
+ && let simple_equal = (path_to_local_id(receiver, filter_param_id)
+ && path_to_local_id(map_arg_peeled, map_param_id))
+ && let eq_fallback = (|a: &Expr<'_>, b: &Expr<'_>| {
+ // in `filter(|x| ..)`, replace `*x` with `x`
+ let a_path = if_chain! {
+ if !is_filter_param_ref;
+ if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind;
+ then { expr_path } else { a }
+ };
+ // let the filter closure arg and the map closure arg be equal
+ path_to_local_id(a_path, filter_param_id)
+ && path_to_local_id(b, map_param_id)
+ && cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b)
+ })
+ && (simple_equal
+ || SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(receiver, map_arg_peeled))
+ {
+ Some(CheckResult::Method {
+ map_arg,
+ side_effect_expr_span,
+ method: match self {
+ OffendingFilterExpr::IsSome { .. } => CalledMethod::OptionIsSome,
+ OffendingFilterExpr::IsOk { .. } => CalledMethod::ResultIsOk,
+ OffendingFilterExpr::Matches { .. } => unreachable!("only IsSome and IsOk can get here"),
+ }
+ })
+ } else {
+ None
+ }
+ },
+ OffendingFilterExpr::Matches { variant_def_id } => {
+ let expr_uses_local = |pat: &Pat<'_>, expr: &Expr<'_>| {
+ if let PatKind::TupleStruct(QPath::Resolved(_, path), [subpat], _) = pat.kind
+ && let PatKind::Binding(_, local_id, ident, _) = subpat.kind
+ && path_to_local_id(expr.peel_blocks(), local_id)
+ && let Some(local_variant_def_id) = path.res.opt_def_id()
+ && local_variant_def_id == variant_def_id
+ {
+ Some((ident, pat.span))
+ } else {
+ None
+ }
+ };
+
+ // look for:
+ // `if let Variant (v) = enum { v } else { unreachable!() }`
+ // ^^^^^^^ ^ ^^^^ ^^^^^^^^^^^^^^^^^^
+ // variant_span variant_ident scrutinee else_ (blocks peeled later)
+ // OR
+ // `match enum { Variant (v) => v, _ => unreachable!() }`
+ // ^^^^ ^^^^^^^ ^ ^^^^^^^^^^^^^^
+ // scrutinee variant_span variant_ident else_
+ let (scrutinee, else_, variant_ident, variant_span) =
+ match higher::IfLetOrMatch::parse(cx, map_body.value) {
+ // For `if let` we want to check that the variant matching arm references the local created by its pattern
+ Some(higher::IfLetOrMatch::IfLet(sc, pat, then, Some(else_)))
+ if let Some((ident, span)) = expr_uses_local(pat, then) =>
+ {
+ (sc, else_, ident, span)
+ },
+ // For `match` we want to check that the "else" arm is the wildcard (`_`) pattern
+ // and that the variant matching arm references the local created by its pattern
+ Some(higher::IfLetOrMatch::Match(sc, [arm, wild_arm], MatchSource::Normal))
+ if let PatKind::Wild = wild_arm.pat.kind
+ && let Some((ident, span)) = expr_uses_local(arm.pat, arm.body.peel_blocks()) =>
+ {
+ (sc, wild_arm.body, ident, span)
+ },
+ _ => return None,
+ };
+
+ if path_to_local_id(scrutinee, map_param_id)
+ // else branch should be a `panic!` or `unreachable!` macro call
+ && let Some(mac) = root_macro_call(else_.peel_blocks().span)
+ && (is_panic(cx, mac.def_id) || cx.tcx.opt_item_name(mac.def_id) == Some(sym::unreachable))
+ {
+ Some(CheckResult::PatternMatching { variant_span, variant_ident })
+ } else {
+ None
+ }
+ },
+ }
+ }
+
+ fn hir(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, filter_param_id: HirId) -> Option<Self> {
+ if let ExprKind::MethodCall(path, receiver, [], _) = expr.kind
+ && let Some(recv_ty) = cx.typeck_results().expr_ty(receiver).peel_refs().ty_adt_def()
+ {
+ // we still want to lint if the expression possibly contains side effects,
+ // *but* it can't be machine-applicable then, because that can change the behavior of the program:
+ // .filter(|x| effect(x).is_some()).map(|x| effect(x).unwrap())
+ // vs.
+ // .filter_map(|x| effect(x))
+ //
+ // the latter only calls `effect` once
+ let side_effect_expr_span = receiver.can_have_side_effects().then_some(receiver.span);
+
+ if cx.tcx.is_diagnostic_item(sym::Option, recv_ty.did())
+ && path.ident.name == sym!(is_some)
+ {
+ Some(Self::IsSome { receiver, side_effect_expr_span })
+ } else if cx.tcx.is_diagnostic_item(sym::Result, recv_ty.did())
+ && path.ident.name == sym!(is_ok)
+ {
+ Some(Self::IsOk { receiver, side_effect_expr_span })
+ } else {
+ None
+ }
+ } else if let Some(macro_call) = root_macro_call(expr.span)
+ && cx.tcx.get_diagnostic_name(macro_call.def_id) == Some(sym::matches_macro)
+ // we know for a fact that the wildcard pattern is the second arm
+ && let ExprKind::Match(scrutinee, [arm, _], _) = expr.kind
+ && path_to_local_id(scrutinee, filter_param_id)
+ && let PatKind::TupleStruct(QPath::Resolved(_, path), ..) = arm.pat.kind
+ && let Some(variant_def_id) = path.res.opt_def_id()
+ {
+ Some(OffendingFilterExpr::Matches { variant_def_id })
+ } else {
+ None
+ }
+ }
+}
+
/// is `filter(|x| x.is_some()).map(|x| x.unwrap())`
fn is_filter_some_map_unwrap(
cx: &LateContext<'_>,
@@ -104,55 +312,18 @@ pub(super) fn check(
} else {
(filter_param.pat, false)
};
- // closure ends with is_some() or is_ok()
+
if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind;
- if let ExprKind::MethodCall(path, filter_arg, [], _) = filter_body.value.kind;
- if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).peel_refs().ty_adt_def();
- if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::Option, opt_ty.did()) {
- Some(false)
- } else if cx.tcx.is_diagnostic_item(sym::Result, opt_ty.did()) {
- Some(true)
- } else {
- None
- };
- if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" };
+ if let Some(mut offending_expr) = OffendingFilterExpr::hir(cx, filter_body.value, filter_param_id);
- // ...map(|x| ...unwrap())
if let ExprKind::Closure(&Closure { body: map_body_id, .. }) = map_arg.kind;
let map_body = cx.tcx.hir().body(map_body_id);
if let [map_param] = map_body.params;
if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind;
- // closure ends with expect() or unwrap()
- if let ExprKind::MethodCall(seg, map_arg, ..) = map_body.value.kind;
- if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or);
-
- // .filter(..).map(|y| f(y).copied().unwrap())
- // ~~~~
- let map_arg_peeled = match map_arg.kind {
- ExprKind::MethodCall(method, original_arg, [], _) if acceptable_methods(method) => {
- original_arg
- },
- _ => map_arg,
- };
- // .filter(|x| x.is_some()).map(|y| y[.acceptable_method()].unwrap())
- let simple_equal = path_to_local_id(filter_arg, filter_param_id)
- && path_to_local_id(map_arg_peeled, map_param_id);
+ if let Some(check_result) =
+ offending_expr.check_map_call(cx, map_body, map_param_id, filter_param_id, is_filter_param_ref);
- let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
- // in `filter(|x| ..)`, replace `*x` with `x`
- let a_path = if_chain! {
- if !is_filter_param_ref;
- if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind;
- then { expr_path } else { a }
- };
- // let the filter closure arg and the map closure arg be equal
- path_to_local_id(a_path, filter_param_id)
- && path_to_local_id(b, map_param_id)
- && cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b)
- };
-
- if simple_equal || SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg_peeled);
then {
let span = filter_span.with_hi(expr.span.hi());
let (filter_name, lint) = if is_find {
@@ -161,22 +332,53 @@ pub(super) fn check(
("filter", MANUAL_FILTER_MAP)
};
let msg = format!("`{filter_name}(..).map(..)` can be simplified as `{filter_name}_map(..)`");
- let (to_opt, deref) = if is_result {
- (".ok()", String::new())
- } else {
- let derefs = cx.typeck_results()
- .expr_adjustments(map_arg)
- .iter()
- .filter(|adj| matches!(adj.kind, Adjust::Deref(_)))
- .count();
- ("", "*".repeat(derefs))
+ let (sugg, note_and_span, applicability) = match check_result {
+ CheckResult::Method { map_arg, method, side_effect_expr_span } => {
+ let (to_opt, deref) = match method {
+ CalledMethod::ResultIsOk => (".ok()", String::new()),
+ CalledMethod::OptionIsSome => {
+ let derefs = cx.typeck_results()
+ .expr_adjustments(map_arg)
+ .iter()
+ .filter(|adj| matches!(adj.kind, Adjust::Deref(_)))
+ .count();
+
+ ("", "*".repeat(derefs))
+ }
+ };
+
+ let sugg = format!(
+ "{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})",
+ snippet(cx, map_arg.span, ".."),
+ );
+ let (note_and_span, applicability) = if let Some(span) = side_effect_expr_span {
+ let note = "the suggestion might change the behavior of the program when merging `filter` and `map`, \
+ because this expression potentially contains side effects and will only execute once";
+
+ (Some((note, span)), Applicability::MaybeIncorrect)
+ } else {
+ (None, Applicability::MachineApplicable)
+ };
+
+ (sugg, note_and_span, applicability)
+ }
+ CheckResult::PatternMatching { variant_span, variant_ident } => {
+ let pat = snippet(cx, variant_span, "<pattern>");
+
+ (format!("{filter_name}_map(|{map_param_ident}| match {map_param_ident} {{ \
+ {pat} => Some({variant_ident}), \
+ _ => None \
+ }})"), None, Applicability::MachineApplicable)
+ }
};
- let sugg = format!(
- "{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})",
- snippet(cx, map_arg.span, ".."),
- );
- span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable);
+ span_lint_and_then(cx, lint, span, &msg, |diag| {
+ diag.span_suggestion(span, "try", sugg, applicability);
+
+ if let Some((note, span)) = note_and_span {
+ diag.span_note(span, note);
+ }
+ });
}
}
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs
new file mode 100644
index 000000000..fafc97097
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs
@@ -0,0 +1,53 @@
+use super::FILTER_MAP_BOOL_THEN;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::paths::BOOL_THEN;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::is_copy;
+use clippy_utils::{is_from_proc_macro, is_trait_method, match_def_path, peel_blocks};
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::Binder;
+use rustc_span::{sym, Span};
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &Expr<'_>, call_span: Span) {
+ if !in_external_macro(cx.sess(), expr.span)
+ && is_trait_method(cx, expr, sym::Iterator)
+ && let ExprKind::Closure(closure) = arg.kind
+ && let body = cx.tcx.hir().body(closure.body)
+ && let value = peel_blocks(body.value)
+ // Indexing should be fine as `filter_map` always has 1 input, we unfortunately need both
+ // `inputs` and `params` here as we need both the type and the span
+ && let param_ty = closure.fn_decl.inputs[0]
+ && let param = body.params[0]
+ // Issue #11309
+ && let param_ty = cx.tcx.liberate_late_bound_regions(
+ closure.def_id.to_def_id(),
+ Binder::bind_with_vars(
+ cx.typeck_results().node_type(param_ty.hir_id),
+ cx.tcx.late_bound_vars(cx.tcx.hir().local_def_id_to_hir_id(closure.def_id)),
+ ),
+ )
+ && is_copy(cx, param_ty)
+ && let ExprKind::MethodCall(_, recv, [then_arg], _) = value.kind
+ && let ExprKind::Closure(then_closure) = then_arg.kind
+ && let then_body = peel_blocks(cx.tcx.hir().body(then_closure.body).value)
+ && let Some(def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id)
+ && match_def_path(cx, def_id, &BOOL_THEN)
+ && !is_from_proc_macro(cx, expr)
+ && let Some(param_snippet) = snippet_opt(cx, param.span)
+ && let Some(filter) = snippet_opt(cx, recv.span)
+ && let Some(map) = snippet_opt(cx, then_body.span)
+ {
+ span_lint_and_sugg(
+ cx,
+ FILTER_MAP_BOOL_THEN,
+ call_span,
+ "usage of `bool::then` in `filter_map`",
+ "use `filter` then `map` instead",
+ format!("filter(|&{param_snippet}| {filter}).map(|{param_snippet}| {map})"),
+ Applicability::MachineApplicable,
+ );
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs
index d1b5e945d..3337b250c 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs
@@ -3,7 +3,8 @@ use clippy_utils::{is_expr_identity_function, is_trait_method};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use rustc_span::{source_map::Span, sym};
+use rustc_span::source_map::Span;
+use rustc_span::sym;
use super::FILTER_MAP_IDENTITY;
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs
index 175e04f8a..3f89e5931 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs
@@ -31,7 +31,7 @@ pub(super) fn check<'tcx>(
FILTER_MAP_NEXT,
expr.span,
msg,
- "try this",
+ "try",
format!("{iter_snippet}.find_map({filter_snippet})"),
Applicability::MachineApplicable,
);
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_next.rs b/src/tools/clippy/clippy_lints/src/methods/filter_next.rs
index edcec0fc1..ac7bc9bcc 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filter_next.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_next.rs
@@ -1,6 +1,7 @@
-use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::source::snippet;
use clippy_utils::ty::implements_trait;
+use rustc_ast::{BindingAnnotation, Mutability};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
@@ -8,6 +9,21 @@ use rustc_span::sym;
use super::FILTER_NEXT;
+fn path_to_local(expr: &hir::Expr<'_>) -> Option<hir::HirId> {
+ match expr.kind {
+ hir::ExprKind::Field(f, _) => path_to_local(f),
+ hir::ExprKind::Index(recv, _, _) => path_to_local(recv),
+ hir::ExprKind::Path(hir::QPath::Resolved(
+ _,
+ hir::Path {
+ res: rustc_hir::def::Res::Local(local),
+ ..
+ },
+ )) => Some(*local),
+ _ => None,
+ }
+}
+
/// lint use of `filter().next()` for `Iterators`
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
@@ -26,15 +42,30 @@ pub(super) fn check<'tcx>(
if filter_snippet.lines().count() <= 1 {
let iter_snippet = snippet(cx, recv.span, "..");
// add note if not multi-line
- span_lint_and_sugg(
- cx,
- FILTER_NEXT,
- expr.span,
- msg,
- "try this",
- format!("{iter_snippet}.find({filter_snippet})"),
- Applicability::MachineApplicable,
- );
+ span_lint_and_then(cx, FILTER_NEXT, expr.span, msg, |diag| {
+ let (applicability, pat) = if let Some(id) = path_to_local(recv)
+ && let Some(hir::Node::Pat(pat)) = cx.tcx.hir().find(id)
+ && let hir::PatKind::Binding(BindingAnnotation(_, Mutability::Not), _, ident, _) = pat.kind
+ {
+ (Applicability::Unspecified, Some((pat.span, ident)))
+ } else {
+ (Applicability::MachineApplicable, None)
+ };
+
+ diag.span_suggestion(
+ expr.span,
+ "try",
+ format!("{iter_snippet}.find({filter_snippet})"),
+ applicability,
+ );
+
+ if let Some((pat_span, ident)) = pat {
+ diag.span_help(
+ pat_span,
+ format!("you will also need to make `{ident}` mutable, because `find` takes `&mut self`"),
+ );
+ }
+ });
} else {
span_lint(cx, FILTER_NEXT, expr.span, msg);
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs
index 6f911d79d..84a21de0a 100644
--- a/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs
@@ -3,7 +3,8 @@ use clippy_utils::{is_expr_identity_function, is_trait_method};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use rustc_span::{source_map::Span, sym};
+use rustc_span::source_map::Span;
+use rustc_span::sym;
use super::FLAT_MAP_IDENTITY;
diff --git a/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs b/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs
index 615bde941..172c397fb 100644
--- a/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs
@@ -4,7 +4,8 @@ use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty;
-use rustc_span::{source_map::Span, sym};
+use rustc_span::source_map::Span;
+use rustc_span::sym;
use super::FLAT_MAP_OPTION;
use clippy_utils::ty::is_type_diagnostic_item;
@@ -15,7 +16,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg
}
let arg_ty = cx.typeck_results().expr_ty_adjusted(arg);
let sig = match arg_ty.kind() {
- ty::Closure(_, substs) => substs.as_closure().sig(),
+ ty::Closure(_, args) => args.as_closure().sig(),
_ if arg_ty.is_fn() => arg_ty.fn_sig(cx.tcx),
_ => return,
};
diff --git a/src/tools/clippy/clippy_lints/src/methods/format_collect.rs b/src/tools/clippy/clippy_lints/src/methods/format_collect.rs
new file mode 100644
index 000000000..1f8863f85
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/format_collect.rs
@@ -0,0 +1,33 @@
+use super::FORMAT_COLLECT;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::macros::{is_format_macro, root_macro_call_first_node};
+use clippy_utils::ty::is_type_lang_item;
+use rustc_hir::{Expr, ExprKind, LangItem};
+use rustc_lint::LateContext;
+use rustc_span::Span;
+
+/// Same as `peel_blocks` but only actually considers blocks that are not from an expansion.
+/// This is needed because always calling `peel_blocks` would otherwise remove parts of the
+/// `format!` macro, which would cause `root_macro_call_first_node` to return `None`.
+fn peel_non_expn_blocks<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
+ match expr.kind {
+ ExprKind::Block(block, _) if !expr.span.from_expansion() => peel_non_expn_blocks(block.expr?),
+ _ => Some(expr),
+ }
+}
+
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, map_arg: &Expr<'_>, map_span: Span) {
+ if is_type_lang_item(cx, cx.typeck_results().expr_ty(expr), LangItem::String)
+ && let ExprKind::Closure(closure) = map_arg.kind
+ && let body = cx.tcx.hir().body(closure.body)
+ && let Some(value) = peel_non_expn_blocks(body.value)
+ && let Some(mac) = root_macro_call_first_node(cx, value)
+ && is_format_macro(cx, mac.def_id)
+ {
+ span_lint_and_then(cx, FORMAT_COLLECT, expr.span, "use of `format!` to build up a string from an iterator", |diag| {
+ diag.span_help(map_span, "call `fold` instead")
+ .span_help(value.span.source_callsite(), "... and use the `write!` macro here")
+ .note("this can be written more efficiently by appending to a `String` directly");
+ });
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/get_first.rs b/src/tools/clippy/clippy_lints/src/methods/get_first.rs
index 945bbf53b..ee063adac 100644
--- a/src/tools/clippy/clippy_lints/src/methods/get_first.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/get_first.rs
@@ -19,7 +19,7 @@ pub(super) fn check<'tcx>(
if_chain! {
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
- if cx.tcx.type_of(impl_id).subst_identity().is_slice();
+ if cx.tcx.type_of(impl_id).instantiate_identity().is_slice();
if let Some(_) = is_slice_of_primitives(cx, recv);
if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = arg.kind;
then {
diff --git a/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs
index e35fb12ed..a8f090d1d 100644
--- a/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/get_unwrap.rs
@@ -71,7 +71,7 @@ pub(super) fn check<'tcx>(
GET_UNWRAP,
span,
&format!("called `.get{mut_str}().unwrap()` on a {caller_type}. Using `[]` is more clear and more concise"),
- "try this",
+ "try",
format!(
"{borrow_str}{}[{get_args_str}]",
snippet_with_applicability(cx, recv.span, "..", &mut applicability)
diff --git a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs
index 5a78a4168..043425300 100644
--- a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs
@@ -54,7 +54,7 @@ pub fn is_clone_like(cx: &LateContext<'_>, method_name: &str, method_def_id: hir
.tcx
.impl_of_method(method_def_id)
.filter(|&impl_did| {
- cx.tcx.type_of(impl_did).subst_identity().is_slice() && cx.tcx.impl_trait_ref(impl_did).is_none()
+ cx.tcx.type_of(impl_did).instantiate_identity().is_slice() && cx.tcx.impl_trait_ref(impl_did).is_none()
})
.is_some(),
_ => false,
diff --git a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs
index 424482859..631741d92 100644
--- a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs
@@ -7,7 +7,7 @@ use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
-use rustc_span::symbol::{Symbol, sym};
+use rustc_span::symbol::{sym, Symbol};
use super::INEFFICIENT_TO_STRING;
@@ -23,9 +23,9 @@ pub fn check(
if args.is_empty() && method_name == sym::to_string;
if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if match_def_path(cx, to_string_meth_did, &paths::TO_STRING_METHOD);
- if let Some(substs) = cx.typeck_results().node_substs_opt(expr.hir_id);
+ if let Some(args) = cx.typeck_results().node_args_opt(expr.hir_id);
let arg_ty = cx.typeck_results().expr_ty_adjusted(receiver);
- let self_ty = substs.type_at(0);
+ let self_ty = args.type_at(0);
let (deref_self_ty, deref_count) = walk_ptrs_ty_depth(self_ty);
if deref_count >= 1;
if specializes_tostring(cx, deref_self_ty);
@@ -64,8 +64,8 @@ fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
return true;
}
- if let ty::Adt(adt, substs) = ty.kind() {
- cx.tcx.is_diagnostic_item(sym::Cow, adt.did()) && substs.type_at(1).is_str()
+ if let ty::Adt(adt, args) = ty.kind() {
+ cx.tcx.is_diagnostic_item(sym::Cow, adt.did()) && args.type_at(1).is_str()
} else {
false
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs b/src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs
index 7fd3ef1a6..23cc192c3 100644
--- a/src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs
@@ -2,7 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::is_trait_method;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use rustc_span::{source_map::Span, sym};
+use rustc_span::source_map::Span;
+use rustc_span::sym;
use super::INSPECT_FOR_EACH;
diff --git a/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs b/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs
index 301aff5ae..120f3d5f4 100644
--- a/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs
@@ -1,10 +1,10 @@
//! Lint for `c.is_digit(10)`
use super::IS_DIGIT_ASCII_RADIX;
+use clippy_utils::consts::{constant_full_int, FullInt};
+use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::{
- consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, source::snippet_with_applicability,
-};
+use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
index c87f5daab..674d34517 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
@@ -9,8 +9,7 @@ use clippy_utils::visitors::is_local_used;
use rustc_hir::{BindingAnnotation, Body, BorrowKind, ByRef, Expr, ExprKind, Mutability, Pat, PatKind};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty;
-use rustc_span::sym;
-use rustc_span::Span;
+use rustc_span::{sym, Span};
/// lint use of:
/// - `hashmap.iter().map(|(_, v)| v)`
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs b/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs
index e2029da80..8f885e9f7 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs
@@ -27,7 +27,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal
if derefs_to_slice(cx, caller_expr, cx.typeck_results().expr_ty(caller_expr)).is_some() {
// caller is a Slice
if_chain! {
- if let hir::ExprKind::Index(caller_var, index_expr) = &caller_expr.kind;
+ if let hir::ExprKind::Index(caller_var, index_expr, _) = &caller_expr.kind;
if let Some(higher::Range { start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen })
= higher::Range::hir(index_expr);
if let hir::ExprKind::Lit(start_lit) = &start_expr.kind;
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs
index b4210d875..9f7ec19aa 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs
@@ -51,7 +51,7 @@ pub(super) fn check<'tcx>(
if let Some(mut snip) = snippet_opt(cx, method_span) {
snip.push_str(trailing_clone);
let replace_span = expr.span.with_lo(cloned_recv.span.hi());
- diag.span_suggestion(replace_span, "try this", snip, Applicability::MachineApplicable);
+ diag.span_suggestion(replace_span, "try", snip, Applicability::MachineApplicable);
}
}
);
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_skip_next.rs b/src/tools/clippy/clippy_lints/src/methods/iter_skip_next.rs
index 279175e20..39af52141 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_skip_next.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_skip_next.rs
@@ -1,7 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::is_trait_method;
-use clippy_utils::path_to_local;
use clippy_utils::source::snippet;
+use clippy_utils::{is_trait_method, path_to_local};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::{BindingAnnotation, Node, PatKind};
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_skip_zero.rs b/src/tools/clippy/clippy_lints/src/methods/iter_skip_zero.rs
new file mode 100644
index 000000000..6b696b42a
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_skip_zero.rs
@@ -0,0 +1,34 @@
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::{is_from_proc_macro, is_trait_method};
+use rustc_errors::Applicability;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::ITER_SKIP_ZERO;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg_expr: &Expr<'_>) {
+ if !expr.span.from_expansion()
+ && is_trait_method(cx, expr, sym::Iterator)
+ && let Some(arg) = constant(cx, cx.typeck_results(), arg_expr).and_then(|constant| {
+ if let Constant::Int(arg) = constant {
+ Some(arg)
+ } else {
+ None
+ }
+ })
+ && arg == 0
+ && !is_from_proc_macro(cx, expr)
+ {
+ span_lint_and_then(cx, ITER_SKIP_ZERO, arg_expr.span, "usage of `.skip(0)`", |diag| {
+ diag.span_suggestion(
+ arg_expr.span,
+ "if you meant to skip the first element, use",
+ "1",
+ Applicability::MaybeIncorrect,
+ )
+ .note("this call to `skip` does nothing and is useless; remove it");
+ });
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs
index f6772c5c6..2ab721ace 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs
@@ -21,7 +21,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span
ITER_WITH_DRAIN,
span.with_hi(expr.span.hi()),
&format!("`drain(..)` used on a `{ty_name}`"),
- "try this",
+ "try",
"into_iter()".to_string(),
Applicability::MaybeIncorrect,
);
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs
index b9a0ec779..3031193e5 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs
@@ -21,7 +21,7 @@ pub(super) fn check<'tcx>(
if_chain! {
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
- if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).subst_identity(), sym::Option);
+ if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Option);
if let ExprKind::Call(err_path, [err_arg]) = or_expr.kind;
if is_res_lang_ctor(cx, path_res(cx, err_path), ResultErr);
if is_ok_wrapping(cx, map_expr);
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs
index a7284c644..540425eef 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs
@@ -21,13 +21,13 @@ pub fn check(
return;
}
- let Some(mm) = is_min_or_max(cx, unwrap_arg) else { return };
+ let Some(mm) = is_min_or_max(cx, unwrap_arg) else {
+ return;
+ };
if ty.is_signed() {
- use self::{
- MinMax::{Max, Min},
- Sign::{Neg, Pos},
- };
+ use self::MinMax::{Max, Min};
+ use self::Sign::{Neg, Pos};
let Some(sign) = lit_sign(arith_rhs) else {
return;
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs b/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs
index a08f72540..ab13d30d8 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs
@@ -88,7 +88,7 @@ pub(super) fn check(
MANUAL_STR_REPEAT,
collect_expr.span,
"manual implementation of `str::repeat` using iterators",
- "try this",
+ "try",
format!("{val_str}.repeat({count_snip})"),
app
)
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs b/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs
index 576a58499..dabed0aff 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs
@@ -1,15 +1,11 @@
-use clippy_utils::{
- diagnostics::span_lint_and_sugg,
- is_from_proc_macro,
- msrvs::{Msrv, ITERATOR_TRY_FOLD},
- source::snippet_opt,
- ty::implements_trait,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_from_proc_macro;
+use clippy_utils::msrvs::{Msrv, ITERATOR_TRY_FOLD};
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::implements_trait;
use rustc_errors::Applicability;
-use rustc_hir::{
- def::{DefKind, Res},
- Expr, ExprKind,
-};
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_span::Span;
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
index 2b26ef014..880efe60c 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
@@ -19,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_
if_chain! {
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id);
if cx.tcx.impl_of_method(method_id)
- .map_or(false, |id| is_type_diagnostic_item(cx, cx.tcx.type_of(id).subst_identity(), sym::Option))
+ .map_or(false, |id| is_type_diagnostic_item(cx, cx.tcx.type_of(id).instantiate_identity(), sym::Option))
|| is_diag_trait_item(cx, method_id, sym::Iterator);
if let hir::ExprKind::Closure(&hir::Closure{ body, .. }) = arg.kind;
then {
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_collect_result_unit.rs b/src/tools/clippy/clippy_lints/src/methods/map_collect_result_unit.rs
index a0300d278..01cdd02e6 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_collect_result_unit.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_collect_result_unit.rs
@@ -15,8 +15,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, iter: &hir::Expr
let collect_ret_ty = cx.typeck_results().expr_ty(expr);
if_chain! {
if is_type_diagnostic_item(cx, collect_ret_ty, sym::Result);
- if let ty::Adt(_, substs) = collect_ret_ty.kind();
- if let Some(result_t) = substs.types().next();
+ if let ty::Adt(_, args) = collect_ret_ty.kind();
+ if let Some(result_t) = args.types().next();
if result_t.is_unit();
// get parts for snippet
then {
@@ -25,7 +25,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, iter: &hir::Expr
MAP_COLLECT_RESULT_UNIT,
expr.span,
"`.map().collect()` can be replaced with `.try_for_each()`",
- "try this",
+ "try",
format!(
"{}.try_for_each({})",
snippet(cx, iter.span, ".."),
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs b/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs
index a5beb291f..fbb83c8ce 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs
@@ -9,7 +9,7 @@ use super::MAP_ERR_IGNORE;
pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, arg: &Expr<'_>) {
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
&& let Some(impl_id) = cx.tcx.impl_of_method(method_id)
- && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).subst_identity(), sym::Result)
+ && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Result)
&& let ExprKind::Closure(&Closure {
capture_clause: CaptureBy::Ref,
body,
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs
index 361ffcb5e..e74a76455 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs
@@ -6,7 +6,8 @@ use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_middle::ty;
-use rustc_span::{symbol::sym, Span};
+use rustc_span::symbol::sym;
+use rustc_span::Span;
use super::MAP_FLATTEN;
@@ -59,7 +60,7 @@ fn is_map_to_option(cx: &LateContext<'_>, map_arg: &Expr<'_>) -> bool {
match map_closure_ty.kind() {
ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => {
let map_closure_sig = match map_closure_ty.kind() {
- ty::Closure(_, substs) => substs.as_closure().sig(),
+ ty::Closure(_, args) => args.as_closure().sig(),
_ => map_closure_ty.fn_sig(cx.tcx),
};
let map_closure_return_ty = cx.tcx.erase_late_bound_regions(map_closure_sig.output());
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs
index 0f25ef82e..7be1ce483 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs
@@ -4,7 +4,8 @@ use clippy_utils::{is_expr_identity_function, is_trait_method};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use rustc_span::{source_map::Span, sym};
+use rustc_span::source_map::Span;
+use rustc_span::sym;
use super::MAP_IDENTITY;
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs
index 3122f72ee..5464e455d 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs
@@ -11,7 +11,8 @@ use rustc_span::symbol::sym;
use super::MAP_UNWRAP_OR;
/// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
-/// Return true if lint triggered
+///
+/// Returns true if the lint was emitted
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'_>,
@@ -63,7 +64,7 @@ pub(super) fn check<'tcx>(
MAP_UNWRAP_OR,
expr.span,
msg,
- "try this",
+ "try",
format!("{var_snippet}.map_or_else({unwrap_snippet}, {map_snippet})"),
Applicability::MachineApplicable,
);
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 24dbe8c1d..42756b27d 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -17,15 +17,16 @@ mod collapsible_str_replace;
mod drain_collect;
mod err_expect;
mod expect_fun_call;
-mod expect_used;
mod extend_with_drain;
mod filetype_is_file;
mod filter_map;
+mod filter_map_bool_then;
mod filter_map_identity;
mod filter_map_next;
mod filter_next;
mod flat_map_identity;
mod flat_map_option;
+mod format_collect;
mod from_iter_instead_of_collect;
mod get_first;
mod get_last_with_len;
@@ -44,6 +45,7 @@ mod iter_nth_zero;
mod iter_on_single_or_empty_collections;
mod iter_overeager_cloned;
mod iter_skip_next;
+mod iter_skip_zero;
mod iter_with_drain;
mod iterator_step_by_zero;
mod manual_next_back;
@@ -72,6 +74,8 @@ mod or_fun_call;
mod or_then_unwrap;
mod path_buf_push_overwrite;
mod range_zip_with_len;
+mod read_line_without_trim;
+mod readonly_write_lock;
mod repeat_once;
mod search_is_some;
mod seek_from_current;
@@ -84,10 +88,12 @@ mod skip_while_next;
mod stable_sort_primitive;
mod str_splitn;
mod string_extend_chars;
+mod string_lit_chars_any;
mod suspicious_command_arg_space;
mod suspicious_map;
mod suspicious_splitn;
mod suspicious_to_owned;
+mod type_id_on_box;
mod uninit_assumed_init;
mod unit_hash;
mod unnecessary_filter_map;
@@ -98,8 +104,7 @@ mod unnecessary_lazy_eval;
mod unnecessary_literal_unwrap;
mod unnecessary_sort_by;
mod unnecessary_to_owned;
-mod unwrap_or_else_default;
-mod unwrap_used;
+mod unwrap_expect_used;
mod useless_asref;
mod utils;
mod vec_resize_to_zero;
@@ -112,7 +117,7 @@ use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item};
-use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, return_ty};
+use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty};
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_hir::{Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind};
@@ -471,29 +476,40 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
- /// Checks for usage of `_.unwrap_or_else(Default::default)` on `Option` and
- /// `Result` values.
+ /// Checks for usages of the following functions with an argument that constructs a default value
+ /// (e.g., `Default::default` or `String::new`):
+ /// - `unwrap_or`
+ /// - `unwrap_or_else`
+ /// - `or_insert`
+ /// - `or_insert_with`
///
/// ### Why is this bad?
- /// Readability, these can be written as `_.unwrap_or_default`, which is
- /// simpler and more concise.
+ /// Readability. Using `unwrap_or_default` in place of `unwrap_or`/`unwrap_or_else`, or `or_default`
+ /// in place of `or_insert`/`or_insert_with`, is simpler and more concise.
+ ///
+ /// ### Known problems
+ /// In some cases, the argument of `unwrap_or`, etc. is needed for type inference. The lint uses a
+ /// heuristic to try to identify such cases. However, the heuristic can produce false negatives.
///
/// ### Examples
/// ```rust
/// # let x = Some(1);
- /// x.unwrap_or_else(Default::default);
- /// x.unwrap_or_else(u32::default);
+ /// # let mut map = std::collections::HashMap::<u64, String>::new();
+ /// x.unwrap_or(Default::default());
+ /// map.entry(42).or_insert_with(String::new);
/// ```
///
/// Use instead:
/// ```rust
/// # let x = Some(1);
+ /// # let mut map = std::collections::HashMap::<u64, String>::new();
/// x.unwrap_or_default();
+ /// map.entry(42).or_default();
/// ```
#[clippy::version = "1.56.0"]
- pub UNWRAP_OR_ELSE_DEFAULT,
+ pub UNWRAP_OR_DEFAULT,
style,
- "using `.unwrap_or_else(Default::default)`, which is more succinctly expressed as `.unwrap_or_default()`"
+ "using `.unwrap_or`, etc. with an argument that constructs a default value"
}
declare_clippy_lint! {
@@ -2927,6 +2943,37 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
+ /// Looks for calls to `<Box<dyn Any> as Any>::type_id`.
+ ///
+ /// ### Why is this bad?
+ /// This most certainly does not do what the user expects and is very easy to miss.
+ /// Calling `type_id` on a `Box<dyn Any>` calls `type_id` on the `Box<..>` itself,
+ /// so this will return the `TypeId` of the `Box<dyn Any>` type (not the type id
+ /// of the value referenced by the box!).
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// use std::any::{Any, TypeId};
+ ///
+ /// let any_box: Box<dyn Any> = Box::new(42_i32);
+ /// assert_eq!(any_box.type_id(), TypeId::of::<i32>()); // ⚠️ this fails!
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// use std::any::{Any, TypeId};
+ ///
+ /// let any_box: Box<dyn Any> = Box::new(42_i32);
+ /// assert_eq!((*any_box).type_id(), TypeId::of::<i32>());
+ /// // ^ dereference first, to call `type_id` on `dyn Any`
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub TYPE_ID_ON_BOX,
+ suspicious,
+ "calling `.type_id()` on `Box<dyn Any>`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
/// Detects `().hash(_)`.
///
/// ### Why is this bad?
@@ -3316,6 +3363,181 @@ declare_clippy_lint! {
"checks for usage of `Iterator::fold` with a type that implements `Try`"
}
+declare_clippy_lint! {
+ /// Looks for calls to [`Stdin::read_line`] to read a line from the standard input
+ /// into a string, then later attempting to parse this string into a type without first trimming it, which will
+ /// always fail because the string has a trailing newline in it.
+ ///
+ /// ### Why is this bad?
+ /// The `.parse()` call will always fail.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// let mut input = String::new();
+ /// std::io::stdin().read_line(&mut input).expect("Failed to read a line");
+ /// let num: i32 = input.parse().expect("Not a number!");
+ /// assert_eq!(num, 42); // we never even get here!
+ /// ```
+ /// Use instead:
+ /// ```rust,ignore
+ /// let mut input = String::new();
+ /// std::io::stdin().read_line(&mut input).expect("Failed to read a line");
+ /// let num: i32 = input.trim_end().parse().expect("Not a number!");
+ /// // ^^^^^^^^^^^ remove the trailing newline
+ /// assert_eq!(num, 42);
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub READ_LINE_WITHOUT_TRIM,
+ correctness,
+ "calling `Stdin::read_line`, then trying to parse it without first trimming"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `<string_lit>.chars().any(|i| i == c)`.
+ ///
+ /// ### Why is this bad?
+ /// It's significantly slower than using a pattern instead, like
+ /// `matches!(c, '\\' | '.' | '+')`.
+ ///
+ /// Despite this being faster, this is not `perf` as this is pretty common, and is a rather nice
+ /// way to check if a `char` is any in a set. In any case, this `restriction` lint is available
+ /// for situations where that additional performance is absolutely necessary.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let c = 'c';
+ /// "\\.+*?()|[]{}^$#&-~".chars().any(|x| x == c);
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # let c = 'c';
+ /// matches!(c, '\\' | '.' | '+' | '*' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~');
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub STRING_LIT_CHARS_ANY,
+ restriction,
+ "checks for `<string_lit>.chars().any(|i| i == c)`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `.map(|_| format!(..)).collect::<String>()`.
+ ///
+ /// ### Why is this bad?
+ /// This allocates a new string for every element in the iterator.
+ /// This can be done more efficiently by creating the `String` once and appending to it in `Iterator::fold`,
+ /// using either the `write!` macro which supports exactly the same syntax as the `format!` macro,
+ /// or concatenating with `+` in case the iterator yields `&str`/`String`.
+ ///
+ /// Note also that `write!`-ing into a `String` can never fail, despite the return type of `write!` being `std::fmt::Result`,
+ /// so it can be safely ignored or unwrapped.
+ ///
+ /// ### Example
+ /// ```rust
+ /// fn hex_encode(bytes: &[u8]) -> String {
+ /// bytes.iter().map(|b| format!("{b:02X}")).collect()
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// use std::fmt::Write;
+ /// fn hex_encode(bytes: &[u8]) -> String {
+ /// bytes.iter().fold(String::new(), |mut output, b| {
+ /// let _ = write!(output, "{b:02X}");
+ /// output
+ /// })
+ /// }
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub FORMAT_COLLECT,
+ perf,
+ "`format!`ing every element in a collection, then collecting the strings into a new `String`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `.skip(0)` on iterators.
+ ///
+ /// ### Why is this bad?
+ /// This was likely intended to be `.skip(1)` to skip the first element, as `.skip(0)` does
+ /// nothing. If not, the call should be removed.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let v = vec![1, 2, 3];
+ /// let x = v.iter().skip(0).collect::<Vec<_>>();
+ /// let y = v.iter().collect::<Vec<_>>();
+ /// assert_eq!(x, y);
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub ITER_SKIP_ZERO,
+ correctness,
+ "disallows `.skip(0)`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `bool::then` in `Iterator::filter_map`.
+ ///
+ /// ### Why is this bad?
+ /// This can be written with `filter` then `map` instead, which would reduce nesting and
+ /// separates the filtering from the transformation phase. This comes with no cost to
+ /// performance and is just cleaner.
+ ///
+ /// ### Limitations
+ /// Does not lint `bool::then_some`, as it eagerly evaluates its arguments rather than lazily.
+ /// This can create differing behavior, so better safe than sorry.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # fn really_expensive_fn(i: i32) -> i32 { i }
+ /// # let v = vec![];
+ /// _ = v.into_iter().filter_map(|i| (i % 2 == 0).then(|| really_expensive_fn(i)));
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # fn really_expensive_fn(i: i32) -> i32 { i }
+ /// # let v = vec![];
+ /// _ = v.into_iter().filter(|i| i % 2 == 0).map(|i| really_expensive_fn(i));
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub FILTER_MAP_BOOL_THEN,
+ style,
+ "checks for usage of `bool::then` in `Iterator::filter_map`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Looks for calls to `RwLock::write` where the lock is only used for reading.
+ ///
+ /// ### Why is this bad?
+ /// The write portion of `RwLock` is exclusive, meaning that no other thread
+ /// can access the lock while this writer is active.
+ ///
+ /// ### Example
+ /// ```rust
+ /// use std::sync::RwLock;
+ /// fn assert_is_zero(lock: &RwLock<i32>) {
+ /// let num = lock.write().unwrap();
+ /// assert_eq!(*num, 0);
+ /// }
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// use std::sync::RwLock;
+ /// fn assert_is_zero(lock: &RwLock<i32>) {
+ /// let num = lock.read().unwrap();
+ /// assert_eq!(*num, 0);
+ /// }
+ /// ```
+ #[clippy::version = "1.73.0"]
+ pub READONLY_WRITE_LOCK,
+ nursery,
+ "acquiring a write lock when a read lock would work"
+}
+
pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Msrv,
@@ -3346,7 +3568,7 @@ impl_lint_pass!(Methods => [
SHOULD_IMPLEMENT_TRAIT,
WRONG_SELF_CONVENTION,
OK_EXPECT,
- UNWRAP_OR_ELSE_DEFAULT,
+ UNWRAP_OR_DEFAULT,
MAP_UNWRAP_OR,
RESULT_MAP_OR_INTO_OPTION,
OPTION_MAP_OR_NONE,
@@ -3389,6 +3611,7 @@ impl_lint_pass!(Methods => [
STRING_EXTEND_CHARS,
ITER_CLONED_COLLECT,
ITER_WITH_DRAIN,
+ TYPE_ID_ON_BOX,
USELESS_ASREF,
UNNECESSARY_FOLD,
UNNECESSARY_FILTER_MAP,
@@ -3435,6 +3658,7 @@ impl_lint_pass!(Methods => [
REPEAT_ONCE,
STABLE_SORT_PRIMITIVE,
UNIT_HASH,
+ READ_LINE_WITHOUT_TRIM,
UNNECESSARY_SORT_BY,
VEC_RESIZE_TO_ZERO,
VERBOSE_FILE_READS,
@@ -3448,6 +3672,11 @@ impl_lint_pass!(Methods => [
UNNECESSARY_LITERAL_UNWRAP,
DRAIN_COLLECT,
MANUAL_TRY_FOLD,
+ FORMAT_COLLECT,
+ STRING_LIT_CHARS_ANY,
+ ITER_SKIP_ZERO,
+ FILTER_MAP_BOOL_THEN,
+ READONLY_WRITE_LOCK
]);
/// Extracts a method call name, args, and `Span` of the method name.
@@ -3508,11 +3737,11 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
let name = impl_item.ident.name.as_str();
let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id()).def_id;
let item = cx.tcx.hir().expect_item(parent);
- let self_ty = cx.tcx.type_of(item.owner_id).subst_identity();
+ let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }));
if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind {
- let method_sig = cx.tcx.fn_sig(impl_item.owner_id).subst_identity();
+ let method_sig = cx.tcx.fn_sig(impl_item.owner_id).instantiate_identity();
let method_sig = cx.tcx.erase_late_bound_regions(method_sig);
let first_arg_ty_opt = method_sig.inputs().iter().next().copied();
// if this impl block implements a trait, lint in trait definition instead
@@ -3602,8 +3831,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
then {
let first_arg_span = first_arg_ty.span;
let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty);
- let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id())
- .self_ty();
+ let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty();
wrong_self_convention::check(
cx,
item.ident.name.as_str(),
@@ -3620,8 +3848,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
if item.ident.name == sym::new;
if let TraitItemKind::Fn(_, _) = item.kind;
let ret_ty = return_ty(cx, item.owner_id);
- let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id())
- .self_ty();
+ let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty();
if !ret_ty.contains(self_ty);
then {
@@ -3653,6 +3880,13 @@ impl Methods {
unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
}
},
+ ("any", [arg]) if let ExprKind::Closure(arg) = arg.kind
+ && let body = cx.tcx.hir().body(arg.body)
+ && let [param] = body.params
+ && let Some(("chars", recv, _, _, _)) = method_call(recv) =>
+ {
+ string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv);
+ }
("arg", [arg]) => {
suspicious_command_arg_space::check(cx, recv, arg, span);
}
@@ -3669,8 +3903,9 @@ impl Methods {
Some((name @ ("cloned" | "copied"), recv2, [], _, _)) => {
iter_cloned_collect::check(cx, name, expr, recv2);
},
- Some(("map", m_recv, [m_arg], _, _)) => {
+ Some(("map", m_recv, [m_arg], m_ident_span, _)) => {
map_collect_result_unit::check(cx, expr, m_recv, m_arg);
+ format_collect::check(cx, expr, m_arg, m_ident_span);
},
Some(("take", take_self_arg, [take_arg], _, _)) => {
if self.msrv.meets(msrvs::STR_REPEAT) {
@@ -3712,13 +3947,27 @@ impl Methods {
match method_call(recv) {
Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv),
Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, span, err_span, &self.msrv),
- _ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests),
+ _ => unwrap_expect_used::check(
+ cx,
+ expr,
+ recv,
+ false,
+ self.allow_expect_in_tests,
+ unwrap_expect_used::Variant::Expect,
+ ),
}
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
},
("expect_err", [_]) => {
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
- expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests);
+ unwrap_expect_used::check(
+ cx,
+ expr,
+ recv,
+ true,
+ self.allow_expect_in_tests,
+ unwrap_expect_used::Variant::Expect,
+ );
},
("extend", [arg]) => {
string_extend_chars::check(cx, expr, recv, arg);
@@ -3726,6 +3975,7 @@ impl Methods {
},
("filter_map", [arg]) => {
unnecessary_filter_map::check(cx, expr, arg, name);
+ filter_map_bool_then::check(cx, expr, arg, call_span);
filter_map_identity::check(cx, expr, arg, span);
},
("find_map", [arg]) => {
@@ -3769,11 +4019,9 @@ impl Methods {
unnecessary_join::check(cx, expr, recv, join_arg, span);
}
},
- ("last", []) | ("skip", [_]) => {
- if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) {
- if let ("cloned", []) = (name2, args2) {
- iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
- }
+ ("last", []) => {
+ if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
+ iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
}
},
("lock", []) => {
@@ -3846,6 +4094,9 @@ impl Methods {
("read_to_string", [_]) => {
verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_STRING_MSG);
},
+ ("read_line", [arg]) => {
+ read_line_without_trim::check(cx, expr, recv, arg);
+ }
("repeat", [arg]) => {
repeat_once::check(cx, expr, recv, arg);
},
@@ -3871,6 +4122,13 @@ impl Methods {
seek_to_start_instead_of_rewind::check(cx, expr, recv, arg, span);
}
},
+ ("skip", [arg]) => {
+ iter_skip_zero::check(cx, expr, arg);
+
+ if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
+ iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
+ }
+ }
("sort", []) => {
stable_sort_primitive::check(cx, expr, recv);
},
@@ -3893,10 +4151,8 @@ impl Methods {
},
("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
("take", [_arg]) => {
- if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) {
- if let ("cloned", []) = (name2, args2) {
- iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
- }
+ if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
+ iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
}
},
("take", []) => needless_option_take::check(cx, expr, recv),
@@ -3914,6 +4170,9 @@ impl Methods {
("to_os_string" | "to_path_buf" | "to_vec", []) => {
implicit_clone::check(cx, name, expr, recv);
},
+ ("type_id", []) => {
+ type_id_on_box::check(cx, recv, expr.span);
+ }
("unwrap", []) => {
match method_call(recv) {
Some(("get", recv, [get_arg], _, _)) => {
@@ -3928,11 +4187,25 @@ impl Methods {
_ => {},
}
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
- unwrap_used::check(cx, expr, recv, false, self.allow_unwrap_in_tests);
+ unwrap_expect_used::check(
+ cx,
+ expr,
+ recv,
+ false,
+ self.allow_unwrap_in_tests,
+ unwrap_expect_used::Variant::Unwrap,
+ );
},
("unwrap_err", []) => {
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
- unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests);
+ unwrap_expect_used::check(
+ cx,
+ expr,
+ recv,
+ true,
+ self.allow_unwrap_in_tests,
+ unwrap_expect_used::Variant::Unwrap,
+ );
},
("unwrap_or", [u_arg]) => {
match method_call(recv) {
@@ -3949,7 +4222,7 @@ impl Methods {
}
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
},
- ("unwrap_or_default", []) => {
+ ("unwrap_or_default" | "unwrap_unchecked" | "unwrap_err_unchecked", []) => {
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
}
("unwrap_or_else", [u_arg]) => {
@@ -3957,12 +4230,14 @@ impl Methods {
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");
},
}
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
},
+ ("write", []) => {
+ readonly_write_lock::check(cx, expr, recv);
+ }
("zip", [arg]) => {
if let ExprKind::MethodCall(name, iter_recv, [], _) = recv.kind
&& name.ident.name == sym::iter
@@ -4113,8 +4388,8 @@ impl SelfKind {
} else if ty.is_box() {
ty.boxed_ty() == parent_ty
} else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) {
- if let ty::Adt(_, substs) = ty.kind() {
- substs.types().next().map_or(false, |t| t == parent_ty)
+ if let ty::Adt(_, args) = ty.kind() {
+ args.types().next().map_or(false, |t| t == parent_ty)
} else {
false
}
@@ -4134,7 +4409,7 @@ impl SelfKind {
};
let Some(trait_def_id) = cx.tcx.get_diagnostic_item(trait_sym) else {
- return false
+ return false;
};
implements_trait(cx, ty, trait_def_id, &[parent_ty.into()])
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs b/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs
index d0aa39d06..2855e23bf 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs
@@ -1,5 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::{expr_custom_deref_adjustment, ty::is_type_diagnostic_item};
+use clippy_utils::expr_custom_deref_adjustment;
+use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, Mutability};
@@ -15,7 +16,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &'
if let ty::Ref(_, _, Mutability::Mut) = cx.typeck_results().expr_ty(recv).kind();
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id);
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
- if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).subst_identity(), sym::Mutex);
+ if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Mutex);
then {
span_lint_and_sugg(
cx,
diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
index 8ca7af810..dbd965d65 100644
--- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
@@ -4,10 +4,9 @@ use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{is_type_diagnostic_item, make_normalized_projection, make_projection};
use clippy_utils::{
- can_move_expr_to_closure, get_enclosing_block, get_parent_node, is_trait_method, path_to_local, path_to_local_id,
- CaptureKind,
+ can_move_expr_to_closure, fn_def_id, get_enclosing_block, get_parent_node, higher, is_trait_method, path_to_local,
+ path_to_local_id, CaptureKind,
};
-use clippy_utils::{fn_def_id, higher};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{Applicability, MultiSpan};
use rustc_hir::intravisit::{walk_block, walk_expr, Visitor};
@@ -163,7 +162,7 @@ fn check_collect_into_intoiterator<'tcx>(
// that contains `collect_expr`
let inputs = cx
.tcx
- .liberate_late_bound_regions(id, cx.tcx.fn_sig(id).subst_identity())
+ .liberate_late_bound_regions(id, cx.tcx.fn_sig(id).instantiate_identity())
.inputs();
// map IntoIterator generic bounds to their signature
@@ -201,7 +200,7 @@ fn check_collect_into_intoiterator<'tcx>(
/// Checks if the given method call matches the expected signature of `([&[mut]] self) -> bool`
fn is_is_empty_sig(cx: &LateContext<'_>, call_id: HirId) -> bool {
cx.typeck_results().type_dependent_def_id(call_id).map_or(false, |id| {
- let sig = cx.tcx.fn_sig(id).subst_identity().skip_binder();
+ let sig = cx.tcx.fn_sig(id).instantiate_identity().skip_binder();
sig.inputs().len() == 1 && sig.output().is_bool()
})
}
@@ -215,7 +214,7 @@ fn iterates_same_ty<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>, collect_ty:
&& let Some(into_iter_item_proj) = make_projection(cx.tcx, into_iter_trait, item, [collect_ty])
&& let Ok(into_iter_item_ty) = cx.tcx.try_normalize_erasing_regions(
cx.param_env,
- Ty::new_projection(cx.tcx,into_iter_item_proj.def_id, into_iter_item_proj.substs)
+ Ty::new_projection(cx.tcx,into_iter_item_proj.def_id, into_iter_item_proj.args)
)
{
iter_item_ty == into_iter_item_ty
@@ -229,7 +228,7 @@ fn iterates_same_ty<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>, collect_ty:
fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) -> bool {
let typeck = cx.typeck_results();
if let Some(id) = typeck.type_dependent_def_id(call_id)
- && let sig = cx.tcx.fn_sig(id).subst_identity()
+ && let sig = cx.tcx.fn_sig(id).instantiate_identity()
&& sig.skip_binder().output().is_bool()
&& let [_, search_ty] = *sig.skip_binder().inputs()
&& let ty::Ref(_, search_ty, Mutability::Not) = *cx.tcx.erase_late_bound_regions(sig.rebind(search_ty)).kind()
@@ -237,11 +236,11 @@ fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) -
&& let Some(iter_item) = cx.tcx
.associated_items(iter_trait)
.find_by_name_and_kind(cx.tcx, Ident::with_dummy_span(Symbol::intern("Item")), AssocKind::Type, iter_trait)
- && let substs = cx.tcx.mk_substs(&[GenericArg::from(typeck.expr_ty_adjusted(iter_expr))])
- && let proj_ty = Ty::new_projection(cx.tcx,iter_item.def_id, substs)
+ && let args = cx.tcx.mk_args(&[GenericArg::from(typeck.expr_ty_adjusted(iter_expr))])
+ && let proj_ty = Ty::new_projection(cx.tcx,iter_item.def_id, args)
&& let Ok(item_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, proj_ty)
{
- item_ty == EarlyBinder::bind(search_ty).subst(cx.tcx, cx.typeck_results().node_substs(call_id))
+ item_ty == EarlyBinder::bind(search_ty).instantiate(cx.tcx, cx.typeck_results().node_args(call_id))
} else {
false
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs b/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs
index 7030baf19..eaae8613d 100644
--- a/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs
@@ -17,7 +17,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name
if is_type_diagnostic_item(cx, outer_ty, sym::Option) && outer_ty == typeck.expr_ty(recv) {
if name == "as_deref_mut" && recv.is_syntactic_place_expr() {
- let Res::Local(binding_id) = path_res(cx, recv) else { return };
+ let Res::Local(binding_id) = path_res(cx, recv) else {
+ return;
+ };
if local_used_after_expr(cx, binding_id, recv) {
return;
@@ -29,7 +31,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name
NEEDLESS_OPTION_AS_DEREF,
expr.span,
"derefed type is same as origin",
- "try this",
+ "try",
snippet_opt(cx, recv.span).unwrap(),
Applicability::MachineApplicable,
);
diff --git a/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs b/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs
index eada530d6..697eab32a 100644
--- a/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs
@@ -1,5 +1,6 @@
use super::OBFUSCATED_IF_ELSE;
-use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet_with_applicability};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
diff --git a/src/tools/clippy/clippy_lints/src/methods/ok_expect.rs b/src/tools/clippy/clippy_lints/src/methods/ok_expect.rs
index 646fc4a7b..f2ef42933 100644
--- a/src/tools/clippy/clippy_lints/src/methods/ok_expect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/ok_expect.rs
@@ -33,7 +33,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
/// Given a `Result<T, E>` type, return its error type (`E`).
fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option<Ty<'a>> {
match ty.kind() {
- ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym::Result) => substs.types().nth(1),
+ ty::Adt(_, args) if is_type_diagnostic_item(cx, ty, sym::Result) => args.types().nth(1),
_ => None,
}
}
diff --git a/src/tools/clippy/clippy_lints/src/methods/open_options.rs b/src/tools/clippy/clippy_lints/src/methods/open_options.rs
index bd625a691..1c664e76d 100644
--- a/src/tools/clippy/clippy_lints/src/methods/open_options.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/open_options.rs
@@ -11,7 +11,7 @@ use super::NONSENSICAL_OPEN_OPTIONS;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
&& let Some(impl_id) = cx.tcx.impl_of_method(method_id)
- && match_type(cx, cx.tcx.type_of(impl_id).subst_identity(), &paths::OPEN_OPTIONS)
+ && match_type(cx, cx.tcx.type_of(impl_id).instantiate_identity(), &paths::OPEN_OPTIONS)
{
let mut options = Vec::new();
get_open_options(cx, recv, &mut options);
diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs
index 41ceef19e..cb6a23068 100644
--- a/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs
@@ -8,8 +8,7 @@ use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
-use super::OPTION_MAP_OR_NONE;
-use super::RESULT_MAP_OR_INTO_OPTION;
+use super::{OPTION_MAP_OR_NONE, RESULT_MAP_OR_INTO_OPTION};
// The expression inside a closure may or may not have surrounding braces
// which causes problems when generating a suggestion.
diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs
index f4f158c04..fcbe005fb 100644
--- a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs
@@ -1,17 +1,12 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::is_copy;
-use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::intravisit::{walk_path, Visitor};
-use rustc_hir::ExprKind;
-use rustc_hir::Node;
-use rustc_hir::PatKind;
-use rustc_hir::QPath;
-use rustc_hir::{self, HirId, Path};
+use rustc_hir::{self, ExprKind, HirId, Node, PatKind, Path, QPath};
use rustc_lint::LateContext;
use rustc_middle::hir::nested_filter;
use rustc_span::source_map::Span;
diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
index 7ce28ea93..8b2f57160 100644
--- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
@@ -1,16 +1,17 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::eager_or_lazy::switch_to_lazy_eval;
use clippy_utils::source::snippet_with_context;
-use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
-use clippy_utils::{contains_return, is_trait_item, last_path_segment};
+use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_type_diagnostic_item};
+use clippy_utils::{contains_return, is_default_equivalent, is_default_equivalent_call, last_path_segment};
use if_chain::if_chain;
use rustc_errors::Applicability;
-use rustc_hir as hir;
use rustc_lint::LateContext;
+use rustc_middle::ty;
use rustc_span::source_map::Span;
-use rustc_span::symbol::{kw, sym, Symbol};
+use rustc_span::symbol::{self, sym, Symbol};
+use {rustc_ast as ast, rustc_hir as hir};
-use super::OR_FUN_CALL;
+use super::{OR_FUN_CALL, UNWRAP_OR_DEFAULT};
/// Checks for the `OR_FUN_CALL` lint.
#[allow(clippy::too_many_lines)]
@@ -24,53 +25,72 @@ pub(super) fn check<'tcx>(
) {
/// Checks for `unwrap_or(T::new())`, `unwrap_or(T::default())`,
/// `or_insert(T::new())` or `or_insert(T::default())`.
+ /// Similarly checks for `unwrap_or_else(T::new)`, `unwrap_or_else(T::default)`,
+ /// `or_insert_with(T::new)` or `or_insert_with(T::default)`.
#[allow(clippy::too_many_arguments)]
fn check_unwrap_or_default(
cx: &LateContext<'_>,
name: &str,
+ receiver: &hir::Expr<'_>,
fun: &hir::Expr<'_>,
- arg: &hir::Expr<'_>,
- or_has_args: bool,
+ call_expr: Option<&hir::Expr<'_>>,
span: Span,
method_span: Span,
) -> bool {
- let is_default_default = || is_trait_item(cx, fun, sym::Default);
+ if !expr_type_is_certain(cx, receiver) {
+ return false;
+ }
- let implements_default = |arg, default_trait_id| {
- let arg_ty = cx.typeck_results().expr_ty(arg);
- implements_trait(cx, arg_ty, default_trait_id, &[])
+ let is_new = |fun: &hir::Expr<'_>| {
+ if let hir::ExprKind::Path(ref qpath) = fun.kind {
+ let path = last_path_segment(qpath).ident.name;
+ matches!(path, sym::new)
+ } else {
+ false
+ }
};
- if_chain! {
- if !or_has_args;
- if let Some(sugg) = match name {
- "unwrap_or" => Some("unwrap_or_default"),
- "or_insert" => Some("or_default"),
- _ => None,
- };
- if let hir::ExprKind::Path(ref qpath) = fun.kind;
- if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default);
- let path = last_path_segment(qpath).ident.name;
- // needs to target Default::default in particular or be *::new and have a Default impl
- // available
- if (matches!(path, kw::Default) && is_default_default())
- || (matches!(path, sym::new) && implements_default(arg, default_trait_id));
-
- then {
- span_lint_and_sugg(
- cx,
- OR_FUN_CALL,
- method_span.with_hi(span.hi()),
- &format!("use of `{name}` followed by a call to `{path}`"),
- "try this",
- format!("{sugg}()"),
- Applicability::MachineApplicable,
- );
-
- true
+ let output_type_implements_default = |fun| {
+ let fun_ty = cx.typeck_results().expr_ty(fun);
+ if let ty::FnDef(def_id, args) = fun_ty.kind() {
+ let output_ty = cx.tcx.fn_sig(def_id).instantiate(cx.tcx, args).skip_binder().output();
+ cx.tcx
+ .get_diagnostic_item(sym::Default)
+ .map_or(false, |default_trait_id| {
+ implements_trait(cx, output_ty, default_trait_id, &[])
+ })
} else {
false
}
+ };
+
+ let sugg = match (name, call_expr.is_some()) {
+ ("unwrap_or", true) | ("unwrap_or_else", false) => "unwrap_or_default",
+ ("or_insert", true) | ("or_insert_with", false) => "or_default",
+ _ => return false,
+ };
+
+ // needs to target Default::default in particular or be *::new and have a Default impl
+ // available
+ if (is_new(fun) && output_type_implements_default(fun))
+ || match call_expr {
+ Some(call_expr) => is_default_equivalent(cx, call_expr),
+ None => is_default_equivalent_call(cx, fun) || closure_body_returns_empty_to_string(cx, fun),
+ }
+ {
+ span_lint_and_sugg(
+ cx,
+ UNWRAP_OR_DEFAULT,
+ method_span.with_hi(span.hi()),
+ &format!("use of `{name}` to construct default value"),
+ "try",
+ format!("{sugg}()"),
+ Applicability::MachineApplicable,
+ );
+
+ true
+ } else {
+ false
}
}
@@ -139,7 +159,7 @@ pub(super) fn check<'tcx>(
OR_FUN_CALL,
span_replace_word,
&format!("use of `{name}` followed by a function call"),
- "try this",
+ "try",
format!("{name}_{suffix}({sugg})"),
app,
);
@@ -168,11 +188,16 @@ pub(super) fn check<'tcx>(
match inner_arg.kind {
hir::ExprKind::Call(fun, or_args) => {
let or_has_args = !or_args.is_empty();
- if !check_unwrap_or_default(cx, name, fun, arg, or_has_args, expr.span, method_span) {
+ if or_has_args
+ || !check_unwrap_or_default(cx, name, receiver, fun, Some(inner_arg), expr.span, method_span)
+ {
let fun_span = if or_has_args { None } else { Some(fun.span) };
check_general_case(cx, name, method_span, receiver, arg, None, expr.span, fun_span);
}
},
+ hir::ExprKind::Path(..) | hir::ExprKind::Closure(..) => {
+ check_unwrap_or_default(cx, name, receiver, inner_arg, None, expr.span, method_span);
+ },
hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => {
check_general_case(cx, name, method_span, receiver, arg, None, expr.span, None);
},
@@ -189,3 +214,22 @@ pub(super) fn check<'tcx>(
}
}
}
+
+fn closure_body_returns_empty_to_string(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> bool {
+ if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = e.kind {
+ let body = cx.tcx.hir().body(body);
+
+ if body.params.is_empty()
+ && let hir::Expr{ kind, .. } = &body.value
+ && let hir::ExprKind::MethodCall(hir::PathSegment {ident, ..}, self_arg, _, _) = kind
+ && ident.name == sym::to_string
+ && let hir::Expr{ kind, .. } = self_arg
+ && let hir::ExprKind::Lit(lit) = kind
+ && let ast::LitKind::Str(symbol::kw::Empty, _) = lit.node
+ {
+ return true;
+ }
+ }
+
+ false
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs
index 55ba6e262..7b0bdcf99 100644
--- a/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs
@@ -1,8 +1,10 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{diagnostics::span_lint_and_sugg, is_res_lang_ctor, path_res};
+use clippy_utils::{is_res_lang_ctor, path_res};
use rustc_errors::Applicability;
-use rustc_hir::{lang_items::LangItem, Expr, ExprKind};
+use rustc_hir::lang_items::LangItem;
+use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_span::{sym, Span};
@@ -50,7 +52,7 @@ pub(super) fn check<'tcx>(
OR_THEN_UNWRAP,
unwrap_expr.span.with_lo(or_span.lo()),
title,
- "try this",
+ "try",
suggestion,
applicability,
);
diff --git a/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs b/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs
index 0284d9dea..1c07d2a3a 100644
--- a/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs
@@ -14,7 +14,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
if_chain! {
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
- if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).subst_identity(), sym::PathBuf);
+ if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::PathBuf);
if let ExprKind::Lit(lit) = arg.kind;
if let LitKind::Str(ref path_lit, _) = lit.node;
if let pushed_path = Path::new(path_lit.as_str());
diff --git a/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs b/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs
index 867a3b402..f253d8de9 100644
--- a/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs
@@ -1,7 +1,6 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::source::snippet;
-use clippy_utils::{higher, SpanlessEq};
-use clippy_utils::{is_integer_const, is_trait_method};
+use clippy_utils::{higher, is_integer_const, is_trait_method, SpanlessEq};
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind, QPath};
use rustc_lint::LateContext;
diff --git a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs
new file mode 100644
index 000000000..81f9e2a77
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs
@@ -0,0 +1,74 @@
+use std::ops::ControlFlow;
+
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::visitors::for_each_local_use_after_expr;
+use clippy_utils::{get_parent_expr, match_def_path};
+use rustc_errors::Applicability;
+use rustc_hir::def::Res;
+use rustc_hir::{Expr, ExprKind, QPath};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, Ty};
+use rustc_span::sym;
+
+use super::READ_LINE_WITHOUT_TRIM;
+
+/// Will a `.parse::<ty>()` call fail if the input has a trailing newline?
+fn parse_fails_on_trailing_newline(ty: Ty<'_>) -> bool {
+ // only allow a very limited set of types for now, for which we 100% know parsing will fail
+ matches!(ty.kind(), ty::Float(_) | ty::Bool | ty::Int(_) | ty::Uint(_))
+}
+
+pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) {
+ if let Some(recv_adt) = cx.typeck_results().expr_ty(recv).ty_adt_def()
+ && match_def_path(cx, recv_adt.did(), &["std", "io", "stdio", "Stdin"])
+ && let ExprKind::Path(QPath::Resolved(_, path)) = arg.peel_borrows().kind
+ && let Res::Local(local_id) = path.res
+ {
+ // We've checked that `call` is a call to `Stdin::read_line()` with the right receiver,
+ // now let's check if the first use of the string passed to `::read_line()` is
+ // parsed into a type that will always fail if it has a trailing newline.
+ for_each_local_use_after_expr(cx, local_id, call.hir_id, |expr| {
+ if let Some(parent) = get_parent_expr(cx, expr)
+ && let ExprKind::MethodCall(segment, .., span) = parent.kind
+ && segment.ident.name == sym!(parse)
+ && let parse_result_ty = cx.typeck_results().expr_ty(parent)
+ && is_type_diagnostic_item(cx, parse_result_ty, sym::Result)
+ && let ty::Adt(_, args) = parse_result_ty.kind()
+ && let Some(ok_ty) = args[0].as_type()
+ && parse_fails_on_trailing_newline(ok_ty)
+ {
+ let local_snippet = snippet(cx, expr.span, "<expr>");
+ span_lint_and_then(
+ cx,
+ READ_LINE_WITHOUT_TRIM,
+ span,
+ "calling `.parse()` without trimming the trailing newline character",
+ |diag| {
+ diag.span_note(call.span, "call to `.read_line()` here, \
+ which leaves a trailing newline character in the buffer, \
+ which in turn will cause `.parse()` to fail");
+
+ diag.span_suggestion(
+ expr.span,
+ "try",
+ format!("{local_snippet}.trim_end()"),
+ Applicability::MachineApplicable,
+ );
+ }
+ );
+ }
+
+ // only consider the first use to prevent this scenario:
+ // ```
+ // let mut s = String::new();
+ // std::io::stdin().read_line(&mut s);
+ // s.pop();
+ // let _x: i32 = s.parse().unwrap();
+ // ```
+ // this is actually fine, because the pop call removes the trailing newline.
+ ControlFlow::<(), ()>::Break(())
+ });
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs b/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs
new file mode 100644
index 000000000..e3ec921da
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs
@@ -0,0 +1,52 @@
+use super::READONLY_WRITE_LOCK;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::mir::{enclosing_mir, visit_local_usage};
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind, Node};
+use rustc_lint::LateContext;
+use rustc_middle::mir::{Location, START_BLOCK};
+use rustc_span::sym;
+
+fn is_unwrap_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ if let ExprKind::MethodCall(path, receiver, ..) = expr.kind
+ && path.ident.name == sym::unwrap
+ {
+ is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver).peel_refs(), sym::Result)
+ } else {
+ false
+ }
+}
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, receiver: &Expr<'_>) {
+ if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver).peel_refs(), sym::RwLock)
+ && let Node::Expr(unwrap_call_expr) = cx.tcx.hir().get_parent(expr.hir_id)
+ && is_unwrap_call(cx, unwrap_call_expr)
+ && let parent = cx.tcx.hir().get_parent(unwrap_call_expr.hir_id)
+ && let Node::Local(local) = parent
+ && let Some(mir) = enclosing_mir(cx.tcx, expr.hir_id)
+ && let Some((local, _)) = mir.local_decls.iter_enumerated().find(|(_, decl)| {
+ local.span.contains(decl.source_info.span)
+ })
+ && let Some(usages) = visit_local_usage(&[local], mir, Location {
+ block: START_BLOCK,
+ statement_index: 0,
+ })
+ && let [usage] = usages.as_slice()
+ {
+ let writer_never_mutated = usage.local_consume_or_mutate_locs.is_empty();
+
+ if writer_never_mutated {
+ span_lint_and_sugg(
+ cx,
+ READONLY_WRITE_LOCK,
+ expr.span,
+ "this write lock is used only for reading",
+ "consider using a read lock instead",
+ format!("{}.read()", snippet(cx, receiver.span, "<receiver>")),
+ Applicability::MaybeIncorrect // write lock might be intentional for enforcing exclusiveness
+ );
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs b/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs
index c028e9543..f3d6a15ed 100644
--- a/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs
@@ -3,10 +3,10 @@ use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
-use clippy_utils::{
- diagnostics::span_lint_and_sugg, get_trait_def_id, match_def_path, paths, source::snippet_with_applicability,
- ty::implements_trait,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::implements_trait;
+use clippy_utils::{get_trait_def_id, match_def_path, paths};
use super::SEEK_FROM_CURRENT;
diff --git a/src/tools/clippy/clippy_lints/src/methods/stable_sort_primitive.rs b/src/tools/clippy/clippy_lints/src/methods/stable_sort_primitive.rs
index b5fd0ad8c..0f4c97022 100644
--- a/src/tools/clippy/clippy_lints/src/methods/stable_sort_primitive.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/stable_sort_primitive.rs
@@ -10,7 +10,7 @@ use super::STABLE_SORT_PRIMITIVE;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
&& let Some(impl_id) = cx.tcx.impl_of_method(method_id)
- && cx.tcx.type_of(impl_id).subst_identity().is_slice()
+ && cx.tcx.type_of(impl_id).instantiate_identity().is_slice()
&& let Some(slice_type) = is_slice_of_primitives(cx, recv)
{
span_lint_and_then(
diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
index 88a3c2620..7016ad0a8 100644
--- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
@@ -55,7 +55,7 @@ fn lint_needless(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_
NEEDLESS_SPLITN,
expr.span,
&format!("unnecessary use of `{r}splitn`"),
- "try this",
+ "try",
format!(
"{}.{r}split({})",
snippet_with_context(cx, self_arg.span, expr.span.ctxt(), "..", &mut app).0,
@@ -110,7 +110,7 @@ fn check_manual_split_once(
IterUsageKind::Nth(_) => return,
};
- span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
+ span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try", sugg, app);
}
/// checks for
@@ -236,7 +236,7 @@ fn indirect_usage<'tcx>(
!matches!(
node,
Node::Expr(Expr {
- kind: ExprKind::Match(.., MatchSource::TryDesugar),
+ kind: ExprKind::Match(.., MatchSource::TryDesugar(_)),
..
})
)
diff --git a/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs b/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs
index 2c20c6d75..c7885f689 100644
--- a/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs
@@ -34,7 +34,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
STRING_EXTEND_CHARS,
expr.span,
"calling `.extend(_.chars())`",
- "try this",
+ "try",
format!(
"{}.push_str({ref_str}{})",
snippet_with_applicability(cx, recv.span, "..", &mut applicability),
diff --git a/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs b/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs
new file mode 100644
index 000000000..70da6ad58
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs
@@ -0,0 +1,58 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{Msrv, MATCHES_MACRO};
+use clippy_utils::source::snippet_opt;
+use clippy_utils::{is_from_proc_macro, is_trait_method, path_to_local};
+use itertools::Itertools;
+use rustc_ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind, Param, PatKind};
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::STRING_LIT_CHARS_ANY;
+
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'tcx>,
+ recv: &Expr<'_>,
+ param: &'tcx Param<'tcx>,
+ body: &Expr<'_>,
+ msrv: &Msrv,
+) {
+ if msrv.meets(MATCHES_MACRO)
+ && is_trait_method(cx, expr, sym::Iterator)
+ && let PatKind::Binding(_, arg, _, _) = param.pat.kind
+ && let ExprKind::Lit(lit_kind) = recv.kind
+ && let LitKind::Str(val, _) = lit_kind.node
+ && let ExprKind::Binary(kind, lhs, rhs) = body.kind
+ && let BinOpKind::Eq = kind.node
+ && let Some(lhs_path) = path_to_local(lhs)
+ && let Some(rhs_path) = path_to_local(rhs)
+ && let scrutinee = match (lhs_path == arg, rhs_path == arg) {
+ (true, false) => rhs,
+ (false, true) => lhs,
+ _ => return,
+ }
+ && !is_from_proc_macro(cx, expr)
+ && let Some(scrutinee_snip) = snippet_opt(cx, scrutinee.span)
+ {
+ // Normalize the char using `map` so `join` doesn't use `Display`, if we don't then
+ // something like `r"\"` will become `'\'`, which is of course invalid
+ let pat_snip = val.as_str().chars().map(|c| format!("{c:?}")).join(" | ");
+
+ span_lint_and_then(
+ cx,
+ STRING_LIT_CHARS_ANY,
+ expr.span,
+ "usage of `.chars().any(...)` to check if a char matches any from a string literal",
+ |diag| {
+ diag.span_suggestion_verbose(
+ expr.span,
+ "use `matches!(...)` instead",
+ format!("matches!({scrutinee_snip}, {pat_snip})"),
+ Applicability::MachineApplicable,
+ );
+ }
+ );
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs
index 73632c5a3..bc8f01767 100644
--- a/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs
@@ -1,11 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::paths;
use clippy_utils::ty::match_type;
-use rustc_ast as ast;
use rustc_errors::{Applicability, Diagnostic};
-use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_span::Span;
+use {rustc_ast as ast, rustc_hir as hir};
use super::SUSPICIOUS_COMMAND_ARG_SPACE;
diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs
index 90ca66bd7..3cb2719e4 100644
--- a/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs
@@ -13,7 +13,7 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se
if let Some(call_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if let Some(impl_id) = cx.tcx.impl_of_method(call_id);
if cx.tcx.impl_trait_ref(impl_id).is_none();
- let self_ty = cx.tcx.type_of(impl_id).subst_identity();
+ let self_ty = cx.tcx.type_of(impl_id).instantiate_identity();
if self_ty.is_slice() || self_ty.is_str();
then {
// Ignore empty slice and string literals when used with a literal count.
diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs
index e818f1892..9eb8d6e6e 100644
--- a/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs
@@ -5,7 +5,8 @@ use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use rustc_middle::ty::{self, print::with_forced_trimmed_paths};
+use rustc_middle::ty::print::with_forced_trimmed_paths;
+use rustc_middle::ty::{self};
use rustc_span::sym;
use super::SUSPICIOUS_TO_OWNED;
diff --git a/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs b/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs
new file mode 100644
index 000000000..3404bdfe7
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs
@@ -0,0 +1,62 @@
+use crate::methods::TYPE_ID_ON_BOX;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use rustc_errors::Applicability;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+use rustc_middle::ty::adjustment::{Adjust, Adjustment};
+use rustc_middle::ty::{self, ExistentialPredicate, Ty};
+use rustc_span::{sym, Span};
+
+fn is_dyn_any(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
+ if let ty::Dynamic(preds, ..) = ty.kind() {
+ preds.iter().any(|p| match p.skip_binder() {
+ ExistentialPredicate::Trait(tr) => cx.tcx.is_diagnostic_item(sym::Any, tr.def_id),
+ _ => false,
+ })
+ } else {
+ false
+ }
+}
+
+pub(super) fn check(cx: &LateContext<'_>, receiver: &Expr<'_>, call_span: Span) {
+ let recv_adjusts = cx.typeck_results().expr_adjustments(receiver);
+
+ if let Some(Adjustment { target: recv_ty, .. }) = recv_adjusts.last()
+ && let ty::Ref(_, ty, _) = recv_ty.kind()
+ && let ty::Adt(adt, args) = ty.kind()
+ && adt.is_box()
+ && is_dyn_any(cx, args.type_at(0))
+ {
+ span_lint_and_then(
+ cx,
+ TYPE_ID_ON_BOX,
+ call_span,
+ "calling `.type_id()` on a `Box<dyn Any>`",
+ |diag| {
+ let derefs = recv_adjusts
+ .iter()
+ .filter(|adj| matches!(adj.kind, Adjust::Deref(None)))
+ .count();
+
+ let mut sugg = "*".repeat(derefs + 1);
+ sugg += &snippet(cx, receiver.span, "<expr>");
+
+ diag.note(
+ "this returns the type id of the literal type `Box<dyn Any>` instead of the \
+ type id of the boxed value, which is most likely not what you want"
+ )
+ .note(
+ "if this is intentional, use `TypeId::of::<Box<dyn Any>>()` instead, \
+ which makes it more clear"
+ )
+ .span_suggestion(
+ receiver.span,
+ "consider dereferencing first",
+ format!("({sugg})"),
+ Applicability::MaybeIncorrect,
+ );
+ },
+ );
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs b/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs
index a1c629473..bc9c518db 100644
--- a/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs
@@ -1,5 +1,6 @@
use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{is_path_diagnostic_item, ty::is_uninit_value_valid_for_ty};
+use clippy_utils::is_path_diagnostic_item;
+use clippy_utils::ty::is_uninit_value_valid_for_ty;
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_lint::LateContext;
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
index 1cef6226a..fabf3fa0c 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
@@ -11,8 +11,7 @@ use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_span::sym;
-use super::UNNECESSARY_FILTER_MAP;
-use super::UNNECESSARY_FIND_MAP;
+use super::{UNNECESSARY_FILTER_MAP, UNNECESSARY_FIND_MAP};
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'tcx>, name: &str) {
if !is_trait_method(cx, expr, sym::Iterator) {
@@ -78,6 +77,16 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc
}
(true, true)
},
+ hir::ExprKind::MethodCall(segment, recv, [arg], _) => {
+ if segment.ident.name == sym!(then_some)
+ && cx.typeck_results().expr_ty(recv).is_bool()
+ && path_to_local_id(arg, arg_id)
+ {
+ (false, true)
+ } else {
+ (true, true)
+ }
+ },
hir::ExprKind::Block(block, _) => block
.expr
.as_ref()
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs
index 8ec15a1c1..6e23754bf 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs
@@ -8,7 +8,8 @@ use rustc_hir as hir;
use rustc_hir::PatKind;
use rustc_lint::LateContext;
use rustc_middle::ty;
-use rustc_span::{source_map::Span, sym};
+use rustc_span::source_map::Span;
+use rustc_span::sym;
use super::UNNECESSARY_FOLD;
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs
index 52a4ff7d1..0c72c13a3 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs
@@ -5,7 +5,8 @@ use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{get_iterator_item_ty, implements_trait};
use clippy_utils::{fn_def_id, get_parent_expr};
use rustc_errors::Applicability;
-use rustc_hir::{def_id::DefId, Expr, ExprKind};
+use rustc_hir::def_id::DefId;
+use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_span::{sym, Symbol};
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs
index 087e1e434..d0c62fb56 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs
@@ -1,4 +1,5 @@
-use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_lang_item};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::is_type_lang_item;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem};
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs
index ea9b894b6..937aac8d2 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs
@@ -1,4 +1,5 @@
-use clippy_utils::{diagnostics::span_lint_and_then, is_res_lang_ctor, last_path_segment, path_res, MaybePath};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::{is_res_lang_ctor, last_path_segment, path_res, MaybePath};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
@@ -32,6 +33,11 @@ pub(super) fn check(
args: &[hir::Expr<'_>],
) {
let init = clippy_utils::expr_or_init(cx, recv);
+ if init.span.from_expansion() {
+ // don't lint if the receiver or binding initializer comes from a macro
+ // (e.g. `let x = option_env!(..); x.unwrap()`)
+ return;
+ }
let (constructor, call_args, ty) = if let hir::ExprKind::Call(call, call_args) = init.kind {
let Some(qpath) = call.qpath_opt() else { return };
@@ -65,6 +71,22 @@ pub(super) fn check(
(expr.span.with_hi(args[0].span.lo()), "panic!(".to_string()),
(expr.span.with_lo(args[0].span.hi()), ")".to_string()),
]),
+ ("Some" | "Ok", "unwrap_unchecked", _) | ("Err", "unwrap_err_unchecked", _) => {
+ let mut suggs = vec![
+ (recv.span.with_hi(call_args[0].span.lo()), String::new()),
+ (expr.span.with_lo(call_args[0].span.hi()), String::new()),
+ ];
+ // try to also remove the unsafe block if present
+ if let hir::Node::Block(block) = cx.tcx.hir().get_parent(expr.hir_id)
+ && let hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::UserProvided) = block.rules
+ {
+ suggs.extend([
+ (block.span.shrink_to_lo().to(expr.span.shrink_to_lo()), String::new()),
+ (expr.span.shrink_to_hi().to(block.span.shrink_to_hi()), String::new())
+ ]);
+ }
+ Some(suggs)
+ },
("None", "unwrap_or_default", _) => {
let ty = cx.typeck_results().expr_ty(expr);
let default_ty_string = if let ty::Adt(def, ..) = ty.kind() {
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs
index 67618f703..e62a65a27 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs
@@ -6,7 +6,7 @@ use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
use rustc_lint::LateContext;
-use rustc_middle::ty::{self, subst::GenericArgKind};
+use rustc_middle::ty::{self, GenericArgKind};
use rustc_span::sym;
use rustc_span::symbol::Ident;
use std::iter;
@@ -118,7 +118,7 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Exp
if_chain! {
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
- if cx.tcx.type_of(impl_id).subst_identity().is_slice();
+ if cx.tcx.type_of(impl_id).instantiate_identity().is_slice();
if let ExprKind::Closure(&Closure { body, .. }) = arg.kind;
if let closure_body = cx.tcx.hir().body(body);
if let &[
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
index 6bd5e9e88..5c5ee2620 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -7,16 +7,20 @@ use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_mid
use clippy_utils::visitors::find_all_ret_expressions;
use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty};
use rustc_errors::Applicability;
-use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, Node};
+use rustc_hir::def_id::DefId;
+use rustc_hir::{BorrowKind, Expr, ExprKind, ItemKind, Node};
use rustc_hir_typeck::{FnCtxt, Inherited};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::LateContext;
use rustc_middle::mir::Mutability;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
-use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
-use rustc_middle::ty::{self, ClauseKind, EarlyBinder, ParamTy, ProjectionPredicate, TraitPredicate, Ty};
+use rustc_middle::ty::{
+ self, ClauseKind, EarlyBinder, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate,
+ TraitPredicate, Ty,
+};
use rustc_span::{sym, Symbol};
-use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
+use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
+use rustc_trait_selection::traits::{Obligation, ObligationCause};
use super::UNNECESSARY_TO_OWNED;
@@ -250,8 +254,8 @@ fn check_other_call_arg<'tcx>(
) -> bool {
if_chain! {
if let Some((maybe_call, maybe_arg)) = skip_addr_of_ancestors(cx, expr);
- if let Some((callee_def_id, _, recv, call_args)) = get_callee_substs_and_args(cx, maybe_call);
- let fn_sig = cx.tcx.fn_sig(callee_def_id).subst_identity().skip_binder();
+ if let Some((callee_def_id, _, recv, call_args)) = get_callee_generic_args_and_args(cx, maybe_call);
+ let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder();
if let Some(i) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == maybe_arg.hir_id);
if let Some(input) = fn_sig.inputs().get(i);
let (input, n_refs) = peel_mid_ty_refs(*input);
@@ -315,26 +319,31 @@ fn skip_addr_of_ancestors<'tcx>(
}
/// Checks whether an expression is a function or method call and, if so, returns its `DefId`,
-/// `Substs`, and arguments.
-fn get_callee_substs_and_args<'tcx>(
+/// `GenericArgs`, and arguments.
+fn get_callee_generic_args_and_args<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
-) -> Option<(DefId, SubstsRef<'tcx>, Option<&'tcx Expr<'tcx>>, &'tcx [Expr<'tcx>])> {
+) -> Option<(
+ DefId,
+ GenericArgsRef<'tcx>,
+ Option<&'tcx Expr<'tcx>>,
+ &'tcx [Expr<'tcx>],
+)> {
if_chain! {
if let ExprKind::Call(callee, args) = expr.kind;
let callee_ty = cx.typeck_results().expr_ty(callee);
if let ty::FnDef(callee_def_id, _) = callee_ty.kind();
then {
- let substs = cx.typeck_results().node_substs(callee.hir_id);
- return Some((*callee_def_id, substs, None, args));
+ let generic_args = cx.typeck_results().node_args(callee.hir_id);
+ return Some((*callee_def_id, generic_args, None, args));
}
}
if_chain! {
if let ExprKind::MethodCall(_, recv, args, _) = expr.kind;
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
then {
- let substs = cx.typeck_results().node_substs(expr.hir_id);
- return Some((method_def_id, substs, Some(recv), args));
+ let generic_args = cx.typeck_results().node_args(expr.hir_id);
+ return Some((method_def_id, generic_args, Some(recv), args));
}
}
None
@@ -388,17 +397,18 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
}
}
Node::Expr(parent_expr) => {
- if let Some((callee_def_id, call_substs, recv, call_args)) = get_callee_substs_and_args(cx, parent_expr)
+ if let Some((callee_def_id, call_generic_args, recv, call_args))
+ = get_callee_generic_args_and_args(cx, parent_expr)
{
- // FIXME: the `subst_identity()` below seems incorrect, since we eventually
+ // FIXME: the `instantiate_identity()` below seems incorrect, since we eventually
// call `tcx.try_subst_and_normalize_erasing_regions` further down
// (i.e., we are explicitly not in the identity context).
- let fn_sig = cx.tcx.fn_sig(callee_def_id).subst_identity().skip_binder();
+ let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder();
if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id)
&& let Some(param_ty) = fn_sig.inputs().get(arg_index)
&& let ty::Param(ParamTy { index: param_index , ..}) = param_ty.kind()
// https://github.com/rust-lang/rust-clippy/issues/9504 and https://github.com/rust-lang/rust-clippy/issues/10021
- && (*param_index as usize) < call_substs.len()
+ && (*param_index as usize) < call_generic_args.len()
{
if fn_sig
.inputs()
@@ -422,8 +432,8 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
}
});
- let new_subst = cx.tcx.mk_substs_from_iter(
- call_substs.iter()
+ let new_subst = cx.tcx.mk_args_from_iter(
+ call_generic_args.iter()
.enumerate()
.map(|(i, t)|
if i == (*param_index as usize) {
@@ -433,7 +443,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
}));
if trait_predicates.any(|predicate| {
- let predicate = EarlyBinder::bind(predicate).subst(cx.tcx, new_subst);
+ let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, new_subst);
let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate);
!cx.tcx.infer_ctxt().build().predicate_must_hold_modulo_regions(&obligation)
}) {
@@ -500,8 +510,8 @@ fn is_to_string_on_string_like<'a>(
return false;
}
- if let Some(substs) = cx.typeck_results().node_substs_opt(call_expr.hir_id)
- && let [generic_arg] = substs.as_slice()
+ if let Some(args) = cx.typeck_results().node_args_opt(call_expr.hir_id)
+ && let [generic_arg] = args.as_slice()
&& let GenericArgKind::Type(ty) = generic_arg.unpack()
&& let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref)
&& let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef)
diff --git a/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs b/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs
new file mode 100644
index 000000000..7bd16b473
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs
@@ -0,0 +1,83 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::ty::{is_never_like, is_type_diagnostic_item};
+use clippy_utils::{is_in_cfg_test, is_in_test_function, is_lint_allowed};
+use rustc_hir::Expr;
+use rustc_lint::{LateContext, Lint};
+use rustc_middle::ty;
+use rustc_span::sym;
+
+use super::{EXPECT_USED, UNWRAP_USED};
+
+#[derive(Clone, Copy, Eq, PartialEq)]
+pub(super) enum Variant {
+ Unwrap,
+ Expect,
+}
+
+impl Variant {
+ fn method_name(self, is_err: bool) -> &'static str {
+ match (self, is_err) {
+ (Variant::Unwrap, true) => "unwrap_err",
+ (Variant::Unwrap, false) => "unwrap",
+ (Variant::Expect, true) => "expect_err",
+ (Variant::Expect, false) => "expect",
+ }
+ }
+
+ fn lint(self) -> &'static Lint {
+ match self {
+ Variant::Unwrap => UNWRAP_USED,
+ Variant::Expect => EXPECT_USED,
+ }
+ }
+}
+
+/// Lint usage of `unwrap` or `unwrap_err` for `Result` and `unwrap()` for `Option` (and their
+/// `expect` counterparts).
+pub(super) fn check(
+ cx: &LateContext<'_>,
+ expr: &Expr<'_>,
+ recv: &Expr<'_>,
+ is_err: bool,
+ allow_unwrap_in_tests: bool,
+ variant: Variant,
+) {
+ let ty = cx.typeck_results().expr_ty(recv).peel_refs();
+
+ let (kind, none_value, none_prefix) = if is_type_diagnostic_item(cx, ty, sym::Option) && !is_err {
+ ("an `Option`", "None", "")
+ } else if is_type_diagnostic_item(cx, ty, sym::Result)
+ && let ty::Adt(_, substs) = ty.kind()
+ && let Some(t_or_e_ty) = substs[usize::from(!is_err)].as_type()
+ {
+ if is_never_like(t_or_e_ty) {
+ return;
+ }
+
+ ("a `Result`", if is_err { "Ok" } else { "Err" }, "an ")
+ } else {
+ return;
+ };
+
+ let method_suffix = if is_err { "_err" } else { "" };
+
+ if allow_unwrap_in_tests && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) {
+ return;
+ }
+
+ span_lint_and_then(
+ cx,
+ variant.lint(),
+ expr.span,
+ &format!("used `{}()` on {kind} value", variant.method_name(is_err)),
+ |diag| {
+ diag.note(format!("if this value is {none_prefix}`{none_value}`, it will panic"));
+
+ if variant == Variant::Unwrap && is_lint_allowed(cx, EXPECT_USED, expr.hir_id) {
+ diag.help(format!(
+ "consider using `expect{method_suffix}()` to provide a better panic message"
+ ));
+ }
+ },
+ );
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/unwrap_or_else_default.rs b/src/tools/clippy/clippy_lints/src/methods/unwrap_or_else_default.rs
deleted file mode 100644
index 045f739e6..000000000
--- a/src/tools/clippy/clippy_lints/src/methods/unwrap_or_else_default.rs
+++ /dev/null
@@ -1,66 +0,0 @@
-//! Lint for `some_result_or_option.unwrap_or_else(Default::default)`
-
-use super::UNWRAP_OR_ELSE_DEFAULT;
-use clippy_utils::{
- diagnostics::span_lint_and_sugg, is_default_equivalent_call, source::snippet_with_applicability,
- ty::is_type_diagnostic_item,
-};
-use rustc_ast::ast::LitKind;
-use rustc_errors::Applicability;
-use rustc_hir as hir;
-use rustc_lint::LateContext;
-use rustc_span::{sym, symbol};
-
-pub(super) fn check<'tcx>(
- cx: &LateContext<'tcx>,
- expr: &'tcx hir::Expr<'_>,
- recv: &'tcx hir::Expr<'_>,
- u_arg: &'tcx hir::Expr<'_>,
-) {
- // something.unwrap_or_else(Default::default)
- // ^^^^^^^^^- recv ^^^^^^^^^^^^^^^^- u_arg
- // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- expr
- let recv_ty = cx.typeck_results().expr_ty(recv);
- let is_option = is_type_diagnostic_item(cx, recv_ty, sym::Option);
- let is_result = is_type_diagnostic_item(cx, recv_ty, sym::Result);
-
- if_chain! {
- if is_option || is_result;
- if closure_body_returns_empty_to_string(cx, u_arg) || is_default_equivalent_call(cx, u_arg);
- then {
- let mut applicability = Applicability::MachineApplicable;
-
- span_lint_and_sugg(
- cx,
- UNWRAP_OR_ELSE_DEFAULT,
- expr.span,
- "use of `.unwrap_or_else(..)` to construct default value",
- "try",
- format!(
- "{}.unwrap_or_default()",
- snippet_with_applicability(cx, recv.span, "..", &mut applicability)
- ),
- applicability,
- );
- }
- }
-}
-
-fn closure_body_returns_empty_to_string(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> bool {
- if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = e.kind {
- let body = cx.tcx.hir().body(body);
-
- if body.params.is_empty()
- && let hir::Expr{ kind, .. } = &body.value
- && let hir::ExprKind::MethodCall(hir::PathSegment {ident, ..}, self_arg, _, _) = kind
- && ident == &symbol::Ident::from_str("to_string")
- && let hir::Expr{ kind, .. } = self_arg
- && let hir::ExprKind::Lit(lit) = kind
- && let LitKind::Str(symbol::kw::Empty, _) = lit.node
- {
- return true;
- }
- }
-
- false
-}
diff --git a/src/tools/clippy/clippy_lints/src/methods/unwrap_used.rs b/src/tools/clippy/clippy_lints/src/methods/unwrap_used.rs
deleted file mode 100644
index 5e4c3daee..000000000
--- a/src/tools/clippy/clippy_lints/src/methods/unwrap_used.rs
+++ /dev/null
@@ -1,53 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_in_cfg_test, is_in_test_function, is_lint_allowed};
-use rustc_hir as hir;
-use rustc_lint::LateContext;
-use rustc_span::sym;
-
-use super::{EXPECT_USED, UNWRAP_USED};
-
-/// lint use of `unwrap()` or `unwrap_err` for `Result` and `unwrap()` for `Option`.
-pub(super) fn check(
- cx: &LateContext<'_>,
- expr: &hir::Expr<'_>,
- recv: &hir::Expr<'_>,
- is_err: bool,
- 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) && !is_err {
- Some((UNWRAP_USED, "an `Option`", "None", ""))
- } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) {
- Some((UNWRAP_USED, "a `Result`", if is_err { "Ok" } else { "Err" }, "an "))
- } else {
- None
- };
-
- let method_suffix = if is_err { "_err" } else { "" };
-
- if allow_unwrap_in_tests && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) {
- return;
- }
-
- if let Some((lint, kind, none_value, none_prefix)) = mess {
- let help = if is_lint_allowed(cx, EXPECT_USED, expr.hir_id) {
- format!(
- "if you don't want to handle the `{none_value}` case gracefully, consider \
- using `expect{method_suffix}()` to provide a better panic message"
- )
- } else {
- format!("if this value is {none_prefix}`{none_value}`, it will panic")
- };
-
- span_lint_and_help(
- cx,
- lint,
- expr.span,
- &format!("used `unwrap{method_suffix}()` on {kind} value"),
- None,
- &help,
- );
- }
-}
diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs
index c1139d84e..b5f810edd 100644
--- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs
@@ -37,7 +37,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str,
USELESS_ASREF,
expr.span,
&format!("this call to `{call_name}` does nothing"),
- "try this",
+ "try",
snippet_with_applicability(cx, recvr.span, "..", &mut applicability).to_string(),
applicability,
);
diff --git a/src/tools/clippy/clippy_lints/src/methods/utils.rs b/src/tools/clippy/clippy_lints/src/methods/utils.rs
index c96d69226..9f1f73e60 100644
--- a/src/tools/clippy/clippy_lints/src/methods/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/utils.rs
@@ -143,7 +143,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> {
if_chain! {
if args.iter().all(|arg| !self.is_binding(arg));
if let Some(method_def_id) = self.cx.typeck_results().type_dependent_def_id(parent.hir_id);
- let method_ty = self.cx.tcx.type_of(method_def_id).subst_identity();
+ let method_ty = self.cx.tcx.type_of(method_def_id).instantiate_identity();
let self_ty = method_ty.fn_sig(self.cx.tcx).input(0).skip_binder();
if matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Not));
then {
diff --git a/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs b/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs
index b0cfc163f..730727186 100644
--- a/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs
@@ -20,7 +20,7 @@ pub(super) fn check<'tcx>(
if_chain! {
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
- if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).subst_identity(), sym::Vec);
+ if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Vec);
if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = count_arg.kind;
if let ExprKind::Lit(Spanned { node: LitKind::Int(..), .. }) = default_arg.kind;
then {
diff --git a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs
index d49bb0ca6..c79a1a7b9 100644
--- a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs
+++ b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs
@@ -1,10 +1,9 @@
-use clippy_utils::{diagnostics::span_lint, is_from_proc_macro};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::is_from_proc_macro;
use rustc_data_structures::fx::FxHashSet;
-use rustc_hir::{
- def::{DefKind, Res},
- intravisit::{walk_item, Visitor},
- GenericParamKind, HirId, Item, ItemKind, ItemLocalId, Node, Pat, PatKind,
-};
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::intravisit::{walk_item, Visitor};
+use rustc_hir::{GenericParamKind, HirId, Item, ItemKind, ItemLocalId, Node, Pat, PatKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -25,7 +24,7 @@ declare_clippy_lint! {
/// ### Example
/// ```rust,ignore
/// for m in movies {
- /// let title = m.t;
+ /// let title = m.t;
/// }
/// ```
/// Use instead:
@@ -130,6 +129,14 @@ impl Visitor<'_> for IdentVisitor<'_, '_> {
return;
}
+ // `struct Array<T, const N: usize>([T; N])`
+ // ^
+ if let Node::GenericParam(generic_param) = node
+ && let GenericParamKind::Const { .. } = generic_param.kind
+ {
+ return;
+ }
+
if is_from_proc_macro(cx, &ident) {
return;
}
diff --git a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
index 4dbb79334..c17f00c42 100644
--- a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
@@ -46,7 +46,9 @@ declare_lint_pass!(MissingAssertMessage => [MISSING_ASSERT_MESSAGE]);
impl<'tcx> LateLintPass<'tcx> for MissingAssertMessage {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
+ let Some(macro_call) = root_macro_call_first_node(cx, expr) else {
+ return;
+ };
let single_argument = match cx.tcx.get_diagnostic_name(macro_call.def_id) {
Some(sym::assert_macro | sym::debug_assert_macro) => true,
Some(
@@ -61,10 +63,14 @@ impl<'tcx> LateLintPass<'tcx> for MissingAssertMessage {
}
let panic_expn = if single_argument {
- let Some((_, panic_expn)) = find_assert_args(cx, expr, macro_call.expn) else { return };
+ let Some((_, panic_expn)) = find_assert_args(cx, expr, macro_call.expn) else {
+ return;
+ };
panic_expn
} else {
- let Some((_, _, panic_expn)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return };
+ let Some((_, _, panic_expn)) = find_assert_eq_args(cx, expr, macro_call.expn) else {
+ return;
+ };
panic_expn
};
diff --git a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs
index 773174679..96d83e114 100644
--- a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs
@@ -1,8 +1,11 @@
-use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet_opt};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_opt;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
-use rustc_hir::{def::Res, def_id::DefId, Item, ItemKind, UseKind};
+use rustc_hir::def::Res;
+use rustc_hir::def_id::DefId;
+use rustc_hir::{Item, ItemKind, UseKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Symbol;
diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs
index 497514fbc..2f63b9b9f 100644
--- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs
@@ -1,23 +1,17 @@
use std::ops::ControlFlow;
-use clippy_utils::{
- diagnostics::span_lint_and_then,
- is_path_lang_item, paths,
- ty::match_type,
- visitors::{for_each_expr, Visitable},
-};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::ty::match_type;
+use clippy_utils::visitors::{for_each_expr, Visitable};
+use clippy_utils::{is_path_lang_item, paths};
use rustc_ast::LitKind;
use rustc_data_structures::fx::FxHashSet;
-use rustc_hir::Block;
+use rustc_hir::def::{DefKind, Res};
use rustc_hir::{
- def::{DefKind, Res},
- Expr, ImplItemKind, LangItem, Node,
+ Block, Expr, ExprKind, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, Node, QPath, TyKind, VariantData,
};
-use rustc_hir::{ExprKind, Impl, ItemKind, QPath, TyKind};
-use rustc_hir::{ImplItem, Item, VariantData};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::Ty;
-use rustc_middle::ty::TypeckResults;
+use rustc_middle::ty::{Ty, TypeckResults};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{sym, Span, Symbol};
diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs
index a41d5a9ce..93f6025c7 100644
--- a/src/tools/clippy/clippy_lints/src/missing_inline.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs
@@ -74,7 +74,6 @@ fn is_executable_or_proc_macro(cx: &LateContext<'_>) -> bool {
use rustc_session::config::CrateType;
cx.tcx
- .sess
.crate_types()
.iter()
.any(|t: &CrateType| matches!(t, CrateType::Executable | CrateType::ProcMacro))
diff --git a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs
index 57ec3a1f1..367cd6bd4 100644
--- a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs
+++ b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs
@@ -239,7 +239,7 @@ fn check_expr<'tcx>(vis: &mut ReadVisitor<'_, 'tcx>, expr: &'tcx Expr<'_>) -> St
| ExprKind::MethodCall(..)
| ExprKind::Call(_, _)
| ExprKind::Assign(..)
- | ExprKind::Index(_, _)
+ | ExprKind::Index(..)
| ExprKind::Repeat(_, _)
| ExprKind::Struct(_, _, _) => {
walk_expr(vis, expr);
diff --git a/src/tools/clippy/clippy_lints/src/module_style.rs b/src/tools/clippy/clippy_lints/src/module_style.rs
index 439cae812..efdc7560e 100644
--- a/src/tools/clippy/clippy_lints/src/module_style.rs
+++ b/src/tools/clippy/clippy_lints/src/module_style.rs
@@ -80,7 +80,9 @@ impl EarlyLintPass for ModStyle {
let files = cx.sess().source_map().files();
- let Some(trim_to_src) = cx.sess().opts.working_dir.local_path() else { return };
+ let Some(trim_to_src) = cx.sess().opts.working_dir.local_path() else {
+ return;
+ };
// `folder_segments` is all unique folder path segments `path/to/foo.rs` gives
// `[path, to]` but not foo
diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
index e6fd65f00..fe35126aa 100644
--- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
+++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
@@ -1,12 +1,8 @@
-use clippy_utils::{
- diagnostics::span_lint_and_then,
- visitors::{for_each_expr_with_closures, Descend, Visitable},
-};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::visitors::{for_each_expr_with_closures, Descend, Visitable};
use core::ops::ControlFlow::Continue;
-use hir::{
- def::{DefKind, Res},
- BlockCheckMode, ExprKind, QPath, UnOp, Unsafety,
-};
+use hir::def::{DefKind, Res};
+use hir::{BlockCheckMode, ExprKind, QPath, UnOp, Unsafety};
use rustc_ast::Mutability;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
diff --git a/src/tools/clippy/clippy_lints/src/mut_key.rs b/src/tools/clippy/clippy_lints/src/mut_key.rs
index 309f67521..5878f8995 100644
--- a/src/tools/clippy/clippy_lints/src/mut_key.rs
+++ b/src/tools/clippy/clippy_lints/src/mut_key.rs
@@ -139,7 +139,7 @@ impl MutableKeyType {
}
fn check_sig(&self, cx: &LateContext<'_>, fn_def_id: LocalDefId, decl: &hir::FnDecl<'_>) {
- let fn_sig = cx.tcx.fn_sig(fn_def_id).subst_identity();
+ let fn_sig = cx.tcx.fn_sig(fn_def_id).instantiate_identity();
for (hir_ty, ty) in iter::zip(decl.inputs, fn_sig.inputs().skip_binder()) {
self.check_ty_(cx, hir_ty.span, *ty);
}
@@ -150,7 +150,7 @@ impl MutableKeyType {
// generics (because the compiler cannot ensure immutability for unknown types).
fn check_ty_<'tcx>(&self, cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
let ty = ty.peel_refs();
- if let Adt(def, substs) = ty.kind() {
+ if let Adt(def, args) = ty.kind() {
let is_keyed_type = [sym::HashMap, sym::BTreeMap, sym::HashSet, sym::BTreeSet]
.iter()
.any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did()));
@@ -158,7 +158,7 @@ impl MutableKeyType {
return;
}
- let subst_ty = substs.type_at(0);
+ let subst_ty = args.type_at(0);
// Determines if a type contains interior mutability which would affect its implementation of
// [`Hash`] or [`Ord`].
if is_interior_mut_ty(cx, subst_ty)
diff --git a/src/tools/clippy/clippy_lints/src/mut_reference.rs b/src/tools/clippy/clippy_lints/src/mut_reference.rs
index e91aac41b..e53e146ec 100644
--- a/src/tools/clippy/clippy_lints/src/mut_reference.rs
+++ b/src/tools/clippy/clippy_lints/src/mut_reference.rs
@@ -37,6 +37,11 @@ declare_lint_pass!(UnnecessaryMutPassed => [UNNECESSARY_MUT_PASSED]);
impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed {
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+ if e.span.from_expansion() {
+ // Issue #11268
+ return;
+ }
+
match e.kind {
ExprKind::Call(fn_expr, arguments) => {
if let ExprKind::Path(ref path) = fn_expr.kind {
@@ -51,8 +56,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed {
},
ExprKind::MethodCall(path, receiver, arguments, _) => {
let def_id = cx.typeck_results().type_dependent_def_id(e.hir_id).unwrap();
- let substs = cx.typeck_results().node_substs(e.hir_id);
- let method_type = cx.tcx.type_of(def_id).subst(cx.tcx, substs);
+ let args = cx.typeck_results().node_args(e.hir_id);
+ let method_type = cx.tcx.type_of(def_id).instantiate(cx.tcx, args);
check_arguments(
cx,
std::iter::once(receiver).chain(arguments.iter()).collect(),
diff --git a/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs b/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs
index d8647a991..dea432fdb 100644
--- a/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs
+++ b/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs
@@ -39,7 +39,9 @@ declare_lint_pass!(DebugAssertWithMutCall => [DEBUG_ASSERT_WITH_MUT_CALL]);
impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall {
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
- let Some(macro_call) = root_macro_call_first_node(cx, e) else { return };
+ let Some(macro_call) = root_macro_call_first_node(cx, e) else {
+ return;
+ };
let macro_name = cx.tcx.item_name(macro_call.def_id);
if !matches!(
macro_name.as_str(),
@@ -47,7 +49,9 @@ impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall {
) {
return;
}
- let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn) else { return };
+ let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn) else {
+ return;
+ };
for arg in [lhs, rhs] {
let mut visitor = MutArgVisitor::new(cx);
visitor.visit_expr(arg);
diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs
index 62af42a39..f6b87b071 100644
--- a/src/tools/clippy/clippy_lints/src/needless_bool.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs
@@ -6,9 +6,9 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
use clippy_utils::{
- get_parent_node, is_else_clause, is_expn_of, peel_blocks, peel_blocks_with_stmt, span_extract_comment,
+ get_parent_node, higher, is_else_clause, is_expn_of, peel_blocks, peel_blocks_with_stmt, span_extract_comment,
+ SpanlessEq,
};
-use clippy_utils::{higher, SpanlessEq};
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Node, UnOp};
@@ -106,7 +106,7 @@ declare_clippy_lint! {
/// # let mut skip: bool;
/// skip = !must_keep(x, y);
/// ```
- #[clippy::version = "1.69.0"]
+ #[clippy::version = "1.71.0"]
pub NEEDLESS_BOOL_ASSIGN,
complexity,
"setting the same boolean variable in both branches of an if-statement"
@@ -119,7 +119,7 @@ fn condition_needs_parentheses(e: &Expr<'_>) -> bool {
| ExprKind::Call(i, _)
| ExprKind::Cast(i, _)
| ExprKind::Type(i, _)
- | ExprKind::Index(i, _) = inner.kind
+ | ExprKind::Index(i, _, _) = inner.kind
{
if matches!(
i.kind,
diff --git a/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs b/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs
index 498e1408e..11bf9e9ca 100644
--- a/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs
@@ -52,7 +52,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef {
}
// Only lint immutable refs, because `&mut ref T` may be useful.
- let PatKind::Ref(pat, Mutability::Not) = ref_pat.kind else { return };
+ let PatKind::Ref(pat, Mutability::Not) = ref_pat.kind else {
+ return;
+ };
match pat.kind {
// Check sub_pat got a `ref` keyword (excluding `ref mut`).
diff --git a/src/tools/clippy/clippy_lints/src/needless_else.rs b/src/tools/clippy/clippy_lints/src/needless_else.rs
index 4ff1bf7ff..03bab86c6 100644
--- a/src/tools/clippy/clippy_lints/src/needless_else.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_else.rs
@@ -1,5 +1,5 @@
-use clippy_utils::source::snippet_opt;
-use clippy_utils::{diagnostics::span_lint_and_sugg, source::trim_span};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::{snippet_opt, trim_span};
use rustc_ast::ast::{Expr, ExprKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
@@ -51,7 +51,7 @@ impl EarlyLintPass for NeedlessElse {
cx,
NEEDLESS_ELSE,
span,
- "this else branch is empty",
+ "this `else` branch is empty",
"you can remove it",
String::new(),
Applicability::MachineApplicable,
diff --git a/src/tools/clippy/clippy_lints/src/needless_for_each.rs b/src/tools/clippy/clippy_lints/src/needless_for_each.rs
index c3b633fd6..98bf122fa 100644
--- a/src/tools/clippy/clippy_lints/src/needless_for_each.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_for_each.rs
@@ -1,11 +1,10 @@
use rustc_errors::Applicability;
-use rustc_hir::{
- intravisit::{walk_expr, Visitor},
- Closure, Expr, ExprKind, Stmt, StmtKind,
-};
+use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::{Closure, Expr, ExprKind, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::{source_map::Span, sym, Symbol};
+use rustc_span::source_map::Span;
+use rustc_span::{sym, Symbol};
use if_chain::if_chain;
@@ -50,7 +49,7 @@ declare_lint_pass!(NeedlessForEach => [NEEDLESS_FOR_EACH]);
impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
let (StmtKind::Expr(expr) | StmtKind::Semi(expr)) = stmt.kind else {
- return
+ return;
};
if_chain! {
diff --git a/src/tools/clippy/clippy_lints/src/needless_if.rs b/src/tools/clippy/clippy_lints/src/needless_if.rs
index ad5c3e1dc..1ed7ea6b3 100644
--- a/src/tools/clippy/clippy_lints/src/needless_if.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_if.rs
@@ -1,4 +1,7 @@
-use clippy_utils::{diagnostics::span_lint_and_sugg, higher::If, is_from_proc_macro, source::snippet_opt};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher::If;
+use clippy_utils::is_from_proc_macro;
+use clippy_utils::source::snippet_opt;
use rustc_errors::Applicability;
use rustc_hir::{ExprKind, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs
index 5a9387b34..948454d13 100644
--- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs
@@ -86,7 +86,9 @@ fn contains_let(cond: &Expr<'_>) -> bool {
}
fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
- let StmtKind::Local(local) = stmt.kind else { return false };
+ let StmtKind::Local(local) = stmt.kind else {
+ return false;
+ };
!local.pat.walk_short(|pat| {
if let PatKind::Binding(.., None) = pat.kind {
!needs_ordered_drop(cx, cx.typeck_results().pat_ty(pat))
diff --git a/src/tools/clippy/clippy_lints/src/needless_parens_on_range_literals.rs b/src/tools/clippy/clippy_lints/src/needless_parens_on_range_literals.rs
index da1b9d999..d17a383e8 100644
--- a/src/tools/clippy/clippy_lints/src/needless_parens_on_range_literals.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_parens_on_range_literals.rs
@@ -1,8 +1,6 @@
-use clippy_utils::{
- diagnostics::span_lint_and_then,
- higher,
- source::{snippet, snippet_with_applicability},
-};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::higher;
+use clippy_utils::source::{snippet, snippet_with_applicability};
use rustc_ast::ast;
use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs
new file mode 100644
index 000000000..7f0a5964a
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs
@@ -0,0 +1,441 @@
+use super::needless_pass_by_value::requires_exact_signature;
+use clippy_utils::diagnostics::span_lint_hir_and_then;
+use clippy_utils::source::snippet;
+use clippy_utils::{get_parent_node, inherits_cfg, is_from_proc_macro, is_self};
+use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
+use rustc_errors::Applicability;
+use rustc_hir::intravisit::{walk_qpath, FnKind, Visitor};
+use rustc_hir::{
+ Body, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, PatKind, QPath,
+};
+use rustc_hir_typeck::expr_use_visitor as euv;
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::hir::map::associated_body;
+use rustc_middle::hir::nested_filter::OnlyBodies;
+use rustc_middle::mir::FakeReadCause;
+use rustc_middle::ty::{self, Ty, TyCtxt, UpvarId, UpvarPath};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::def_id::LocalDefId;
+use rustc_span::symbol::kw;
+use rustc_span::Span;
+use rustc_target::spec::abi::Abi;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Check if a `&mut` function argument is actually used mutably.
+ ///
+ /// Be careful if the function is publicly reexported as it would break compatibility with
+ /// users of this function.
+ ///
+ /// ### Why is this bad?
+ /// Less `mut` means less fights with the borrow checker. It can also lead to more
+ /// opportunities for parallelization.
+ ///
+ /// ### Example
+ /// ```rust
+ /// fn foo(y: &mut i32) -> i32 {
+ /// 12 + *y
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// fn foo(y: &i32) -> i32 {
+ /// 12 + *y
+ /// }
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub NEEDLESS_PASS_BY_REF_MUT,
+ nursery,
+ "using a `&mut` argument when it's not mutated"
+}
+
+#[derive(Clone)]
+pub struct NeedlessPassByRefMut<'tcx> {
+ avoid_breaking_exported_api: bool,
+ used_fn_def_ids: FxHashSet<LocalDefId>,
+ fn_def_ids_to_maybe_unused_mut: FxIndexMap<LocalDefId, Vec<rustc_hir::Ty<'tcx>>>,
+}
+
+impl NeedlessPassByRefMut<'_> {
+ pub fn new(avoid_breaking_exported_api: bool) -> Self {
+ Self {
+ avoid_breaking_exported_api,
+ used_fn_def_ids: FxHashSet::default(),
+ fn_def_ids_to_maybe_unused_mut: FxIndexMap::default(),
+ }
+ }
+}
+
+impl_lint_pass!(NeedlessPassByRefMut<'_> => [NEEDLESS_PASS_BY_REF_MUT]);
+
+fn should_skip<'tcx>(
+ cx: &LateContext<'tcx>,
+ input: rustc_hir::Ty<'tcx>,
+ ty: Ty<'_>,
+ arg: &rustc_hir::Param<'_>,
+) -> bool {
+ // We check if this a `&mut`. `ref_mutability` returns `None` if it's not a reference.
+ if !matches!(ty.ref_mutability(), Some(Mutability::Mut)) {
+ return true;
+ }
+
+ if is_self(arg) {
+ return true;
+ }
+
+ if let PatKind::Binding(.., name, _) = arg.pat.kind {
+ // If it's a potentially unused variable, we don't check it.
+ if name.name == kw::Underscore || name.as_str().starts_with('_') {
+ return true;
+ }
+ }
+
+ // All spans generated from a proc-macro invocation are the same...
+ is_from_proc_macro(cx, &input)
+}
+
+impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
+ fn check_fn(
+ &mut self,
+ cx: &LateContext<'tcx>,
+ kind: FnKind<'tcx>,
+ decl: &'tcx FnDecl<'tcx>,
+ body: &'tcx Body<'_>,
+ span: Span,
+ fn_def_id: LocalDefId,
+ ) {
+ if span.from_expansion() {
+ return;
+ }
+
+ let hir_id = cx.tcx.hir().local_def_id_to_hir_id(fn_def_id);
+ let is_async = match kind {
+ FnKind::ItemFn(.., header) => {
+ let attrs = cx.tcx.hir().attrs(hir_id);
+ if header.abi != Abi::Rust || requires_exact_signature(attrs) {
+ return;
+ }
+ header.is_async()
+ },
+ FnKind::Method(.., sig) => sig.header.is_async(),
+ FnKind::Closure => return,
+ };
+
+ // Exclude non-inherent impls
+ if let Some(Node::Item(item)) = cx.tcx.hir().find_parent(hir_id) {
+ if matches!(
+ item.kind,
+ ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..)
+ ) {
+ return;
+ }
+ }
+
+ let fn_sig = cx.tcx.fn_sig(fn_def_id).instantiate_identity();
+ let fn_sig = cx.tcx.liberate_late_bound_regions(fn_def_id.to_def_id(), fn_sig);
+
+ // If there are no `&mut` argument, no need to go any further.
+ let mut it = decl
+ .inputs
+ .iter()
+ .zip(fn_sig.inputs())
+ .zip(body.params)
+ .filter(|((&input, &ty), arg)| !should_skip(cx, input, ty, arg))
+ .peekable();
+ if it.peek().is_none() {
+ return;
+ }
+ // Collect variables mutably used and spans which will need dereferencings from the
+ // function body.
+ let MutablyUsedVariablesCtxt { mutably_used_vars, .. } = {
+ let mut ctx = MutablyUsedVariablesCtxt {
+ mutably_used_vars: HirIdSet::default(),
+ prev_bind: None,
+ prev_move_to_closure: HirIdSet::default(),
+ aliases: HirIdMap::default(),
+ async_closures: FxHashSet::default(),
+ tcx: cx.tcx,
+ };
+ let infcx = cx.tcx.infer_ctxt().build();
+ euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body);
+ if is_async {
+ let mut checked_closures = FxHashSet::default();
+ while !ctx.async_closures.is_empty() {
+ let closures = ctx.async_closures.clone();
+ ctx.async_closures.clear();
+ let hir = cx.tcx.hir();
+ for closure in closures {
+ if !checked_closures.insert(closure) {
+ continue;
+ }
+ ctx.prev_bind = None;
+ ctx.prev_move_to_closure.clear();
+ if let Some(body) = hir
+ .find_by_def_id(closure)
+ .and_then(associated_body)
+ .map(|(_, body_id)| hir.body(body_id))
+ {
+ euv::ExprUseVisitor::new(&mut ctx, &infcx, closure, cx.param_env, cx.typeck_results())
+ .consume_body(body);
+ }
+ }
+ }
+ }
+ ctx
+ };
+ for ((&input, &_), arg) in it {
+ // Only take `&mut` arguments.
+ if let PatKind::Binding(_, canonical_id, ..) = arg.pat.kind
+ && !mutably_used_vars.contains(&canonical_id)
+ {
+ self.fn_def_ids_to_maybe_unused_mut.entry(fn_def_id).or_default().push(input);
+ }
+ }
+ }
+
+ fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
+ cx.tcx.hir().visit_all_item_likes_in_crate(&mut FnNeedsMutVisitor {
+ cx,
+ used_fn_def_ids: &mut self.used_fn_def_ids,
+ });
+
+ for (fn_def_id, unused) in self
+ .fn_def_ids_to_maybe_unused_mut
+ .iter()
+ .filter(|(def_id, _)| !self.used_fn_def_ids.contains(def_id))
+ {
+ let show_semver_warning =
+ self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(*fn_def_id);
+
+ let mut is_cfged = None;
+ for input in unused {
+ // If the argument is never used mutably, we emit the warning.
+ let sp = input.span;
+ if let rustc_hir::TyKind::Ref(_, inner_ty) = input.kind {
+ let is_cfged = is_cfged.get_or_insert_with(|| inherits_cfg(cx.tcx, *fn_def_id));
+ span_lint_hir_and_then(
+ cx,
+ NEEDLESS_PASS_BY_REF_MUT,
+ cx.tcx.hir().local_def_id_to_hir_id(*fn_def_id),
+ sp,
+ "this argument is a mutable reference, but not used mutably",
+ |diag| {
+ diag.span_suggestion(
+ sp,
+ "consider changing to".to_string(),
+ format!("&{}", snippet(cx, cx.tcx.hir().span(inner_ty.ty.hir_id), "_"),),
+ Applicability::Unspecified,
+ );
+ if show_semver_warning {
+ diag.warn("changing this function will impact semver compatibility");
+ }
+ if *is_cfged {
+ diag.note("this is cfg-gated and may require further changes");
+ }
+ },
+ );
+ }
+ }
+ }
+ }
+}
+
+struct MutablyUsedVariablesCtxt<'tcx> {
+ mutably_used_vars: HirIdSet,
+ prev_bind: Option<HirId>,
+ prev_move_to_closure: HirIdSet,
+ aliases: HirIdMap<HirId>,
+ async_closures: FxHashSet<LocalDefId>,
+ tcx: TyCtxt<'tcx>,
+}
+
+impl<'tcx> MutablyUsedVariablesCtxt<'tcx> {
+ fn add_mutably_used_var(&mut self, mut used_id: HirId) {
+ while let Some(id) = self.aliases.get(&used_id) {
+ self.mutably_used_vars.insert(used_id);
+ used_id = *id;
+ }
+ self.mutably_used_vars.insert(used_id);
+ }
+
+ fn would_be_alias_cycle(&self, alias: HirId, mut target: HirId) -> bool {
+ while let Some(id) = self.aliases.get(&target) {
+ if *id == alias {
+ return true;
+ }
+ target = *id;
+ }
+ false
+ }
+
+ fn add_alias(&mut self, alias: HirId, target: HirId) {
+ // This is to prevent alias loop.
+ if alias == target || self.would_be_alias_cycle(alias, target) {
+ return;
+ }
+ self.aliases.insert(alias, target);
+ }
+}
+
+impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> {
+ fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId) {
+ if let euv::Place {
+ base:
+ euv::PlaceBase::Local(vid)
+ | euv::PlaceBase::Upvar(UpvarId {
+ var_path: UpvarPath { hir_id: vid },
+ ..
+ }),
+ base_ty,
+ ..
+ } = &cmt.place
+ {
+ if let Some(bind_id) = self.prev_bind.take() {
+ if bind_id != *vid {
+ self.add_alias(bind_id, *vid);
+ }
+ } else if !self.prev_move_to_closure.contains(vid)
+ && matches!(base_ty.ref_mutability(), Some(Mutability::Mut))
+ {
+ self.add_mutably_used_var(*vid);
+ }
+ self.prev_bind = None;
+ self.prev_move_to_closure.remove(vid);
+ }
+ }
+
+ fn borrow(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId, borrow: ty::BorrowKind) {
+ self.prev_bind = None;
+ if let euv::Place {
+ base: euv::PlaceBase::Local(vid),
+ base_ty,
+ ..
+ } = &cmt.place
+ {
+ // If this is a mutable borrow, it was obviously used mutably so we add it. However
+ // for `UniqueImmBorrow`, it's interesting because if you do: `array[0] = value` inside
+ // a closure, it'll return this variant whereas if you have just an index access, it'll
+ // return `ImmBorrow`. So if there is "Unique" and it's a mutable reference, we add it
+ // to the mutably used variables set.
+ if borrow == ty::BorrowKind::MutBorrow
+ || (borrow == ty::BorrowKind::UniqueImmBorrow && base_ty.ref_mutability() == Some(Mutability::Mut))
+ {
+ self.add_mutably_used_var(*vid);
+ }
+ } else if borrow == ty::ImmBorrow {
+ // If there is an `async block`, it'll contain a call to a closure which we need to
+ // go into to ensure all "mutate" checks are found.
+ if let Node::Expr(Expr {
+ kind:
+ ExprKind::Call(
+ _,
+ [
+ Expr {
+ kind: ExprKind::Closure(Closure { def_id, .. }),
+ ..
+ },
+ ],
+ ),
+ ..
+ }) = self.tcx.hir().get(cmt.hir_id)
+ {
+ self.async_closures.insert(*def_id);
+ }
+ }
+ }
+
+ fn mutate(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId) {
+ self.prev_bind = None;
+ if let euv::Place {
+ projections,
+ base:
+ euv::PlaceBase::Local(vid)
+ | euv::PlaceBase::Upvar(UpvarId {
+ var_path: UpvarPath { hir_id: vid },
+ ..
+ }),
+ ..
+ } = &cmt.place
+ {
+ if !projections.is_empty() {
+ self.add_mutably_used_var(*vid);
+ }
+ }
+ }
+
+ fn copy(&mut self, _cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId) {
+ self.prev_bind = None;
+ }
+
+ fn fake_read(
+ &mut self,
+ cmt: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>,
+ cause: FakeReadCause,
+ _id: HirId,
+ ) {
+ if let euv::Place {
+ base:
+ euv::PlaceBase::Upvar(UpvarId {
+ var_path: UpvarPath { hir_id: vid },
+ ..
+ }),
+ ..
+ } = &cmt.place
+ {
+ if let FakeReadCause::ForLet(Some(inner)) = cause {
+ // Seems like we are inside an async function. We need to store the closure `DefId`
+ // to go through it afterwards.
+ self.async_closures.insert(inner);
+ self.add_alias(cmt.hir_id, *vid);
+ self.prev_move_to_closure.insert(*vid);
+ self.prev_bind = None;
+ }
+ }
+ }
+
+ fn bind(&mut self, _cmt: &euv::PlaceWithHirId<'tcx>, id: HirId) {
+ self.prev_bind = Some(id);
+ }
+}
+
+/// A final pass to check for paths referencing this function that require the argument to be
+/// `&mut`, basically if the function is ever used as a `fn`-like argument.
+struct FnNeedsMutVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ used_fn_def_ids: &'a mut FxHashSet<LocalDefId>,
+}
+
+impl<'tcx> Visitor<'tcx> for FnNeedsMutVisitor<'_, 'tcx> {
+ type NestedFilter = OnlyBodies;
+
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.cx.tcx.hir()
+ }
+
+ fn visit_qpath(&mut self, qpath: &'tcx QPath<'tcx>, hir_id: HirId, _: Span) {
+ walk_qpath(self, qpath, hir_id);
+
+ let Self { cx, used_fn_def_ids } = self;
+
+ // #11182; do not lint if mutability is required elsewhere
+ if let Node::Expr(expr) = cx.tcx.hir().get(hir_id)
+ && let Some(parent) = get_parent_node(cx.tcx, expr.hir_id)
+ && let ty::FnDef(def_id, _) = cx.tcx.typeck(cx.tcx.hir().enclosing_body_owner(hir_id)).expr_ty(expr).kind()
+ && let Some(def_id) = def_id.as_local()
+ {
+ if let Node::Expr(e) = parent
+ && let ExprKind::Call(call, _) = e.kind
+ && call.hir_id == expr.hir_id
+ {
+ return;
+ }
+
+ // We don't need to check each argument individually as you cannot coerce a function
+ // taking `&mut` -> `&`, for some reason, so if we've gotten this far we know it's
+ // passed as a `fn`-like argument (or is unified) and should ignore every "unused"
+ // argument entirely
+ used_fn_def_ids.insert(def_id);
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
index f11d5773d..5ee26966f 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
use clippy_utils::ptr::get_spans;
use clippy_utils::source::{snippet, snippet_opt};
use clippy_utils::ty::{
- implements_trait, implements_trait_with_env, is_copy, is_type_diagnostic_item, is_type_lang_item,
+ implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item,
};
use clippy_utils::{get_trait_def_id, is_self, paths};
use if_chain::if_chain;
@@ -10,14 +10,14 @@ use rustc_ast::ast::Attribute;
use rustc_errors::{Applicability, Diagnostic};
use rustc_hir::intravisit::FnKind;
use rustc_hir::{
- BindingAnnotation, Body, FnDecl, GenericArg, HirId, Impl, ItemKind, Mutability, Node, PatKind, QPath, TyKind,
+ BindingAnnotation, Body, FnDecl, GenericArg, HirId, HirIdSet, Impl, ItemKind, LangItem, Mutability, Node, PatKind,
+ QPath, TyKind,
};
-use rustc_hir::{HirIdSet, LangItem};
use rustc_hir_typeck::expr_use_visitor as euv;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::mir::FakeReadCause;
-use rustc_middle::ty::{self, TypeVisitableExt, Ty};
+use rustc_middle::ty::{self, Ty, TypeVisitableExt};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::def_id::LocalDefId;
use rustc_span::symbol::kw;
@@ -140,7 +140,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
ctx
};
- let fn_sig = cx.tcx.fn_sig(fn_def_id).subst_identity();
+ let fn_sig = cx.tcx.fn_sig(fn_def_id).instantiate_identity();
let fn_sig = cx.tcx.liberate_late_bound_regions(fn_def_id.to_def_id(), fn_sig);
for (idx, ((input, &ty), arg)) in decl.inputs.iter().zip(fn_sig.inputs()).zip(body.params).enumerate() {
@@ -168,9 +168,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
(
preds.iter().any(|t| cx.tcx.is_diagnostic_item(sym::Borrow, t.def_id())),
!preds.is_empty() && {
- let ty_empty_region = Ty::new_imm_ref(cx.tcx,cx.tcx.lifetimes.re_erased, ty);
+ let ty_empty_region = Ty::new_imm_ref(cx.tcx, cx.tcx.lifetimes.re_erased, ty);
preds.iter().all(|t| {
- let ty_params = t.trait_ref.substs.iter().skip(1).collect::<Vec<_>>();
+ let ty_params = t.trait_ref.args.iter().skip(1).collect::<Vec<_>>();
implements_trait(cx, ty_empty_region, t.def_id(), &ty_params)
})
},
@@ -182,7 +182,13 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
if !ty.is_mutable_ptr();
if !is_copy(cx, ty);
if ty.is_sized(cx.tcx, cx.param_env);
- if !allowed_traits.iter().any(|&t| implements_trait_with_env(cx.tcx, cx.param_env, ty, t, [None]));
+ if !allowed_traits.iter().any(|&t| implements_trait_with_env_from_iter(
+ cx.tcx,
+ cx.param_env,
+ ty,
+ t,
+ [Option::<ty::GenericArg<'tcx>>::None],
+ ));
if !implements_borrow_trait;
if !all_borrowable_trait;
@@ -289,7 +295,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
}
/// Functions marked with these attributes must have the exact signature.
-fn requires_exact_signature(attrs: &[Attribute]) -> bool {
+pub(crate) fn requires_exact_signature(attrs: &[Attribute]) -> bool {
attrs.iter().any(|attr| {
[sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
.iter()
diff --git a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs
index e2a7ba02a..7b0f7eaf1 100644
--- a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs
@@ -122,7 +122,7 @@ fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
} else {
return;
};
- if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &arg.kind;
+ if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar(_)) = &arg.kind;
if let ExprKind::Call(called, [inner_expr]) = &inner_expr_with_q.kind;
if let ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, ..)) = &called.kind;
if expr.span.ctxt() == inner_expr.span.ctxt();
diff --git a/src/tools/clippy/clippy_lints/src/new_without_default.rs b/src/tools/clippy/clippy_lints/src/new_without_default.rs
index 653b1a8a0..cf7cd671d 100644
--- a/src/tools/clippy/clippy_lints/src/new_without_default.rs
+++ b/src/tools/clippy/clippy_lints/src/new_without_default.rs
@@ -98,14 +98,14 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
if name == sym::new;
if cx.effective_visibilities.is_reachable(impl_item.owner_id.def_id);
let self_def_id = cx.tcx.hir().get_parent_item(id.into());
- let self_ty = cx.tcx.type_of(self_def_id).subst_identity();
+ let self_ty = cx.tcx.type_of(self_def_id).instantiate_identity();
if self_ty == return_ty(cx, id);
if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default);
then {
if self.impling_types.is_none() {
let mut impls = HirIdSet::default();
cx.tcx.for_each_impl(default_trait_id, |d| {
- let ty = cx.tcx.type_of(d).subst_identity();
+ let ty = cx.tcx.type_of(d).instantiate_identity();
if let Some(ty_def) = ty.ty_adt_def() {
if let Some(local_def_id) = ty_def.did().as_local() {
impls.insert(cx.tcx.hir().local_def_id_to_hir_id(local_def_id));
@@ -119,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
// generics
if_chain! {
if let Some(ref impling_types) = self.impling_types;
- let self_def = cx.tcx.type_of(self_def_id).subst_identity();
+ let self_def = cx.tcx.type_of(self_def_id).instantiate_identity();
if let Some(self_def) = self_def.ty_adt_def();
if let Some(self_local_did) = self_def.did().as_local();
let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did);
diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs
index a4c7da7e4..5f2a324b0 100644
--- a/src/tools/clippy/clippy_lints/src/no_effect.rs
+++ b/src/tools/clippy/clippy_lints/src/no_effect.rs
@@ -1,8 +1,7 @@
use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
-use clippy_utils::peel_blocks;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::has_drop;
-use clippy_utils::{get_parent_node, is_lint_allowed};
+use clippy_utils::{get_parent_node, is_lint_allowed, peel_blocks};
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{
@@ -161,7 +160,7 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
match peel_blocks(expr).kind {
ExprKind::Lit(..) | ExprKind::Closure { .. } => true,
ExprKind::Path(..) => !has_drop(cx, cx.typeck_results().expr_ty(expr)),
- ExprKind::Index(a, b) | ExprKind::Binary(_, a, b) => has_no_effect(cx, a) && has_no_effect(cx, b),
+ ExprKind::Index(a, b, _) | ExprKind::Binary(_, a, b) => has_no_effect(cx, a) && has_no_effect(cx, b),
ExprKind::Array(v) | ExprKind::Tup(v) => v.iter().all(|val| has_no_effect(cx, val)),
ExprKind::Repeat(inner, _)
| ExprKind::Cast(inner, _)
@@ -264,7 +263,7 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec
return None;
}
match expr.kind {
- ExprKind::Index(a, b) => Some(vec![a, b]),
+ ExprKind::Index(a, b, _) => Some(vec![a, b]),
ExprKind::Binary(ref binop, a, b) if binop.node != BinOpKind::And && binop.node != BinOpKind::Or => {
Some(vec![a, b])
},
diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
index 75f1e9527..243192385 100644
--- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs
+++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
@@ -15,14 +15,12 @@ use rustc_hir::{
};
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::{LateContext, LateLintPass, Lint};
-use rustc_middle::mir::interpret::ErrorHandled;
+use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId};
use rustc_middle::ty::adjustment::Adjust;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{sym, InnerSpan, Span};
use rustc_target::abi::VariantIdx;
-use rustc_middle::mir::interpret::EvalToValTreeResult;
-use rustc_middle::mir::interpret::GlobalId;
// FIXME: this is a correctness problem but there's no suitable
// warn-by-default category.
@@ -154,24 +152,32 @@ fn is_value_unfrozen_raw<'tcx>(
// As of 2022-09-08 miri doesn't track which union field is active so there's no safe way to check the
// contained value.
ty::Adt(def, ..) if def.is_union() => false,
- ty::Array(ty, _) => {
- val.unwrap_branch().iter().any(|field| inner(cx, *field, ty))
- },
+ ty::Array(ty, _) => val.unwrap_branch().iter().any(|field| inner(cx, *field, ty)),
ty::Adt(def, _) if def.is_union() => false,
- ty::Adt(def, substs) if def.is_enum() => {
+ ty::Adt(def, args) if def.is_enum() => {
let (&variant_index, fields) = val.unwrap_branch().split_first().unwrap();
- let variant_index =
- VariantIdx::from_u32(variant_index.unwrap_leaf().try_to_u32().ok().unwrap());
- fields.iter().copied().zip(
- def.variants()[variant_index]
- .fields
- .iter()
- .map(|field| field.ty(cx.tcx, substs))).any(|(field, ty)| inner(cx, field, ty))
- }
- ty::Adt(def, substs) => {
- val.unwrap_branch().iter().zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, substs))).any(|(field, ty)| inner(cx, *field, ty))
- }
- ty::Tuple(tys) => val.unwrap_branch().iter().zip(tys).any(|(field, ty)| inner(cx, *field, ty)),
+ let variant_index = VariantIdx::from_u32(variant_index.unwrap_leaf().try_to_u32().ok().unwrap());
+ fields
+ .iter()
+ .copied()
+ .zip(
+ def.variants()[variant_index]
+ .fields
+ .iter()
+ .map(|field| field.ty(cx.tcx, args)),
+ )
+ .any(|(field, ty)| inner(cx, field, ty))
+ },
+ ty::Adt(def, args) => val
+ .unwrap_branch()
+ .iter()
+ .zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, args)))
+ .any(|(field, ty)| inner(cx, *field, ty)),
+ ty::Tuple(tys) => val
+ .unwrap_branch()
+ .iter()
+ .zip(tys)
+ .any(|(field, ty)| inner(cx, *field, ty)),
_ => false,
}
}
@@ -206,33 +212,38 @@ fn is_value_unfrozen_raw<'tcx>(
fn is_value_unfrozen_poly<'tcx>(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool {
let def_id = body_id.hir_id.owner.to_def_id();
- let substs = ty::InternalSubsts::identity_for_item(cx.tcx, def_id);
- let instance = ty::Instance::new(def_id, substs);
- let cid = rustc_middle::mir::interpret::GlobalId { instance, promoted: None };
+ let args = ty::GenericArgs::identity_for_item(cx.tcx, def_id);
+ let instance = ty::Instance::new(def_id, args);
+ let cid = rustc_middle::mir::interpret::GlobalId {
+ instance,
+ promoted: None,
+ };
let param_env = cx.tcx.param_env(def_id).with_reveal_all_normalized(cx.tcx);
let result = cx.tcx.const_eval_global_id_for_typeck(param_env, cid, None);
is_value_unfrozen_raw(cx, result, ty)
}
fn is_value_unfrozen_expr<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool {
- let substs = cx.typeck_results().node_substs(hir_id);
+ let args = cx.typeck_results().node_args(hir_id);
- let result = const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, substs), None);
+ let result = const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, args), None);
is_value_unfrozen_raw(cx, result, ty)
}
-
pub fn const_eval_resolve<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
ct: ty::UnevaluatedConst<'tcx>,
span: Option<Span>,
) -> EvalToValTreeResult<'tcx> {
- match ty::Instance::resolve(tcx, param_env, ct.def, ct.substs) {
+ match ty::Instance::resolve(tcx, param_env, ct.def, ct.args) {
Ok(Some(instance)) => {
- let cid = GlobalId { instance, promoted: None };
+ let cid = GlobalId {
+ instance,
+ promoted: None,
+ };
tcx.const_eval_global_id_for_typeck(param_env, cid, span)
- }
+ },
Ok(None) => Err(ErrorHandled::TooGeneric),
Err(err) => Err(ErrorHandled::Reported(err.into())),
}
@@ -286,7 +297,7 @@ declare_lint_pass!(NonCopyConst => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTER
impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) {
- if let ItemKind::Const(hir_ty, body_id) = it.kind {
+ if let ItemKind::Const(hir_ty, _generics, body_id) = it.kind {
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
if !ignored_macro(cx, it) && is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, body_id, ty) {
lint(cx, Source::Item { item: it.span });
@@ -347,7 +358,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
// and, in that case, the definition is *not* generic.
cx.tcx.normalize_erasing_regions(
cx.tcx.param_env(of_trait_def_id),
- cx.tcx.type_of(of_assoc_item).subst_identity(),
+ cx.tcx.type_of(of_assoc_item).instantiate_identity(),
),
))
.is_err();
@@ -392,7 +403,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
// Make sure it is a const item.
let Res::Def(DefKind::Const | DefKind::AssocConst, item_def_id) = cx.qpath_res(qpath, expr.hir_id) else {
- return
+ return;
};
// Climb up to resolve any field access and explicit referencing.
@@ -427,7 +438,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
dereferenced_expr = parent_expr;
},
- ExprKind::Index(e, _) if ptr::eq(&**e, cur_expr) => {
+ ExprKind::Index(e, _, _) if ptr::eq(&**e, cur_expr) => {
// `e[i]` => desugared to `*Index::index(&e, i)`,
// meaning `e` must be referenced.
// no need to go further up since a method call is involved now.
diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
index 9f6917c14..d562047cb 100644
--- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
+++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
@@ -91,7 +91,7 @@ struct ExistingName {
struct SimilarNamesLocalVisitor<'a, 'tcx> {
names: Vec<ExistingName>,
cx: &'a EarlyContext<'tcx>,
- lint: &'a NonExpressiveNames,
+ lint: NonExpressiveNames,
/// A stack of scopes containing the single-character bindings in each scope.
single_char_names: Vec<Vec<Ident>>,
@@ -365,7 +365,7 @@ impl EarlyLintPass for NonExpressiveNames {
..
}) = item.kind
{
- do_check(self, cx, &item.attrs, &sig.decl, blk);
+ do_check(*self, cx, &item.attrs, &sig.decl, blk);
}
}
@@ -380,12 +380,12 @@ impl EarlyLintPass for NonExpressiveNames {
..
}) = item.kind
{
- do_check(self, cx, &item.attrs, &sig.decl, blk);
+ do_check(*self, cx, &item.attrs, &sig.decl, blk);
}
}
}
-fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) {
+fn do_check(lint: NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) {
if !attrs.iter().any(|attr| attr.has_name(sym::test)) {
let mut visitor = SimilarNamesLocalVisitor {
names: Vec::new(),
diff --git a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs
index 7eaa7db78..c5e777c20 100644
--- a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs
+++ b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs
@@ -7,7 +7,7 @@ use rustc_hir::def_id::DefId;
use rustc_hir::{FieldDef, Item, ItemKind, Node};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
+use rustc_middle::ty::{self, GenericArgKind, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::sym;
@@ -90,8 +90,8 @@ impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy {
if send_trait == trait_id;
if hir_impl.polarity == ImplPolarity::Positive;
if let Some(ty_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id);
- if let self_ty = ty_trait_ref.subst_identity().self_ty();
- if let ty::Adt(adt_def, impl_trait_substs) = self_ty.kind();
+ if let self_ty = ty_trait_ref.instantiate_identity().self_ty();
+ if let ty::Adt(adt_def, impl_trait_args) = self_ty.kind();
then {
let mut non_send_fields = Vec::new();
@@ -104,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy {
.as_local()
.map(|local_def_id| hir_map.local_def_id_to_hir_id(local_def_id));
if !is_lint_allowed(cx, NON_SEND_FIELDS_IN_SEND_TY, field_hir_id);
- if let field_ty = field.ty(cx.tcx, impl_trait_substs);
+ if let field_ty = field.ty(cx.tcx, impl_trait_args);
if !ty_allowed_in_send(cx, field_ty, send_trait);
if let Node::Field(field_def) = hir_map.get(field_hir_id);
then {
@@ -206,10 +206,10 @@ fn ty_allowed_with_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'t
.iter()
.all(|ty| ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait)),
ty::Array(ty, _) | ty::Slice(ty) => ty_allowed_with_raw_pointer_heuristic(cx, *ty, send_trait),
- ty::Adt(_, substs) => {
+ ty::Adt(_, args) => {
if contains_pointer_like(cx, ty) {
// descends only if ADT contains any raw pointers
- substs.iter().all(|generic_arg| match generic_arg.unpack() {
+ args.iter().all(|generic_arg| match generic_arg.unpack() {
GenericArgKind::Type(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait),
// Lifetimes and const generics are not solid part of ADT and ignored
GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => true,
@@ -224,7 +224,7 @@ fn ty_allowed_with_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'t
}
}
-/// Checks if the type contains any pointer-like types in substs (including nested ones)
+/// Checks if the type contains any pointer-like types in args (including nested ones)
fn contains_pointer_like<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> bool {
for ty_node in target_ty.walk() {
if let GenericArgKind::Type(inner_ty) = ty_node.unpack() {
diff --git a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
index 2d79a5c90..bd194b935 100644
--- a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
+++ b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
@@ -1,7 +1,5 @@
-use std::{
- fmt,
- hash::{Hash, Hasher},
-};
+use std::fmt;
+use std::hash::{Hash, Hasher};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt;
diff --git a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs
index 8b77a5c99..3dc652f9d 100644
--- a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs
+++ b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs
@@ -7,8 +7,7 @@ use rustc_hir::def_id::DefId;
use rustc_hir::hir_id::HirIdMap;
use rustc_hir::{Body, Expr, ExprKind, HirId, ImplItem, ImplItemKind, Node, PatKind, TraitItem, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::subst::{EarlyBinder, GenericArgKind, SubstsRef};
-use rustc_middle::ty::{self, ConstKind};
+use rustc_middle::ty::{self, ConstKind, EarlyBinder, GenericArgKind, GenericArgsRef};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::{kw, Ident};
use rustc_span::Span;
@@ -90,7 +89,7 @@ impl_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]);
enum FnKind {
Fn,
TraitFn,
- // This is a hack. Ideally we would store a `SubstsRef<'tcx>` type here, but a lint pass must be `'static`.
+ // This is a hack. Ideally we would store a `GenericArgsRef<'tcx>` type here, but a lint pass must be `'static`.
// Substitutions are, however, interned. This allows us to store the pointer as a `usize` when comparing for
// equality.
ImplTraitFn(usize),
@@ -244,12 +243,12 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
})) => {
#[allow(trivial_casts)]
if let Some(Node::Item(item)) = get_parent_node(cx.tcx, owner_id.into())
- && let Some(trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::subst_identity)
+ && let Some(trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::instantiate_identity)
&& let Some(trait_item_id) = cx.tcx.associated_item(owner_id).trait_item_def_id
{
(
trait_item_id,
- FnKind::ImplTraitFn(cx.tcx.erase_regions(trait_ref.substs) as *const _ as usize),
+ FnKind::ImplTraitFn(cx.tcx.erase_regions(trait_ref.args) as *const _ as usize),
usize::from(sig.decl.implicit_self.has_implicit_self()),
)
} else {
@@ -289,7 +288,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
ExprKind::Call(callee, args)
if path_def_id(cx, callee).map_or(false, |id| {
id == param.fn_id
- && has_matching_substs(param.fn_kind, typeck.node_substs(callee.hir_id))
+ && has_matching_args(param.fn_kind, typeck.node_args(callee.hir_id))
}) =>
{
if let Some(idx) = args.iter().position(|arg| arg.hir_id == child_id) {
@@ -300,7 +299,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
ExprKind::MethodCall(_, receiver, args, _)
if typeck.type_dependent_def_id(parent.hir_id).map_or(false, |id| {
id == param.fn_id
- && has_matching_substs(param.fn_kind, typeck.node_substs(parent.hir_id))
+ && has_matching_args(param.fn_kind, typeck.node_args(parent.hir_id))
}) =>
{
if let Some(idx) = iter::once(receiver).chain(args).position(|arg| arg.hir_id == child_id) {
@@ -381,15 +380,15 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
}
}
-fn has_matching_substs(kind: FnKind, substs: SubstsRef<'_>) -> bool {
+fn has_matching_args(kind: FnKind, args: GenericArgsRef<'_>) -> bool {
match kind {
FnKind::Fn => true,
- FnKind::TraitFn => substs.iter().enumerate().all(|(idx, subst)| match subst.unpack() {
+ FnKind::TraitFn => args.iter().enumerate().all(|(idx, subst)| match subst.unpack() {
GenericArgKind::Lifetime(_) => true,
GenericArgKind::Type(ty) => matches!(*ty.kind(), ty::Param(ty) if ty.index as usize == idx),
GenericArgKind::Const(c) => matches!(c.kind(), ConstKind::Param(c) if c.index as usize == idx),
}),
#[allow(trivial_casts)]
- FnKind::ImplTraitFn(expected_substs) => substs as *const _ as usize == expected_substs,
+ FnKind::ImplTraitFn(expected_args) => args as *const _ as usize == expected_args,
}
}
diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
index 5c240276b..f9108145c 100644
--- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
@@ -1,26 +1,20 @@
use super::ARITHMETIC_SIDE_EFFECTS;
-use clippy_utils::is_from_proc_macro;
-use clippy_utils::{
- consts::{constant, constant_simple, Constant},
- diagnostics::span_lint,
- is_lint_allowed, peel_hir_expr_refs, peel_hir_expr_unary,
-};
-use rustc_ast as ast;
+use clippy_utils::consts::{constant, constant_simple, Constant};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{expr_or_init, is_from_proc_macro, is_lint_allowed, peel_hir_expr_refs, peel_hir_expr_unary};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::Ty;
use rustc_session::impl_lint_pass;
-use rustc_span::{
- source_map::{Span, Spanned},
- Symbol,
-};
+use rustc_span::source_map::{Span, Spanned};
+use rustc_span::Symbol;
+use {rustc_ast as ast, rustc_hir as hir};
const HARD_CODED_ALLOWED_BINARY: &[[&str; 2]] = &[
["f32", "f32"],
["f64", "f64"],
- ["std::num::Saturating", "std::num::Saturating"],
- ["std::num::Wrapping", "std::num::Wrapping"],
+ ["std::num::Saturating", "*"],
+ ["std::num::Wrapping", "*"],
["std::string::String", "str"],
];
const HARD_CODED_ALLOWED_UNARY: &[&str] = &["f32", "f64", "std::num::Saturating", "std::num::Wrapping"];
@@ -144,8 +138,10 @@ impl ArithmeticSideEffects {
) {
return;
};
- let (actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs);
- let (actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs);
+ let (mut actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs);
+ let (mut actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs);
+ actual_lhs = expr_or_init(cx, actual_lhs);
+ actual_rhs = expr_or_init(cx, actual_rhs);
let lhs_ty = cx.typeck_results().expr_ty(actual_lhs).peel_refs();
let rhs_ty = cx.typeck_results().expr_ty(actual_rhs).peel_refs();
if self.has_allowed_binary(lhs_ty, rhs_ty) {
@@ -200,7 +196,9 @@ impl ArithmeticSideEffects {
ps: &hir::PathSegment<'tcx>,
receiver: &hir::Expr<'tcx>,
) {
- let Some(arg) = args.first() else { return; };
+ let Some(arg) = args.first() else {
+ return;
+ };
if constant_simple(cx, cx.typeck_results(), receiver).is_some() {
return;
}
@@ -225,7 +223,9 @@ impl ArithmeticSideEffects {
un_expr: &hir::Expr<'tcx>,
un_op: hir::UnOp,
) {
- let hir::UnOp::Neg = un_op else { return; };
+ let hir::UnOp::Neg = un_op else {
+ return;
+ };
if constant(cx, cx.typeck_results(), un_expr).is_some() {
return;
}
diff --git a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs
index 9bbf385fb..c4572a09d 100644
--- a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs
@@ -1,9 +1,8 @@
-use clippy_utils::binop_traits;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::implements_trait;
use clippy_utils::visitors::for_each_expr;
-use clippy_utils::{eq_expr_value, trait_ref_of_method};
+use clippy_utils::{binop_traits, eq_expr_value, trait_ref_of_method};
use core::ops::ControlFlow;
use if_chain::if_chain;
use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs b/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs
index 1fddf0f50..c146f3ae9 100644
--- a/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs
@@ -40,9 +40,9 @@ fn check_compare(cx: &LateContext<'_>, bit_op: &Expr<'_>, cmp_op: BinOpKind, cmp
if op.node != BinOpKind::BitAnd && op.node != BinOpKind::BitOr {
return;
}
- fetch_int_literal(cx, right)
- .or_else(|| fetch_int_literal(cx, left))
- .map_or((), |mask| check_bit_mask(cx, op.node, cmp_op, mask, cmp_value, span));
+ if let Some(mask) = fetch_int_literal(cx, right).or_else(|| fetch_int_literal(cx, left)) {
+ check_bit_mask(cx, op.node, cmp_op, mask, cmp_value, span);
+ }
}
}
diff --git a/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs b/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs
new file mode 100644
index 000000000..abe8df195
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs
@@ -0,0 +1,207 @@
+#![allow(clippy::match_same_arms)]
+
+use std::cmp::Ordering;
+
+use clippy_utils::consts::{constant, Constant};
+use if_chain::if_chain;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::layout::HasTyCtxt;
+use rustc_middle::ty::{Ty, TypeckResults};
+use rustc_span::source_map::{Span, Spanned};
+
+use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::source::snippet;
+use clippy_utils::SpanlessEq;
+
+use super::{IMPOSSIBLE_COMPARISONS, REDUNDANT_COMPARISONS};
+
+// Extract a comparison between a const and non-const
+// Flip yoda conditionals, turnings expressions like `42 < x` into `x > 42`
+fn comparison_to_const<'tcx>(
+ cx: &LateContext<'tcx>,
+ typeck: &TypeckResults<'tcx>,
+ expr: &'tcx Expr<'tcx>,
+) -> Option<(CmpOp, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Constant<'tcx>, Ty<'tcx>)> {
+ if_chain! {
+ if let ExprKind::Binary(operator, left, right) = expr.kind;
+ if let Ok(cmp_op) = CmpOp::try_from(operator.node);
+ then {
+ match (constant(cx, typeck, left), constant(cx, typeck, right)) {
+ (Some(_), Some(_)) => None,
+ (_, Some(con)) => Some((cmp_op, left, right, con, typeck.expr_ty(right))),
+ (Some(con), _) => Some((cmp_op.reverse(), right, left, con, typeck.expr_ty(left))),
+ _ => None,
+ }
+ } else {
+ None
+ }
+ }
+}
+
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ and_op: Spanned<BinOpKind>,
+ left_cond: &'tcx Expr<'tcx>,
+ right_cond: &'tcx Expr<'tcx>,
+ span: Span,
+) {
+ if_chain! {
+ // Ensure that the binary operator is &&
+ if and_op.node == BinOpKind::And;
+
+ // Check that both operands to '&&' are themselves a binary operation
+ // The `comparison_to_const` step also checks this, so this step is just an optimization
+ if let ExprKind::Binary(_, _, _) = left_cond.kind;
+ if let ExprKind::Binary(_, _, _) = right_cond.kind;
+
+ let typeck = cx.typeck_results();
+
+ // Check that both operands to '&&' compare a non-literal to a literal
+ if let Some((left_cmp_op, left_expr, left_const_expr, left_const, left_type)) =
+ comparison_to_const(cx, typeck, left_cond);
+ if let Some((right_cmp_op, right_expr, right_const_expr, right_const, right_type)) =
+ comparison_to_const(cx, typeck, right_cond);
+
+ if left_type == right_type;
+
+ // Check that the same expression is compared in both comparisons
+ if SpanlessEq::new(cx).eq_expr(left_expr, right_expr);
+
+ if !left_expr.can_have_side_effects();
+
+ // Compare the two constant expressions
+ if let Some(ordering) = Constant::partial_cmp(cx.tcx(), left_type, &left_const, &right_const);
+
+ // Rule out the `x >= 42 && x <= 42` corner case immediately
+ // Mostly to simplify the implementation, but it is also covered by `clippy::double_comparisons`
+ if !matches!(
+ (&left_cmp_op, &right_cmp_op, ordering),
+ (CmpOp::Le | CmpOp::Ge, CmpOp::Le | CmpOp::Ge, Ordering::Equal)
+ );
+
+ then {
+ if left_cmp_op.direction() == right_cmp_op.direction() {
+ let lhs_str = snippet(cx, left_cond.span, "<lhs>");
+ let rhs_str = snippet(cx, right_cond.span, "<rhs>");
+ // We already know that either side of `&&` has no effect,
+ // but emit a different error message depending on which side it is
+ if left_side_is_useless(left_cmp_op, ordering) {
+ span_lint_and_note(
+ cx,
+ REDUNDANT_COMPARISONS,
+ span,
+ "left-hand side of `&&` operator has no effect",
+ Some(left_cond.span.until(right_cond.span)),
+ &format!("`if `{rhs_str}` evaluates to true, {lhs_str}` will always evaluate to true as well"),
+ );
+ } else {
+ span_lint_and_note(
+ cx,
+ REDUNDANT_COMPARISONS,
+ span,
+ "right-hand side of `&&` operator has no effect",
+ Some(and_op.span.to(right_cond.span)),
+ &format!("`if `{lhs_str}` evaluates to true, {rhs_str}` will always evaluate to true as well"),
+ );
+ }
+ // We could autofix this error but choose not to,
+ // because code triggering this lint probably not behaving correctly in the first place
+ }
+ else if !comparison_is_possible(left_cmp_op.direction(), ordering) {
+ let expr_str = snippet(cx, left_expr.span, "..");
+ let lhs_str = snippet(cx, left_const_expr.span, "<lhs>");
+ let rhs_str = snippet(cx, right_const_expr.span, "<rhs>");
+ let note = match ordering {
+ Ordering::Less => format!("since `{lhs_str}` < `{rhs_str}`, the expression evaluates to false for any value of `{expr_str}`"),
+ Ordering::Equal => format!("`{expr_str}` cannot simultaneously be greater than and less than `{lhs_str}`"),
+ Ordering::Greater => format!("since `{lhs_str}` > `{rhs_str}`, the expression evaluates to false for any value of `{expr_str}`"),
+ };
+ span_lint_and_note(
+ cx,
+ IMPOSSIBLE_COMPARISONS,
+ span,
+ "boolean expression will never evaluate to 'true'",
+ None,
+ &note,
+ );
+ };
+ }
+ }
+}
+
+fn left_side_is_useless(left_cmp_op: CmpOp, ordering: Ordering) -> bool {
+ // Special-case for equal constants with an inclusive comparison
+ if ordering == Ordering::Equal {
+ match left_cmp_op {
+ CmpOp::Lt | CmpOp::Gt => false,
+ CmpOp::Le | CmpOp::Ge => true,
+ }
+ } else {
+ match (left_cmp_op.direction(), ordering) {
+ (CmpOpDirection::Lesser, Ordering::Less) => false,
+ (CmpOpDirection::Lesser, Ordering::Equal) => false,
+ (CmpOpDirection::Lesser, Ordering::Greater) => true,
+ (CmpOpDirection::Greater, Ordering::Less) => true,
+ (CmpOpDirection::Greater, Ordering::Equal) => false,
+ (CmpOpDirection::Greater, Ordering::Greater) => false,
+ }
+ }
+}
+
+fn comparison_is_possible(left_cmp_direction: CmpOpDirection, ordering: Ordering) -> bool {
+ match (left_cmp_direction, ordering) {
+ (CmpOpDirection::Lesser, Ordering::Less | Ordering::Equal) => false,
+ (CmpOpDirection::Lesser, Ordering::Greater) => true,
+ (CmpOpDirection::Greater, Ordering::Greater | Ordering::Equal) => false,
+ (CmpOpDirection::Greater, Ordering::Less) => true,
+ }
+}
+
+#[derive(PartialEq, Eq, Clone, Copy)]
+enum CmpOpDirection {
+ Lesser,
+ Greater,
+}
+
+#[derive(Clone, Copy)]
+enum CmpOp {
+ Lt,
+ Le,
+ Ge,
+ Gt,
+}
+
+impl CmpOp {
+ fn reverse(self) -> Self {
+ match self {
+ CmpOp::Lt => CmpOp::Gt,
+ CmpOp::Le => CmpOp::Ge,
+ CmpOp::Ge => CmpOp::Le,
+ CmpOp::Gt => CmpOp::Lt,
+ }
+ }
+
+ fn direction(self) -> CmpOpDirection {
+ match self {
+ CmpOp::Lt => CmpOpDirection::Lesser,
+ CmpOp::Le => CmpOpDirection::Lesser,
+ CmpOp::Ge => CmpOpDirection::Greater,
+ CmpOp::Gt => CmpOpDirection::Greater,
+ }
+ }
+}
+
+impl TryFrom<BinOpKind> for CmpOp {
+ type Error = ();
+
+ fn try_from(bin_op: BinOpKind) -> Result<Self, Self::Error> {
+ match bin_op {
+ BinOpKind::Lt => Ok(CmpOp::Lt),
+ BinOpKind::Le => Ok(CmpOp::Le),
+ BinOpKind::Ge => Ok(CmpOp::Ge),
+ BinOpKind::Gt => Ok(CmpOp::Gt),
+ _ => Err(()),
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/operators/eq_op.rs b/src/tools/clippy/clippy_lints/src/operators/eq_op.rs
index 78965b7d6..88d566318 100644
--- a/src/tools/clippy/clippy_lints/src/operators/eq_op.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/eq_op.rs
@@ -1,6 +1,7 @@
+use clippy_utils::ast_utils::is_useless_with_eq_exprs;
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace};
-use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, is_in_test_function};
+use clippy_utils::{eq_expr_value, is_in_test_function};
use rustc_hir::{BinOpKind, Expr};
use rustc_lint::LateContext;
diff --git a/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs b/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs
index 015f6c14e..5eabb349e 100644
--- a/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs
@@ -1,7 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::eq_expr_value;
use clippy_utils::source::snippet_opt;
-use clippy_utils::sugg;
+use clippy_utils::{eq_expr_value, sugg};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs
index 2cf15adda..4635e1164 100644
--- a/src/tools/clippy/clippy_lints/src/operators/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs
@@ -2,6 +2,7 @@ mod absurd_extreme_comparisons;
mod assign_op_pattern;
mod bit_mask;
mod cmp_owned;
+mod const_comparisons;
mod double_comparison;
mod duration_subsec;
mod eq_op;
@@ -300,6 +301,45 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
+ /// Checks for double comparisons that can never succeed
+ ///
+ /// ### Why is this bad?
+ /// The whole expression can be replaced by `false`,
+ /// which is probably not the programmer's intention
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let status_code = 200;
+ /// if status_code <= 400 && status_code > 500 {}
+ /// ```
+ #[clippy::version = "1.71.0"]
+ pub IMPOSSIBLE_COMPARISONS,
+ correctness,
+ "double comparisons that will never evaluate to `true`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for ineffective double comparisons against constants.
+ ///
+ /// ### Why is this bad?
+ /// Only one of the comparisons has any effect on the result, the programmer
+ /// probably intended to flip one of the comparison operators, or compare a
+ /// different value entirely.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let status_code = 200;
+ /// if status_code <= 400 && status_code < 500 {}
+ /// ```
+ #[clippy::version = "1.71.0"]
+ pub REDUNDANT_COMPARISONS,
+ correctness,
+ "double comparisons where one of them can be removed"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
/// Checks for calculation of subsecond microseconds or milliseconds
/// from other `Duration` methods.
///
@@ -742,6 +782,8 @@ impl_lint_pass!(Operators => [
INEFFECTIVE_BIT_MASK,
VERBOSE_BIT_MASK,
DOUBLE_COMPARISONS,
+ IMPOSSIBLE_COMPARISONS,
+ REDUNDANT_COMPARISONS,
DURATION_SUBSEC,
EQ_OP,
OP_REF,
@@ -786,6 +828,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators {
bit_mask::check(cx, e, op.node, lhs, rhs);
verbose_bit_mask::check(cx, e, op.node, lhs, rhs, self.verbose_bit_mask_threshold);
double_comparison::check(cx, op.node, lhs, rhs, e.span);
+ const_comparisons::check(cx, op, lhs, rhs, e.span);
duration_subsec::check(cx, e, op.node, lhs, rhs);
float_equality_without_abs::check(cx, e, op.node, lhs, rhs);
integer_division::check(cx, e, op.node, lhs, rhs);
diff --git a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs
index d7917e86a..932dd470f 100644
--- a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs
@@ -4,7 +4,9 @@ use clippy_utils::source::snippet;
use clippy_utils::ty::{implements_trait, is_copy};
use if_chain::if_chain;
use rustc_errors::Applicability;
-use rustc_hir::{def::Res, def_id::DefId, BinOpKind, BorrowKind, Expr, ExprKind, GenericArg, ItemKind, QPath, TyKind};
+use rustc_hir::def::Res;
+use rustc_hir::def_id::DefId;
+use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, GenericArg, ItemKind, QPath, TyKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
diff --git a/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs b/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs
index 377bddeaa..9c7f7e1cd 100644
--- a/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/option_env_unwrap.rs
@@ -1,10 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::is_direct_expn_of;
-use if_chain::if_chain;
use rustc_ast::ast::{Expr, ExprKind, MethodCall};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
+use rustc_span::{sym, Span};
declare_clippy_lint! {
/// ### What it does
@@ -36,21 +35,27 @@ declare_lint_pass!(OptionEnvUnwrap => [OPTION_ENV_UNWRAP]);
impl EarlyLintPass for OptionEnvUnwrap {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
- if_chain! {
- if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind;
- if matches!(seg.ident.name, sym::expect | sym::unwrap);
- if let ExprKind::Call(caller, _) = &receiver.kind;
- if is_direct_expn_of(caller.span, "option_env").is_some();
- then {
- span_lint_and_help(
- cx,
- OPTION_ENV_UNWRAP,
- expr.span,
- "this will panic at run-time if the environment variable doesn't exist at compile-time",
- None,
- "consider using the `env!` macro instead"
- );
- }
+ fn lint(cx: &EarlyContext<'_>, span: Span) {
+ span_lint_and_help(
+ cx,
+ OPTION_ENV_UNWRAP,
+ span,
+ "this will panic at run-time if the environment variable doesn't exist at compile-time",
+ None,
+ "consider using the `env!` macro instead",
+ );
}
+
+ if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind &&
+ matches!(seg.ident.name, sym::expect | sym::unwrap) {
+ if let ExprKind::Call(caller, _) = &receiver.kind &&
+ // If it exists, it will be ::core::option::Option::Some("<env var>").unwrap() (A method call in the HIR)
+ is_direct_expn_of(caller.span, "option_env").is_some() {
+ lint(cx, expr.span);
+ } else if let ExprKind::Path(_, caller) = &receiver.kind && // If it doesn't exist, it will be ::core::option::Option::None::<&'static str>.unwrap() (A path in the HIR)
+ is_direct_expn_of(caller.span, "option_env").is_some() {
+ lint(cx, expr.span);
+ }
+ }
}
}
diff --git a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
index abdccc47f..a7a7f4fd8 100644
--- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
+++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
@@ -6,10 +6,9 @@ use clippy_utils::{
};
use if_chain::if_chain;
use rustc_errors::Applicability;
+use rustc_hir::def::Res;
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
-use rustc_hir::{
- def::Res, Arm, BindingAnnotation, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, UnOp,
-};
+use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::SyntaxContext;
@@ -156,7 +155,7 @@ fn try_get_option_occurrence<'tcx>(
});
if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(local_id), .. })) = e.kind {
match some_captures.get(local_id)
- .or_else(|| (method_sugg == "map_or_else").then_some(()).and_then(|_| none_captures.get(local_id)))
+ .or_else(|| (method_sugg == "map_or_else").then_some(()).and_then(|()| none_captures.get(local_id)))
{
Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None,
Some(CaptureKind::Ref(Mutability::Not)) if as_mut => return None,
diff --git a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
index 849cd03dd..a049427d8 100644
--- a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
@@ -13,7 +13,7 @@ use rustc_span::{sym, Span};
declare_clippy_lint! {
/// ### What it does
- /// Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result.
+ /// Checks for usage of `panic!` or assertions in a function of type result.
///
/// ### Why is this bad?
/// For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence panicking macros should be avoided.
@@ -37,7 +37,7 @@ declare_clippy_lint! {
#[clippy::version = "1.48.0"]
pub PANIC_IN_RESULT_FN,
restriction,
- "functions of type `Result<..>` that contain `panic!()`, `todo!()`, `unreachable()`, `unimplemented()` or assertion"
+ "functions of type `Result<..>` that contain `panic!()` or assertion"
}
declare_lint_pass!(PanicInResultFn => [PANIC_IN_RESULT_FN]);
@@ -70,7 +70,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir
};
if matches!(
cx.tcx.item_name(macro_call.def_id).as_str(),
- "unimplemented" | "unreachable" | "panic" | "todo" | "assert" | "assert_eq" | "assert_ne"
+ "panic" | "assert" | "assert_eq" | "assert_ne"
) {
panics.push(macro_call.span);
ControlFlow::Continue(Descend::No)
@@ -83,10 +83,10 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir
cx,
PANIC_IN_RESULT_FN,
impl_span,
- "used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`",
+ "used `panic!()` or assertion in a function that returns `Result`",
move |diag| {
diag.help(
- "`unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing",
+ "`panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing",
);
diag.span_note(panics, "return Err() instead of panicking");
},
diff --git a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
index 2f3007658..a72aefe91 100644
--- a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
+++ b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
@@ -76,7 +76,9 @@ declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANI
impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
+ let Some(macro_call) = root_macro_call_first_node(cx, expr) else {
+ return;
+ };
if is_panic(cx, macro_call.def_id) {
if cx.tcx.hir().is_inside_const_context(expr.hir_id) {
return;
diff --git a/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs b/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs
index 456ded3fc..d9f5d1642 100644
--- a/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs
+++ b/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs
@@ -1,7 +1,6 @@
-use clippy_utils::{
- diagnostics::span_lint_and_sugg, is_res_lang_ctor, path_res, peel_hir_expr_refs, peel_ref_operators, sugg,
- ty::is_type_diagnostic_item,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{is_res_lang_ctor, path_res, peel_hir_expr_refs, peel_ref_operators, sugg};
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem};
use rustc_lint::{LateContext, LateLintPass};
diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
index eab725de1..41513647f 100644
--- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
+++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
@@ -1,5 +1,4 @@
-use std::cmp;
-use std::iter;
+use std::{cmp, iter};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
@@ -143,7 +142,7 @@ impl<'tcx> PassByRefOrValue {
return;
}
- let fn_sig = cx.tcx.fn_sig(def_id).subst_identity();
+ let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
let fn_body = cx.enclosing_body.map(|id| cx.tcx.hir().body(id));
// Gather all the lifetimes found in the output type which may affect whether
diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs
index 32213718b..8009b00b4 100644
--- a/src/tools/clippy/clippy_lints/src/ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr.rs
@@ -26,10 +26,10 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
use rustc_span::sym;
use rustc_span::symbol::Symbol;
+use rustc_target::spec::abi::Abi;
use rustc_trait_selection::infer::InferCtxtExt as _;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
-use std::fmt;
-use std::iter;
+use std::{fmt, iter};
declare_clippy_lint! {
/// ### What it does
@@ -164,9 +164,19 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
}
check_mut_from_ref(cx, sig, None);
+
+ if !matches!(sig.header.abi, Abi::Rust) {
+ // Ignore `extern` functions with non-Rust calling conventions
+ return;
+ }
+
for arg in check_fn_args(
cx,
- cx.tcx.fn_sig(item.owner_id).subst_identity().skip_binder().inputs(),
+ cx.tcx
+ .fn_sig(item.owner_id)
+ .instantiate_identity()
+ .skip_binder()
+ .inputs(),
sig.decl.inputs,
&sig.decl.output,
&[],
@@ -219,8 +229,14 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
};
check_mut_from_ref(cx, sig, Some(body));
+
+ if !matches!(sig.header.abi, Abi::Rust) {
+ // Ignore `extern` functions with non-Rust calling conventions
+ return;
+ }
+
let decl = sig.decl;
- let sig = cx.tcx.fn_sig(item_id).subst_identity().skip_binder();
+ let sig = cx.tcx.fn_sig(item_id).instantiate_identity().skip_binder();
let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, &decl.output, body.params)
.filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not)
.collect();
@@ -389,11 +405,12 @@ impl<'tcx> DerefTy<'tcx> {
fn ty(&self, cx: &LateContext<'tcx>) -> Ty<'tcx> {
match *self {
Self::Str => cx.tcx.types.str_,
- Self::Path => Ty::new_adt(cx.tcx,
+ Self::Path => Ty::new_adt(
+ cx.tcx,
cx.tcx.adt_def(cx.tcx.get_diagnostic_item(sym::Path).unwrap()),
List::empty(),
),
- Self::Slice(_, ty) => Ty::new_slice(cx.tcx,ty),
+ Self::Slice(_, ty) => Ty::new_slice(cx.tcx, ty),
}
}
@@ -423,7 +440,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
.enumerate()
.filter_map(move |(i, (ty, hir_ty))| {
if let ty::Ref(_, ty, mutability) = *ty.kind()
- && let ty::Adt(adt, substs) = *ty.kind()
+ && let ty::Adt(adt, args) = *ty.kind()
&& let TyKind::Ref(lt, ref ty) = hir_ty.kind
&& let TyKind::Path(QPath::Resolved(None, path)) = ty.ty.kind
// Check that the name as typed matches the actual name of the type.
@@ -443,7 +460,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
} else {
None
}),
- substs.type_at(0),
+ args.type_at(0),
),
),
_ if Some(adt.did()) == cx.tcx.lang_items().string() => (
@@ -496,7 +513,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
}
let ty_name =
- snippet_opt(cx, ty.span()).unwrap_or_else(|| substs.type_at(1).to_string());
+ snippet_opt(cx, ty.span()).unwrap_or_else(|| args.type_at(1).to_string());
span_lint_hir_and_then(
cx,
@@ -659,7 +676,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args:
return;
};
- match *self.cx.tcx.fn_sig(id).subst_identity().skip_binder().inputs()[i]
+ match *self.cx.tcx.fn_sig(id).instantiate_identity().skip_binder().inputs()[i]
.peel_refs()
.kind()
{
@@ -678,7 +695,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args:
}
},
// Indexing is fine for currently supported types.
- ExprKind::Index(e, _) if e.hir_id == child_id => (),
+ ExprKind::Index(e, _, _) if e.hir_id == child_id => (),
_ => set_skip_flag(),
},
_ => set_skip_flag(),
@@ -725,7 +742,7 @@ fn matches_preds<'tcx>(
let infcx = cx.tcx.infer_ctxt().build();
preds.iter().all(|&p| match cx.tcx.erase_late_bound_regions(p) {
ExistentialPredicate::Trait(p) => infcx
- .type_implements_trait(p.def_id, [ty.into()].into_iter().chain(p.substs.iter()), cx.param_env)
+ .type_implements_trait(p.def_id, [ty.into()].into_iter().chain(p.args.iter()), cx.param_env)
.must_apply_modulo_regions(),
ExistentialPredicate::Projection(p) => infcx.predicate_must_hold_modulo_regions(&Obligation::new(
cx.tcx,
diff --git a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs
index 47b8891e1..20e032d4b 100644
--- a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs
@@ -50,12 +50,12 @@ impl<'tcx> LateLintPass<'tcx> for PtrOffsetWithCast {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
// Check if the expressions is a ptr.offset or ptr.wrapping_offset method call
let Some((receiver_expr, arg_expr, method)) = expr_as_ptr_offset_call(cx, expr) else {
- return
+ return;
};
// Check if the argument to the method call is a cast from usize
let Some(cast_lhs_expr) = expr_as_cast_from_usize(cx, arg_expr) else {
- return
+ return;
};
let msg = format!("use of `{method}` with a `usize` casted to an `isize`");
diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs
index e3d940ad2..734ca2914 100644
--- a/src/tools/clippy/clippy_lints/src/question_mark.rs
+++ b/src/tools/clippy/clippy_lints/src/question_mark.rs
@@ -1,21 +1,26 @@
+use crate::manual_let_else::{MatchLintBehaviour, MANUAL_LET_ELSE};
+use crate::question_mark_used::QUESTION_MARK_USED;
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::Msrv;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{
- eq_expr_value, get_parent_node, in_constant, is_else_clause, is_res_lang_ctor, path_to_local, path_to_local_id,
- peel_blocks, peel_blocks_with_stmt,
+ eq_expr_value, get_parent_node, higher, in_constant, is_else_clause, is_lint_allowed, is_path_lang_item,
+ is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks,
+ peel_blocks_with_stmt,
};
-use clippy_utils::{higher, is_path_lang_item};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk};
-use rustc_hir::{BindingAnnotation, ByRef, Expr, ExprKind, Node, PatKind, PathSegment, QPath};
+use rustc_hir::{
+ BindingAnnotation, Block, ByRef, Expr, ExprKind, Local, Node, PatKind, PathSegment, QPath, Stmt, StmtKind,
+};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::Ty;
-use rustc_session::declare_tool_lint;
-use rustc_session::impl_lint_pass;
-use rustc_span::{sym, symbol::Symbol};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::sym;
+use rustc_span::symbol::Symbol;
declare_clippy_lint! {
/// ### What it does
@@ -42,8 +47,9 @@ declare_clippy_lint! {
"checks for expressions that could be replaced by the question mark operator"
}
-#[derive(Default)]
pub struct QuestionMark {
+ pub(crate) msrv: Msrv,
+ pub(crate) matches_behaviour: MatchLintBehaviour,
/// Keeps track of how many try blocks we are in at any point during linting.
/// This allows us to answer the question "are we inside of a try block"
/// very quickly, without having to walk up the parent chain, by simply checking
@@ -51,7 +57,19 @@ pub struct QuestionMark {
/// As for why we need this in the first place: <https://github.com/rust-lang/rust-clippy/issues/8628>
try_block_depth_stack: Vec<u32>,
}
-impl_lint_pass!(QuestionMark => [QUESTION_MARK]);
+
+impl_lint_pass!(QuestionMark => [QUESTION_MARK, MANUAL_LET_ELSE]);
+
+impl QuestionMark {
+ #[must_use]
+ pub fn new(msrv: Msrv, matches_behaviour: MatchLintBehaviour) -> Self {
+ Self {
+ msrv,
+ matches_behaviour,
+ try_block_depth_stack: Vec::new(),
+ }
+ }
+}
enum IfBlockType<'hir> {
/// An `if x.is_xxx() { a } else { b } ` expression.
@@ -78,6 +96,29 @@ enum IfBlockType<'hir> {
),
}
+fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
+ if let StmtKind::Local(Local { pat, init: Some(init_expr), els: Some(els), .. }) = stmt.kind &&
+ let Block { stmts: &[], expr: Some(els), .. } = els &&
+ let Some(inner_pat) = pat_and_expr_can_be_question_mark(cx, pat, els)
+ {
+ let mut applicability = Applicability::MaybeIncorrect;
+ let init_expr_str = snippet_with_applicability(cx, init_expr.span, "..", &mut applicability);
+ let receiver_str = snippet_with_applicability(cx, inner_pat.span, "..", &mut applicability);
+ let sugg = format!(
+ "let {receiver_str} = {init_expr_str}?;",
+ );
+ span_lint_and_sugg(
+ cx,
+ QUESTION_MARK,
+ stmt.span,
+ "this `let...else` may be rewritten with the `?` operator",
+ "replace it with",
+ sugg,
+ applicability,
+ );
+ }
+}
+
fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_>) -> bool {
match *if_block {
IfBlockType::IfIs(caller, caller_ty, call_sym, if_then, _) => {
@@ -259,8 +300,18 @@ fn is_try_block(cx: &LateContext<'_>, bl: &rustc_hir::Block<'_>) -> bool {
}
impl<'tcx> LateLintPass<'tcx> for QuestionMark {
+ fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
+ if !is_lint_allowed(cx, QUESTION_MARK_USED, stmt.hir_id) {
+ return;
+ }
+
+ if !in_constant(cx, stmt.hir_id) {
+ check_let_some_else_return_none(cx, stmt);
+ }
+ self.check_manual_let_else(cx, stmt);
+ }
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if !in_constant(cx, expr.hir_id) {
+ if !in_constant(cx, expr.hir_id) && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id) {
self.check_is_none_or_err_and_early_return(cx, expr);
self.check_if_let_some_or_err_and_early_return(cx, expr);
}
@@ -291,4 +342,5 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark {
.expect("blocks are always part of bodies and must have a depth") -= 1;
}
}
+ extract_msrv_attr!(LateContext);
}
diff --git a/src/tools/clippy/clippy_lints/src/question_mark_used.rs b/src/tools/clippy/clippy_lints/src/question_mark_used.rs
index ff66b8a00..d0de33e3c 100644
--- a/src/tools/clippy/clippy_lints/src/question_mark_used.rs
+++ b/src/tools/clippy/clippy_lints/src/question_mark_used.rs
@@ -34,7 +34,7 @@ declare_lint_pass!(QuestionMarkUsed => [QUESTION_MARK_USED]);
impl<'tcx> LateLintPass<'tcx> for QuestionMarkUsed {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if let ExprKind::Match(_, _, MatchSource::TryDesugar) = expr.kind {
+ if let ExprKind::Match(_, _, MatchSource::TryDesugar(_)) = expr.kind {
if !span_is_local(expr.span) {
return;
}
diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs
index d2018aba9..3287675a8 100644
--- a/src/tools/clippy/clippy_lints/src/ranges.rs
+++ b/src/tools/clippy/clippy_lints/src/ranges.rs
@@ -1,10 +1,9 @@
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::higher;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
use clippy_utils::sugg::Sugg;
-use clippy_utils::{get_parent_expr, in_constant, is_integer_const, path_to_local};
+use clippy_utils::{get_parent_expr, higher, in_constant, is_integer_const, path_to_local};
use if_chain::if_chain;
use rustc_ast::ast::RangeLimits;
use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/raw_strings.rs b/src/tools/clippy/clippy_lints/src/raw_strings.rs
index f45bb1ef3..ccabb577c 100644
--- a/src/tools/clippy/clippy_lints/src/raw_strings.rs
+++ b/src/tools/clippy/clippy_lints/src/raw_strings.rs
@@ -1,10 +1,10 @@
-use std::{iter::once, ops::ControlFlow};
+use std::iter::once;
+use std::ops::ControlFlow;
-use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet};
-use rustc_ast::{
- ast::{Expr, ExprKind},
- token::LitKind,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use rustc_ast::ast::{Expr, ExprKind};
+use rustc_ast::token::LitKind;
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
@@ -95,7 +95,7 @@ impl EarlyLintPass for RawStrings {
// `once` so a raw string ending in hashes is still checked
let num = str.as_bytes().iter().chain(once(&0)).try_fold(0u8, |acc, &b| {
match b {
- b'"' => (following_quote, req) = (true, 1),
+ b'"' if !following_quote => (following_quote, req) = (true, 1),
// I'm a bit surprised the compiler didn't optimize this out, there's no
// branch but it still ends up doing an unnecessary comparison, it's:
// - cmp r9b,1h
diff --git a/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs
index e82aa3a7b..8e85c55e7 100644
--- a/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs
+++ b/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs
@@ -1,10 +1,9 @@
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::paths;
use clippy_utils::source::{indent_of, snippet};
use clippy_utils::ty::match_type;
+use clippy_utils::{last_path_segment, paths};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, QPath, TyKind};
use rustc_lint::{LateContext, LateLintPass};
@@ -50,9 +49,15 @@ 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, func_span)) = ref_init(cx, elem) else { return; };
+ 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, func_span)) = ref_init(cx, elem) else {
+ return;
+ };
emit_lint(cx, symbol, macro_call.span, elem, len, func_span);
}
diff --git a/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs b/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs
index fa1078588..2bf90815c 100644
--- a/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs
+++ b/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs
@@ -1,9 +1,7 @@
-use clippy_utils::{
- diagnostics::{span_lint, span_lint_and_sugg},
- higher::{get_vec_init_kind, VecInitKind},
- source::snippet,
- visitors::for_each_expr,
-};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::higher::{get_vec_init_kind, VecInitKind};
+use clippy_utils::source::snippet;
+use clippy_utils::visitors::for_each_expr;
use core::ops::ControlFlow;
use hir::{Expr, ExprKind, Local, PatKind, PathSegment, QPath, StmtKind};
use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
index 05e52e6b3..534b2762b 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
@@ -1,15 +1,14 @@
use std::ops::ControlFlow;
-use clippy_utils::{
- diagnostics::span_lint_and_sugg,
- peel_blocks,
- source::{snippet, walk_span_to_context},
- visitors::for_each_expr,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::peel_blocks;
+use clippy_utils::source::{snippet, walk_span_to_context};
+use clippy_utils::visitors::for_each_expr;
use rustc_errors::Applicability;
use rustc_hir::{AsyncGeneratorKind, Closure, Expr, ExprKind, GeneratorKind, MatchSource};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::{lint::in_external_macro, ty::UpvarCapture};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::UpvarCapture;
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
index b6ce4ebc2..fc49b58e0 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
@@ -6,8 +6,7 @@ use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::intravisit as hir_visit;
-use rustc_hir::intravisit::Visitor as HirVisitor;
-use rustc_hir::intravisit::Visitor;
+use rustc_hir::intravisit::{Visitor as HirVisitor, Visitor};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter;
use rustc_middle::lint::in_external_macro;
@@ -53,7 +52,7 @@ impl ReturnVisitor {
impl<'tcx> Visitor<'tcx> for ReturnVisitor {
fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
- if let hir::ExprKind::Ret(_) | hir::ExprKind::Match(.., hir::MatchSource::TryDesugar) = ex.kind {
+ if let hir::ExprKind::Ret(_) | hir::ExprKind::Match(.., hir::MatchSource::TryDesugar(_)) = ex.kind {
self.found_return = true;
} else {
hir_visit::walk_expr(self, ex);
diff --git a/src/tools/clippy/clippy_lints/src/redundant_locals.rs b/src/tools/clippy/clippy_lints/src/redundant_locals.rs
new file mode 100644
index 000000000..0c89c7ee4
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/redundant_locals.rs
@@ -0,0 +1,126 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_from_proc_macro;
+use clippy_utils::ty::needs_ordered_drop;
+use rustc_ast::Mutability;
+use rustc_hir::def::Res;
+use rustc_hir::{
+ BindingAnnotation, ByRef, Expr, ExprKind, HirId, Local, Node, Pat, PatKind, QPath,
+};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::{in_external_macro, is_from_async_await};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::Ident;
+use rustc_span::DesugaringKind;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for redundant redefinitions of local bindings.
+ ///
+ /// ### Why is this bad?
+ /// Redundant redefinitions of local bindings do not change behavior and are likely to be unintended.
+ ///
+ /// Note that although these bindings do not affect your code's meaning, they _may_ affect `rustc`'s stack allocation.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let a = 0;
+ /// let a = a;
+ ///
+ /// fn foo(b: i32) {
+ /// let b = b;
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let a = 0;
+ /// // no redefinition with the same name
+ ///
+ /// fn foo(b: i32) {
+ /// // no redefinition with the same name
+ /// }
+ /// ```
+ #[clippy::version = "1.72.0"]
+ pub REDUNDANT_LOCALS,
+ correctness,
+ "redundant redefinition of a local binding"
+}
+declare_lint_pass!(RedundantLocals => [REDUNDANT_LOCALS]);
+
+impl<'tcx> LateLintPass<'tcx> for RedundantLocals {
+ fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
+ if_chain! {
+ if !local.span.is_desugaring(DesugaringKind::Async);
+ // the pattern is a single by-value binding
+ if let PatKind::Binding(BindingAnnotation(ByRef::No, mutability), _, ident, None) = local.pat.kind;
+ // the binding is not type-ascribed
+ if local.ty.is_none();
+ // the expression is a resolved path
+ if let Some(expr) = local.init;
+ if let ExprKind::Path(qpath @ QPath::Resolved(None, path)) = expr.kind;
+ // the path is a single segment equal to the local's name
+ if let [last_segment] = path.segments;
+ if last_segment.ident == ident;
+ // resolve the path to its defining binding pattern
+ if let Res::Local(binding_id) = cx.qpath_res(&qpath, expr.hir_id);
+ if let Node::Pat(binding_pat) = cx.tcx.hir().get(binding_id);
+ // the previous binding has the same mutability
+ if find_binding(binding_pat, ident).unwrap().1 == mutability;
+ // the local does not change the effect of assignments to the binding. see #11290
+ if !affects_assignments(cx, mutability, binding_id, local.hir_id);
+ // the local does not affect the code's drop behavior
+ if !affects_drop_behavior(cx, binding_id, local.hir_id, expr);
+ // the local is user-controlled
+ if !in_external_macro(cx.sess(), local.span);
+ if !is_from_proc_macro(cx, expr);
+ // Async function parameters are lowered into the closure body, so we can't lint them.
+ // see `lower_maybe_async_body` in `rust_ast_lowering`
+ if !is_from_async_await(local.span);
+ then {
+ span_lint_and_help(
+ cx,
+ REDUNDANT_LOCALS,
+ vec![binding_pat.span, local.span],
+ "redundant redefinition of a binding",
+ None,
+ &format!("remove the redefinition of `{ident}`"),
+ );
+ }
+ }
+ }
+}
+
+/// Find the annotation of a binding introduced by a pattern, or `None` if it's not introduced.
+fn find_binding(pat: &Pat<'_>, name: Ident) -> Option<BindingAnnotation> {
+ let mut ret = None;
+
+ pat.each_binding_or_first(&mut |annotation, _, _, ident| {
+ if ident == name {
+ ret = Some(annotation);
+ }
+ });
+
+ ret
+}
+
+/// Check if a rebinding of a local changes the effect of assignments to the binding.
+fn affects_assignments(cx: &LateContext<'_>, mutability: Mutability, bind: HirId, rebind: HirId) -> bool {
+ let hir = cx.tcx.hir();
+
+ // the binding is mutable and the rebinding is in a different scope than the original binding
+ mutability == Mutability::Mut && hir.get_enclosing_scope(bind) != hir.get_enclosing_scope(rebind)
+}
+
+/// Check if a rebinding of a local affects the code's drop behavior.
+fn affects_drop_behavior<'tcx>(
+ cx: &LateContext<'tcx>,
+ bind: HirId,
+ rebind: HirId,
+ rebind_expr: &Expr<'tcx>,
+) -> bool {
+ let hir = cx.tcx.hir();
+
+ // the rebinding is in a different scope than the original binding
+ // and the type of the binding cares about drop order
+ hir.get_enclosing_scope(bind) != hir.get_enclosing_scope(rebind)
+ && needs_ordered_drop(cx, cx.typeck_results().expr_ty(rebind_expr))
+}
diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs
index c70ce83a9..4abfa0fc3 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs
@@ -7,9 +7,8 @@ use rustc_ast::util::parser::PREC_PREFIX;
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability};
use rustc_lint::{LateContext, LateLintPass, Lint};
-use rustc_middle::ty::Ty;
use rustc_middle::ty::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability};
-use rustc_middle::ty::subst::GenericArg;
+use rustc_middle::ty::{GenericArg, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
@@ -82,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
if_chain! {
if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind;
if addressee.span.ctxt() == ctxt;
- if let ExprKind::Index(indexed, range) = addressee.kind;
+ if let ExprKind::Index(indexed, range, _) = addressee.kind;
if is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull);
then {
let (expr_ty, expr_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(expr));
@@ -135,7 +134,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
} else if let Some(target_id) = cx.tcx.lang_items().deref_target() {
if let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions(
cx.param_env,
- Ty::new_projection(cx.tcx,target_id, cx.tcx.mk_substs(&[GenericArg::from(indexed_ty)])),
+ Ty::new_projection(cx.tcx,target_id, cx.tcx.mk_args(&[GenericArg::from(indexed_ty)])),
) {
if deref_ty == expr_ty {
let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0;
diff --git a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
index 038dfe8e4..ed42a422b 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
@@ -5,6 +5,7 @@ use rustc_ast::ast::{ConstItem, Item, ItemKind, StaticItem, Ty, TyKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::symbol::kw;
declare_clippy_lint! {
/// ### What it does
@@ -64,7 +65,7 @@ impl RedundantStaticLifetimes {
if let Some(lifetime) = *optional_lifetime {
match borrow_type.ty.kind {
TyKind::Path(..) | TyKind::Slice(..) | TyKind::Array(..) | TyKind::Tup(..) => {
- if lifetime.ident.name == rustc_span::symbol::kw::StaticLifetime {
+ if lifetime.ident.name == kw::StaticLifetime {
let snip = snippet(cx, borrow_type.ty.span, "<type>");
let sugg = format!("&{}{snip}", borrow_type.mutbl.prefix_str());
span_lint_and_then(
diff --git a/src/tools/clippy/clippy_lints/src/reference.rs b/src/tools/clippy/clippy_lints/src/reference.rs
index a642e2da3..db870ec4c 100644
--- a/src/tools/clippy/clippy_lints/src/reference.rs
+++ b/src/tools/clippy/clippy_lints/src/reference.rs
@@ -94,7 +94,7 @@ impl EarlyLintPass for DerefAddrOf {
DEREF_ADDROF,
e.span,
"immediately dereferencing a reference",
- "try this",
+ "try",
sugg.to_string(),
applicability,
);
diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs
index 674f8bf4c..b795e4b15 100644
--- a/src/tools/clippy/clippy_lints/src/regex.rs
+++ b/src/tools/clippy/clippy_lints/src/regex.rs
@@ -3,12 +3,12 @@ use std::fmt::Display;
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
use clippy_utils::source::snippet_opt;
-use clippy_utils::{match_def_path, paths};
-use if_chain::if_chain;
+use clippy_utils::{def_path_def_ids, path_def_id, paths};
use rustc_ast::ast::{LitKind, StrStyle};
+use rustc_hir::def_id::DefIdMap;
use rustc_hir::{BorrowKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::{BytePos, Span};
declare_clippy_lint! {
@@ -55,26 +55,52 @@ declare_clippy_lint! {
"trivial regular expressions"
}
-declare_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]);
+#[derive(Copy, Clone)]
+enum RegexKind {
+ Unicode,
+ UnicodeSet,
+ Bytes,
+ BytesSet,
+}
+
+#[derive(Default)]
+pub struct Regex {
+ definitions: DefIdMap<RegexKind>,
+}
+
+impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]);
impl<'tcx> LateLintPass<'tcx> for Regex {
+ fn check_crate(&mut self, cx: &LateContext<'tcx>) {
+ // We don't use `match_def_path` here because that relies on matching the exact path, which changed
+ // between regex 1.8 and 1.9
+ //
+ // `def_path_def_ids` will resolve through re-exports but is relatively heavy, so we only perform
+ // the operation once and store the results
+ let mut resolve = |path, kind| {
+ for id in def_path_def_ids(cx, path) {
+ self.definitions.insert(id, kind);
+ }
+ };
+
+ resolve(&paths::REGEX_NEW, RegexKind::Unicode);
+ resolve(&paths::REGEX_BUILDER_NEW, RegexKind::Unicode);
+ resolve(&paths::REGEX_SET_NEW, RegexKind::UnicodeSet);
+ resolve(&paths::REGEX_BYTES_NEW, RegexKind::Bytes);
+ resolve(&paths::REGEX_BYTES_BUILDER_NEW, RegexKind::Bytes);
+ resolve(&paths::REGEX_BYTES_SET_NEW, RegexKind::BytesSet);
+ }
+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if_chain! {
- if let ExprKind::Call(fun, [arg]) = expr.kind;
- if let ExprKind::Path(ref qpath) = fun.kind;
- if let Some(def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
- then {
- if match_def_path(cx, def_id, &paths::REGEX_NEW) ||
- match_def_path(cx, def_id, &paths::REGEX_BUILDER_NEW) {
- check_regex(cx, arg, true);
- } else if match_def_path(cx, def_id, &paths::REGEX_BYTES_NEW) ||
- match_def_path(cx, def_id, &paths::REGEX_BYTES_BUILDER_NEW) {
- check_regex(cx, arg, false);
- } else if match_def_path(cx, def_id, &paths::REGEX_SET_NEW) {
- check_set(cx, arg, true);
- } else if match_def_path(cx, def_id, &paths::REGEX_BYTES_SET_NEW) {
- check_set(cx, arg, false);
- }
+ if let ExprKind::Call(fun, [arg]) = expr.kind
+ && let Some(def_id) = path_def_id(cx, fun)
+ && let Some(regex_kind) = self.definitions.get(&def_id)
+ {
+ match regex_kind {
+ RegexKind::Unicode => check_regex(cx, arg, true),
+ RegexKind::UnicodeSet => check_set(cx, arg, true),
+ RegexKind::Bytes => check_regex(cx, arg, false),
+ RegexKind::BytesSet => check_set(cx, arg, false),
}
}
}
diff --git a/src/tools/clippy/clippy_lints/src/renamed_lints.rs b/src/tools/clippy/clippy_lints/src/renamed_lints.rs
index 44e7cbfba..fc1fabcc0 100644
--- a/src/tools/clippy/clippy_lints/src/renamed_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/renamed_lints.rs
@@ -30,6 +30,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[
("clippy::single_char_push_str", "clippy::single_char_add_str"),
("clippy::stutter", "clippy::module_name_repetitions"),
("clippy::to_string_in_display", "clippy::recursive_format_impl"),
+ ("clippy::unwrap_or_else_default", "clippy::unwrap_or_default"),
("clippy::zero_width_space", "clippy::invisible_characters"),
("clippy::cast_ref_to_mut", "invalid_reference_casting"),
("clippy::clone_double_ref", "suspicious_double_ref_op"),
@@ -42,6 +43,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[
("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"),
("clippy::forget_copy", "forgetting_copy_types"),
("clippy::forget_ref", "forgetting_references"),
+ ("clippy::fn_null_check", "useless_ptr_null_checks"),
("clippy::into_iter_on_array", "array_into_iter"),
("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"),
("clippy::invalid_ref", "invalid_value"),
diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs
index 958351ad8..d6b9a49d2 100644
--- a/src/tools/clippy/clippy_lints/src/returns.rs
+++ b/src/tools/clippy/clippy_lints/src/returns.rs
@@ -1,15 +1,17 @@
-use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::source::{snippet_opt, snippet_with_context};
-use clippy_utils::visitors::{for_each_expr, Descend};
-use clippy_utils::{fn_def_id, path_to_local_id, span_find_starting_semi};
+use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
+use clippy_utils::{fn_def_id, is_from_proc_macro, path_to_local_id, span_find_starting_semi};
use core::ops::ControlFlow;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
-use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, LangItem, MatchSource, PatKind, QPath, StmtKind};
+use rustc_hir::{
+ Block, Body, Expr, ExprKind, FnDecl, ItemKind, LangItem, MatchSource, OwnerNode, PatKind, QPath, Stmt, StmtKind,
+};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
+use rustc_middle::ty::{self, GenericArgKind, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::def_id::LocalDefId;
use rustc_span::source_map::Span;
@@ -76,6 +78,46 @@ declare_clippy_lint! {
"using a return statement like `return expr;` where an expression would suffice"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for return statements on `Err` paired with the `?` operator.
+ ///
+ /// ### Why is this bad?
+ /// The `return` is unnecessary.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// fn foo(x: usize) -> Result<(), Box<dyn Error>> {
+ /// if x == 0 {
+ /// return Err(...)?;
+ /// }
+ /// Ok(())
+ /// }
+ /// ```
+ /// simplify to
+ /// ```rust,ignore
+ /// fn foo(x: usize) -> Result<(), Box<dyn Error>> {
+ /// if x == 0 {
+ /// Err(...)?;
+ /// }
+ /// Ok(())
+ /// }
+ /// ```
+ /// if paired with `try_err`, use instead:
+ /// ```rust,ignore
+ /// fn foo(x: usize) -> Result<(), Box<dyn Error>> {
+ /// if x == 0 {
+ /// return Err(...);
+ /// }
+ /// Ok(())
+ /// }
+ /// ```
+ #[clippy::version = "1.73.0"]
+ pub NEEDLESS_RETURN_WITH_QUESTION_MARK,
+ style,
+ "using a return statement like `return Err(expr)?;` where removing it would suffice"
+}
+
#[derive(PartialEq, Eq)]
enum RetReplacement<'tcx> {
Empty,
@@ -115,9 +157,35 @@ impl<'tcx> ToString for RetReplacement<'tcx> {
}
}
-declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN]);
+declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN, NEEDLESS_RETURN_WITH_QUESTION_MARK]);
impl<'tcx> LateLintPass<'tcx> for Return {
+ fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
+ if !in_external_macro(cx.sess(), stmt.span)
+ && let StmtKind::Semi(expr) = stmt.kind
+ && let ExprKind::Ret(Some(ret)) = expr.kind
+ && let ExprKind::Match(.., MatchSource::TryDesugar(_)) = ret.kind
+ // Ensure this is not the final stmt, otherwise removing it would cause a compile error
+ && let OwnerNode::Item(item) = cx.tcx.hir().owner(cx.tcx.hir().get_parent_item(expr.hir_id))
+ && let ItemKind::Fn(_, _, body) = item.kind
+ && let block = cx.tcx.hir().body(body).value
+ && let ExprKind::Block(block, _) = block.kind
+ && let [.., final_stmt] = block.stmts
+ && final_stmt.hir_id != stmt.hir_id
+ && !is_from_proc_macro(cx, expr)
+ {
+ span_lint_and_sugg(
+ cx,
+ NEEDLESS_RETURN_WITH_QUESTION_MARK,
+ expr.span.until(ret.span),
+ "unneeded `return` statement with `?` operator",
+ "remove it",
+ String::new(),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
// we need both a let-binding stmt and an expr
if_chain! {
@@ -173,6 +241,10 @@ impl<'tcx> LateLintPass<'tcx> for Return {
sp: Span,
_: LocalDefId,
) {
+ if sp.from_expansion() {
+ return;
+ }
+
match kind {
FnKind::Closure => {
// when returning without value in closure, replace this `return`
@@ -328,16 +400,16 @@ fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, semi_spans: Vec<Span>,
}
fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
- for_each_expr(expr, |e| {
+ for_each_expr_with_closures(cx, expr, |e| {
if let Some(def_id) = fn_def_id(cx, e)
&& cx
.tcx
.fn_sig(def_id)
- .subst_identity()
+ .instantiate_identity()
.skip_binder()
.output()
.walk()
- .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
+ .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if !re.is_static()))
{
ControlFlow::Break(())
} else {
diff --git a/src/tools/clippy/clippy_lints/src/self_named_constructors.rs b/src/tools/clippy/clippy_lints/src/self_named_constructors.rs
index beca203c8..b92014f68 100644
--- a/src/tools/clippy/clippy_lints/src/self_named_constructors.rs
+++ b/src/tools/clippy/clippy_lints/src/self_named_constructors.rs
@@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for SelfNamedConstructors {
let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id()).def_id;
let item = cx.tcx.hir().expect_item(parent);
- let self_ty = cx.tcx.type_of(item.owner_id).subst_identity();
+ let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
let ret_ty = return_ty(cx, impl_item.owner_id);
// Do not check trait impls
diff --git a/src/tools/clippy/clippy_lints/src/semicolon_block.rs b/src/tools/clippy/clippy_lints/src/semicolon_block.rs
index 419d7991f..88f295c72 100644
--- a/src/tools/clippy/clippy_lints/src/semicolon_block.rs
+++ b/src/tools/clippy/clippy_lints/src/semicolon_block.rs
@@ -148,12 +148,18 @@ impl LateLintPass<'_> for SemicolonBlock {
expr: None,
stmts: [.., stmt],
..
- } = block else { return };
+ } = block
+ else {
+ return;
+ };
let &Stmt {
kind: StmtKind::Semi(expr),
span,
..
- } = stmt else { return };
+ } = stmt
+ else {
+ return;
+ };
self.semicolon_outside_block(cx, block, expr, span);
},
StmtKind::Semi(Expr {
diff --git a/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs b/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs
index 355f907e2..c9547cd95 100644
--- a/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs
+++ b/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs
@@ -43,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned {
if let Some(expr) = block.expr;
let t_expr = cx.typeck_results().expr_ty(expr);
if t_expr.is_unit();
- let mut app = Applicability::MaybeIncorrect;
+ let mut app = Applicability::MachineApplicable;
if let snippet = snippet_with_context(cx, expr.span, block.span.ctxt(), "}", &mut app).0;
if !snippet.ends_with('}') && !snippet.ends_with(';');
if cx.sess().source_map().is_multiline(block.span);
diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs
index 993f9373d..78418b223 100644
--- a/src/tools/clippy/clippy_lints/src/shadow.rs
+++ b/src/tools/clippy/clippy_lints/src/shadow.rs
@@ -106,7 +106,9 @@ impl_lint_pass!(Shadow => [SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED]);
impl<'tcx> LateLintPass<'tcx> for Shadow {
fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
- let PatKind::Binding(_, id, ident, _) = pat.kind else { return };
+ let PatKind::Binding(_, id, ident, _) = pat.kind else {
+ return;
+ };
if pat.span.desugaring_kind().is_some() || pat.span.from_expansion() {
return;
diff --git a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs
index fffa8a380..4b248c9c7 100644
--- a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs
+++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs
@@ -1,18 +1,16 @@
-use clippy_utils::{
- diagnostics::span_lint_and_then,
- expr_or_init, get_attr, path_to_local,
- source::{indent_of, snippet},
-};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::{indent_of, snippet};
+use clippy_utils::{expr_or_init, get_attr, path_to_local, peel_hir_expr_unary};
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
use rustc_errors::Applicability;
-use rustc_hir::{
- self as hir,
- intravisit::{walk_expr, Visitor},
-};
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::{self as hir};
use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::ty::{subst::GenericArgKind, Ty, TypeAndMut};
+use rustc_middle::ty::{GenericArgKind, Ty, TypeAndMut};
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::{symbol::Ident, Span, DUMMY_SP};
+use rustc_span::symbol::Ident;
+use rustc_span::{sym, Span, DUMMY_SP};
use std::borrow::Cow;
declare_clippy_lint! {
@@ -237,7 +235,7 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> StmtsChecker<'ap, 'lc, 'others, 'stmt, 'tcx
fn manage_has_expensive_expr_after_last_attr(&mut self) {
let has_expensive_stmt = match self.ap.curr_stmt.kind {
- hir::StmtKind::Expr(expr) if !is_expensive_expr(expr) => false,
+ hir::StmtKind::Expr(expr) if is_inexpensive_expr(expr) => false,
hir::StmtKind::Local(local) if let Some(expr) = local.init
&& let hir::ExprKind::Path(_) = expr.kind => false,
_ => true
@@ -332,13 +330,13 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> Visitor<'tcx> for StmtsChecker<'ap, 'lc, 'o
apa.last_method_span = span;
}
},
- hir::StmtKind::Semi(expr) => {
- if has_drop(expr, &apa.first_bind_ident) {
+ hir::StmtKind::Semi(semi_expr) => {
+ if has_drop(semi_expr, &apa.first_bind_ident, self.cx) {
apa.has_expensive_expr_after_last_attr = false;
apa.last_stmt_span = DUMMY_SP;
return;
}
- if let hir::ExprKind::MethodCall(_, _, _, span) = expr.kind {
+ if let hir::ExprKind::MethodCall(_, _, _, span) = semi_expr.kind {
apa.last_method_span = span;
}
},
@@ -430,22 +428,37 @@ fn dummy_stmt_expr<'any>(expr: &'any hir::Expr<'any>) -> hir::Stmt<'any> {
}
}
-fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: &Ident) -> bool {
+fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: &Ident, lcx: &LateContext<'_>) -> bool {
if let hir::ExprKind::Call(fun, args) = expr.kind
&& let hir::ExprKind::Path(hir::QPath::Resolved(_, fun_path)) = &fun.kind
- && let [fun_ident, ..] = fun_path.segments
- && fun_ident.ident.name == rustc_span::sym::drop
+ && let Res::Def(DefKind::Fn, did) = fun_path.res
+ && lcx.tcx.is_diagnostic_item(sym::mem_drop, did)
&& let [first_arg, ..] = args
- && let hir::ExprKind::Path(hir::QPath::Resolved(_, arg_path)) = &first_arg.kind
- && let [first_arg_ps, .. ] = arg_path.segments
{
- &first_arg_ps.ident == first_bind_ident
- }
- else {
- false
+ let has_ident = |local_expr: &hir::Expr<'_>| {
+ if let hir::ExprKind::Path(hir::QPath::Resolved(_, arg_path)) = &local_expr.kind
+ && let [first_arg_ps, .. ] = arg_path.segments
+ && &first_arg_ps.ident == first_bind_ident
+ {
+ true
+ }
+ else {
+ false
+ }
+ };
+ if has_ident(first_arg) {
+ return true;
+ }
+ if let hir::ExprKind::Tup(value) = &first_arg.kind && value.iter().any(has_ident) {
+ return true;
+ }
}
+ false
}
-fn is_expensive_expr(expr: &hir::Expr<'_>) -> bool {
- !matches!(expr.kind, hir::ExprKind::Path(_))
+fn is_inexpensive_expr(expr: &hir::Expr<'_>) -> bool {
+ let actual = peel_hir_expr_unary(expr).0;
+ let is_path = matches!(actual.kind, hir::ExprKind::Path(_));
+ let is_lit = matches!(actual.kind, hir::ExprKind::Lit(_));
+ is_path || is_lit
}
diff --git a/src/tools/clippy/clippy_lints/src/single_call_fn.rs b/src/tools/clippy/clippy_lints/src/single_call_fn.rs
index 42753d2e9..7bbe98e0a 100644
--- a/src/tools/clippy/clippy_lints/src/single_call_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/single_call_fn.rs
@@ -2,8 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::{is_from_proc_macro, is_in_test_function};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::LocalDefId;
-use rustc_hir::intravisit::{walk_expr, Visitor};
-use rustc_hir::{intravisit::FnKind, Body, Expr, ExprKind, FnDecl};
+use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
+use rustc_hir::{Body, Expr, ExprKind, FnDecl};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::hir::nested_filter::OnlyBodies;
use rustc_middle::lint::in_external_macro;
diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
index 5743dd21c..9c21d70c8 100644
--- a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
@@ -1,11 +1,14 @@
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
use rustc_ast::node_id::{NodeId, NodeMap};
+use rustc_ast::ptr::P;
use rustc_ast::visit::{walk_expr, Visitor};
-use rustc_ast::{ptr::P, Crate, Expr, ExprKind, Item, ItemKind, MacroDef, ModKind, Ty, TyKind, UseTreeKind};
+use rustc_ast::{Crate, Expr, ExprKind, Item, ItemKind, MacroDef, ModKind, Ty, TyKind, UseTreeKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::{edition::Edition, symbol::kw, Span, Symbol};
+use rustc_span::edition::Edition;
+use rustc_span::symbol::kw;
+use rustc_span::{Span, Symbol};
declare_clippy_lint! {
/// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs
index dfe8be7a6..321c89889 100644
--- a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs
+++ b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs
@@ -1,7 +1,9 @@
-use clippy_utils::{
- diagnostics::span_lint_and_then, get_trait_def_id, higher::VecArgs, macros::root_macro_call_first_node,
- source::snippet_opt, ty::implements_trait,
-};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::get_trait_def_id;
+use clippy_utils::higher::VecArgs;
+use clippy_utils::macros::root_macro_call_first_node;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::implements_trait;
use rustc_ast::{LitIntType, LitKind, UintTy};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem, QPath};
diff --git a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs
index ac4e29e9d..bd783b4e0 100644
--- a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs
+++ b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs
@@ -4,8 +4,7 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::{match_def_path, paths};
use if_chain::if_chain;
-use rustc_hir::BinOpKind;
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, Ty, TypeAndMut};
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -47,7 +46,7 @@ fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted:
if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id();
if matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::mem_size_of | sym::mem_size_of_val));
then {
- cx.typeck_results().node_substs(count_func.hir_id).types().next()
+ cx.typeck_results().node_args(count_func.hir_id).types().next()
} else {
None
}
@@ -101,7 +100,7 @@ fn get_pointee_ty_and_count_expr<'tcx>(
if FUNCTIONS.iter().any(|func_path| match_def_path(cx, def_id, func_path));
// Get the pointee type
- if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next();
+ if let Some(pointee_ty) = cx.typeck_results().node_args(func.hir_id).types().next();
then {
return Some((pointee_ty, count));
}
diff --git a/src/tools/clippy/clippy_lints/src/size_of_ref.rs b/src/tools/clippy/clippy_lints/src/size_of_ref.rs
index 8abec06c6..89ac8cd8c 100644
--- a/src/tools/clippy/clippy_lints/src/size_of_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/size_of_ref.rs
@@ -1,4 +1,6 @@
-use clippy_utils::{diagnostics::span_lint_and_help, path_def_id, ty::peel_mid_ty_refs};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::path_def_id;
+use clippy_utils::ty::peel_mid_ty_refs;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
index 858135c8d..c9ab622ad 100644
--- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
+++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
@@ -1,13 +1,13 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{
- get_enclosing_block, is_integer_literal, is_path_diagnostic_item, path_to_local, path_to_local_id, SpanlessEq,
+ get_enclosing_block, is_expr_path_def_path, is_integer_literal, is_path_diagnostic_item, path_to_local,
+ path_to_local_id, paths, SpanlessEq,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, Visitor};
-use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, QPath, Stmt, StmtKind};
+use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym;
@@ -20,18 +20,27 @@ declare_clippy_lint! {
/// These structures are non-idiomatic and less efficient than simply using
/// `vec![0; len]`.
///
+ /// Specifically, for `vec![0; len]`, the compiler can use a specialized type of allocation
+ /// that also zero-initializes the allocated memory in the same call
+ /// (see: [alloc_zeroed](https://doc.rust-lang.org/stable/std/alloc/trait.GlobalAlloc.html#method.alloc_zeroed)).
+ ///
+ /// Writing `Vec::new()` followed by `vec.resize(len, 0)` is suboptimal because,
+ /// while it does do the same number of allocations,
+ /// it involves two operations for allocating and initializing.
+ /// The `resize` call first allocates memory (since `Vec::new()` did not), and only *then* zero-initializes it.
+ ///
/// ### Example
/// ```rust
/// # use core::iter::repeat;
/// # let len = 4;
- /// let mut vec1 = Vec::with_capacity(len);
+ /// let mut vec1 = Vec::new();
/// vec1.resize(len, 0);
///
- /// let mut vec1 = Vec::with_capacity(len);
- /// vec1.resize(vec1.capacity(), 0);
- ///
/// let mut vec2 = Vec::with_capacity(len);
- /// vec2.extend(repeat(0).take(len));
+ /// vec2.resize(len, 0);
+ ///
+ /// let mut vec3 = Vec::with_capacity(len);
+ /// vec3.extend(repeat(0).take(len));
/// ```
///
/// Use instead:
@@ -39,6 +48,7 @@ declare_clippy_lint! {
/// # let len = 4;
/// let mut vec1 = vec![0; len];
/// let mut vec2 = vec![0; len];
+ /// let mut vec3 = vec![0; len];
/// ```
#[clippy::version = "1.32.0"]
pub SLOW_VECTOR_INITIALIZATION,
@@ -60,7 +70,24 @@ struct VecAllocation<'tcx> {
/// Reference to the expression used as argument on `with_capacity` call. This is used
/// to only match slow zero-filling idioms of the same length than vector initialization.
- len_expr: &'tcx Expr<'tcx>,
+ size_expr: InitializedSize<'tcx>,
+}
+
+/// Initializer for the creation of the vector.
+///
+/// When `Vec::with_capacity(size)` is found, the `size` expression will be in
+/// `InitializedSize::Initialized`.
+///
+/// Otherwise, for `Vec::new()` calls, there is no allocation initializer yet, so
+/// `InitializedSize::Uninitialized` is used.
+/// Later, when a call to `.resize(size, 0)` or similar is found, it's set
+/// to `InitializedSize::Initialized(size)`.
+///
+/// Since it will be set to `InitializedSize::Initialized(size)` when a slow initialization is
+/// found, it is always safe to "unwrap" it at lint time.
+enum InitializedSize<'tcx> {
+ Initialized(&'tcx Expr<'tcx>),
+ Uninitialized,
}
/// Type of slow initialization
@@ -77,18 +104,14 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit {
// Matches initialization on reassignments. For example: `vec = Vec::with_capacity(100)`
if_chain! {
if let ExprKind::Assign(left, right, _) = expr.kind;
-
- // Extract variable
if let Some(local_id) = path_to_local(left);
-
- // Extract len argument
- if let Some(len_arg) = Self::is_vec_with_capacity(cx, right);
+ if let Some(size_expr) = Self::as_vec_initializer(cx, right);
then {
let vi = VecAllocation {
local_id,
allocation_expr: right,
- len_expr: len_arg,
+ size_expr,
};
Self::search_initialization(cx, vi, expr.hir_id);
@@ -98,17 +121,18 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit {
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
// Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)`
+ // or `Vec::new()`
if_chain! {
if let StmtKind::Local(local) = stmt.kind;
if let PatKind::Binding(BindingAnnotation::MUT, local_id, _, None) = local.pat.kind;
if let Some(init) = local.init;
- if let Some(len_arg) = Self::is_vec_with_capacity(cx, init);
+ if let Some(size_expr) = Self::as_vec_initializer(cx, init);
then {
let vi = VecAllocation {
local_id,
allocation_expr: init,
- len_expr: len_arg,
+ size_expr,
};
Self::search_initialization(cx, vi, stmt.hir_id);
@@ -118,19 +142,20 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit {
}
impl SlowVectorInit {
- /// Checks if the given expression is `Vec::with_capacity(..)`. It will return the expression
- /// of the first argument of `with_capacity` call if it matches or `None` if it does not.
- fn is_vec_with_capacity<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
- if_chain! {
- if let ExprKind::Call(func, [arg]) = expr.kind;
- if let ExprKind::Path(QPath::TypeRelative(ty, name)) = func.kind;
- if name.ident.as_str() == "with_capacity";
- if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::Vec);
- then {
- Some(arg)
- } else {
- None
- }
+ /// Looks for `Vec::with_capacity(size)` or `Vec::new()` calls and returns the initialized size,
+ /// if any. More specifically, it returns:
+ /// - `Some(InitializedSize::Initialized(size))` for `Vec::with_capacity(size)`
+ /// - `Some(InitializedSize::Uninitialized)` for `Vec::new()`
+ /// - `None` for other, unrelated kinds of expressions
+ fn as_vec_initializer<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<InitializedSize<'tcx>> {
+ if let ExprKind::Call(func, [len_expr]) = expr.kind
+ && is_expr_path_def_path(cx, func, &paths::VEC_WITH_CAPACITY)
+ {
+ Some(InitializedSize::Initialized(len_expr))
+ } else if matches!(expr.kind, ExprKind::Call(func, _) if is_expr_path_def_path(cx, func, &paths::VEC_NEW)) {
+ Some(InitializedSize::Uninitialized)
+ } else {
+ None
}
}
@@ -169,12 +194,19 @@ impl SlowVectorInit {
}
fn emit_lint(cx: &LateContext<'_>, slow_fill: &Expr<'_>, vec_alloc: &VecAllocation<'_>, msg: &str) {
- let len_expr = Sugg::hir(cx, vec_alloc.len_expr, "len");
+ let len_expr = Sugg::hir(
+ cx,
+ match vec_alloc.size_expr {
+ InitializedSize::Initialized(expr) => expr,
+ InitializedSize::Uninitialized => unreachable!("size expression must be set by this point"),
+ },
+ "len",
+ );
span_lint_and_then(cx, SLOW_VECTOR_INITIALIZATION, slow_fill.span, msg, |diag| {
diag.span_suggestion(
vec_alloc.allocation_expr.span,
- "consider replace allocation with",
+ "consider replacing this with",
format!("vec![0; {len_expr}]"),
Applicability::Unspecified,
);
@@ -214,36 +246,45 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
}
/// Checks if the given expression is resizing a vector with 0
- fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'_>) {
+ fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'tcx>) {
if self.initialization_found
&& let ExprKind::MethodCall(path, self_arg, [len_arg, fill_arg], _) = expr.kind
&& path_to_local_id(self_arg, self.vec_alloc.local_id)
&& path.ident.name == sym!(resize)
// Check that is filled with 0
- && is_integer_literal(fill_arg, 0) {
- // Check that len expression is equals to `with_capacity` expression
- if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr) {
- self.slow_expression = Some(InitializationType::Resize(expr));
- } else if let ExprKind::MethodCall(path, ..) = len_arg.kind && path.ident.as_str() == "capacity" {
- self.slow_expression = Some(InitializationType::Resize(expr));
- }
+ && is_integer_literal(fill_arg, 0)
+ {
+ let is_matching_resize = if let InitializedSize::Initialized(size_expr) = self.vec_alloc.size_expr {
+ // If we have a size expression, check that it is equal to what's passed to `resize`
+ SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr)
+ || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.as_str() == "capacity")
+ } else {
+ self.vec_alloc.size_expr = InitializedSize::Initialized(len_arg);
+ true
+ };
+
+ if is_matching_resize {
+ self.slow_expression = Some(InitializationType::Resize(expr));
}
+ }
}
/// Returns `true` if give expression is `repeat(0).take(...)`
- fn is_repeat_take(&self, expr: &Expr<'_>) -> bool {
+ fn is_repeat_take(&mut self, expr: &'tcx Expr<'tcx>) -> bool {
if_chain! {
if let ExprKind::MethodCall(take_path, recv, [len_arg, ..], _) = expr.kind;
if take_path.ident.name == sym!(take);
// Check that take is applied to `repeat(0)`
if self.is_repeat_zero(recv);
then {
- // Check that len expression is equals to `with_capacity` expression
- if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr) {
- return true;
- } else if let ExprKind::MethodCall(path, ..) = len_arg.kind && path.ident.as_str() == "capacity" {
- return true;
+ if let InitializedSize::Initialized(size_expr) = self.vec_alloc.size_expr {
+ // Check that len expression is equals to `with_capacity` expression
+ return SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr)
+ || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.as_str() == "capacity")
}
+
+ self.vec_alloc.size_expr = InitializedSize::Initialized(len_arg);
+ return true;
}
}
diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
index a13bc7a51..f23916527 100644
--- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
+++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
@@ -1,9 +1,11 @@
use clippy_utils::diagnostics::span_lint_and_help;
+use rustc_hir::def::Res;
use rustc_hir::def_id::DefId;
-use rustc_hir::{def::Res, HirId, Path, PathSegment};
+use rustc_hir::{HirId, Path, PathSegment};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::{sym, symbol::kw, Span};
+use rustc_span::symbol::kw;
+use rustc_span::{sym, Span};
declare_clippy_lint! {
/// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs
index 8658009eb..76f463fff 100644
--- a/src/tools/clippy/clippy_lints/src/strings.rs
+++ b/src/tools/clippy/clippy_lints/src/strings.rs
@@ -1,8 +1,10 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg};
use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::ty::is_type_lang_item;
-use clippy_utils::{get_expr_use_or_unification_node, peel_blocks, SpanlessEq};
-use clippy_utils::{get_parent_expr, is_lint_allowed, is_path_diagnostic_item, method_calls};
+use clippy_utils::{
+ get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, is_path_diagnostic_item, method_calls,
+ peel_blocks, SpanlessEq,
+};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
@@ -188,7 +190,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd {
);
}
},
- ExprKind::Index(target, _idx) => {
+ ExprKind::Index(target, _idx, _) => {
let e_ty = cx.typeck_results().expr_ty(target).peel_refs();
if e_ty.is_str() || is_type_lang_item(cx, e_ty, LangItem::String) {
span_lint(
@@ -260,7 +262,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
// Find string::as_bytes
if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args[0].kind;
- if let ExprKind::Index(left, right) = args.kind;
+ if let ExprKind::Index(left, right, _) = args.kind;
let (method_names, expressions, _) = method_calls(left, 1);
if method_names.len() == 1;
if expressions.len() == 1;
@@ -326,7 +328,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
{
// Don't lint. Byte strings produce `&[u8; N]` whereas `as_bytes()` produces
// `&[u8]`. This change would prevent matching with different sized slices.
- } else {
+ } else if !callsite.starts_with("env!") {
span_lint_and_sugg(
cx,
STRING_LIT_AS_BYTES,
diff --git a/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs b/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs
index 2f2e84fa3..b3db5e9a4 100644
--- a/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs
+++ b/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs
@@ -78,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings {
STRLEN_ON_C_STRINGS,
span,
"using `libc::strlen` on a `CString` or `CStr` value",
- "try this",
+ "try",
format!("{val_name}.{method_name}().len()"),
app,
);
diff --git a/src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs b/src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs
index e5746ca99..8be4ec3dc 100644
--- a/src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs
+++ b/src/tools/clippy/clippy_lints/src/suspicious_doc_comments.rs
@@ -1,6 +1,7 @@
use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then};
use if_chain::if_chain;
-use rustc_ast::{token::CommentKind, AttrKind, AttrStyle, Attribute, Item};
+use rustc_ast::token::CommentKind;
+use rustc_ast::{AttrKind, AttrStyle, Attribute, Item};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
index e2cdc48b5..23d6e2a84 100644
--- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
+++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
@@ -572,7 +572,7 @@ fn ident_difference_expr_with_base_location(
| (AddrOf(_, _, _), AddrOf(_, _, _))
| (Path(_, _), Path(_, _))
| (Range(_, _, _), Range(_, _, _))
- | (Index(_, _), Index(_, _))
+ | (Index(_, _, _), Index(_, _, _))
| (Field(_, _), Field(_, _))
| (AssignOp(_, _, _), AssignOp(_, _, _))
| (Assign(_, _, _), Assign(_, _, _))
diff --git a/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs b/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs
index 9c0dc8096..8e156b882 100644
--- a/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs
+++ b/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs
@@ -1,4 +1,7 @@
-use clippy_utils::{numeric_literal::NumericLiteral, source::snippet_with_context};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::numeric_literal::NumericLiteral;
+use clippy_utils::source::snippet;
+use rustc_ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -27,27 +30,29 @@ declare_lint_pass!(ConfusingXorAndPow => [SUSPICIOUS_XOR_USED_AS_POW]);
impl LateLintPass<'_> for ConfusingXorAndPow {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
- if !in_external_macro(cx.sess(), expr.span) &&
- let ExprKind::Binary(op, left, right) = &expr.kind &&
- op.node == BinOpKind::BitXor &&
- left.span.ctxt() == right.span.ctxt() &&
- let ExprKind::Lit(lit_left) = &left.kind &&
- let ExprKind::Lit(lit_right) = &right.kind &&
- let snip_left = snippet_with_context(cx, lit_left.span, lit_left.span.ctxt(), "..", &mut Applicability::MaybeIncorrect) &&
- let snip_right = snippet_with_context(cx, lit_right.span, lit_right.span.ctxt(), "..", &mut Applicability::MaybeIncorrect) &&
- let Some(left_val) = NumericLiteral::from_lit_kind(&snip_left.0, &lit_left.node) &&
- let Some(right_val) = NumericLiteral::from_lit_kind(&snip_right.0, &lit_right.node) &&
- left_val.is_decimal() &&
- right_val.is_decimal() {
- clippy_utils::diagnostics::span_lint_and_sugg(
- cx,
- SUSPICIOUS_XOR_USED_AS_POW,
- expr.span,
- "`^` is not the exponentiation operator",
- "did you mean to write",
- format!("{}.pow({})", left_val.format(), right_val.format()),
- Applicability::MaybeIncorrect,
- );
+ if !in_external_macro(cx.sess(), expr.span)
+ && let ExprKind::Binary(op, left, right) = &expr.kind
+ && op.node == BinOpKind::BitXor
+ && left.span.ctxt() == right.span.ctxt()
+ && let ExprKind::Lit(lit_left) = &left.kind
+ && let ExprKind::Lit(lit_right) = &right.kind
+ && matches!(lit_right.node, LitKind::Int(..) | LitKind::Float(..))
+ && matches!(lit_left.node, LitKind::Int(..) | LitKind::Float(..))
+ && NumericLiteral::from_lit_kind(&snippet(cx, lit_right.span, ".."), &lit_right.node).is_some_and(|x| x.is_decimal())
+ {
+ span_lint_and_sugg(
+ cx,
+ SUSPICIOUS_XOR_USED_AS_POW,
+ expr.span,
+ "`^` is not the exponentiation operator",
+ "did you mean to write",
+ format!(
+ "{}.pow({})",
+ lit_left.node,
+ lit_right.node
+ ),
+ Applicability::MaybeIncorrect,
+ );
}
}
}
diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs
index f7eef03d1..548fabb8b 100644
--- a/src/tools/clippy/clippy_lints/src/swap.rs
+++ b/src/tools/clippy/clippy_lints/src/swap.rs
@@ -11,8 +11,8 @@ use rustc_middle::lint::in_external_macro;
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Spanned;
-use rustc_span::SyntaxContext;
-use rustc_span::{sym, symbol::Ident, Span};
+use rustc_span::symbol::Ident;
+use rustc_span::{sym, Span, SyntaxContext};
declare_clippy_lint! {
/// ### What it does
@@ -86,8 +86,8 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa
let mut applicability = Applicability::MachineApplicable;
if !can_mut_borrow_both(cx, e1, e2) {
- if let ExprKind::Index(lhs1, idx1) = e1.kind
- && let ExprKind::Index(lhs2, idx2) = e2.kind
+ if let ExprKind::Index(lhs1, idx1, _) = e1.kind
+ && let ExprKind::Index(lhs2, idx2, _) = e2.kind
&& eq_expr_value(cx, lhs1, lhs2)
&& e1.span.ctxt() == ctxt
&& e2.span.ctxt() == ctxt
diff --git a/src/tools/clippy/clippy_lints/src/temporary_assignment.rs b/src/tools/clippy/clippy_lints/src/temporary_assignment.rs
index 3766b8f8e..b6b653f66 100644
--- a/src/tools/clippy/clippy_lints/src/temporary_assignment.rs
+++ b/src/tools/clippy/clippy_lints/src/temporary_assignment.rs
@@ -33,7 +33,7 @@ impl<'tcx> LateLintPass<'tcx> for TemporaryAssignment {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Assign(target, ..) = &expr.kind {
let mut base = target;
- while let ExprKind::Field(f, _) | ExprKind::Index(f, _) = &base.kind {
+ while let ExprKind::Field(f, _) | ExprKind::Index(f, _, _) = &base.kind {
base = f;
}
if is_temporary(base) && !is_adjusted(cx, base) {
diff --git a/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs b/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs
index 0a0a77082..b356666d8 100644
--- a/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs
+++ b/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs
@@ -1,8 +1,11 @@
-use clippy_utils::{diagnostics::span_lint_and_note, is_in_cfg_test, is_in_test_function};
-use rustc_hir::{intravisit::FnKind, Body, FnDecl};
+use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::{is_in_cfg_test, is_in_test_function};
+use rustc_hir::intravisit::FnKind;
+use rustc_hir::{Body, FnDecl};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::{def_id::LocalDefId, Span};
+use rustc_span::def_id::LocalDefId;
+use rustc_span::Span;
declare_clippy_lint! {
/// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs
index 2512500a6..f1b703fde 100644
--- a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs
+++ b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs
@@ -82,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome {
TO_DIGIT_IS_SOME,
expr.span,
"use of `.to_digit(..).is_some()`",
- "try this",
+ "try",
if is_method_call {
format!("{char_arg_snip}.is_digit({radix_snip})")
} else {
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs
index 550365325..c0d0d2b93 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs
@@ -4,10 +4,8 @@ use clippy_utils::sugg;
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
-use rustc_middle::{
- query::Key,
- ty::{self, Ty},
-};
+use rustc_middle::query::Key;
+use rustc_middle::ty::{self, Ty};
use rustc_span::symbol::sym;
/// Checks for `transmute_int_to_non_zero` lint.
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs
index 857d2ad82..4ae4359ee 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs
@@ -24,7 +24,7 @@ pub(super) fn check<'tcx>(
"transmute from a pointer to a pointer",
|diag| {
if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
- let sugg = arg.as_ty(Ty::new_ptr(cx.tcx,*to_ty));
+ let sugg = arg.as_ty(Ty::new_ptr(cx.tcx, *to_ty));
diag.span_suggestion(e.span, "try", sugg, Applicability::Unspecified);
}
},
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs
index 5e24213d0..c61eb0a93 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs
@@ -3,8 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::is_c_void;
use rustc_hir::Expr;
use rustc_lint::LateContext;
-use rustc_middle::ty::SubstsRef;
-use rustc_middle::ty::{self, IntTy, Ty, TypeAndMut, UintTy};
+use rustc_middle::ty::{self, GenericArgsRef, IntTy, Ty, TypeAndMut, UintTy};
#[expect(clippy::too_many_lines)]
pub(super) fn check<'tcx>(
@@ -268,12 +267,12 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx>
}
ReducedTy::UnorderedFields(ty)
},
- ty::Adt(def, substs) if def.is_struct() => {
+ ty::Adt(def, args) if def.is_struct() => {
let mut iter = def
.non_enum_variant()
.fields
.iter()
- .map(|f| cx.tcx.type_of(f.did).subst(cx.tcx, substs));
+ .map(|f| cx.tcx.type_of(f.did).instantiate(cx.tcx, args));
let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else {
return ReducedTy::TypeErasure { raw_ptr_only: false };
};
@@ -322,7 +321,7 @@ fn is_size_pair(ty: Ty<'_>) -> bool {
}
}
-fn same_except_params<'tcx>(subs1: SubstsRef<'tcx>, subs2: SubstsRef<'tcx>) -> bool {
+fn same_except_params<'tcx>(subs1: GenericArgsRef<'tcx>, subs2: GenericArgsRef<'tcx>) -> bool {
// TODO: check const parameters as well. Currently this will consider `Array<5>` the same as
// `Array<6>`
for (ty1, ty2) in subs1.types().zip(subs2.types()).filter(|(ty1, ty2)| ty1 != ty2) {
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
index 85cd74f23..513a913f5 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
@@ -6,7 +6,8 @@ use rustc_ast::ExprPrecedence;
use rustc_errors::Applicability;
use rustc_hir::{Expr, Node};
use rustc_lint::LateContext;
-use rustc_middle::ty::{cast::CastKind, Ty};
+use rustc_middle::ty::cast::CastKind;
+use rustc_middle::ty::Ty;
/// Checks for `transmutes_expressible_as_ptr_casts` lint.
/// Returns `true` if it's triggered, otherwise returns `false`.
diff --git a/src/tools/clippy/clippy_lints/src/transmute/unsound_collection_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/unsound_collection_transmute.rs
index b1445311b..891fefc17 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/unsound_collection_transmute.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/unsound_collection_transmute.rs
@@ -10,7 +10,7 @@ use rustc_span::symbol::sym;
/// Returns `true` if it's triggered, otherwise returns `false`.
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> bool {
match (&from_ty.kind(), &to_ty.kind()) {
- (ty::Adt(from_adt, from_substs), ty::Adt(to_adt, to_substs)) => {
+ (ty::Adt(from_adt, from_args), ty::Adt(to_adt, to_args)) => {
if from_adt.did() != to_adt.did() {
return false;
}
@@ -28,9 +28,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty
) {
return false;
}
- if from_substs
+ if from_args
.types()
- .zip(to_substs.types())
+ .zip(to_args.types())
.any(|(from_ty, to_ty)| is_layout_incompatible(cx, from_ty, to_ty))
{
span_lint(
diff --git a/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs
index b6615410e..088c8fda8 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs
@@ -43,7 +43,7 @@ pub(super) fn check<'tcx>(
let sugg = if *ptr_ty == rty_and_mut {
arg.as_ty(to_ty)
} else {
- arg.as_ty(Ty::new_ptr(cx.tcx,rty_and_mut)).as_ty(to_ty)
+ arg.as_ty(Ty::new_ptr(cx.tcx, rty_and_mut)).as_ty(to_ty)
};
diag.span_suggestion(e.span, "try", sugg, Applicability::Unspecified);
diff --git a/src/tools/clippy/clippy_lints/src/transmute/utils.rs b/src/tools/clippy/clippy_lints/src/transmute/utils.rs
index 62efd13b8..1cf6cf854 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/utils.rs
@@ -2,7 +2,8 @@ use rustc_hir as hir;
use rustc_hir::Expr;
use rustc_hir_typeck::{cast, FnCtxt, Inherited};
use rustc_lint::LateContext;
-use rustc_middle::ty::{cast::CastKind, Ty};
+use rustc_middle::ty::cast::CastKind;
+use rustc_middle::ty::Ty;
use rustc_span::DUMMY_SP;
// check if the component types of the transmuted collection and the result have different ABI,
diff --git a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs
index 90eb45a09..78ad52d8a 100644
--- a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs
+++ b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs
@@ -1,23 +1,29 @@
-use clippy_utils::{
- diagnostics::span_lint_and_help,
- is_from_proc_macro,
- msrvs::{self, Msrv},
- path_to_local,
-};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::visitors::for_each_local_use_after_expr;
+use clippy_utils::{is_from_proc_macro, path_to_local};
+use itertools::Itertools;
use rustc_ast::LitKind;
-use rustc_hir::{Expr, ExprKind, HirId, Node, Pat};
+use rustc_hir::{Expr, ExprKind, Node, PatKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::{lint::in_external_macro, ty};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use std::iter::once;
+use std::ops::ControlFlow;
declare_clippy_lint! {
/// ### What it does
/// Checks for tuple<=>array conversions that are not done with `.into()`.
///
/// ### Why is this bad?
- /// It's unnecessary complexity. `.into()` works for tuples<=>arrays at or below 12 elements and
- /// conveys the intent a lot better, while also leaving less room for hard to spot bugs!
+ /// It may be unnecessary complexity. `.into()` works for converting tuples<=> arrays of up to
+ /// 12 elements and conveys the intent more clearly, while also leaving less room for hard to
+ /// spot bugs!
+ ///
+ /// ### Known issues
+ /// The suggested code may hide potential asymmetry in some cases. See
+ /// [#11085](https://github.com/rust-lang/rust-clippy/issues/11085) for more info.
///
/// ### Example
/// ```rust,ignore
@@ -43,130 +49,152 @@ pub struct TupleArrayConversions {
impl LateLintPass<'_> for TupleArrayConversions {
fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
- if !in_external_macro(cx.sess(), expr.span) && self.msrv.meets(msrvs::TUPLE_ARRAY_CONVERSIONS) {
- match expr.kind {
- ExprKind::Array(elements) if (1..=12).contains(&elements.len()) => check_array(cx, expr, elements),
- ExprKind::Tup(elements) if (1..=12).contains(&elements.len()) => check_tuple(cx, expr, elements),
- _ => {},
- }
+ if in_external_macro(cx.sess(), expr.span) || !self.msrv.meets(msrvs::TUPLE_ARRAY_CONVERSIONS) {
+ return;
+ }
+
+ match expr.kind {
+ ExprKind::Array(elements) if (1..=12).contains(&elements.len()) => check_array(cx, expr, elements),
+ ExprKind::Tup(elements) if (1..=12).contains(&elements.len()) => check_tuple(cx, expr, elements),
+ _ => {},
}
}
extract_msrv_attr!(LateContext);
}
-#[expect(
- clippy::blocks_in_if_conditions,
- reason = "not a FP, but this is much easier to understand"
-)]
fn check_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: &'tcx [Expr<'tcx>]) {
- if should_lint(
- cx,
- elements,
- // This is cursed.
- Some,
- |(first_id, local)| {
- if let Node::Pat(pat) = local
- && let parent = parent_pat(cx, pat)
- && parent.hir_id == first_id
- {
- return matches!(
- cx.typeck_results().pat_ty(parent).peel_refs().kind(),
- ty::Tuple(len) if len.len() == elements.len()
- );
- }
-
- false
- },
- ) || should_lint(
- cx,
- elements,
- |(i, expr)| {
- if let ExprKind::Field(path, field) = expr.kind && field.as_str() == i.to_string() {
- return Some((i, path));
- };
-
- None
- },
- |(first_id, local)| {
- if let Node::Pat(pat) = local
- && let parent = parent_pat(cx, pat)
- && parent.hir_id == first_id
- {
- return matches!(
- cx.typeck_results().pat_ty(parent).peel_refs().kind(),
- ty::Tuple(len) if len.len() == elements.len()
- );
- }
+ let (ty::Array(ty, _) | ty::Slice(ty)) = cx.typeck_results().expr_ty(expr).kind() else {
+ unreachable!("`expr` must be an array or slice due to `ExprKind::Array`");
+ };
+
+ if let [first, ..] = elements
+ && let Some(locals) = (match first.kind {
+ ExprKind::Field(_, _) => elements
+ .iter()
+ .enumerate()
+ .map(|(i, f)| -> Option<&'tcx Expr<'tcx>> {
+ let ExprKind::Field(lhs, ident) = f.kind else {
+ return None;
+ };
+ (ident.name.as_str() == i.to_string()).then_some(lhs)
+ })
+ .collect::<Option<Vec<_>>>(),
+ ExprKind::Path(_) => Some(elements.iter().collect()),
+ _ => None,
+ })
+ && all_bindings_are_for_conv(cx, &[*ty], expr, elements, &locals, ToType::Array)
+ && !is_from_proc_macro(cx, expr)
+ {
+ span_lint_and_help(
+ cx,
+ TUPLE_ARRAY_CONVERSIONS,
+ expr.span,
+ "it looks like you're trying to convert a tuple to an array",
+ None,
+ "use `.into()` instead, or `<[T; N]>::from` if type annotations are needed",
+ );
+ }
+}
- false
- },
- ) {
- emit_lint(cx, expr, ToType::Array);
+fn check_tuple<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: &'tcx [Expr<'tcx>]) {
+ if let ty::Tuple(tys) = cx.typeck_results().expr_ty(expr).kind()
+ && let [first, ..] = elements
+ // Fix #11100
+ && tys.iter().all_equal()
+ && let Some(locals) = (match first.kind {
+ ExprKind::Index(..) => elements
+ .iter()
+ .enumerate()
+ .map(|(i, i_expr)| -> Option<&'tcx Expr<'tcx>> {
+ if let ExprKind::Index(lhs, index, _) = i_expr.kind
+ && let ExprKind::Lit(lit) = index.kind
+ && let LitKind::Int(val, _) = lit.node
+ {
+ return (val == i as u128).then_some(lhs);
+ };
+
+ None
+ })
+ .collect::<Option<Vec<_>>>(),
+ ExprKind::Path(_) => Some(elements.iter().collect()),
+ _ => None,
+ })
+ && all_bindings_are_for_conv(cx, tys, expr, elements, &locals, ToType::Tuple)
+ && !is_from_proc_macro(cx, expr)
+ {
+ span_lint_and_help(
+ cx,
+ TUPLE_ARRAY_CONVERSIONS,
+ expr.span,
+ "it looks like you're trying to convert an array to a tuple",
+ None,
+ "use `.into()` instead, or `<(T0, T1, ..., Tn)>::from` if type annotations are needed",
+ );
}
}
-#[expect(
- clippy::blocks_in_if_conditions,
- reason = "not a FP, but this is much easier to understand"
-)]
+/// Checks that every binding in `elements` comes from the same parent `Pat` with the kind if there
+/// is a parent `Pat`. Returns false in any of the following cases:
+/// * `kind` does not match `pat.kind`
+/// * one or more elements in `elements` is not a binding
+/// * one or more bindings does not have the same parent `Pat`
+/// * one or more bindings are used after `expr`
+/// * the bindings do not all have the same type
#[expect(clippy::cast_possible_truncation)]
-fn check_tuple<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: &'tcx [Expr<'tcx>]) {
- if should_lint(cx, elements, Some, |(first_id, local)| {
- if let Node::Pat(pat) = local
- && let parent = parent_pat(cx, pat)
- && parent.hir_id == first_id
- {
- return matches!(
- cx.typeck_results().pat_ty(parent).peel_refs().kind(),
- ty::Array(_, len) if len.eval_target_usize(cx.tcx, cx.param_env) as usize == elements.len()
- );
+fn all_bindings_are_for_conv<'tcx>(
+ cx: &LateContext<'tcx>,
+ final_tys: &[Ty<'tcx>],
+ expr: &Expr<'_>,
+ elements: &[Expr<'_>],
+ locals: &[&Expr<'_>],
+ kind: ToType,
+) -> bool {
+ let Some(locals) = locals.iter().map(|e| path_to_local(e)).collect::<Option<Vec<_>>>() else {
+ return false;
+ };
+ let Some(local_parents) = locals
+ .iter()
+ .map(|&l| cx.tcx.hir().find_parent(l))
+ .collect::<Option<Vec<_>>>()
+ else {
+ return false;
+ };
+
+ local_parents
+ .iter()
+ .map(|node| match node {
+ Node::Pat(pat) => kind.eq(&pat.kind).then_some(pat.hir_id),
+ Node::Local(l) => Some(l.hir_id),
+ _ => None,
+ })
+ .all_equal()
+ // Fix #11124, very convenient utils function! ❤️
+ && locals
+ .iter()
+ .all(|&l| for_each_local_use_after_expr(cx, l, expr.hir_id, |_| ControlFlow::Break::<()>(())).is_continue())
+ && local_parents.first().is_some_and(|node| {
+ let Some(ty) = match node {
+ Node::Pat(pat) => Some(pat.hir_id),
+ Node::Local(l) => Some(l.hir_id),
+ _ => None,
}
-
- false
- }) || should_lint(
- cx,
- elements,
- |(i, expr)| {
- if let ExprKind::Index(path, index) = expr.kind
- && let ExprKind::Lit(lit) = index.kind
- && let LitKind::Int(val, _) = lit.node
- && val as usize == i
- {
- return Some((i, path));
+ .map(|hir_id| cx.typeck_results().node_type(hir_id)) else {
+ return false;
};
-
- None
- },
- |(first_id, local)| {
- if let Node::Pat(pat) = local
- && let parent = parent_pat(cx, pat)
- && parent.hir_id == first_id
- {
- return matches!(
- cx.typeck_results().pat_ty(parent).peel_refs().kind(),
- ty::Array(_, len) if len.eval_target_usize(cx.tcx, cx.param_env) as usize == elements.len()
- );
+ match (kind, ty.kind()) {
+ // Ensure the final type and the original type have the same length, and that there
+ // is no implicit `&mut`<=>`&` anywhere (#11100). Bit ugly, I know, but it works.
+ (ToType::Array, ty::Tuple(tys)) => {
+ tys.len() == elements.len() && tys.iter().chain(final_tys.iter().copied()).all_equal()
+ },
+ (ToType::Tuple, ty::Array(ty, len)) => {
+ len.eval_target_usize(cx.tcx, cx.param_env) as usize == elements.len()
+ && final_tys.iter().chain(once(ty)).all_equal()
+ },
+ _ => false,
}
-
- false
- },
- ) {
- emit_lint(cx, expr, ToType::Tuple);
- }
-}
-
-/// Walks up the `Pat` until it's reached the final containing `Pat`.
-fn parent_pat<'tcx>(cx: &LateContext<'tcx>, start: &'tcx Pat<'tcx>) -> &'tcx Pat<'tcx> {
- let mut end = start;
- for (_, node) in cx.tcx.hir().parent_iter(start.hir_id) {
- if let Node::Pat(pat) = node {
- end = pat;
- } else {
- break;
- }
- }
- end
+ })
}
#[derive(Clone, Copy)]
@@ -175,61 +203,11 @@ enum ToType {
Tuple,
}
-impl ToType {
- fn msg(self) -> &'static str {
- match self {
- ToType::Array => "it looks like you're trying to convert a tuple to an array",
- ToType::Tuple => "it looks like you're trying to convert an array to a tuple",
- }
- }
-
- fn help(self) -> &'static str {
+impl PartialEq<PatKind<'_>> for ToType {
+ fn eq(&self, other: &PatKind<'_>) -> bool {
match self {
- ToType::Array => "use `.into()` instead, or `<[T; N]>::from` if type annotations are needed",
- ToType::Tuple => "use `.into()` instead, or `<(T0, T1, ..., Tn)>::from` if type annotations are needed",
+ ToType::Array => matches!(other, PatKind::Tuple(_, _)),
+ ToType::Tuple => matches!(other, PatKind::Slice(_, _, _)),
}
}
}
-
-fn emit_lint<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, to_type: ToType) -> bool {
- if !is_from_proc_macro(cx, expr) {
- span_lint_and_help(
- cx,
- TUPLE_ARRAY_CONVERSIONS,
- expr.span,
- to_type.msg(),
- None,
- to_type.help(),
- );
-
- return true;
- }
-
- false
-}
-
-fn should_lint<'tcx>(
- cx: &LateContext<'tcx>,
- elements: &'tcx [Expr<'tcx>],
- map: impl FnMut((usize, &'tcx Expr<'tcx>)) -> Option<(usize, &Expr<'_>)>,
- predicate: impl FnMut((HirId, &Node<'tcx>)) -> bool,
-) -> bool {
- if let Some(elements) = elements
- .iter()
- .enumerate()
- .map(map)
- .collect::<Option<Vec<_>>>()
- && let Some(locals) = elements
- .iter()
- .map(|(_, element)| path_to_local(element).and_then(|local| cx.tcx.hir().find(local)))
- .collect::<Option<Vec<_>>>()
- && let [first, rest @ ..] = &*locals
- && let Node::Pat(first_pat) = first
- && let parent = parent_pat(cx, first_pat).hir_id
- && rest.iter().chain(once(first)).map(|i| (parent, i)).all(predicate)
- {
- return true;
- }
-
- false
-}
diff --git a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
index acdf54710..306ca5724 100644
--- a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
+++ b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
@@ -2,8 +2,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use if_chain::if_chain;
use rustc_errors::Applicability;
-use rustc_hir::{self as hir, GenericArg, GenericBounds, GenericParamKind};
-use rustc_hir::{HirId, Lifetime, MutTy, Mutability, Node, QPath, TyKind};
+use rustc_hir::{
+ self as hir, GenericArg, GenericBounds, GenericParamKind, HirId, Lifetime, MutTy, Mutability, Node, QPath, TyKind,
+};
use rustc_lint::LateContext;
use rustc_span::sym;
diff --git a/src/tools/clippy/clippy_lints/src/types/box_collection.rs b/src/tools/clippy/clippy_lints/src/types/box_collection.rs
index 43665a922..4a5a94f26 100644
--- a/src/tools/clippy/clippy_lints/src/types/box_collection.rs
+++ b/src/tools/clippy/clippy_lints/src/types/box_collection.rs
@@ -1,6 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::{path_def_id, qpath_generic_tys};
-use rustc_hir::{self as hir, def_id::DefId, QPath};
+use rustc_hir::def_id::DefId;
+use rustc_hir::{self as hir, QPath};
use rustc_lint::LateContext;
use rustc_span::{sym, Symbol};
diff --git a/src/tools/clippy/clippy_lints/src/types/linked_list.rs b/src/tools/clippy/clippy_lints/src/types/linked_list.rs
index 5fb708741..fba804bbe 100644
--- a/src/tools/clippy/clippy_lints/src/types/linked_list.rs
+++ b/src/tools/clippy/clippy_lints/src/types/linked_list.rs
@@ -1,5 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_help;
-use rustc_hir::{self as hir, def_id::DefId};
+use rustc_hir::def_id::DefId;
+use rustc_hir::{self as hir};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs
index 3c873a590..79f9d45d5 100644
--- a/src/tools/clippy/clippy_lints/src/types/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/types/mod.rs
@@ -349,7 +349,7 @@ impl<'tcx> LateLintPass<'tcx> for Types {
let is_exported = cx.effective_visibilities.is_exported(item.owner_id.def_id);
match item.kind {
- ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _) => self.check_ty(
+ ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _, _) => self.check_ty(
cx,
ty,
CheckTyContext {
diff --git a/src/tools/clippy/clippy_lints/src/types/option_option.rs b/src/tools/clippy/clippy_lints/src/types/option_option.rs
index 8767e3c30..60622903a 100644
--- a/src/tools/clippy/clippy_lints/src/types/option_option.rs
+++ b/src/tools/clippy/clippy_lints/src/types/option_option.rs
@@ -1,7 +1,8 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::{path_def_id, qpath_generic_tys};
use if_chain::if_chain;
-use rustc_hir::{self as hir, def_id::DefId, QPath};
+use rustc_hir::def_id::DefId;
+use rustc_hir::{self as hir, QPath};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
diff --git a/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs b/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs
index 855137b14..f6c2d8d5a 100644
--- a/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs
+++ b/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs
@@ -2,7 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{path_def_id, qpath_generic_tys};
use rustc_errors::Applicability;
-use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind};
+use rustc_hir::def_id::DefId;
+use rustc_hir::{self as hir, QPath, TyKind};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
@@ -22,7 +23,9 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
app,
);
} else {
- let Some(ty) = qpath_generic_tys(qpath).next() else { return false };
+ let Some(ty) = qpath_generic_tys(qpath).next() else {
+ return false;
+ };
let Some(id) = path_def_id(cx, ty) else { return false };
if !cx.tcx.is_diagnostic_item(sym::Vec, id) {
return false;
diff --git a/src/tools/clippy/clippy_lints/src/types/rc_mutex.rs b/src/tools/clippy/clippy_lints/src/types/rc_mutex.rs
index a75972cf3..a616c3e4e 100644
--- a/src/tools/clippy/clippy_lints/src/types/rc_mutex.rs
+++ b/src/tools/clippy/clippy_lints/src/types/rc_mutex.rs
@@ -1,7 +1,8 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::{path_def_id, qpath_generic_tys};
use if_chain::if_chain;
-use rustc_hir::{self as hir, def_id::DefId, QPath};
+use rustc_hir::def_id::DefId;
+use rustc_hir::{self as hir, QPath};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
diff --git a/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs b/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs
index f7adc9d35..5a986254f 100644
--- a/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs
+++ b/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs
@@ -2,7 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::{path_def_id, qpath_generic_tys};
use rustc_errors::Applicability;
-use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind};
+use rustc_hir::def_id::DefId;
+use rustc_hir::{self as hir, QPath, TyKind};
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::LateContext;
use rustc_middle::ty::TypeVisitableExt;
@@ -39,7 +40,9 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
return true;
}
- let Some(ty) = qpath_generic_tys(qpath).next() else { return false };
+ let Some(ty) = qpath_generic_tys(qpath).next() else {
+ return false;
+ };
let Some(id) = path_def_id(cx, ty) else { return false };
let (inner_sym, ty) = match cx.tcx.get_diagnostic_name(id) {
Some(sym::Arc) => ("Arc", ty),
@@ -49,7 +52,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
};
let TyKind::Path(inner_qpath) = &ty.kind else {
- return false
+ return false;
};
let inner_span = match qpath_generic_tys(inner_qpath).next() {
Some(hir_ty) => {
diff --git a/src/tools/clippy/clippy_lints/src/types/vec_box.rs b/src/tools/clippy/clippy_lints/src/types/vec_box.rs
index d3062f3d2..decc183ad 100644
--- a/src/tools/clippy/clippy_lints/src/types/vec_box.rs
+++ b/src/tools/clippy/clippy_lints/src/types/vec_box.rs
@@ -3,7 +3,8 @@ use clippy_utils::last_path_segment;
use clippy_utils::source::snippet;
use if_chain::if_chain;
use rustc_errors::Applicability;
-use rustc_hir::{self as hir, def_id::DefId, GenericArg, QPath, TyKind};
+use rustc_hir::def_id::DefId;
+use rustc_hir::{self as hir, GenericArg, QPath, TyKind};
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::LateContext;
use rustc_middle::ty::layout::LayoutOf;
diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
index a9deee967..f2ef60201 100644
--- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
+++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
@@ -158,11 +158,12 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
}
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &hir::Stmt<'tcx>) {
- let (
- hir::StmtKind::Local(&hir::Local { init: Some(expr), .. })
- | hir::StmtKind::Expr(expr)
- | hir::StmtKind::Semi(expr)
- ) = stmt.kind else { return };
+ let (hir::StmtKind::Local(&hir::Local { init: Some(expr), .. })
+ | hir::StmtKind::Expr(expr)
+ | hir::StmtKind::Semi(expr)) = stmt.kind
+ else {
+ return;
+ };
if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, stmt.hir_id)
&& !in_external_macro(cx.tcx.sess, stmt.span)
&& let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, stmt.span, stmt.hir_id)
diff --git a/src/tools/clippy/clippy_lints/src/uninit_vec.rs b/src/tools/clippy/clippy_lints/src/uninit_vec.rs
index 1ab0162a8..6756df8e7 100644
--- a/src/tools/clippy/clippy_lints/src/uninit_vec.rs
+++ b/src/tools/clippy/clippy_lints/src/uninit_vec.rs
@@ -88,7 +88,7 @@ fn handle_uninit_vec_pair<'tcx>(
if let Some((set_len_self, call_span)) = extract_set_len_self(cx, maybe_set_len);
if vec.location.eq_expr(cx, set_len_self);
if let ty::Ref(_, vec_ty, _) = cx.typeck_results().expr_ty_adjusted(set_len_self).kind();
- if let ty::Adt(_, substs) = vec_ty.kind();
+ if let ty::Adt(_, args) = vec_ty.kind();
// `#[allow(...)]` attribute can be set on enclosing unsafe block of `set_len()`
if !is_lint_allowed(cx, UNINIT_VEC, maybe_set_len.hir_id);
then {
@@ -96,7 +96,7 @@ fn handle_uninit_vec_pair<'tcx>(
// with_capacity / reserve -> set_len
// Check T of Vec<T>
- if !is_uninit_value_valid_for_ty(cx, substs.type_at(0)) {
+ if !is_uninit_value_valid_for_ty(cx, args.type_at(0)) {
// FIXME: #7698, false positive of the internal lints
#[expect(clippy::collapsible_span_lint_calls)]
span_lint_and_then(
diff --git a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs
index 99a1d1976..dd829ded0 100644
--- a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs
@@ -65,7 +65,7 @@ fn get_projection_pred<'tcx>(
generics.predicates.iter().find_map(|(proj_pred, _)| {
if let ClauseKind::Projection(pred) = proj_pred.kind().skip_binder() {
let projection_pred = cx.tcx.erase_late_bound_regions(proj_pred.kind().rebind(pred));
- if projection_pred.projection_ty.substs == trait_pred.trait_ref.substs {
+ if projection_pred.projection_ty.args == trait_pred.trait_ref.args {
return Some(projection_pred);
}
}
@@ -76,7 +76,7 @@ fn get_projection_pred<'tcx>(
fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Vec<(usize, String)> {
let mut args_to_check = Vec::new();
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
- let fn_sig = cx.tcx.fn_sig(def_id).subst_identity();
+ let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity();
let generics = cx.tcx.predicates_of(def_id);
let fn_mut_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().fn_mut_trait());
let ord_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.get_diagnostic_item(sym::Ord));
@@ -120,8 +120,8 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve
fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Span, Option<Span>)> {
if_chain! {
if let ExprKind::Closure(&Closure { body, fn_decl_span, .. }) = arg.kind;
- if let ty::Closure(_def_id, substs) = &cx.typeck_results().node_type(arg.hir_id).kind();
- let ret_ty = substs.as_closure().sig().output();
+ if let ty::Closure(_def_id, args) = &cx.typeck_results().node_type(arg.hir_id).kind();
+ let ret_ty = args.as_closure().sig().output();
let ty = cx.tcx.erase_late_bound_regions(ret_ty);
if ty.is_unit();
then {
diff --git a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
index cc7c2b039..704d7abd7 100644
--- a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
@@ -161,7 +161,7 @@ fn needs_inferred_result_ty(
},
_ => return false,
};
- let sig = cx.tcx.fn_sig(id).subst_identity().skip_binder();
+ let sig = cx.tcx.fn_sig(id).instantiate_identity().skip_binder();
if let ty::Param(output_ty) = *sig.output().kind() {
let args: Vec<&Expr<'_>> = if let Some(receiver) = receiver {
std::iter::once(receiver).chain(args.iter()).collect()
diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs
index dd120599c..462b1aa81 100644
--- a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs
@@ -42,7 +42,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if cx.typeck_results().expr_ty(arg).is_unit() && !utils::is_unit_literal(arg) {
!matches!(
&arg.kind,
- ExprKind::Match(.., MatchSource::TryDesugar) | ExprKind::Path(..)
+ ExprKind::Match(.., MatchSource::TryDesugar(_)) | ExprKind::Path(..)
)
} else {
false
diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs
index 226495dcb..d4342ec51 100644
--- a/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs
@@ -14,7 +14,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
"assert_ne" | "debug_assert_ne" => "fail",
_ => return,
};
- let Some ((left, _, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return };
+ let Some((left, _, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else {
+ return;
+ };
if !cx.typeck_results().expr_ty(left).is_unit() {
return;
}
diff --git a/src/tools/clippy/clippy_lints/src/unnamed_address.rs b/src/tools/clippy/clippy_lints/src/unnamed_address.rs
index 0f5cdb6aa..dea8a1e35 100644
--- a/src/tools/clippy/clippy_lints/src/unnamed_address.rs
+++ b/src/tools/clippy/clippy_lints/src/unnamed_address.rs
@@ -97,7 +97,7 @@ impl LateLintPass<'_> for UnnamedAddress {
if let ExprKind::Path(ref func_qpath) = func.kind;
if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
if match_def_path(cx, def_id, &paths::PTR_EQ);
- let ty_param = cx.typeck_results().node_substs(func.hir_id).type_at(0);
+ let ty_param = cx.typeck_results().node_args(func.hir_id).type_at(0);
if ty_param.is_trait();
then {
span_lint_and_help(
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs
index e7449639f..ed2ef5063 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs
@@ -1,6 +1,8 @@
-use clippy_utils::{diagnostics::span_lint_and_then, ty::approx_ty_size};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::ty::approx_ty_size;
use rustc_errors::Applicability;
-use rustc_hir::{def_id::LocalDefId, FnDecl, FnRetTy, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind};
+use rustc_hir::def_id::LocalDefId;
+use rustc_hir::{FnDecl, FnRetTy, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Symbol;
@@ -63,7 +65,9 @@ impl UnnecessaryBoxReturns {
return;
}
- let FnRetTy::Return(return_ty_hir) = &decl.output else { return };
+ let FnRetTy::Return(return_ty_hir) = &decl.output else {
+ return;
+ };
let return_ty = cx
.tcx
@@ -103,25 +107,33 @@ impl UnnecessaryBoxReturns {
impl LateLintPass<'_> for UnnecessaryBoxReturns {
fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) {
- let TraitItemKind::Fn(signature, _) = &item.kind else { return };
+ let TraitItemKind::Fn(signature, _) = &item.kind else {
+ return;
+ };
self.check_fn_item(cx, signature.decl, item.owner_id.def_id, item.ident.name);
}
fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::ImplItem<'_>) {
// Ignore implementations of traits, because the lint should be on the
// trait, not on the implementation of it.
- let Node::Item(parent) = cx.tcx.hir().get_parent(item.hir_id()) else { return };
+ let Node::Item(parent) = cx.tcx.hir().get_parent(item.hir_id()) else {
+ return;
+ };
let ItemKind::Impl(parent) = parent.kind else { return };
if parent.of_trait.is_some() {
return;
}
- let ImplItemKind::Fn(signature, ..) = &item.kind else { return };
+ let ImplItemKind::Fn(signature, ..) = &item.kind else {
+ return;
+ };
self.check_fn_item(cx, signature.decl, item.owner_id.def_id, item.ident.name);
}
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
- let ItemKind::Fn(signature, ..) = &item.kind else { return };
+ let ItemKind::Fn(signature, ..) = &item.kind else {
+ return;
+ };
self.check_fn_item(cx, signature.decl, item.owner_id.def_id, item.ident.name);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs b/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs
index 6e802794f..57a4a429e 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs
@@ -1,4 +1,5 @@
-use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_lang_item};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::is_type_lang_item;
use clippy_utils::{match_def_path, paths};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
index 084b03198..f4111186c 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
@@ -1,4 +1,7 @@
-use clippy_utils::{diagnostics::span_lint_and_sugg, get_parent_expr, path_to_local, source::snippet, ty::is_copy};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_copy;
+use clippy_utils::{get_parent_expr, path_to_local};
use rustc_hir::{BindingAnnotation, Expr, ExprKind, Node, PatKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
index 5073eb02b..f34f8d0e3 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
@@ -1,6 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet;
-use clippy_utils::{contains_return, is_res_lang_ctor, path_res, return_ty, visitors::find_all_ret_expressions};
+use clippy_utils::visitors::find_all_ret_expressions;
+use clippy_utils::{contains_return, is_res_lang_ctor, path_res, return_ty};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
index a57bf7ee8..9cf595772 100644
--- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
@@ -6,7 +6,8 @@ use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::over;
use rustc_ast::mut_visit::*;
use rustc_ast::ptr::P;
-use rustc_ast::{self as ast, Mutability, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
+use rustc_ast::PatKind::*;
+use rustc_ast::{self as ast, Mutability, Pat, PatKind, DUMMY_NODE_ID};
use rustc_ast_pretty::pprust;
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
@@ -162,9 +163,7 @@ fn unnest_or_patterns(pat: &mut P<Pat>) -> bool {
noop_visit_pat(p, self);
// Don't have an or-pattern? Just quit early on.
- let Or(alternatives) = &mut p.kind else {
- return
- };
+ let Or(alternatives) = &mut p.kind else { return };
// Collapse or-patterns directly nested in or-patterns.
let mut idx = 0;
diff --git a/src/tools/clippy/clippy_lints/src/unused_async.rs b/src/tools/clippy/clippy_lints/src/unused_async.rs
index 5e42cf7e4..bc7c3897a 100644
--- a/src/tools/clippy/clippy_lints/src/unused_async.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_async.rs
@@ -1,11 +1,12 @@
-use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::is_def_id_trait_method;
+use rustc_hir::def::DefKind;
use rustc_hir::intravisit::{walk_body, walk_expr, walk_fn, FnKind, Visitor};
-use rustc_hir::{Body, Expr, ExprKind, FnDecl, YieldSource};
+use rustc_hir::{Body, Expr, ExprKind, FnDecl, Node, YieldSource};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::def_id::LocalDefId;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::def_id::{LocalDefId, LocalDefIdSet};
use rustc_span::Span;
declare_clippy_lint! {
@@ -38,7 +39,24 @@ declare_clippy_lint! {
"finds async functions with no await statements"
}
-declare_lint_pass!(UnusedAsync => [UNUSED_ASYNC]);
+#[derive(Default)]
+pub struct UnusedAsync {
+ /// Keeps track of async functions used as values (i.e. path expressions to async functions that
+ /// are not immediately called)
+ async_fns_as_value: LocalDefIdSet,
+ /// Functions with unused `async`, linted post-crate after we've found all uses of local async
+ /// functions
+ unused_async_fns: Vec<UnusedAsyncFn>,
+}
+
+#[derive(Copy, Clone)]
+struct UnusedAsyncFn {
+ def_id: LocalDefId,
+ fn_span: Span,
+ await_in_async_block: Option<Span>,
+}
+
+impl_lint_pass!(UnusedAsync => [UNUSED_ASYNC]);
struct AsyncFnVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
@@ -101,24 +119,70 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync {
};
walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), def_id);
if !visitor.found_await {
- span_lint_and_then(
- cx,
- UNUSED_ASYNC,
- span,
- "unused `async` for function with no await statements",
- |diag| {
- diag.help("consider removing the `async` from this function");
-
- if let Some(span) = visitor.await_in_async_block {
- diag.span_note(
- span,
- "`await` used in an async block, which does not require \
- the enclosing function to be `async`",
- );
- }
- },
- );
+ // Don't lint just yet, but store the necessary information for later.
+ // The actual linting happens in `check_crate_post`, once we've found all
+ // uses of local async functions that do require asyncness to pass typeck
+ self.unused_async_fns.push(UnusedAsyncFn {
+ await_in_async_block: visitor.await_in_async_block,
+ fn_span: span,
+ def_id,
+ });
}
}
}
+
+ fn check_path(&mut self, cx: &LateContext<'tcx>, path: &rustc_hir::Path<'tcx>, hir_id: rustc_hir::HirId) {
+ fn is_node_func_call(node: Node<'_>, expected_receiver: Span) -> bool {
+ matches!(
+ node,
+ Node::Expr(Expr {
+ kind: ExprKind::Call(Expr { span, .. }, _) | ExprKind::MethodCall(_, Expr { span, .. }, ..),
+ ..
+ }) if *span == expected_receiver
+ )
+ }
+
+ // Find paths to local async functions that aren't immediately called.
+ // E.g. `async fn f() {}; let x = f;`
+ // Depending on how `x` is used, f's asyncness might be required despite not having any `await`
+ // statements, so don't lint at all if there are any such paths.
+ if let Some(def_id) = path.res.opt_def_id()
+ && let Some(local_def_id) = def_id.as_local()
+ && let Some(DefKind::Fn) = cx.tcx.opt_def_kind(def_id)
+ && cx.tcx.asyncness(def_id).is_async()
+ && !is_node_func_call(cx.tcx.hir().get_parent(hir_id), path.span)
+ {
+ self.async_fns_as_value.insert(local_def_id);
+ }
+ }
+
+ // After collecting all unused `async` and problematic paths to such functions,
+ // lint those unused ones that didn't have any path expressions to them.
+ fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
+ let iter = self
+ .unused_async_fns
+ .iter()
+ .filter(|UnusedAsyncFn { def_id, .. }| (!self.async_fns_as_value.contains(def_id)));
+
+ for fun in iter {
+ span_lint_hir_and_then(
+ cx,
+ UNUSED_ASYNC,
+ cx.tcx.local_def_id_to_hir_id(fun.def_id),
+ fun.fn_span,
+ "unused `async` for function with no await statements",
+ |diag| {
+ diag.help("consider removing the `async` from this function");
+
+ if let Some(span) = fun.await_in_async_block {
+ diag.span_note(
+ span,
+ "`await` used in an async block, which does not require \
+ the enclosing function to be `async`",
+ );
+ }
+ },
+ );
+ }
+ }
}
diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
index 0e526c216..0fcb62017 100644
--- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
@@ -48,7 +48,7 @@ declare_lint_pass!(UnusedIoAmount => [UNUSED_IO_AMOUNT]);
impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
let (hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr)) = s.kind else {
- return
+ return;
};
match expr.kind {
diff --git a/src/tools/clippy/clippy_lints/src/unused_unit.rs b/src/tools/clippy/clippy_lints/src/unused_unit.rs
index cad8da18c..95e74718d 100644
--- a/src/tools/clippy/clippy_lints/src/unused_unit.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_unit.rs
@@ -1,7 +1,8 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{position_before_rarrow, snippet_opt};
use if_chain::if_chain;
-use rustc_ast::{ast, visit::FnKind, ClosureBinder};
+use rustc_ast::visit::FnKind;
+use rustc_ast::{ast, ClosureBinder};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs
index 377d3fb6f..c99b0290c 100644
--- a/src/tools/clippy/clippy_lints/src/unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/unwrap.rs
@@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_hir_and_then;
-use clippy_utils::higher;
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{path_to_local, usage::is_potentially_mutated};
+use clippy_utils::usage::is_potentially_mutated;
+use clippy_utils::{higher, path_to_local};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor};
diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs
index 5a0298745..50231d930 100644
--- a/src/tools/clippy/clippy_lints/src/use_self.rs
+++ b/src/tools/clippy/clippy_lints/src/use_self.rs
@@ -5,13 +5,12 @@ use clippy_utils::ty::same_type_and_consts;
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
+use rustc_hir::def::{CtorOf, DefKind, Res};
+use rustc_hir::def_id::LocalDefId;
+use rustc_hir::intravisit::{walk_inf, walk_ty, Visitor};
use rustc_hir::{
- self as hir,
- def::{CtorOf, DefKind, Res},
- def_id::LocalDefId,
- intravisit::{walk_inf, walk_ty, Visitor},
- Expr, ExprKind, FnRetTy, FnSig, GenericArg, GenericArgsParentheses, GenericParam, GenericParamKind, HirId, Impl,
- ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind,
+ self as hir, Expr, ExprKind, FnRetTy, FnSig, GenericArg, GenericArgsParentheses, GenericParam, GenericParamKind,
+ HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind,
};
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::{LateContext, LateLintPass};
@@ -145,7 +144,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
then {
// `self_ty` is the semantic self type of `impl <trait> for <type>`. This cannot be
// `Self`.
- let self_ty = impl_trait_ref.subst_identity().self_ty();
+ let self_ty = impl_trait_ref.instantiate_identity().self_ty();
// `trait_method_sig` is the signature of the function, how it is declared in the
// trait, not in the impl of the trait.
@@ -154,7 +153,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
.associated_item(impl_item.owner_id)
.trait_item_def_id
.expect("impl method matches a trait method");
- let trait_method_sig = cx.tcx.fn_sig(trait_method).subst_identity();
+ let trait_method_sig = cx.tcx.fn_sig(trait_method).instantiate_identity();
let trait_method_sig = cx.tcx.erase_late_bound_regions(trait_method_sig);
// `impl_inputs_outputs` is an iterator over the types (`hir::Ty`) declared in the
@@ -226,7 +225,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
} else {
hir_ty_to_ty(cx.tcx, hir_ty)
};
- if same_type_and_consts(ty, cx.tcx.type_of(impl_id).subst_identity());
+ if same_type_and_consts(ty, cx.tcx.type_of(impl_id).instantiate_identity());
then {
span_lint(cx, hir_ty.span);
}
@@ -238,7 +237,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
if !expr.span.from_expansion();
if self.msrv.meets(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).subst_identity();
+ if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id).instantiate_identity();
then {} else { return; }
}
match expr.kind {
@@ -262,7 +261,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
if let PatKind::Path(QPath::Resolved(_, path))
| PatKind::TupleStruct(QPath::Resolved(_, path), _, _)
| PatKind::Struct(QPath::Resolved(_, path), _, _) = pat.kind;
- if cx.typeck_results().pat_ty(pat) == cx.tcx.type_of(impl_id).subst_identity();
+ if cx.typeck_results().pat_ty(pat) == cx.tcx.type_of(impl_id).instantiate_identity();
then {
check_path(cx, path);
}
diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs
index 22de383ea..5ac4f0aa4 100644
--- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs
+++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs
@@ -1,9 +1,8 @@
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::is_ty_alias;
use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts};
-use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, path_to_local, paths};
+use clippy_utils::{get_parent_expr, is_trait_method, is_ty_alias, match_def_path, path_to_local, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::def::DefKind;
@@ -117,9 +116,9 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
}
match e.kind {
- ExprKind::Match(_, arms, MatchSource::TryDesugar) => {
+ ExprKind::Match(_, arms, MatchSource::TryDesugar(_)) => {
let (ExprKind::Ret(Some(e)) | ExprKind::Break(_, Some(e))) = arms[0].body.kind else {
- return
+ return;
};
if let ExprKind::Call(_, [arg, ..]) = e.kind {
self.try_desugar_arm.push(arg.hir_id);
@@ -236,8 +235,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
let a = cx.typeck_results().expr_ty(e);
let b = cx.typeck_results().expr_ty(recv);
if is_type_diagnostic_item(cx, a, sym::Result);
- if let ty::Adt(_, substs) = a.kind();
- if let Some(a_type) = substs.types().next();
+ if let ty::Adt(_, args) = a.kind();
+ if let Some(a_type) = args.types().next();
if same_type_and_consts(a_type, b);
then {
@@ -264,8 +263,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
if_chain! {
if match_def_path(cx, def_id, &paths::TRY_FROM);
if is_type_diagnostic_item(cx, a, sym::Result);
- if let ty::Adt(_, substs) = a.kind();
- if let Some(a_type) = substs.types().next();
+ if let ty::Adt(_, args) = a.kind();
+ if let Some(a_type) = args.types().next();
if same_type_and_consts(a_type, b);
then {
diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs
index 6b51974d7..f02c33cc6 100644
--- a/src/tools/clippy/clippy_lints/src/utils/author.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/author.rs
@@ -526,7 +526,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
self.ident(field_name);
self.expr(object);
},
- ExprKind::Index(object, index) => {
+ ExprKind::Index(object, index, _) => {
bind!(self, object, index);
kind!("Index({object}, {index})");
self.expr(object);
diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs
index f1d05c752..58ae0656d 100644
--- a/src/tools/clippy/clippy_lints/src/utils/conf.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs
@@ -319,7 +319,7 @@ define_Conf! {
/// Lint: DISALLOWED_NAMES.
///
/// The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value
- /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the
+ /// `".."` can be used as part of the list to indicate that the configured values should be appended to the
/// default configuration of Clippy. By default, any configuration will replace the default value.
(disallowed_names: Vec<String> = super::DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect()),
/// Lint: SEMICOLON_INSIDE_BLOCK.
@@ -551,6 +551,16 @@ define_Conf! {
///
/// Whether to allow `r#""#` when `r""` can be used
(allow_one_hash_in_raw_strings: bool = false),
+ /// Lint: ABSOLUTE_PATHS.
+ ///
+ /// The maximum number of segments a path can have before being linted, anything above this will
+ /// be linted.
+ (absolute_paths_max_segments: u64 = 2),
+ /// Lint: ABSOLUTE_PATHS.
+ ///
+ /// Which crates to allow absolute paths from
+ (absolute_paths_allowed_crates: rustc_data_structures::fx::FxHashSet<String> =
+ rustc_data_structures::fx::FxHashSet::default()),
}
/// Search for the configuration file.
diff --git a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
index 09fcb82c3..6d3493523 100644
--- a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
@@ -71,7 +71,9 @@ fn has_span_from_proc_macro(cx: &EarlyContext<'_>, args: &FormatArgs) -> bool {
for between_span in between_spans {
let mut seen_comma = false;
- let Some(snippet) = snippet_opt(cx, between_span) else { return true };
+ let Some(snippet) = snippet_opt(cx, between_span) else {
+ return true;
+ };
for token in tokenize(&snippet) {
match token.kind {
TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace => {},
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/if_chain_style.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/if_chain_style.rs
index 883a5c08e..fe2f12fe8 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/if_chain_style.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/if_chain_style.rs
@@ -46,7 +46,9 @@ impl<'tcx> LateLintPass<'tcx> for IfChainStyle {
} else {
return;
};
- let ExprKind::Block(then_block, _) = then.kind else { return };
+ let ExprKind::Block(then_block, _) = then.kind else {
+ return;
+ };
let if_chain_span = is_expn_of(expr.span, "if_chain");
if !els {
check_nested_if_chains(cx, expr, then_block, if_chain_span);
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs
index dced9fcf9..da8654d93 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs
@@ -78,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
for item in cx.tcx.module_children(def_id) {
if_chain! {
if let Res::Def(DefKind::Const, item_def_id) = item.res;
- let ty = cx.tcx.type_of(item_def_id).subst_identity();
+ let ty = cx.tcx.type_of(item_def_id).instantiate_identity();
if match_type(cx, ty, &paths::SYMBOL);
if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id);
if let Ok(value) = value.to_u32();
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs
index 9afe02c1e..4ed985f54 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs
@@ -7,7 +7,8 @@ use rustc_hir::def::DefKind;
use rustc_hir::Item;
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{self, fast_reject::SimplifiedType, FloatTy};
+use rustc_middle::ty::fast_reject::SimplifiedType;
+use rustc_middle::ty::{self, FloatTy};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::Symbol;
@@ -33,7 +34,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
if_chain! {
if mod_name.as_str() == "paths";
- if let hir::ItemKind::Const(ty, body_id) = item.kind;
+ if let hir::ItemKind::Const(ty, _, body_id) = item.kind;
let ty = hir_ty_to_ty(cx.tcx, ty);
if let ty::Array(el_ty, _) = &ty.kind();
if let ty::Ref(_, el_ty, _) = &el_ty.kind();
@@ -73,10 +74,10 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
let lang_items = cx.tcx.lang_items();
// This list isn't complete, but good enough for our current list of paths.
let incoherent_impls = [
- SimplifiedType::FloatSimplifiedType(FloatTy::F32),
- SimplifiedType::FloatSimplifiedType(FloatTy::F64),
- SimplifiedType::SliceSimplifiedType,
- SimplifiedType::StrSimplifiedType,
+ SimplifiedType::Float(FloatTy::F32),
+ SimplifiedType::Float(FloatTy::F64),
+ SimplifiedType::Slice,
+ SimplifiedType::Str,
]
.iter()
.flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter().copied());
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
index f71820765..87380f14f 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
@@ -3,10 +3,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
use clippy_utils::macros::root_macro_call_first_node;
use clippy_utils::{is_lint_allowed, match_def_path, paths};
use if_chain::if_chain;
-use rustc_ast as ast;
use rustc_ast::ast::LitKind;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::hir_id::CRATE_HIR_ID;
use rustc_hir::intravisit::Visitor;
@@ -18,6 +16,7 @@ use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Spanned;
use rustc_span::symbol::Symbol;
use rustc_span::{sym, Span};
+use {rustc_ast as ast, rustc_hir as hir};
declare_clippy_lint! {
/// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
index 107a62806..f49c3fadb 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
@@ -8,11 +8,8 @@
//! a simple mistake)
use crate::renamed_lints::RENAMED_LINTS;
-use crate::utils::{
- collect_configs,
- internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type},
- ClippyConfiguration,
-};
+use crate::utils::internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type};
+use crate::utils::{collect_configs, ClippyConfiguration};
use clippy_utils::diagnostics::span_lint;
use clippy_utils::ty::{match_type, walk_ptrs_ty_depth};
@@ -21,22 +18,22 @@ use if_chain::if_chain;
use itertools::Itertools;
use rustc_ast as ast;
use rustc_data_structures::fx::FxHashMap;
-use rustc_hir::{
- self as hir, def::DefKind, intravisit, intravisit::Visitor, Closure, ExprKind, Item, ItemKind, Mutability, QPath,
-};
+use rustc_hir::def::DefKind;
+use rustc_hir::intravisit::Visitor;
+use rustc_hir::{self as hir, intravisit, Closure, ExprKind, Item, ItemKind, Mutability, QPath};
use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId};
use rustc_middle::hir::nested_filter;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::Ident;
use rustc_span::{sym, Loc, Span, Symbol};
-use serde::{ser::SerializeStruct, Serialize, Serializer};
+use serde::ser::SerializeStruct;
+use serde::{Serialize, Serializer};
use std::collections::{BTreeSet, BinaryHeap};
use std::fmt;
use std::fmt::Write as _;
use std::fs::{self, OpenOptions};
use std::io::prelude::*;
-use std::path::Path;
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
use std::process::Command;
/// This is the json output file of the lint collector.
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs
index 09f0f0d0a..bf835f89c 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs
@@ -7,7 +7,7 @@ use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::ty::{self, subst::GenericArgKind};
+use rustc_middle::ty::{self, GenericArgKind};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
@@ -39,7 +39,7 @@ impl LateLintPass<'_> for MsrvAttrImpl {
if self_ty_def.all_fields().any(|f| {
cx.tcx
.type_of(f.did)
- .subst_identity()
+ .instantiate_identity()
.walk()
.filter(|t| matches!(t.unpack(), GenericArgKind::Type(_)))
.any(|t| match_type(cx, t.expect_ty(), &paths::MSRV))
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs
index 008423766..f66f33fee 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs
@@ -229,11 +229,11 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Ve
Res::Def(DefKind::Static(_), def_id) => read_mir_alloc_def_path(
cx,
cx.tcx.eval_static_initializer(def_id).ok()?.inner(),
- cx.tcx.type_of(def_id).subst_identity(),
+ cx.tcx.type_of(def_id).instantiate_identity(),
),
Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? {
ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => {
- read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id).subst_identity())
+ read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id).instantiate_identity())
},
_ => None,
},
diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs
index fb0825693..4fef8c071 100644
--- a/src/tools/clippy/clippy_lints/src/utils/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs
@@ -18,7 +18,7 @@ const BOOK_CONFIGS_PATH: &str = "https://doc.rust-lang.org/clippy/lint_configura
// ==================================================================
// Configuration
// ==================================================================
-#[derive(Debug, Clone, Default)] //~ ERROR no such field
+#[derive(Debug, Clone, Default)]
pub struct ClippyConfiguration {
pub name: String,
#[allow(dead_code)]
diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs
index 2a594e750..fc17e7c6d 100644
--- a/src/tools/clippy/clippy_lints/src/vec.rs
+++ b/src/tools/clippy/clippy_lints/src/vec.rs
@@ -154,6 +154,10 @@ impl UselessVec {
span: Span,
suggest_slice: SuggestedType,
) {
+ if span.from_expansion() {
+ return;
+ }
+
let mut applicability = Applicability::MachineApplicable;
let snippet = match *vec_args {
@@ -181,7 +185,7 @@ impl UselessVec {
if args.len() as u64 * size_of(cx, last) > self.too_large_for_stack {
return;
}
- let span = args[0].span.to(last.span);
+ let span = args[0].span.source_callsite().to(last.span.source_callsite());
let args = snippet_with_applicability(cx, span, "..", &mut applicability);
match suggest_slice {
@@ -230,8 +234,8 @@ fn size_of(cx: &LateContext<'_>, expr: &Expr<'_>) -> u64 {
/// Returns the item type of the vector (i.e., the `T` in `Vec<T>`).
fn vec_type(ty: Ty<'_>) -> Ty<'_> {
- if let ty::Adt(_, substs) = ty.kind() {
- substs.type_at(0)
+ if let ty::Adt(_, args) = ty.kind() {
+ args.type_at(0)
} else {
panic!("The type of `vec!` is a not a struct?");
}
diff --git a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
index bd5be0c9d..3fa51216c 100644
--- a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
+++ b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
@@ -74,7 +74,7 @@ impl VecPushSearcher {
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(())
+ return ControlFlow::Continue(());
};
let adjusted_ty = cx.typeck_results().expr_ty_adjusted(e);
let adjusted_mut = adjusted_ty.ref_mutability().unwrap_or(Mutability::Not);
@@ -88,7 +88,7 @@ impl VecPushSearcher {
let mut last_place = parent;
while let Some(parent) = get_parent_expr(cx, last_place) {
if matches!(parent.kind, ExprKind::Unary(UnOp::Deref, _) | ExprKind::Field(..))
- || matches!(parent.kind, ExprKind::Index(e, _) if e.hir_id == last_place.hir_id)
+ || matches!(parent.kind, ExprKind::Index(e, _, _) if e.hir_id == last_place.hir_id)
{
last_place = parent;
} else {
diff --git a/src/tools/clippy/clippy_lints/src/visibility.rs b/src/tools/clippy/clippy_lints/src/visibility.rs
index 43248bccc..496376520 100644
--- a/src/tools/clippy/clippy_lints/src/visibility.rs
+++ b/src/tools/clippy/clippy_lints/src/visibility.rs
@@ -1,10 +1,12 @@
-use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet_opt};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_opt;
use rustc_ast::ast::{Item, VisibilityKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::{symbol::kw, Span};
+use rustc_span::symbol::kw;
+use rustc_span::Span;
declare_clippy_lint! {
/// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
index 2a3d86988..d09d02a7d 100644
--- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
@@ -3,10 +3,8 @@ use clippy_utils::is_test_module_or_function;
use clippy_utils::source::{snippet, snippet_with_applicability};
use if_chain::if_chain;
use rustc_errors::Applicability;
-use rustc_hir::{
- def::{DefKind, Res},
- Item, ItemKind, PathSegment, UseKind,
-};
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::{Item, ItemKind, PathSegment, UseKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index f194dc5d4..a9957b18a 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -272,9 +272,15 @@ impl<'tcx> LateLintPass<'tcx> for Write {
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
- let Some(diag_name) = cx.tcx.get_diagnostic_name(macro_call.def_id) else { return };
- let Some(name) = diag_name.as_str().strip_suffix("_macro") else { return };
+ let Some(macro_call) = root_macro_call_first_node(cx, expr) else {
+ return;
+ };
+ let Some(diag_name) = cx.tcx.get_diagnostic_name(macro_call.def_id) else {
+ return;
+ };
+ let Some(name) = diag_name.as_str().strip_suffix("_macro") else {
+ return;
+ };
let is_build_script = cx
.sess()
@@ -343,7 +349,9 @@ fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
}
fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) {
- let Some(FormatArgsPiece::Literal(last)) = format_args.template.last() else { return };
+ let Some(FormatArgsPiece::Literal(last)) = format_args.template.last() else {
+ return;
+ };
let count_vertical_whitespace = || {
format_args
@@ -379,7 +387,9 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &Ma
&format!("using `{name}!()` with a format string that ends in a single newline"),
|diag| {
let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!');
- let Some(format_snippet) = snippet_opt(cx, format_string_span) else { return };
+ let Some(format_snippet) = snippet_opt(cx, format_string_span) else {
+ return;
+ };
if format_args.template.len() == 1 && last.as_str() == "\n" {
// print!("\n"), write!(f, "\n")
@@ -522,7 +532,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
{
let replacement = replacement.replace('{', "{{").replace('}', "}}");
diag.multipart_suggestion(
- "try this",
+ "try",
vec![(*placeholder_span, replacement), (removal_span, String::new())],
Applicability::MachineApplicable,
);
diff --git a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs
index 93e4b023c..002304f88 100644
--- a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs
+++ b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs
@@ -51,8 +51,8 @@ impl LateLintPass<'_> for ZeroSizedMapValues {
if !in_trait_impl(cx, hir_ty.hir_id);
let ty = ty_from_hir_ty(cx, hir_ty);
if is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap);
- if let Adt(_, substs) = ty.kind();
- let ty = substs.type_at(1);
+ if let Adt(_, args) = ty.kind();
+ let ty = args.type_at(1);
// Fixes https://github.com/rust-lang/rust-clippy/issues/7447 because of
// https://github.com/rust-lang/rust/blob/master/compiler/rustc_middle/src/ty/sty.rs#L968
if !ty.has_escaping_bound_vars();